From 0b15f4f5e2f3caccf83ded26c3c3d6bb27b7eab7 Mon Sep 17 00:00:00 2001 From: df51d <122315498+df51d@users.noreply.github.com> Date: Tue, 23 May 2023 04:33:44 +0200 Subject: [PATCH 0001/1371] change: rename `core` crate to `service` in examples (#1662) * change: rename `core` crate to `service` in examples * fix(jsonrpsee-example): broken dependency --- examples/actix3_example/Cargo.toml | 2 +- examples/actix3_example/README.md | 6 +- examples/actix3_example/api/Cargo.toml | 2 +- examples/actix3_example/api/src/lib.rs | 2 +- .../service}/Cargo.toml | 2 +- .../{core => service}/src/lib.rs | 0 .../{core => service}/src/mutation.rs | 0 .../{core => service}/src/query.rs | 0 .../service}/tests/mock.rs | 2 +- .../{core => service}/tests/prepare.rs | 0 examples/actix_example/Cargo.toml | 2 +- examples/actix_example/README.md | 6 +- examples/actix_example/api/Cargo.toml | 2 +- examples/actix_example/api/src/lib.rs | 2 +- .../core => actix_example/service}/Cargo.toml | 2 +- .../{core => service}/src/lib.rs | 0 .../{core => service}/src/mutation.rs | 0 .../{core => service}/src/query.rs | 0 .../service}/tests/mock.rs | 2 +- .../{core => service}/tests/prepare.rs | 0 examples/axum_example/Cargo.toml | 2 +- examples/axum_example/README.md | 6 +- examples/axum_example/api/Cargo.toml | 2 +- examples/axum_example/api/src/lib.rs | 2 +- .../axum_example/{core => service}/Cargo.toml | 2 +- .../axum_example/{core => service}/src/lib.rs | 0 .../{core => service}/src/mutation.rs | 0 .../{core => service}/src/query.rs | 0 .../{core => service}/tests/mock.rs | 2 +- .../{core => service}/tests/prepare.rs | 0 examples/graphql_example/Cargo.toml | 2 +- examples/graphql_example/README.md | 6 +- examples/graphql_example/api/Cargo.toml | 2 +- examples/graphql_example/api/src/db.rs | 4 +- .../api/src/graphql/mutation/note.rs | 2 +- .../api/src/graphql/query/note.rs | 2 +- .../{core => service}/Cargo.toml | 2 +- .../{core => service}/src/lib.rs | 0 .../{core => service}/src/mutation.rs | 0 .../{core => service}/src/query.rs | 0 .../{core => service}/tests/mock.rs | 2 +- .../{core => service}/tests/prepare.rs | 0 examples/jsonrpsee_example/Cargo.toml | 2 +- examples/jsonrpsee_example/README.md | 6 +- examples/jsonrpsee_example/api/Cargo.toml | 2 +- examples/jsonrpsee_example/api/src/lib.rs | 4 +- .../{core => service}/Cargo.toml | 2 +- .../{core => service}/src/lib.rs | 0 .../{core => service}/src/mutation.rs | 0 .../{core => service}/src/query.rs | 0 .../jsonrpsee_example/service/tests/mock.rs | 79 +++++++++++++++++++ .../{core => service}/tests/prepare.rs | 0 examples/poem_example/Cargo.toml | 2 +- examples/poem_example/README.md | 6 +- examples/poem_example/api/Cargo.toml | 2 +- examples/poem_example/api/src/lib.rs | 2 +- .../poem_example/{core => service}/Cargo.toml | 2 +- .../poem_example/{core => service}/src/lib.rs | 0 .../{core => service}/src/mutation.rs | 0 .../{core => service}/src/query.rs | 0 .../service}/tests/mock.rs | 2 +- .../{core => service}/tests/prepare.rs | 0 examples/rocket_example/Cargo.toml | 2 +- examples/rocket_example/README.md | 6 +- examples/rocket_example/api/Cargo.toml | 2 +- examples/rocket_example/api/src/lib.rs | 2 +- examples/rocket_example/api/src/pool.rs | 2 +- examples/rocket_example/core/tests/mock.rs | 79 ------------------- .../{core => service}/Cargo.toml | 2 +- .../{core => service}/src/lib.rs | 0 .../{core => service}/src/mutation.rs | 0 .../{core => service}/src/query.rs | 0 examples/rocket_example/service/tests/mock.rs | 79 +++++++++++++++++++ .../{core => service}/tests/prepare.rs | 0 examples/rocket_okapi_example/Cargo.toml | 2 +- examples/rocket_okapi_example/README.md | 2 +- examples/rocket_okapi_example/api/Cargo.toml | 2 +- .../api/src/okapi_example.rs | 2 +- examples/rocket_okapi_example/api/src/pool.rs | 2 +- .../rocket_okapi_example/core/tests/mock.rs | 79 ------------------- .../{core => service}/Cargo.toml | 2 +- .../{core => service}/src/lib.rs | 0 .../{core => service}/src/mutation.rs | 0 .../{core => service}/src/query.rs | 0 .../service/tests/mock.rs | 79 +++++++++++++++++++ .../{core => service}/tests/prepare.rs | 0 examples/salvo_example/Cargo.toml | 2 +- examples/salvo_example/README.md | 6 +- examples/salvo_example/api/Cargo.toml | 2 +- examples/salvo_example/api/src/lib.rs | 2 +- examples/salvo_example/core/tests/mock.rs | 79 ------------------- .../{core => service}/Cargo.toml | 2 +- .../{core => service}/src/lib.rs | 0 .../{core => service}/src/mutation.rs | 0 .../{core => service}/src/query.rs | 0 .../service}/tests/mock.rs | 2 +- .../{core => service}/tests/prepare.rs | 0 examples/tonic_example/Cargo.toml | 2 +- examples/tonic_example/README.md | 4 +- examples/tonic_example/api/Cargo.toml | 2 +- examples/tonic_example/api/src/lib.rs | 2 +- examples/tonic_example/core/tests/mock.rs | 79 ------------------- .../{core => service}/Cargo.toml | 2 +- .../{core => service}/src/lib.rs | 0 .../{core => service}/src/mutation.rs | 0 .../{core => service}/src/query.rs | 0 examples/tonic_example/service/tests/mock.rs | 79 +++++++++++++++++++ .../{core => service}/tests/prepare.rs | 0 issues/249/Cargo.toml | 2 +- issues/249/app/Cargo.toml | 2 +- issues/249/app/src/main.rs | 2 +- issues/249/{core => service}/Cargo.toml | 2 +- issues/249/{core => service}/src/lib.rs | 0 113 files changed, 399 insertions(+), 399 deletions(-) rename examples/{actix_example/core => actix3_example/service}/Cargo.toml (95%) rename examples/actix3_example/{core => service}/src/lib.rs (100%) rename examples/actix3_example/{core => service}/src/mutation.rs (100%) rename examples/actix3_example/{core => service}/src/query.rs (100%) rename examples/{jsonrpsee_example/core => actix3_example/service}/tests/mock.rs (97%) rename examples/actix3_example/{core => service}/tests/prepare.rs (100%) rename examples/{actix3_example/core => actix_example/service}/Cargo.toml (95%) rename examples/actix_example/{core => service}/src/lib.rs (100%) rename examples/actix_example/{core => service}/src/mutation.rs (100%) rename examples/actix_example/{core => service}/src/query.rs (100%) rename examples/{poem_example/core => actix_example/service}/tests/mock.rs (97%) rename examples/actix_example/{core => service}/tests/prepare.rs (100%) rename examples/axum_example/{core => service}/Cargo.toml (95%) rename examples/axum_example/{core => service}/src/lib.rs (100%) rename examples/axum_example/{core => service}/src/mutation.rs (100%) rename examples/axum_example/{core => service}/src/query.rs (100%) rename examples/axum_example/{core => service}/tests/mock.rs (97%) rename examples/axum_example/{core => service}/tests/prepare.rs (100%) rename examples/graphql_example/{core => service}/Cargo.toml (94%) rename examples/graphql_example/{core => service}/src/lib.rs (100%) rename examples/graphql_example/{core => service}/src/mutation.rs (100%) rename examples/graphql_example/{core => service}/src/query.rs (100%) rename examples/graphql_example/{core => service}/tests/mock.rs (97%) rename examples/graphql_example/{core => service}/tests/prepare.rs (100%) rename examples/jsonrpsee_example/{core => service}/Cargo.toml (94%) rename examples/jsonrpsee_example/{core => service}/src/lib.rs (100%) rename examples/jsonrpsee_example/{core => service}/src/mutation.rs (100%) rename examples/jsonrpsee_example/{core => service}/src/query.rs (100%) create mode 100644 examples/jsonrpsee_example/service/tests/mock.rs rename examples/jsonrpsee_example/{core => service}/tests/prepare.rs (100%) rename examples/poem_example/{core => service}/Cargo.toml (95%) rename examples/poem_example/{core => service}/src/lib.rs (100%) rename examples/poem_example/{core => service}/src/mutation.rs (100%) rename examples/poem_example/{core => service}/src/query.rs (100%) rename examples/{actix3_example/core => poem_example/service}/tests/mock.rs (97%) rename examples/poem_example/{core => service}/tests/prepare.rs (100%) delete mode 100644 examples/rocket_example/core/tests/mock.rs rename examples/rocket_example/{core => service}/Cargo.toml (94%) rename examples/rocket_example/{core => service}/src/lib.rs (100%) rename examples/rocket_example/{core => service}/src/mutation.rs (100%) rename examples/rocket_example/{core => service}/src/query.rs (100%) create mode 100644 examples/rocket_example/service/tests/mock.rs rename examples/rocket_example/{core => service}/tests/prepare.rs (100%) delete mode 100644 examples/rocket_okapi_example/core/tests/mock.rs rename examples/rocket_okapi_example/{core => service}/Cargo.toml (94%) rename examples/rocket_okapi_example/{core => service}/src/lib.rs (100%) rename examples/rocket_okapi_example/{core => service}/src/mutation.rs (100%) rename examples/rocket_okapi_example/{core => service}/src/query.rs (100%) create mode 100644 examples/rocket_okapi_example/service/tests/mock.rs rename examples/rocket_okapi_example/{core => service}/tests/prepare.rs (100%) delete mode 100644 examples/salvo_example/core/tests/mock.rs rename examples/salvo_example/{core => service}/Cargo.toml (95%) rename examples/salvo_example/{core => service}/src/lib.rs (100%) rename examples/salvo_example/{core => service}/src/mutation.rs (100%) rename examples/salvo_example/{core => service}/src/query.rs (100%) rename examples/{actix_example/core => salvo_example/service}/tests/mock.rs (97%) rename examples/salvo_example/{core => service}/tests/prepare.rs (100%) delete mode 100644 examples/tonic_example/core/tests/mock.rs rename examples/tonic_example/{core => service}/Cargo.toml (95%) rename examples/tonic_example/{core => service}/src/lib.rs (100%) rename examples/tonic_example/{core => service}/src/mutation.rs (100%) rename examples/tonic_example/{core => service}/src/query.rs (100%) create mode 100644 examples/tonic_example/service/tests/mock.rs rename examples/tonic_example/{core => service}/tests/prepare.rs (100%) rename issues/249/{core => service}/Cargo.toml (92%) rename issues/249/{core => service}/src/lib.rs (100%) diff --git a/examples/actix3_example/Cargo.toml b/examples/actix3_example/Cargo.toml index a449de4ca4..c6ad416ad9 100644 --- a/examples/actix3_example/Cargo.toml +++ b/examples/actix3_example/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" publish = false [workspace] -members = [".", "api", "core", "entity", "migration"] +members = [".", "api", "service", "entity", "migration"] [dependencies] actix3-example-api = { path = "api" } diff --git a/examples/actix3_example/README.md b/examples/actix3_example/README.md index 4726382613..0e65f57680 100644 --- a/examples/actix3_example/README.md +++ b/examples/actix3_example/README.md @@ -6,7 +6,7 @@ 1. Modify the `DATABASE_URL` var in `.env` to point to your chosen database -1. Turn on the appropriate database feature for your chosen db in `core/Cargo.toml` (the `"sqlx-mysql",` line) +1. Turn on the appropriate database feature for your chosen db in `service/Cargo.toml` (the `"sqlx-mysql",` line) 1. Execute `cargo run` to start the server @@ -19,9 +19,9 @@ cargo install systemfd systemfd --no-pid -s http::8000 -- cargo watch -x run ``` -Run mock test on the core logic crate: +Run mock test on the service logic crate: ```bash -cd core +cd service cargo test --features mock ``` diff --git a/examples/actix3_example/api/Cargo.toml b/examples/actix3_example/api/Cargo.toml index 63540ec988..7d17eab291 100644 --- a/examples/actix3_example/api/Cargo.toml +++ b/examples/actix3_example/api/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" publish = false [dependencies] -actix3-example-core = { path = "../core" } +actix3-example-service = { path = "../service" } actix-http = "2" actix-web = "3" actix-flash = "0.2" diff --git a/examples/actix3_example/api/src/lib.rs b/examples/actix3_example/api/src/lib.rs index 32f6f5f5ac..8a2be80055 100644 --- a/examples/actix3_example/api/src/lib.rs +++ b/examples/actix3_example/api/src/lib.rs @@ -1,4 +1,4 @@ -use actix3_example_core::{ +use actix3_example_service::{ sea_orm::{Database, DatabaseConnection}, Mutation, Query, }; diff --git a/examples/actix_example/core/Cargo.toml b/examples/actix3_example/service/Cargo.toml similarity index 95% rename from examples/actix_example/core/Cargo.toml rename to examples/actix3_example/service/Cargo.toml index b9d4927666..bdd960cc9f 100644 --- a/examples/actix_example/core/Cargo.toml +++ b/examples/actix3_example/service/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "actix-example-core" +name = "actix3-example-service" version = "0.1.0" edition = "2021" diff --git a/examples/actix3_example/core/src/lib.rs b/examples/actix3_example/service/src/lib.rs similarity index 100% rename from examples/actix3_example/core/src/lib.rs rename to examples/actix3_example/service/src/lib.rs diff --git a/examples/actix3_example/core/src/mutation.rs b/examples/actix3_example/service/src/mutation.rs similarity index 100% rename from examples/actix3_example/core/src/mutation.rs rename to examples/actix3_example/service/src/mutation.rs diff --git a/examples/actix3_example/core/src/query.rs b/examples/actix3_example/service/src/query.rs similarity index 100% rename from examples/actix3_example/core/src/query.rs rename to examples/actix3_example/service/src/query.rs diff --git a/examples/jsonrpsee_example/core/tests/mock.rs b/examples/actix3_example/service/tests/mock.rs similarity index 97% rename from examples/jsonrpsee_example/core/tests/mock.rs rename to examples/actix3_example/service/tests/mock.rs index 068f31b6c8..bef2d1fdb6 100644 --- a/examples/jsonrpsee_example/core/tests/mock.rs +++ b/examples/actix3_example/service/tests/mock.rs @@ -1,7 +1,7 @@ mod prepare; +use actix3_example_service::{Mutation, Query}; use entity::post; -use jsonrpsee_example_core::{Mutation, Query}; use prepare::prepare_mock_db; #[tokio::test] diff --git a/examples/actix3_example/core/tests/prepare.rs b/examples/actix3_example/service/tests/prepare.rs similarity index 100% rename from examples/actix3_example/core/tests/prepare.rs rename to examples/actix3_example/service/tests/prepare.rs diff --git a/examples/actix_example/Cargo.toml b/examples/actix_example/Cargo.toml index 7dc877c2c6..ab9ab7ae73 100644 --- a/examples/actix_example/Cargo.toml +++ b/examples/actix_example/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" publish = false [workspace] -members = [".", "api", "core", "entity", "migration"] +members = [".", "api", "service", "entity", "migration"] [dependencies] actix-example-api = { path = "api" } diff --git a/examples/actix_example/README.md b/examples/actix_example/README.md index d844b96a24..88fab093e8 100644 --- a/examples/actix_example/README.md +++ b/examples/actix_example/README.md @@ -4,7 +4,7 @@ 1. Modify the `DATABASE_URL` var in `.env` to point to your chosen database -1. Turn on the appropriate database feature for your chosen db in `core/Cargo.toml` (the `"sqlx-mysql",` line) +1. Turn on the appropriate database feature for your chosen db in `service/Cargo.toml` (the `"sqlx-mysql",` line) 1. Execute `cargo run` to start the server @@ -17,9 +17,9 @@ cargo install systemfd cargo-watch systemfd --no-pid -s http::8000 -- cargo watch -x run ``` -Run mock test on the core logic crate: +Run mock test on the service logic crate: ```bash -cd core +cd service cargo test --features mock ``` diff --git a/examples/actix_example/api/Cargo.toml b/examples/actix_example/api/Cargo.toml index a552466704..18374e7073 100644 --- a/examples/actix_example/api/Cargo.toml +++ b/examples/actix_example/api/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" publish = false [dependencies] -actix-example-core = { path = "../core" } +actix-example-service = { path = "../service" } actix-files = "0.6" actix-http = "3" actix-rt = "2.7" diff --git a/examples/actix_example/api/src/lib.rs b/examples/actix_example/api/src/lib.rs index 83cfa17f5b..f57e3e769f 100644 --- a/examples/actix_example/api/src/lib.rs +++ b/examples/actix_example/api/src/lib.rs @@ -1,4 +1,4 @@ -use actix_example_core::{ +use actix_example_service::{ sea_orm::{Database, DatabaseConnection}, Mutation, Query, }; diff --git a/examples/actix3_example/core/Cargo.toml b/examples/actix_example/service/Cargo.toml similarity index 95% rename from examples/actix3_example/core/Cargo.toml rename to examples/actix_example/service/Cargo.toml index 53277a72e7..f9972b5102 100644 --- a/examples/actix3_example/core/Cargo.toml +++ b/examples/actix_example/service/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "actix3-example-core" +name = "actix-example-service" version = "0.1.0" edition = "2021" diff --git a/examples/actix_example/core/src/lib.rs b/examples/actix_example/service/src/lib.rs similarity index 100% rename from examples/actix_example/core/src/lib.rs rename to examples/actix_example/service/src/lib.rs diff --git a/examples/actix_example/core/src/mutation.rs b/examples/actix_example/service/src/mutation.rs similarity index 100% rename from examples/actix_example/core/src/mutation.rs rename to examples/actix_example/service/src/mutation.rs diff --git a/examples/actix_example/core/src/query.rs b/examples/actix_example/service/src/query.rs similarity index 100% rename from examples/actix_example/core/src/query.rs rename to examples/actix_example/service/src/query.rs diff --git a/examples/poem_example/core/tests/mock.rs b/examples/actix_example/service/tests/mock.rs similarity index 97% rename from examples/poem_example/core/tests/mock.rs rename to examples/actix_example/service/tests/mock.rs index e4b3ef4f5e..ff2f851c7b 100644 --- a/examples/poem_example/core/tests/mock.rs +++ b/examples/actix_example/service/tests/mock.rs @@ -1,7 +1,7 @@ mod prepare; +use actix_example_service::{Mutation, Query}; use entity::post; -use poem_example_core::{Mutation, Query}; use prepare::prepare_mock_db; #[tokio::test] diff --git a/examples/actix_example/core/tests/prepare.rs b/examples/actix_example/service/tests/prepare.rs similarity index 100% rename from examples/actix_example/core/tests/prepare.rs rename to examples/actix_example/service/tests/prepare.rs diff --git a/examples/axum_example/Cargo.toml b/examples/axum_example/Cargo.toml index 0387b657bb..09eafb0216 100644 --- a/examples/axum_example/Cargo.toml +++ b/examples/axum_example/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" publish = false [workspace] -members = [".", "api", "core", "entity", "migration"] +members = [".", "api", "service", "entity", "migration"] [dependencies] axum-example-api = { path = "api" } diff --git a/examples/axum_example/README.md b/examples/axum_example/README.md index fe120e7151..49abc89235 100644 --- a/examples/axum_example/README.md +++ b/examples/axum_example/README.md @@ -4,15 +4,15 @@ 1. Modify the `DATABASE_URL` var in `.env` to point to your chosen database -1. Turn on the appropriate database feature for your chosen db in `core/Cargo.toml` (the `"sqlx-postgres",` line) +1. Turn on the appropriate database feature for your chosen db in `service/Cargo.toml` (the `"sqlx-postgres",` line) 1. Execute `cargo run` to start the server 1. Visit [localhost:8000](http://localhost:8000) in browser -Run mock test on the core logic crate: +Run mock test on the service logic crate: ```bash -cd core +cd service cargo test --features mock ``` diff --git a/examples/axum_example/api/Cargo.toml b/examples/axum_example/api/Cargo.toml index 8b19a6da82..541034e392 100644 --- a/examples/axum_example/api/Cargo.toml +++ b/examples/axum_example/api/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" publish = false [dependencies] -axum-example-core = { path = "../core" } +axum-example-service = { path = "../service" } tokio = { version = "1.23.0", features = ["full"] } axum = "0.6.1" tower = "0.4.13" diff --git a/examples/axum_example/api/src/lib.rs b/examples/axum_example/api/src/lib.rs index a77a14b749..324840337c 100644 --- a/examples/axum_example/api/src/lib.rs +++ b/examples/axum_example/api/src/lib.rs @@ -7,7 +7,7 @@ use axum::{ routing::{get, get_service, post}, Router, Server, }; -use axum_example_core::{ +use axum_example_service::{ sea_orm::{Database, DatabaseConnection}, Mutation as MutationCore, Query as QueryCore, }; diff --git a/examples/axum_example/core/Cargo.toml b/examples/axum_example/service/Cargo.toml similarity index 95% rename from examples/axum_example/core/Cargo.toml rename to examples/axum_example/service/Cargo.toml index 7e78682a2a..551f127c4d 100644 --- a/examples/axum_example/core/Cargo.toml +++ b/examples/axum_example/service/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "axum-example-core" +name = "axum-example-service" version = "0.1.0" edition = "2021" diff --git a/examples/axum_example/core/src/lib.rs b/examples/axum_example/service/src/lib.rs similarity index 100% rename from examples/axum_example/core/src/lib.rs rename to examples/axum_example/service/src/lib.rs diff --git a/examples/axum_example/core/src/mutation.rs b/examples/axum_example/service/src/mutation.rs similarity index 100% rename from examples/axum_example/core/src/mutation.rs rename to examples/axum_example/service/src/mutation.rs diff --git a/examples/axum_example/core/src/query.rs b/examples/axum_example/service/src/query.rs similarity index 100% rename from examples/axum_example/core/src/query.rs rename to examples/axum_example/service/src/query.rs diff --git a/examples/axum_example/core/tests/mock.rs b/examples/axum_example/service/tests/mock.rs similarity index 97% rename from examples/axum_example/core/tests/mock.rs rename to examples/axum_example/service/tests/mock.rs index 8321053026..ecbb53cb91 100644 --- a/examples/axum_example/core/tests/mock.rs +++ b/examples/axum_example/service/tests/mock.rs @@ -1,6 +1,6 @@ mod prepare; -use axum_example_core::{Mutation, Query}; +use axum_example_service::{Mutation, Query}; use entity::post; use prepare::prepare_mock_db; diff --git a/examples/axum_example/core/tests/prepare.rs b/examples/axum_example/service/tests/prepare.rs similarity index 100% rename from examples/axum_example/core/tests/prepare.rs rename to examples/axum_example/service/tests/prepare.rs diff --git a/examples/graphql_example/Cargo.toml b/examples/graphql_example/Cargo.toml index c3e342dc3b..49229ad2bf 100644 --- a/examples/graphql_example/Cargo.toml +++ b/examples/graphql_example/Cargo.toml @@ -7,7 +7,7 @@ publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [workspace] -members = [".", "api", "core", "entity", "migration"] +members = [".", "api", "service", "entity", "migration"] [dependencies] graphql-example-api = { path = "api" } diff --git a/examples/graphql_example/README.md b/examples/graphql_example/README.md index 351c7df557..c5698a22a2 100644 --- a/examples/graphql_example/README.md +++ b/examples/graphql_example/README.md @@ -6,15 +6,15 @@ 1. Modify the `DATABASE_URL` var in `.env` to point to your chosen database -1. Turn on the appropriate database feature for your chosen db in `core/Cargo.toml` (the `"sqlx-sqlite",` line) +1. Turn on the appropriate database feature for your chosen db in `service/Cargo.toml` (the `"sqlx-sqlite",` line) 1. Execute `cargo run` to start the server 1. Visit [localhost:3000/api/graphql](http://localhost:3000/api/graphql) in browser -Run mock test on the core logic crate: +Run mock test on the service logic crate: ```bash -cd core +cd service cargo test --features mock ``` diff --git a/examples/graphql_example/api/Cargo.toml b/examples/graphql_example/api/Cargo.toml index aecf0466ec..1115d83966 100644 --- a/examples/graphql_example/api/Cargo.toml +++ b/examples/graphql_example/api/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" publish = false [dependencies] -graphql-example-core = { path = "../core" } +graphql-example-service = { path = "../service" } tokio = { version = "1.0", features = ["full"] } axum = "0.5.1" dotenvy = "0.15.0" diff --git a/examples/graphql_example/api/src/db.rs b/examples/graphql_example/api/src/db.rs index a1b79cd414..6db0400732 100644 --- a/examples/graphql_example/api/src/db.rs +++ b/examples/graphql_example/api/src/db.rs @@ -1,4 +1,4 @@ -use graphql_example_core::sea_orm::DatabaseConnection; +use graphql_example_service::sea_orm::DatabaseConnection; pub struct Database { pub connection: DatabaseConnection, @@ -6,7 +6,7 @@ pub struct Database { impl Database { pub async fn new() -> Self { - let connection = graphql_example_core::sea_orm::Database::connect( + let connection = graphql_example_service::sea_orm::Database::connect( std::env::var("DATABASE_URL").unwrap(), ) .await diff --git a/examples/graphql_example/api/src/graphql/mutation/note.rs b/examples/graphql_example/api/src/graphql/mutation/note.rs index a3ce9f48f6..54f8a444b4 100644 --- a/examples/graphql_example/api/src/graphql/mutation/note.rs +++ b/examples/graphql_example/api/src/graphql/mutation/note.rs @@ -1,7 +1,7 @@ use async_graphql::{Context, Object, Result}; use entity::async_graphql::{self, InputObject, SimpleObject}; use entity::note; -use graphql_example_core::Mutation; +use graphql_example_service::Mutation; use crate::db::Database; diff --git a/examples/graphql_example/api/src/graphql/query/note.rs b/examples/graphql_example/api/src/graphql/query/note.rs index 1ac2549d9e..8ad2e0bb3c 100644 --- a/examples/graphql_example/api/src/graphql/query/note.rs +++ b/examples/graphql_example/api/src/graphql/query/note.rs @@ -1,6 +1,6 @@ use async_graphql::{Context, Object, Result}; use entity::{async_graphql, note}; -use graphql_example_core::Query; +use graphql_example_service::Query; use crate::db::Database; diff --git a/examples/graphql_example/core/Cargo.toml b/examples/graphql_example/service/Cargo.toml similarity index 94% rename from examples/graphql_example/core/Cargo.toml rename to examples/graphql_example/service/Cargo.toml index c9984c74b6..0cf7539e7e 100644 --- a/examples/graphql_example/core/Cargo.toml +++ b/examples/graphql_example/service/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "graphql-example-core" +name = "graphql-example-service" version = "0.1.0" edition = "2021" diff --git a/examples/graphql_example/core/src/lib.rs b/examples/graphql_example/service/src/lib.rs similarity index 100% rename from examples/graphql_example/core/src/lib.rs rename to examples/graphql_example/service/src/lib.rs diff --git a/examples/graphql_example/core/src/mutation.rs b/examples/graphql_example/service/src/mutation.rs similarity index 100% rename from examples/graphql_example/core/src/mutation.rs rename to examples/graphql_example/service/src/mutation.rs diff --git a/examples/graphql_example/core/src/query.rs b/examples/graphql_example/service/src/query.rs similarity index 100% rename from examples/graphql_example/core/src/query.rs rename to examples/graphql_example/service/src/query.rs diff --git a/examples/graphql_example/core/tests/mock.rs b/examples/graphql_example/service/tests/mock.rs similarity index 97% rename from examples/graphql_example/core/tests/mock.rs rename to examples/graphql_example/service/tests/mock.rs index 16a56189f5..e049c7d33b 100644 --- a/examples/graphql_example/core/tests/mock.rs +++ b/examples/graphql_example/service/tests/mock.rs @@ -1,7 +1,7 @@ mod prepare; use entity::note; -use graphql_example_core::{Mutation, Query}; +use graphql_example_service::{Mutation, Query}; use prepare::prepare_mock_db; #[tokio::test] diff --git a/examples/graphql_example/core/tests/prepare.rs b/examples/graphql_example/service/tests/prepare.rs similarity index 100% rename from examples/graphql_example/core/tests/prepare.rs rename to examples/graphql_example/service/tests/prepare.rs diff --git a/examples/jsonrpsee_example/Cargo.toml b/examples/jsonrpsee_example/Cargo.toml index 7365aa551c..bf3458f474 100644 --- a/examples/jsonrpsee_example/Cargo.toml +++ b/examples/jsonrpsee_example/Cargo.toml @@ -6,7 +6,7 @@ publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [workspace] -members = [".", "api", "core", "entity", "migration"] +members = [".", "api", "service", "entity", "migration"] [dependencies] jsonrpsee-example-api = { path = "api" } diff --git a/examples/jsonrpsee_example/README.md b/examples/jsonrpsee_example/README.md index 4d45e5b640..0cb907525a 100644 --- a/examples/jsonrpsee_example/README.md +++ b/examples/jsonrpsee_example/README.md @@ -2,7 +2,7 @@ 1. Modify the `DATABASE_URL` var in `.env` to point to your chosen database -1. Turn on the appropriate database feature for your chosen db in `core/Cargo.toml` (the `"sqlx-sqlite",` line) +1. Turn on the appropriate database feature for your chosen db in `service/Cargo.toml` (the `"sqlx-sqlite",` line) 1. Execute `cargo run` to start the server @@ -63,9 +63,9 @@ curl --location --request POST 'http://127.0.0.1:8000' \ ``` -Run mock test on the core logic crate: +Run mock test on the service logic crate: ```bash -cd core +cd service cargo test --features mock ``` diff --git a/examples/jsonrpsee_example/api/Cargo.toml b/examples/jsonrpsee_example/api/Cargo.toml index 6440e00417..fc8883917b 100644 --- a/examples/jsonrpsee_example/api/Cargo.toml +++ b/examples/jsonrpsee_example/api/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" publish = false [dependencies] -jsonrpsee-example-core = { path = "../core" } +jsonrpsee-example-service = { path = "../service" } jsonrpsee = { version = "0.8.0", features = ["full"] } jsonrpsee-core = "0.9.0" tokio = { version = "1.8.0", features = ["full"] } diff --git a/examples/jsonrpsee_example/api/src/lib.rs b/examples/jsonrpsee_example/api/src/lib.rs index 5eace0d137..de52523844 100644 --- a/examples/jsonrpsee_example/api/src/lib.rs +++ b/examples/jsonrpsee_example/api/src/lib.rs @@ -6,8 +6,8 @@ use jsonrpsee::core::{async_trait, RpcResult}; use jsonrpsee::http_server::HttpServerBuilder; use jsonrpsee::proc_macros::rpc; use jsonrpsee::types::error::CallError; -use jsonrpsee_example_core::sea_orm::{Database, DatabaseConnection}; -use jsonrpsee_example_core::{Mutation, Query}; +use jsonrpsee_example_service::sea_orm::{Database, DatabaseConnection}; +use jsonrpsee_example_service::{Mutation, Query}; use log::info; use migration::{Migrator, MigratorTrait}; use simplelog::*; diff --git a/examples/jsonrpsee_example/core/Cargo.toml b/examples/jsonrpsee_example/service/Cargo.toml similarity index 94% rename from examples/jsonrpsee_example/core/Cargo.toml rename to examples/jsonrpsee_example/service/Cargo.toml index 0c3c04b9e7..22d8b32998 100644 --- a/examples/jsonrpsee_example/core/Cargo.toml +++ b/examples/jsonrpsee_example/service/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "jsonrpsee-example-core" +name = "jsonrpsee-example-service" version = "0.1.0" edition = "2021" diff --git a/examples/jsonrpsee_example/core/src/lib.rs b/examples/jsonrpsee_example/service/src/lib.rs similarity index 100% rename from examples/jsonrpsee_example/core/src/lib.rs rename to examples/jsonrpsee_example/service/src/lib.rs diff --git a/examples/jsonrpsee_example/core/src/mutation.rs b/examples/jsonrpsee_example/service/src/mutation.rs similarity index 100% rename from examples/jsonrpsee_example/core/src/mutation.rs rename to examples/jsonrpsee_example/service/src/mutation.rs diff --git a/examples/jsonrpsee_example/core/src/query.rs b/examples/jsonrpsee_example/service/src/query.rs similarity index 100% rename from examples/jsonrpsee_example/core/src/query.rs rename to examples/jsonrpsee_example/service/src/query.rs diff --git a/examples/jsonrpsee_example/service/tests/mock.rs b/examples/jsonrpsee_example/service/tests/mock.rs new file mode 100644 index 0000000000..cb6de1896b --- /dev/null +++ b/examples/jsonrpsee_example/service/tests/mock.rs @@ -0,0 +1,79 @@ +mod prepare; + +use entity::post; +use jsonrpsee_example_service::{Mutation, Query}; +use prepare::prepare_mock_db; + +#[tokio::test] +async fn main() { + let db = &prepare_mock_db(); + + { + let post = Query::find_post_by_id(db, 1).await.unwrap().unwrap(); + + assert_eq!(post.id, 1); + } + + { + let post = Query::find_post_by_id(db, 5).await.unwrap().unwrap(); + + assert_eq!(post.id, 5); + } + + { + let post = Mutation::create_post( + db, + post::Model { + id: 0, + title: "Title D".to_owned(), + text: "Text D".to_owned(), + }, + ) + .await + .unwrap(); + + assert_eq!( + post, + post::ActiveModel { + id: sea_orm::ActiveValue::Unchanged(6), + title: sea_orm::ActiveValue::Unchanged("Title D".to_owned()), + text: sea_orm::ActiveValue::Unchanged("Text D".to_owned()) + } + ); + } + + { + let post = Mutation::update_post_by_id( + db, + 1, + post::Model { + id: 1, + title: "New Title A".to_owned(), + text: "New Text A".to_owned(), + }, + ) + .await + .unwrap(); + + assert_eq!( + post, + post::Model { + id: 1, + title: "New Title A".to_owned(), + text: "New Text A".to_owned(), + } + ); + } + + { + let result = Mutation::delete_post(db, 5).await.unwrap(); + + assert_eq!(result.rows_affected, 1); + } + + { + let result = Mutation::delete_all_posts(db).await.unwrap(); + + assert_eq!(result.rows_affected, 5); + } +} diff --git a/examples/jsonrpsee_example/core/tests/prepare.rs b/examples/jsonrpsee_example/service/tests/prepare.rs similarity index 100% rename from examples/jsonrpsee_example/core/tests/prepare.rs rename to examples/jsonrpsee_example/service/tests/prepare.rs diff --git a/examples/poem_example/Cargo.toml b/examples/poem_example/Cargo.toml index 9024d729c5..226a7fd8f7 100644 --- a/examples/poem_example/Cargo.toml +++ b/examples/poem_example/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [workspace] -members = [".", "api", "core", "entity", "migration"] +members = [".", "api", "service", "entity", "migration"] [dependencies] poem-example-api = { path = "api" } diff --git a/examples/poem_example/README.md b/examples/poem_example/README.md index d0aa6973b7..f38f6e0ad2 100644 --- a/examples/poem_example/README.md +++ b/examples/poem_example/README.md @@ -4,15 +4,15 @@ 1. Modify the `DATABASE_URL` var in `.env` to point to your chosen database -1. Turn on the appropriate database feature for your chosen db in `core/Cargo.toml` (the `"sqlx-sqlite",` line) +1. Turn on the appropriate database feature for your chosen db in `service/Cargo.toml` (the `"sqlx-sqlite",` line) 1. Execute `cargo run` to start the server 1. Visit [localhost:8000](http://localhost:8000) in browser after seeing the `server started` line -Run mock test on the core logic crate: +Run mock test on the service logic crate: ```bash -cd core +cd service cargo test --features mock ``` diff --git a/examples/poem_example/api/Cargo.toml b/examples/poem_example/api/Cargo.toml index 912218ce8e..383843f4a8 100644 --- a/examples/poem_example/api/Cargo.toml +++ b/examples/poem_example/api/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -poem-example-core = { path = "../core" } +poem-example-service = { path = "../service" } tokio = { version = "1.15.0", features = ["macros", "rt-multi-thread"] } poem = { version = "1.2.33", features = ["static-files"] } tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } diff --git a/examples/poem_example/api/src/lib.rs b/examples/poem_example/api/src/lib.rs index 02c7ba76e5..035113a75e 100644 --- a/examples/poem_example/api/src/lib.rs +++ b/examples/poem_example/api/src/lib.rs @@ -8,7 +8,7 @@ use poem::http::StatusCode; use poem::listener::TcpListener; use poem::web::{Data, Form, Html, Path, Query}; use poem::{get, handler, post, EndpointExt, Error, IntoResponse, Result, Route, Server}; -use poem_example_core::{ +use poem_example_service::{ sea_orm::{Database, DatabaseConnection}, Mutation as MutationCore, Query as QueryCore, }; diff --git a/examples/poem_example/core/Cargo.toml b/examples/poem_example/service/Cargo.toml similarity index 95% rename from examples/poem_example/core/Cargo.toml rename to examples/poem_example/service/Cargo.toml index e0e578458f..8f76a2f209 100644 --- a/examples/poem_example/core/Cargo.toml +++ b/examples/poem_example/service/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "poem-example-core" +name = "poem-example-service" version = "0.1.0" edition = "2021" diff --git a/examples/poem_example/core/src/lib.rs b/examples/poem_example/service/src/lib.rs similarity index 100% rename from examples/poem_example/core/src/lib.rs rename to examples/poem_example/service/src/lib.rs diff --git a/examples/poem_example/core/src/mutation.rs b/examples/poem_example/service/src/mutation.rs similarity index 100% rename from examples/poem_example/core/src/mutation.rs rename to examples/poem_example/service/src/mutation.rs diff --git a/examples/poem_example/core/src/query.rs b/examples/poem_example/service/src/query.rs similarity index 100% rename from examples/poem_example/core/src/query.rs rename to examples/poem_example/service/src/query.rs diff --git a/examples/actix3_example/core/tests/mock.rs b/examples/poem_example/service/tests/mock.rs similarity index 97% rename from examples/actix3_example/core/tests/mock.rs rename to examples/poem_example/service/tests/mock.rs index 190cb29064..6c62158ca9 100644 --- a/examples/actix3_example/core/tests/mock.rs +++ b/examples/poem_example/service/tests/mock.rs @@ -1,7 +1,7 @@ mod prepare; -use actix3_example_core::{Mutation, Query}; use entity::post; +use poem_example_service::{Mutation, Query}; use prepare::prepare_mock_db; #[tokio::test] diff --git a/examples/poem_example/core/tests/prepare.rs b/examples/poem_example/service/tests/prepare.rs similarity index 100% rename from examples/poem_example/core/tests/prepare.rs rename to examples/poem_example/service/tests/prepare.rs diff --git a/examples/rocket_example/Cargo.toml b/examples/rocket_example/Cargo.toml index 992ad03609..d67d3df3d1 100644 --- a/examples/rocket_example/Cargo.toml +++ b/examples/rocket_example/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" publish = false [workspace] -members = [".", "api", "core", "entity", "migration"] +members = [".", "api", "service", "entity", "migration"] [dependencies] rocket-example-api = { path = "api" } diff --git a/examples/rocket_example/README.md b/examples/rocket_example/README.md index 20845db6a0..023bbe2f1a 100644 --- a/examples/rocket_example/README.md +++ b/examples/rocket_example/README.md @@ -4,15 +4,15 @@ 1. Modify the `url` var in `api/Rocket.toml` to point to your chosen database -1. Turn on the appropriate database feature for your chosen db in `core/Cargo.toml` (the `"sqlx-postgres",` line) +1. Turn on the appropriate database feature for your chosen db in `service/Cargo.toml` (the `"sqlx-postgres",` line) 1. Execute `cargo run` to start the server 1. Visit [localhost:8000](http://localhost:8000) in browser after seeing the `🚀 Rocket has launched from http://localhost:8000` line -Run mock test on the core logic crate: +Run mock test on the service logic crate: ```bash -cd core +cd service cargo test --features mock ``` diff --git a/examples/rocket_example/api/Cargo.toml b/examples/rocket_example/api/Cargo.toml index 4361c46004..bb2d5cdeec 100644 --- a/examples/rocket_example/api/Cargo.toml +++ b/examples/rocket_example/api/Cargo.toml @@ -8,7 +8,7 @@ publish = false [dependencies] async-stream = { version = "0.3" } async-trait = { version = "0.1" } -rocket-example-core = { path = "../core" } +rocket-example-service = { path = "../service" } futures = { version = "0.3" } futures-util = { version = "0.3" } rocket = { version = "0.5.0-rc.1", features = [ diff --git a/examples/rocket_example/api/src/lib.rs b/examples/rocket_example/api/src/lib.rs index 29eb621d02..f539d0c13d 100644 --- a/examples/rocket_example/api/src/lib.rs +++ b/examples/rocket_example/api/src/lib.rs @@ -8,7 +8,7 @@ use rocket::request::FlashMessage; use rocket::response::{Flash, Redirect}; use rocket::{Build, Request, Rocket}; use rocket_dyn_templates::Template; -use rocket_example_core::{Mutation, Query}; +use rocket_example_service::{Mutation, Query}; use serde_json::json; use migration::MigratorTrait; diff --git a/examples/rocket_example/api/src/pool.rs b/examples/rocket_example/api/src/pool.rs index da8470acea..1681ca1119 100644 --- a/examples/rocket_example/api/src/pool.rs +++ b/examples/rocket_example/api/src/pool.rs @@ -1,4 +1,4 @@ -use rocket_example_core::sea_orm; +use rocket_example_service::sea_orm; use async_trait::async_trait; use sea_orm::ConnectOptions; diff --git a/examples/rocket_example/core/tests/mock.rs b/examples/rocket_example/core/tests/mock.rs deleted file mode 100644 index 84b187e5c1..0000000000 --- a/examples/rocket_example/core/tests/mock.rs +++ /dev/null @@ -1,79 +0,0 @@ -mod prepare; - -use entity::post; -use prepare::prepare_mock_db; -use rocket_example_core::{Mutation, Query}; - -#[tokio::test] -async fn main() { - let db = &prepare_mock_db(); - - { - let post = Query::find_post_by_id(db, 1).await.unwrap().unwrap(); - - assert_eq!(post.id, 1); - } - - { - let post = Query::find_post_by_id(db, 5).await.unwrap().unwrap(); - - assert_eq!(post.id, 5); - } - - { - let post = Mutation::create_post( - db, - post::Model { - id: 0, - title: "Title D".to_owned(), - text: "Text D".to_owned(), - }, - ) - .await - .unwrap(); - - assert_eq!( - post, - post::ActiveModel { - id: sea_orm::ActiveValue::Unchanged(6), - title: sea_orm::ActiveValue::Unchanged("Title D".to_owned()), - text: sea_orm::ActiveValue::Unchanged("Text D".to_owned()) - } - ); - } - - { - let post = Mutation::update_post_by_id( - db, - 1, - post::Model { - id: 1, - title: "New Title A".to_owned(), - text: "New Text A".to_owned(), - }, - ) - .await - .unwrap(); - - assert_eq!( - post, - post::Model { - id: 1, - title: "New Title A".to_owned(), - text: "New Text A".to_owned(), - } - ); - } - - { - let result = Mutation::delete_post(db, 5).await.unwrap(); - - assert_eq!(result.rows_affected, 1); - } - - { - let result = Mutation::delete_all_posts(db).await.unwrap(); - - assert_eq!(result.rows_affected, 5); - } -} diff --git a/examples/rocket_example/core/Cargo.toml b/examples/rocket_example/service/Cargo.toml similarity index 94% rename from examples/rocket_example/core/Cargo.toml rename to examples/rocket_example/service/Cargo.toml index 6ee1abf942..957ad5268c 100644 --- a/examples/rocket_example/core/Cargo.toml +++ b/examples/rocket_example/service/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "rocket-example-core" +name = "rocket-example-service" version = "0.1.0" edition = "2021" diff --git a/examples/rocket_example/core/src/lib.rs b/examples/rocket_example/service/src/lib.rs similarity index 100% rename from examples/rocket_example/core/src/lib.rs rename to examples/rocket_example/service/src/lib.rs diff --git a/examples/rocket_example/core/src/mutation.rs b/examples/rocket_example/service/src/mutation.rs similarity index 100% rename from examples/rocket_example/core/src/mutation.rs rename to examples/rocket_example/service/src/mutation.rs diff --git a/examples/rocket_example/core/src/query.rs b/examples/rocket_example/service/src/query.rs similarity index 100% rename from examples/rocket_example/core/src/query.rs rename to examples/rocket_example/service/src/query.rs diff --git a/examples/rocket_example/service/tests/mock.rs b/examples/rocket_example/service/tests/mock.rs new file mode 100644 index 0000000000..1f752615ed --- /dev/null +++ b/examples/rocket_example/service/tests/mock.rs @@ -0,0 +1,79 @@ +mod prepare; + +use entity::post; +use prepare::prepare_mock_db; +use rocket_example_service::{Mutation, Query}; + +#[tokio::test] +async fn main() { + let db = &prepare_mock_db(); + + { + let post = Query::find_post_by_id(db, 1).await.unwrap().unwrap(); + + assert_eq!(post.id, 1); + } + + { + let post = Query::find_post_by_id(db, 5).await.unwrap().unwrap(); + + assert_eq!(post.id, 5); + } + + { + let post = Mutation::create_post( + db, + post::Model { + id: 0, + title: "Title D".to_owned(), + text: "Text D".to_owned(), + }, + ) + .await + .unwrap(); + + assert_eq!( + post, + post::ActiveModel { + id: sea_orm::ActiveValue::Unchanged(6), + title: sea_orm::ActiveValue::Unchanged("Title D".to_owned()), + text: sea_orm::ActiveValue::Unchanged("Text D".to_owned()) + } + ); + } + + { + let post = Mutation::update_post_by_id( + db, + 1, + post::Model { + id: 1, + title: "New Title A".to_owned(), + text: "New Text A".to_owned(), + }, + ) + .await + .unwrap(); + + assert_eq!( + post, + post::Model { + id: 1, + title: "New Title A".to_owned(), + text: "New Text A".to_owned(), + } + ); + } + + { + let result = Mutation::delete_post(db, 5).await.unwrap(); + + assert_eq!(result.rows_affected, 1); + } + + { + let result = Mutation::delete_all_posts(db).await.unwrap(); + + assert_eq!(result.rows_affected, 5); + } +} diff --git a/examples/rocket_example/core/tests/prepare.rs b/examples/rocket_example/service/tests/prepare.rs similarity index 100% rename from examples/rocket_example/core/tests/prepare.rs rename to examples/rocket_example/service/tests/prepare.rs diff --git a/examples/rocket_okapi_example/Cargo.toml b/examples/rocket_okapi_example/Cargo.toml index 87dfde6f37..87cc8931da 100644 --- a/examples/rocket_okapi_example/Cargo.toml +++ b/examples/rocket_okapi_example/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" publish = false [workspace] -members = [".", "api", "core", "entity", "migration", "dto"] +members = [".", "api", "service", "entity", "migration", "dto"] [dependencies] rocket-example-api = { path = "api" } diff --git a/examples/rocket_okapi_example/README.md b/examples/rocket_okapi_example/README.md index 560baa9b1f..15b2c0683d 100644 --- a/examples/rocket_okapi_example/README.md +++ b/examples/rocket_okapi_example/README.md @@ -2,7 +2,7 @@ 1. Modify the `url` var in `api/Rocket.toml` to point to your chosen database -1. Turn on the appropriate database feature for your chosen db in `core/Cargo.toml` (the `"sqlx-postgres",` line) +1. Turn on the appropriate database feature for your chosen db in `service/Cargo.toml` (the `"sqlx-postgres",` line) 1. Execute `cargo run` to start the server diff --git a/examples/rocket_okapi_example/api/Cargo.toml b/examples/rocket_okapi_example/api/Cargo.toml index f2e78e1750..6bd5abfdb9 100644 --- a/examples/rocket_okapi_example/api/Cargo.toml +++ b/examples/rocket_okapi_example/api/Cargo.toml @@ -8,7 +8,7 @@ publish = false [dependencies] async-stream = { version = "0.3" } async-trait = { version = "0.1" } -rocket-example-core = { path = "../core" } +rocket-example-service = { path = "../service" } futures = { version = "0.3" } futures-util = { version = "0.3" } rocket = { version = "0.5.0-rc.1", features = [ diff --git a/examples/rocket_okapi_example/api/src/okapi_example.rs b/examples/rocket_okapi_example/api/src/okapi_example.rs index 69d15d1812..e801ffeda7 100644 --- a/examples/rocket_okapi_example/api/src/okapi_example.rs +++ b/examples/rocket_okapi_example/api/src/okapi_example.rs @@ -1,6 +1,6 @@ use dto::dto; use rocket::serde::json::Json; -use rocket_example_core::{Mutation, Query}; +use rocket_example_service::{Mutation, Query}; use sea_orm_rocket::Connection; diff --git a/examples/rocket_okapi_example/api/src/pool.rs b/examples/rocket_okapi_example/api/src/pool.rs index b1c0567794..465ad53b8a 100644 --- a/examples/rocket_okapi_example/api/src/pool.rs +++ b/examples/rocket_okapi_example/api/src/pool.rs @@ -1,4 +1,4 @@ -use rocket_example_core::sea_orm; +use rocket_example_service::sea_orm; use async_trait::async_trait; use sea_orm::ConnectOptions; diff --git a/examples/rocket_okapi_example/core/tests/mock.rs b/examples/rocket_okapi_example/core/tests/mock.rs deleted file mode 100644 index 84b187e5c1..0000000000 --- a/examples/rocket_okapi_example/core/tests/mock.rs +++ /dev/null @@ -1,79 +0,0 @@ -mod prepare; - -use entity::post; -use prepare::prepare_mock_db; -use rocket_example_core::{Mutation, Query}; - -#[tokio::test] -async fn main() { - let db = &prepare_mock_db(); - - { - let post = Query::find_post_by_id(db, 1).await.unwrap().unwrap(); - - assert_eq!(post.id, 1); - } - - { - let post = Query::find_post_by_id(db, 5).await.unwrap().unwrap(); - - assert_eq!(post.id, 5); - } - - { - let post = Mutation::create_post( - db, - post::Model { - id: 0, - title: "Title D".to_owned(), - text: "Text D".to_owned(), - }, - ) - .await - .unwrap(); - - assert_eq!( - post, - post::ActiveModel { - id: sea_orm::ActiveValue::Unchanged(6), - title: sea_orm::ActiveValue::Unchanged("Title D".to_owned()), - text: sea_orm::ActiveValue::Unchanged("Text D".to_owned()) - } - ); - } - - { - let post = Mutation::update_post_by_id( - db, - 1, - post::Model { - id: 1, - title: "New Title A".to_owned(), - text: "New Text A".to_owned(), - }, - ) - .await - .unwrap(); - - assert_eq!( - post, - post::Model { - id: 1, - title: "New Title A".to_owned(), - text: "New Text A".to_owned(), - } - ); - } - - { - let result = Mutation::delete_post(db, 5).await.unwrap(); - - assert_eq!(result.rows_affected, 1); - } - - { - let result = Mutation::delete_all_posts(db).await.unwrap(); - - assert_eq!(result.rows_affected, 5); - } -} diff --git a/examples/rocket_okapi_example/core/Cargo.toml b/examples/rocket_okapi_example/service/Cargo.toml similarity index 94% rename from examples/rocket_okapi_example/core/Cargo.toml rename to examples/rocket_okapi_example/service/Cargo.toml index 6ee1abf942..957ad5268c 100644 --- a/examples/rocket_okapi_example/core/Cargo.toml +++ b/examples/rocket_okapi_example/service/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "rocket-example-core" +name = "rocket-example-service" version = "0.1.0" edition = "2021" diff --git a/examples/rocket_okapi_example/core/src/lib.rs b/examples/rocket_okapi_example/service/src/lib.rs similarity index 100% rename from examples/rocket_okapi_example/core/src/lib.rs rename to examples/rocket_okapi_example/service/src/lib.rs diff --git a/examples/rocket_okapi_example/core/src/mutation.rs b/examples/rocket_okapi_example/service/src/mutation.rs similarity index 100% rename from examples/rocket_okapi_example/core/src/mutation.rs rename to examples/rocket_okapi_example/service/src/mutation.rs diff --git a/examples/rocket_okapi_example/core/src/query.rs b/examples/rocket_okapi_example/service/src/query.rs similarity index 100% rename from examples/rocket_okapi_example/core/src/query.rs rename to examples/rocket_okapi_example/service/src/query.rs diff --git a/examples/rocket_okapi_example/service/tests/mock.rs b/examples/rocket_okapi_example/service/tests/mock.rs new file mode 100644 index 0000000000..1f752615ed --- /dev/null +++ b/examples/rocket_okapi_example/service/tests/mock.rs @@ -0,0 +1,79 @@ +mod prepare; + +use entity::post; +use prepare::prepare_mock_db; +use rocket_example_service::{Mutation, Query}; + +#[tokio::test] +async fn main() { + let db = &prepare_mock_db(); + + { + let post = Query::find_post_by_id(db, 1).await.unwrap().unwrap(); + + assert_eq!(post.id, 1); + } + + { + let post = Query::find_post_by_id(db, 5).await.unwrap().unwrap(); + + assert_eq!(post.id, 5); + } + + { + let post = Mutation::create_post( + db, + post::Model { + id: 0, + title: "Title D".to_owned(), + text: "Text D".to_owned(), + }, + ) + .await + .unwrap(); + + assert_eq!( + post, + post::ActiveModel { + id: sea_orm::ActiveValue::Unchanged(6), + title: sea_orm::ActiveValue::Unchanged("Title D".to_owned()), + text: sea_orm::ActiveValue::Unchanged("Text D".to_owned()) + } + ); + } + + { + let post = Mutation::update_post_by_id( + db, + 1, + post::Model { + id: 1, + title: "New Title A".to_owned(), + text: "New Text A".to_owned(), + }, + ) + .await + .unwrap(); + + assert_eq!( + post, + post::Model { + id: 1, + title: "New Title A".to_owned(), + text: "New Text A".to_owned(), + } + ); + } + + { + let result = Mutation::delete_post(db, 5).await.unwrap(); + + assert_eq!(result.rows_affected, 1); + } + + { + let result = Mutation::delete_all_posts(db).await.unwrap(); + + assert_eq!(result.rows_affected, 5); + } +} diff --git a/examples/rocket_okapi_example/core/tests/prepare.rs b/examples/rocket_okapi_example/service/tests/prepare.rs similarity index 100% rename from examples/rocket_okapi_example/core/tests/prepare.rs rename to examples/rocket_okapi_example/service/tests/prepare.rs diff --git a/examples/salvo_example/Cargo.toml b/examples/salvo_example/Cargo.toml index aef0228449..a62dc271a8 100644 --- a/examples/salvo_example/Cargo.toml +++ b/examples/salvo_example/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [workspace] -members = [".", "api", "core", "entity", "migration"] +members = [".", "api", "service", "entity", "migration"] [dependencies] salvo-example-api = { path = "api" } diff --git a/examples/salvo_example/README.md b/examples/salvo_example/README.md index d0aa6973b7..f38f6e0ad2 100644 --- a/examples/salvo_example/README.md +++ b/examples/salvo_example/README.md @@ -4,15 +4,15 @@ 1. Modify the `DATABASE_URL` var in `.env` to point to your chosen database -1. Turn on the appropriate database feature for your chosen db in `core/Cargo.toml` (the `"sqlx-sqlite",` line) +1. Turn on the appropriate database feature for your chosen db in `service/Cargo.toml` (the `"sqlx-sqlite",` line) 1. Execute `cargo run` to start the server 1. Visit [localhost:8000](http://localhost:8000) in browser after seeing the `server started` line -Run mock test on the core logic crate: +Run mock test on the service logic crate: ```bash -cd core +cd service cargo test --features mock ``` diff --git a/examples/salvo_example/api/Cargo.toml b/examples/salvo_example/api/Cargo.toml index 0c2003ba86..6635ccbe2e 100644 --- a/examples/salvo_example/api/Cargo.toml +++ b/examples/salvo_example/api/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -salvo-example-core = { path = "../core" } +salvo-example-service = { path = "../service" } tokio = { version = "1.15.0", features = ["macros", "rt-multi-thread"] } salvo = { version = "0.27", features = ["affix", "serve-static"] } tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } diff --git a/examples/salvo_example/api/src/lib.rs b/examples/salvo_example/api/src/lib.rs index 3b7da0ba73..76927c9531 100644 --- a/examples/salvo_example/api/src/lib.rs +++ b/examples/salvo_example/api/src/lib.rs @@ -6,7 +6,7 @@ use salvo::extra::affix; use salvo::extra::serve_static::DirHandler; use salvo::prelude::*; use salvo::writer::Text; -use salvo_example_core::{ +use salvo_example_service::{ sea_orm::{Database, DatabaseConnection}, Mutation, Query, }; diff --git a/examples/salvo_example/core/tests/mock.rs b/examples/salvo_example/core/tests/mock.rs deleted file mode 100644 index 261652bfee..0000000000 --- a/examples/salvo_example/core/tests/mock.rs +++ /dev/null @@ -1,79 +0,0 @@ -mod prepare; - -use entity::post; -use prepare::prepare_mock_db; -use salvo_example_core::{Mutation, Query}; - -#[tokio::test] -async fn main() { - let db = &prepare_mock_db(); - - { - let post = Query::find_post_by_id(db, 1).await.unwrap().unwrap(); - - assert_eq!(post.id, 1); - } - - { - let post = Query::find_post_by_id(db, 5).await.unwrap().unwrap(); - - assert_eq!(post.id, 5); - } - - { - let post = Mutation::create_post( - db, - post::Model { - id: 0, - title: "Title D".to_owned(), - text: "Text D".to_owned(), - }, - ) - .await - .unwrap(); - - assert_eq!( - post, - post::ActiveModel { - id: sea_orm::ActiveValue::Unchanged(6), - title: sea_orm::ActiveValue::Unchanged("Title D".to_owned()), - text: sea_orm::ActiveValue::Unchanged("Text D".to_owned()) - } - ); - } - - { - let post = Mutation::update_post_by_id( - db, - 1, - post::Model { - id: 1, - title: "New Title A".to_owned(), - text: "New Text A".to_owned(), - }, - ) - .await - .unwrap(); - - assert_eq!( - post, - post::Model { - id: 1, - title: "New Title A".to_owned(), - text: "New Text A".to_owned(), - } - ); - } - - { - let result = Mutation::delete_post(db, 5).await.unwrap(); - - assert_eq!(result.rows_affected, 1); - } - - { - let result = Mutation::delete_all_posts(db).await.unwrap(); - - assert_eq!(result.rows_affected, 5); - } -} diff --git a/examples/salvo_example/core/Cargo.toml b/examples/salvo_example/service/Cargo.toml similarity index 95% rename from examples/salvo_example/core/Cargo.toml rename to examples/salvo_example/service/Cargo.toml index a6e33269aa..87888a1413 100644 --- a/examples/salvo_example/core/Cargo.toml +++ b/examples/salvo_example/service/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "salvo-example-core" +name = "salvo-example-service" version = "0.1.0" edition = "2021" diff --git a/examples/salvo_example/core/src/lib.rs b/examples/salvo_example/service/src/lib.rs similarity index 100% rename from examples/salvo_example/core/src/lib.rs rename to examples/salvo_example/service/src/lib.rs diff --git a/examples/salvo_example/core/src/mutation.rs b/examples/salvo_example/service/src/mutation.rs similarity index 100% rename from examples/salvo_example/core/src/mutation.rs rename to examples/salvo_example/service/src/mutation.rs diff --git a/examples/salvo_example/core/src/query.rs b/examples/salvo_example/service/src/query.rs similarity index 100% rename from examples/salvo_example/core/src/query.rs rename to examples/salvo_example/service/src/query.rs diff --git a/examples/actix_example/core/tests/mock.rs b/examples/salvo_example/service/tests/mock.rs similarity index 97% rename from examples/actix_example/core/tests/mock.rs rename to examples/salvo_example/service/tests/mock.rs index 76531b6710..13a809b863 100644 --- a/examples/actix_example/core/tests/mock.rs +++ b/examples/salvo_example/service/tests/mock.rs @@ -1,8 +1,8 @@ mod prepare; -use actix_example_core::{Mutation, Query}; use entity::post; use prepare::prepare_mock_db; +use salvo_example_service::{Mutation, Query}; #[tokio::test] async fn main() { diff --git a/examples/salvo_example/core/tests/prepare.rs b/examples/salvo_example/service/tests/prepare.rs similarity index 100% rename from examples/salvo_example/core/tests/prepare.rs rename to examples/salvo_example/service/tests/prepare.rs diff --git a/examples/tonic_example/Cargo.toml b/examples/tonic_example/Cargo.toml index d3d147048f..115d1cb0f7 100644 --- a/examples/tonic_example/Cargo.toml +++ b/examples/tonic_example/Cargo.toml @@ -7,7 +7,7 @@ publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [workspace] -members = [".", "api", "core", "entity", "migration"] +members = [".", "api", "service", "entity", "migration"] [dependencies] tonic-example-api = { path = "api" } diff --git a/examples/tonic_example/README.md b/examples/tonic_example/README.md index 7c11feace5..6cd842c21e 100644 --- a/examples/tonic_example/README.md +++ b/examples/tonic_example/README.md @@ -14,9 +14,9 @@ run client using cargo run --bin client ``` -Run mock test on the core logic crate: +Run mock test on the service logic crate: ```bash -cd core +cd service cargo test --features mock ``` diff --git a/examples/tonic_example/api/Cargo.toml b/examples/tonic_example/api/Cargo.toml index 6253c53116..6d1e650cff 100644 --- a/examples/tonic_example/api/Cargo.toml +++ b/examples/tonic_example/api/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" publish = false [dependencies] -tonic-example-core = { path = "../core" } +tonic-example-service = { path = "../service" } tonic = "0.7" tokio = { version = "1.17", features = ["macros", "rt-multi-thread", "full"] } entity = { path = "../entity" } diff --git a/examples/tonic_example/api/src/lib.rs b/examples/tonic_example/api/src/lib.rs index 32d97981b7..18c2705021 100644 --- a/examples/tonic_example/api/src/lib.rs +++ b/examples/tonic_example/api/src/lib.rs @@ -3,7 +3,7 @@ use tonic::{Request, Response, Status}; use entity::post; use migration::{Migrator, MigratorTrait}; -use tonic_example_core::{ +use tonic_example_service::{ sea_orm::{Database, DatabaseConnection}, Mutation, Query, }; diff --git a/examples/tonic_example/core/tests/mock.rs b/examples/tonic_example/core/tests/mock.rs deleted file mode 100644 index 522d3e4521..0000000000 --- a/examples/tonic_example/core/tests/mock.rs +++ /dev/null @@ -1,79 +0,0 @@ -mod prepare; - -use entity::post; -use prepare::prepare_mock_db; -use tonic_example_core::{Mutation, Query}; - -#[tokio::test] -async fn main() { - let db = &prepare_mock_db(); - - { - let post = Query::find_post_by_id(db, 1).await.unwrap().unwrap(); - - assert_eq!(post.id, 1); - } - - { - let post = Query::find_post_by_id(db, 5).await.unwrap().unwrap(); - - assert_eq!(post.id, 5); - } - - { - let post = Mutation::create_post( - db, - post::Model { - id: 0, - title: "Title D".to_owned(), - text: "Text D".to_owned(), - }, - ) - .await - .unwrap(); - - assert_eq!( - post, - post::ActiveModel { - id: sea_orm::ActiveValue::Unchanged(6), - title: sea_orm::ActiveValue::Unchanged("Title D".to_owned()), - text: sea_orm::ActiveValue::Unchanged("Text D".to_owned()) - } - ); - } - - { - let post = Mutation::update_post_by_id( - db, - 1, - post::Model { - id: 1, - title: "New Title A".to_owned(), - text: "New Text A".to_owned(), - }, - ) - .await - .unwrap(); - - assert_eq!( - post, - post::Model { - id: 1, - title: "New Title A".to_owned(), - text: "New Text A".to_owned(), - } - ); - } - - { - let result = Mutation::delete_post(db, 5).await.unwrap(); - - assert_eq!(result.rows_affected, 1); - } - - { - let result = Mutation::delete_all_posts(db).await.unwrap(); - - assert_eq!(result.rows_affected, 5); - } -} diff --git a/examples/tonic_example/core/Cargo.toml b/examples/tonic_example/service/Cargo.toml similarity index 95% rename from examples/tonic_example/core/Cargo.toml rename to examples/tonic_example/service/Cargo.toml index 91dbcd1644..fb06f9d919 100644 --- a/examples/tonic_example/core/Cargo.toml +++ b/examples/tonic_example/service/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "tonic-example-core" +name = "tonic-example-service" version = "0.1.0" edition = "2021" diff --git a/examples/tonic_example/core/src/lib.rs b/examples/tonic_example/service/src/lib.rs similarity index 100% rename from examples/tonic_example/core/src/lib.rs rename to examples/tonic_example/service/src/lib.rs diff --git a/examples/tonic_example/core/src/mutation.rs b/examples/tonic_example/service/src/mutation.rs similarity index 100% rename from examples/tonic_example/core/src/mutation.rs rename to examples/tonic_example/service/src/mutation.rs diff --git a/examples/tonic_example/core/src/query.rs b/examples/tonic_example/service/src/query.rs similarity index 100% rename from examples/tonic_example/core/src/query.rs rename to examples/tonic_example/service/src/query.rs diff --git a/examples/tonic_example/service/tests/mock.rs b/examples/tonic_example/service/tests/mock.rs new file mode 100644 index 0000000000..6b0fbe1e6c --- /dev/null +++ b/examples/tonic_example/service/tests/mock.rs @@ -0,0 +1,79 @@ +mod prepare; + +use entity::post; +use prepare::prepare_mock_db; +use tonic_example_service::{Mutation, Query}; + +#[tokio::test] +async fn main() { + let db = &prepare_mock_db(); + + { + let post = Query::find_post_by_id(db, 1).await.unwrap().unwrap(); + + assert_eq!(post.id, 1); + } + + { + let post = Query::find_post_by_id(db, 5).await.unwrap().unwrap(); + + assert_eq!(post.id, 5); + } + + { + let post = Mutation::create_post( + db, + post::Model { + id: 0, + title: "Title D".to_owned(), + text: "Text D".to_owned(), + }, + ) + .await + .unwrap(); + + assert_eq!( + post, + post::ActiveModel { + id: sea_orm::ActiveValue::Unchanged(6), + title: sea_orm::ActiveValue::Unchanged("Title D".to_owned()), + text: sea_orm::ActiveValue::Unchanged("Text D".to_owned()) + } + ); + } + + { + let post = Mutation::update_post_by_id( + db, + 1, + post::Model { + id: 1, + title: "New Title A".to_owned(), + text: "New Text A".to_owned(), + }, + ) + .await + .unwrap(); + + assert_eq!( + post, + post::Model { + id: 1, + title: "New Title A".to_owned(), + text: "New Text A".to_owned(), + } + ); + } + + { + let result = Mutation::delete_post(db, 5).await.unwrap(); + + assert_eq!(result.rows_affected, 1); + } + + { + let result = Mutation::delete_all_posts(db).await.unwrap(); + + assert_eq!(result.rows_affected, 5); + } +} diff --git a/examples/tonic_example/core/tests/prepare.rs b/examples/tonic_example/service/tests/prepare.rs similarity index 100% rename from examples/tonic_example/core/tests/prepare.rs rename to examples/tonic_example/service/tests/prepare.rs diff --git a/issues/249/Cargo.toml b/issues/249/Cargo.toml index cadab33f1a..a8f712b90d 100644 --- a/issues/249/Cargo.toml +++ b/issues/249/Cargo.toml @@ -1,2 +1,2 @@ [workspace] -members = ["core", "app"] +members = ["service", "app"] diff --git a/issues/249/app/Cargo.toml b/issues/249/app/Cargo.toml index 0834138161..b2f03ea3c2 100644 --- a/issues/249/app/Cargo.toml +++ b/issues/249/app/Cargo.toml @@ -5,5 +5,5 @@ edition = "2021" publish = false [dependencies] -core = { path = "../core" } +service = { path = "../service" } sea-orm = { path = "../../../", default-features = false, features = ["macros", "tests-cfg", "sqlx-sqlite", "runtime-async-std-native-tls"] } diff --git a/issues/249/app/src/main.rs b/issues/249/app/src/main.rs index 98fbb89db0..296084595c 100644 --- a/issues/249/app/src/main.rs +++ b/issues/249/app/src/main.rs @@ -1,4 +1,4 @@ -use core::clone_a_model; +use service::clone_a_model; use sea_orm::tests_cfg::cake; diff --git a/issues/249/core/Cargo.toml b/issues/249/service/Cargo.toml similarity index 92% rename from issues/249/core/Cargo.toml rename to issues/249/service/Cargo.toml index 0b741c9c66..e1f96200e7 100644 --- a/issues/249/core/Cargo.toml +++ b/issues/249/service/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "core" +name = "service" version = "0.1.0" edition = "2021" publish = false diff --git a/issues/249/core/src/lib.rs b/issues/249/service/src/lib.rs similarity index 100% rename from issues/249/core/src/lib.rs rename to issues/249/service/src/lib.rs From 33f3f9282d9eaff0c2eaeb2d1c876412350760c8 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Mon, 29 May 2023 20:35:02 +0800 Subject: [PATCH 0002/1371] feat: implement `ColumnTypeTrait` for `ColumnDef` (#1576) --- src/entity/column.rs | 25 +++++++---- src/tests_cfg/lunch_set_expanded.rs | 64 +++++++++++++++++++++++++++++ src/tests_cfg/mod.rs | 2 + 3 files changed, 84 insertions(+), 7 deletions(-) create mode 100644 src/tests_cfg/lunch_set_expanded.rs diff --git a/src/entity/column.rs b/src/entity/column.rs index 92d83a6528..e2ade89b5b 100644 --- a/src/entity/column.rs +++ b/src/entity/column.rs @@ -315,17 +315,28 @@ impl ColumnTypeTrait for ColumnType { } fn get_enum_name(&self) -> Option<&DynIden> { - fn enum_name(col_type: &ColumnType) -> Option<&DynIden> { - match col_type { - ColumnType::Enum { name, .. } => Some(name), - ColumnType::Array(col_type) => enum_name(col_type), - _ => None, - } - } enum_name(self) } } +impl ColumnTypeTrait for ColumnDef { + fn def(self) -> ColumnDef { + self + } + + fn get_enum_name(&self) -> Option<&DynIden> { + enum_name(&self.col_type) + } +} + +fn enum_name(col_type: &ColumnType) -> Option<&DynIden> { + match col_type { + ColumnType::Enum { name, .. } => Some(name), + ColumnType::Array(col_type) => enum_name(col_type), + _ => None, + } +} + impl ColumnDef { /// Marks the column as `UNIQUE` pub fn unique(mut self) -> Self { diff --git a/src/tests_cfg/lunch_set_expanded.rs b/src/tests_cfg/lunch_set_expanded.rs new file mode 100644 index 0000000000..c15bb59c78 --- /dev/null +++ b/src/tests_cfg/lunch_set_expanded.rs @@ -0,0 +1,64 @@ +use super::sea_orm_active_enums::*; +use crate as sea_orm; +use crate::entity::prelude::*; + +#[derive(Copy, Clone, Default, Debug, DeriveEntity)] +pub struct Entity; + +impl EntityName for Entity { + fn table_name(&self) -> &str { + "lunch_set" + } +} + +#[derive(Clone, Debug, PartialEq, Eq, DeriveModel, DeriveActiveModel)] +#[sea_orm(table_name = "lunch_set")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub name: String, + pub tea: Tea, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] +pub enum Column { + Id, + Name, + Tea, +} + +#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] +pub enum PrimaryKey { + Id, +} + +impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + + fn auto_increment() -> bool { + true + } +} + +#[derive(Copy, Clone, Debug, EnumIter)] +pub enum Relation {} + +impl ColumnTrait for Column { + type EntityName = Entity; + + fn def(&self) -> ColumnDef { + match self { + Self::Id => ColumnType::Integer.def(), + Self::Name => ColumnType::String(None).def(), + Self::Tea => Tea::db_type().def(), + } + } +} + +impl RelationTrait for Relation { + fn def(&self) -> RelationDef { + panic!("No RelationDef") + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/src/tests_cfg/mod.rs b/src/tests_cfg/mod.rs index 9210657c76..a83475eb83 100644 --- a/src/tests_cfg/mod.rs +++ b/src/tests_cfg/mod.rs @@ -9,6 +9,7 @@ pub mod filling; pub mod fruit; pub mod indexes; pub mod lunch_set; +pub mod lunch_set_expanded; pub mod rust_keyword; pub mod sea_orm_active_enums; pub mod vendor; @@ -20,5 +21,6 @@ pub use cake_filling_price::Entity as CakeFillingPrice; pub use filling::Entity as Filling; pub use fruit::Entity as Fruit; pub use lunch_set::Entity as LunchSet; +pub use lunch_set_expanded::Entity as LunchSetExpanded; pub use rust_keyword::Entity as RustKeyword; pub use vendor::Entity as Vendor; From 0f50f55ce4ad3f37cca14878a9aa29b8fad43a97 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Mon, 29 May 2023 20:39:44 +0800 Subject: [PATCH 0003/1371] CHANGELOG --- CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 644766f8ce..f2b495970a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -316,6 +316,20 @@ CREATE TABLE users_saved_bills ); ``` * [sea-orm-cli] fixed entity generation includes partitioned tables https://github.com/SeaQL/sea-orm/issues/1582, https://github.com/SeaQL/sea-schema/pull/105 +* Fixed `ActiveEnum::db_type()` return type does not implement `ColumnTypeTrait` https://github.com/SeaQL/sea-orm/pull/1576 +```rs +impl ColumnTrait for Column { + type EntityName = Entity; + fn def(&self) -> ColumnDef { + match self { +... + // `db_type()` returns `ColumnDef`; now it implements `ColumnTypeTrait` + Self::Thing => AnActiveEnumThing::db_type().def(), +... + } + } +} +``` ### Breaking changes From 3343e2118b3daf1df559809f90c77809701552ab Mon Sep 17 00:00:00 2001 From: Joseph D Date: Thu, 1 Jun 2023 06:11:08 -0400 Subject: [PATCH 0004/1371] Adding ruuth to COMMUNITY.md (#1681) --- COMMUNITY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/COMMUNITY.md b/COMMUNITY.md index 394c7a9087..ccadd8255c 100644 --- a/COMMUNITY.md +++ b/COMMUNITY.md @@ -60,6 +60,7 @@ If you have built an app using SeaORM and want to showcase it, feel free to open - [RSS aggregator](https://github.com/fistons/rss-aggregator)| A small RSS aggregator and API using Actix Web and SeaORM | DB: Postgres - [snmp-sim-rust](https://github.com/sonalake/snmp-sim-rust) | SNMP Simulator | DB: SQLite - [Wikipedia Speedrun](https://wikipediaspeedrun.com) ([repository](https://github.com/hut8/wikipedia-speedrun)) | Finds shortest paths between Wikipedia articles | DB: SQLite +- [ruuth](https://github.com/outurnate/ruuth) ([repository](https://github.com/outurnate/ruuth)) | A simple nginx auth_request backend providing MFA and lockout mechanisms | DB: MySQL, Postgres, SQLite #### Url Shortener - [Dinoly](https://github.com/ippsav/Dinoly) | An url shortener using Axum web framework and SeaORM | DB: Postgres From 3ccb9cdbf78b8f396939229d45eecb3a4d5ccff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Guiton?= Date: Thu, 1 Jun 2023 12:21:38 +0200 Subject: [PATCH 0005/1371] Feat: Expose ping method from SQLx (#1627) * feat: add connection.ping method * fmt * impl `ConnectionTrait::ping` method for `SchemaManagerConnection` * Mock connection should always be online * remove needless clippy lint * fmt * Remove needless instrument * Update src/driver/sqlx_sqlite.rs * (Ping) removed ping() from ConnectionTrait * removed ping from transaction * removed unused imports * Revert --------- Co-authored-by: Billy Chan Co-authored-by: Chris Tsang --- src/database/db_connection.rs | 15 ++++++++++ src/database/mock.rs | 4 +++ src/driver/mock.rs | 8 ++++++ src/driver/sqlx_mysql.rs | 14 ++++++++- src/driver/sqlx_postgres.rs | 14 ++++++++- src/driver/sqlx_sqlite.rs | 14 ++++++++- tests/connection_tests.rs | 53 +++++++++++++++++++++++++++++++++++ 7 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 tests/connection_tests.rs diff --git a/src/database/db_connection.rs b/src/database/db_connection.rs index 6aa9edb1e9..158cf224b4 100644 --- a/src/database/db_connection.rs +++ b/src/database/db_connection.rs @@ -390,6 +390,21 @@ impl DatabaseConnection { } } + /// Checks if a connection to the database is still valid. + pub async fn ping(&self) -> Result<(), DbErr> { + match self { + #[cfg(feature = "sqlx-mysql")] + DatabaseConnection::SqlxMySqlPoolConnection(conn) => conn.ping().await, + #[cfg(feature = "sqlx-postgres")] + DatabaseConnection::SqlxPostgresPoolConnection(conn) => conn.ping().await, + #[cfg(feature = "sqlx-sqlite")] + DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.ping().await, + #[cfg(feature = "mock")] + DatabaseConnection::MockDatabaseConnection(conn) => conn.ping(), + DatabaseConnection::Disconnected => Err(conn_err("Disconnected")), + } + } + /// Explicitly close the database connection pub async fn close(self) -> Result<(), DbErr> { match self { diff --git a/src/database/mock.rs b/src/database/mock.rs index e6cac77305..df222d8eea 100644 --- a/src/database/mock.rs +++ b/src/database/mock.rs @@ -203,6 +203,10 @@ impl MockDatabaseTrait for MockDatabase { fn get_database_backend(&self) -> DbBackend { self.db_backend } + + fn ping(&self) -> Result<(), DbErr> { + Ok(()) + } } impl MockRow { diff --git a/src/driver/mock.rs b/src/driver/mock.rs index 570b5e5302..af3145cbb9 100644 --- a/src/driver/mock.rs +++ b/src/driver/mock.rs @@ -47,6 +47,9 @@ pub trait MockDatabaseTrait: Send + Debug { /// Get the backend being used in the [MockDatabase] fn get_database_backend(&self) -> DbBackend; + + /// Ping the [MockDatabase] + fn ping(&self) -> Result<(), DbErr>; } impl MockDatabaseConnector { @@ -194,4 +197,9 @@ impl MockDatabaseConnection { .expect("Failed to acquire mocker") .rollback() } + + /// Checks if a connection to the database is still valid. + pub fn ping(&self) -> Result<(), DbErr> { + self.mocker.lock().map_err(query_err)?.ping() + } } diff --git a/src/driver/sqlx_mysql.rs b/src/driver/sqlx_mysql.rs index 8268ee943f..ebea1b837d 100644 --- a/src/driver/sqlx_mysql.rs +++ b/src/driver/sqlx_mysql.rs @@ -4,7 +4,7 @@ use std::{future::Future, pin::Pin, sync::Arc}; use sqlx::{ mysql::{MySqlConnectOptions, MySqlQueryResult, MySqlRow}, pool::PoolConnection, - Executor, MySql, MySqlPool, + Connection, Executor, MySql, MySqlPool, }; use sea_query_binder::SqlxValues; @@ -222,6 +222,18 @@ impl SqlxMySqlPoolConnection { self.metric_callback = Some(Arc::new(callback)); } + /// Checks if a connection to the database is still valid. + pub async fn ping(&self) -> Result<(), DbErr> { + if let Ok(conn) = &mut self.pool.acquire().await { + match conn.ping().await { + Ok(_) => Ok(()), + Err(err) => Err(sqlx_error_to_conn_err(err)), + } + } else { + Err(DbErr::ConnectionAcquire) + } + } + /// Explicitly close the MySQL connection pub async fn close(self) -> Result<(), DbErr> { self.pool.close().await; diff --git a/src/driver/sqlx_postgres.rs b/src/driver/sqlx_postgres.rs index d24ebf2b50..e78aa8d842 100644 --- a/src/driver/sqlx_postgres.rs +++ b/src/driver/sqlx_postgres.rs @@ -4,7 +4,7 @@ use std::{future::Future, pin::Pin, sync::Arc}; use sqlx::{ pool::PoolConnection, postgres::{PgConnectOptions, PgQueryResult, PgRow}, - Executor, PgPool, Postgres, + Connection, Executor, PgPool, Postgres, }; use sea_query_binder::SqlxValues; @@ -237,6 +237,18 @@ impl SqlxPostgresPoolConnection { self.metric_callback = Some(Arc::new(callback)); } + /// Checks if a connection to the database is still valid. + pub async fn ping(&self) -> Result<(), DbErr> { + if let Ok(conn) = &mut self.pool.acquire().await { + match conn.ping().await { + Ok(_) => Ok(()), + Err(err) => Err(sqlx_error_to_conn_err(err)), + } + } else { + Err(DbErr::ConnectionAcquire) + } + } + /// Explicitly close the Postgres connection pub async fn close(self) -> Result<(), DbErr> { self.pool.close().await; diff --git a/src/driver/sqlx_sqlite.rs b/src/driver/sqlx_sqlite.rs index 6309208d57..5e1875495a 100644 --- a/src/driver/sqlx_sqlite.rs +++ b/src/driver/sqlx_sqlite.rs @@ -4,7 +4,7 @@ use std::{future::Future, pin::Pin, sync::Arc}; use sqlx::{ pool::PoolConnection, sqlite::{SqliteConnectOptions, SqliteQueryResult, SqliteRow}, - Executor, Sqlite, SqlitePool, + Connection, Executor, Sqlite, SqlitePool, }; use sea_query_binder::SqlxValues; @@ -229,6 +229,18 @@ impl SqlxSqlitePoolConnection { self.metric_callback = Some(Arc::new(callback)); } + /// Checks if a connection to the database is still valid. + pub async fn ping(&self) -> Result<(), DbErr> { + if let Ok(conn) = &mut self.pool.acquire().await { + match conn.ping().await { + Ok(_) => Ok(()), + Err(err) => Err(sqlx_error_to_conn_err(err)), + } + } else { + Err(DbErr::ConnectionAcquire) + } + } + /// Explicitly close the SQLite connection pub async fn close(self) -> Result<(), DbErr> { self.pool.close().await; diff --git a/tests/connection_tests.rs b/tests/connection_tests.rs new file mode 100644 index 0000000000..ae0304d577 --- /dev/null +++ b/tests/connection_tests.rs @@ -0,0 +1,53 @@ +pub mod common; + +pub use common::{bakery_chain::*, setup::*, TestContext}; +use pretty_assertions::assert_eq; +pub use sea_orm::entity::*; +pub use sea_orm::*; + +#[sea_orm_macros::test] +#[cfg(any( + feature = "sqlx-mysql", + feature = "sqlx-sqlite", + feature = "sqlx-postgres" +))] +pub async fn connection_ping() { + let ctx = TestContext::new("connection_ping").await; + + ctx.db.ping().await.unwrap(); + + ctx.delete().await; +} + +#[sea_orm_macros::test] +#[cfg(feature = "sqlx-mysql")] +pub async fn connection_ping_closed_mysql() { + let ctx = std::rc::Rc::new(Box::new(TestContext::new("connection_ping_closed").await)); + let ctx_ping = std::rc::Rc::clone(&ctx); + + ctx.db.get_mysql_connection_pool().close().await; + assert_eq!(ctx_ping.db.ping().await, Err(DbErr::ConnectionAcquire)); + ctx.delete().await; +} + +#[sea_orm_macros::test] +#[cfg(feature = "sqlx-sqlite")] +pub async fn connection_ping_closed_sqlite() { + let ctx = std::rc::Rc::new(Box::new(TestContext::new("connection_ping_closed").await)); + let ctx_ping = std::rc::Rc::clone(&ctx); + + ctx.db.get_sqlite_connection_pool().close().await; + assert_eq!(ctx_ping.db.ping().await, Err(DbErr::ConnectionAcquire)); + ctx.delete().await; +} + +#[sea_orm_macros::test] +#[cfg(feature = "sqlx-postgres")] +pub async fn connection_ping_closed_postgres() { + let ctx = std::rc::Rc::new(Box::new(TestContext::new("connection_ping_closed").await)); + let ctx_ping = std::rc::Rc::clone(&ctx); + + ctx.db.get_postgres_connection_pool().close().await; + assert_eq!(ctx_ping.db.ping().await, Err(DbErr::ConnectionAcquire)); + ctx.delete().await; +} From 00c90b2d79e94b26fc23e7599e3a91c74b0789cb Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 1 Jun 2023 18:30:49 +0800 Subject: [PATCH 0006/1371] Changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2b495970a..cf31bd8d9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -236,6 +236,14 @@ assert_eq!(migration.status(), MigrationStatus::Pending); * Changed the parameter of method `ConnectOptions::set_schema_search_path(T) where T: Into` to takes any string * Changed the parameter of method `ColumnTrait::like()`, `ColumnTrait::not_like()`, `ColumnTrait::starts_with()`, `ColumnTrait::ends_with()` and `ColumnTrait::contains()` to takes any string * Re-export `sea_query::{DynIden, RcOrArc, SeaRc}` in `sea_orm::entity::prelude` module https://github.com/SeaQL/sea-orm/pull/1661 +* Added `DatabaseConnection::ping` https://github.com/SeaQL/sea-orm/pull/1627 +```rust +|db: DatabaseConnection| { + assert!(db.ping().await.is_ok()); + db.clone().close().await; + assert!(matches!(db.ping().await, Err(DbErr::ConnectionAcquire))); +} +``` ### Upgrades From f6c9d81918af7cc202ccd1db677d2ad29e3b50ff Mon Sep 17 00:00:00 2001 From: Aditya Yadav <96418027+aadi58002@users.noreply.github.com> Date: Sat, 3 Jun 2023 09:11:59 +0530 Subject: [PATCH 0007/1371] Moving clap to version 4 (#1468) * Moving clap to vesion 4 * Moving the sea-orm-migration cli to clap v4 * Removing the deprecated flag from clapv4 * Upgrade clap to 4.3 * Fixup * . * Try compile [CLI] --------- Co-authored-by: aadi58002 Co-authored-by: Billy Chan --- sea-orm-cli/Cargo.toml | 2 +- sea-orm-cli/src/bin/main.rs | 2 +- sea-orm-cli/src/bin/sea.rs | 2 +- sea-orm-cli/src/cli.rs | 155 ++++++++++----------------- sea-orm-cli/src/commands/generate.rs | 2 +- sea-orm-migration/Cargo.toml | 2 +- sea-orm-migration/src/cli.rs | 12 +-- 7 files changed, 65 insertions(+), 112 deletions(-) diff --git a/sea-orm-cli/Cargo.toml b/sea-orm-cli/Cargo.toml index e40061eed2..57dd5fee3b 100644 --- a/sea-orm-cli/Cargo.toml +++ b/sea-orm-cli/Cargo.toml @@ -34,7 +34,7 @@ path = "src/bin/sea.rs" required-features = ["cli", "codegen"] [dependencies] -clap = { version = "3.2", default-features = false, features = ["std", "env", "derive"], optional = true } +clap = { version = "4.3", features = ["env", "derive"], optional = true } dotenvy = { version = "0.15", default-features = false, optional = true } async-std = { version = "1.9", default-features = false, features = ["attributes", "tokio1"], optional = true } sea-orm-codegen = { version = "=0.12.0", path = "../sea-orm-codegen", default-features = false, optional = true } diff --git a/sea-orm-cli/src/bin/main.rs b/sea-orm-cli/src/bin/main.rs index e847d003f1..3a4e28ac85 100644 --- a/sea-orm-cli/src/bin/main.rs +++ b/sea-orm-cli/src/bin/main.rs @@ -1,4 +1,4 @@ -use clap::StructOpt; +use clap::Parser; use dotenvy::dotenv; use sea_orm_cli::{handle_error, run_generate_command, run_migrate_command, Cli, Commands}; diff --git a/sea-orm-cli/src/bin/sea.rs b/sea-orm-cli/src/bin/sea.rs index edf15c8315..cef653910c 100644 --- a/sea-orm-cli/src/bin/sea.rs +++ b/sea-orm-cli/src/bin/sea.rs @@ -1,6 +1,6 @@ //! COPY FROM bin/main.rs -use clap::StructOpt; +use clap::Parser; use dotenvy::dotenv; use sea_orm_cli::{handle_error, run_generate_command, run_migrate_command, Cli, Commands}; diff --git a/sea-orm-cli/src/cli.rs b/sea-orm-cli/src/cli.rs index d37c5ca085..1421646f34 100644 --- a/sea-orm-cli/src/cli.rs +++ b/sea-orm-cli/src/cli.rs @@ -1,7 +1,7 @@ -use clap::{ArgEnum, ArgGroup, Parser, Subcommand}; +use clap::{ArgGroup, Parser, Subcommand, ValueEnum}; #[derive(Parser, Debug)] -#[clap( +#[command( version, author, help_template = r#"{before-help}{name} {version} @@ -37,28 +37,27 @@ AUTHORS: "# )] pub struct Cli { - #[clap(action, global = true, short, long, help = "Show debug messages")] + #[arg(global = true, short, long, help = "Show debug messages")] pub verbose: bool, - #[clap(subcommand)] + #[command(subcommand)] pub command: Commands, } #[derive(Subcommand, PartialEq, Eq, Debug)] pub enum Commands { - #[clap( + #[command( about = "Codegen related commands", arg_required_else_help = true, display_order = 10 )] Generate { - #[clap(subcommand)] + #[command(subcommand)] command: GenerateSubcommands, }, - #[clap(about = "Migration related commands", display_order = 20)] + #[command(about = "Migration related commands", display_order = 20)] Migrate { - #[clap( - value_parser, + #[arg( global = true, short = 'd', long, @@ -71,8 +70,7 @@ you should provide the directory of that submodule.", )] migration_dir: String, - #[clap( - value_parser, + #[arg( global = true, short = 's', long, @@ -83,8 +81,7 @@ you should provide the directory of that submodule.", )] database_schema: Option, - #[clap( - value_parser, + #[arg( global = true, short = 'u', long, @@ -93,76 +90,59 @@ you should provide the directory of that submodule.", )] database_url: Option, - #[clap(subcommand)] + #[command(subcommand)] command: Option, }, } #[derive(Subcommand, PartialEq, Eq, Debug)] pub enum MigrateSubcommands { - #[clap(about = "Initialize migration directory", display_order = 10)] + #[command(about = "Initialize migration directory", display_order = 10)] Init, - #[clap(about = "Generate a new, empty migration", display_order = 20)] + #[command(about = "Generate a new, empty migration", display_order = 20)] Generate { - #[clap( - value_parser, - required = true, - takes_value = true, - help = "Name of the new migration" - )] + #[arg(required = true, help = "Name of the new migration")] migration_name: String, - #[clap( - action, + #[arg( long, default_value = "true", help = "Generate migration file based on Utc time", - conflicts_with = "local-time", + conflicts_with = "local_time", display_order = 1001 )] universal_time: bool, - #[clap( - action, + #[arg( long, help = "Generate migration file based on Local time", - conflicts_with = "universal-time", + conflicts_with = "universal_time", display_order = 1002 )] local_time: bool, }, - #[clap( + #[command( about = "Drop all tables from the database, then reapply all migrations", display_order = 30 )] Fresh, - #[clap( + #[command( about = "Rollback all applied migrations, then reapply all migrations", display_order = 40 )] Refresh, - #[clap(about = "Rollback all applied migrations", display_order = 50)] + #[command(about = "Rollback all applied migrations", display_order = 50)] Reset, - #[clap(about = "Check the status of all migrations", display_order = 60)] + #[command(about = "Check the status of all migrations", display_order = 60)] Status, - #[clap(about = "Apply pending migrations", display_order = 70)] + #[command(about = "Apply pending migrations", display_order = 70)] Up { - #[clap( - value_parser, - short, - long, - help = "Number of pending migrations to apply" - )] + #[arg(short, long, help = "Number of pending migrations to apply")] num: Option, }, - #[clap( - value_parser, - about = "Rollback applied migrations", - display_order = 80 - )] + #[command(about = "Rollback applied migrations", display_order = 80)] Down { - #[clap( - value_parser, + #[arg( short, long, default_value = "1", @@ -175,54 +155,46 @@ pub enum MigrateSubcommands { #[derive(Subcommand, PartialEq, Eq, Debug)] pub enum GenerateSubcommands { - #[clap(about = "Generate entity")] - #[clap(arg_required_else_help = true)] - #[clap(group(ArgGroup::new("formats").args(&["compact-format", "expanded-format"])))] - #[clap(group(ArgGroup::new("group-tables").args(&["tables", "include-hidden-tables"])))] + #[command(about = "Generate entity")] + #[command(group(ArgGroup::new("formats").args(&["compact_format", "expanded_format"])))] + #[command(group(ArgGroup::new("group-tables").args(&["tables", "include_hidden_tables"])))] Entity { - #[clap(action, long, help = "Generate entity file of compact format")] + #[arg(long, help = "Generate entity file of compact format")] compact_format: bool, - #[clap(action, long, help = "Generate entity file of expanded format")] + #[arg(long, help = "Generate entity file of expanded format")] expanded_format: bool, - #[clap( - action, + #[arg( long, help = "Generate entity file for hidden tables (i.e. table name starts with an underscore)" )] include_hidden_tables: bool, - #[clap( - value_parser, + #[arg( short = 't', long, - use_value_delimiter = true, - takes_value = true, + value_delimiter = ',', help = "Generate entity file for specified tables only (comma separated)" )] tables: Vec, - #[clap( - value_parser, + #[arg( long, - use_value_delimiter = true, - takes_value = true, + value_delimiter = ',', default_value = "seaql_migrations", help = "Skip generating entity file for specified tables (comma separated)" )] ignore_tables: Vec, - #[clap( - value_parser, + #[arg( long, default_value = "1", help = "The maximum amount of connections to use when connecting to the database." )] max_connections: u32, - #[clap( - value_parser, + #[arg( short = 'o', long, default_value = "./", @@ -230,8 +202,7 @@ pub enum GenerateSubcommands { )] output_dir: String, - #[clap( - value_parser, + #[arg( short = 's', long, env = "DATABASE_SCHEMA", @@ -242,17 +213,10 @@ pub enum GenerateSubcommands { )] database_schema: String, - #[clap( - value_parser, - short = 'u', - long, - env = "DATABASE_URL", - help = "Database URL" - )] + #[arg(short = 'u', long, env = "DATABASE_URL", help = "Database URL")] database_url: String, - #[clap( - value_parser, + #[arg( long, default_value = "none", help = "Automatically derive serde Serialize / Deserialize traits for the entity (none, \ @@ -260,23 +224,20 @@ pub enum GenerateSubcommands { )] with_serde: String, - #[clap( - action, + #[arg( long, help = "Generate a serde field attribute, '#[serde(skip_deserializing)]', for the primary key fields to skip them during deserialization, this flag will be affective only when '--with-serde' is 'both' or 'deserialize'" )] serde_skip_deserializing_primary_key: bool, - #[clap( - action, + #[arg( long, default_value = "false", help = "Opt-in to add skip attributes to hidden columns (i.e. when 'with-serde' enabled and column name starts with an underscore)" )] serde_skip_hidden_column: bool, - #[clap( - action, + #[arg( long, default_value = "false", long_help = "Automatically derive the Copy trait on generated enums.\n\ @@ -286,17 +247,15 @@ pub enum GenerateSubcommands { )] with_copy_enums: bool, - #[clap( - arg_enum, - value_parser, + #[arg( long, - default_value = "chrono", + default_value_t, + value_enum, help = "The datetime crate to use for generating entities." )] date_time_crate: DateTimeCrate, - #[clap( - action, + #[arg( long, short = 'l', default_value = "false", @@ -304,26 +263,21 @@ pub enum GenerateSubcommands { )] lib: bool, - #[clap( - value_parser, + #[arg( long, - use_value_delimiter = true, - takes_value = true, + value_delimiter = ',', help = "Add extra derive macros to generated model struct (comma separated), e.g. `--model-extra-derives 'ts_rs::Ts','CustomDerive'`" )] model_extra_derives: Vec, - #[clap( - value_parser, + #[arg( long, - use_value_delimiter = true, - takes_value = true, + value_delimiter = ',', help = r#"Add extra attributes to generated model struct, no need for `#[]` (comma separated), e.g. `--model-extra-attributes 'serde(rename_all = "camelCase")','ts(export)'`"# )] model_extra_attributes: Vec, - #[clap( - action, + #[arg( long, default_value = "false", long_help = "Generate helper Enumerations that are used by Seaography." @@ -332,8 +286,9 @@ pub enum GenerateSubcommands { }, } -#[derive(ArgEnum, Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum, Default)] pub enum DateTimeCrate { + #[default] Chrono, Time, } diff --git a/sea-orm-cli/src/commands/generate.rs b/sea-orm-cli/src/commands/generate.rs index af6d9f7b70..7de19d9aa1 100644 --- a/sea-orm-cli/src/commands/generate.rs +++ b/sea-orm-cli/src/commands/generate.rs @@ -237,7 +237,7 @@ impl From for CodegenDateTimeCrate { #[cfg(test)] mod tests { - use clap::StructOpt; + use clap::Parser; use super::*; use crate::{Cli, Commands}; diff --git a/sea-orm-migration/Cargo.toml b/sea-orm-migration/Cargo.toml index a77eb316cc..73ab20084f 100644 --- a/sea-orm-migration/Cargo.toml +++ b/sea-orm-migration/Cargo.toml @@ -21,7 +21,7 @@ path = "src/lib.rs" [dependencies] async-trait = { version = "0.1", default-features = false } -clap = { version = "3.2", default-features = false, features = ["std", "env", "derive"], optional = true } +clap = { version = "4.3", features = ["env", "derive"], optional = true } dotenvy = { version = "0.15", default-features = false, optional = true } sea-orm = { version = "0.12.0", path = "../", default-features = false, features = ["macros"] } sea-orm-cli = { version = "0.12.0", path = "../sea-orm-cli", default-features = false, optional = true } diff --git a/sea-orm-migration/src/cli.rs b/sea-orm-migration/src/cli.rs index a7a72539f5..aa0d3668bc 100644 --- a/sea-orm-migration/src/cli.rs +++ b/sea-orm-migration/src/cli.rs @@ -87,13 +87,12 @@ where } #[derive(Parser)] -#[clap(version)] +#[command(version)] pub struct Cli { - #[clap(action, short = 'v', long, global = true, help = "Show debug messages")] + #[arg(short = 'v', long, global = true, help = "Show debug messages")] verbose: bool, - #[clap( - value_parser, + #[arg( global = true, short = 's', long, @@ -104,8 +103,7 @@ pub struct Cli { )] database_schema: Option, - #[clap( - value_parser, + #[arg( global = true, short = 'u', long, @@ -114,7 +112,7 @@ pub struct Cli { )] database_url: Option, - #[clap(subcommand)] + #[command(subcommand)] command: Option, } From 0c7e456251322359b412534823b1e93cfe3d3256 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Sat, 3 Jun 2023 11:43:34 +0800 Subject: [PATCH 0008/1371] CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf31bd8d9b..b462236328 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -252,6 +252,7 @@ assert_eq!(migration.status(), MigrationStatus::Pending); * Upgrade `sea-query` to `0.29` https://github.com/SeaQL/sea-orm/pull/1562 * Upgrade `sea-query-binder` to `0.4` https://github.com/SeaQL/sea-orm/pull/1562 * Upgrade `sea-schema` to `0.12` https://github.com/SeaQL/sea-orm/pull/1562 +* Upgrade `clap` to `4.3` https://github.com/SeaQL/sea-orm/pull/1468 ### Bug Fixes From 126f96b5c68a4dc3370e53afa3cff240a85a5f9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojt=C4=9Bch=20Jungmann?= Date: Tue, 6 Jun 2023 05:07:59 +0200 Subject: [PATCH 0009/1371] community: Add Portfolio to COMMUNITY.md (#1686) --- COMMUNITY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/COMMUNITY.md b/COMMUNITY.md index ccadd8255c..85b9289a05 100644 --- a/COMMUNITY.md +++ b/COMMUNITY.md @@ -38,6 +38,7 @@ If you have built an app using SeaORM and want to showcase it, feel free to open - [JinShu](https://github.com/gengteng/jinshu) | A cross-platform **I**nstant **M**essaging system written in 🦀 | DB: MySQL, Postgres - [Kitsune](https://github.com/kitsune-soc/kitsune) | 🦊 ActivityPub-federated microblogging | DB: Postgres, SQLite - [THUBurrow](https://github.com/BobAnkh/THUBurrow) | A campus forum built by Next.js and Rocket | DB: Postgres +- [Portfolio](https://github.com/admisio/Portfolio) | Encrypted high school 🏫 admissions service | DB: Postgres, SQLite #### Bots - [bulbbot-gw](https://github.com/TeamBulbbot/bulbbot-gw) | The gateway code for the Discord bot Bulbbot | DB: Postgres From d099669ad8d031afe43c3afff1c73b3e27e4de95 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Tue, 6 Jun 2023 15:00:05 +0800 Subject: [PATCH 0010/1371] Cleanup ambiguous glob imports (#1692) * Cleanup ambiguous glob imports * fix --- tests/common/features/schema.rs | 2 -- tests/connection_tests.rs | 3 +-- tests/loader_tests.rs | 2 +- tests/relational_tests.rs | 4 ++-- tests/returning_tests.rs | 4 ++-- tests/self_join_tests.rs | 2 +- tests/sequential_op_tests.rs | 4 +++- tests/transaction_tests.rs | 3 +-- 8 files changed, 11 insertions(+), 13 deletions(-) diff --git a/tests/common/features/schema.rs b/tests/common/features/schema.rs index e2855b5571..12897ca896 100644 --- a/tests/common/features/schema.rs +++ b/tests/common/features/schema.rs @@ -1,5 +1,3 @@ -pub use super::super::bakery_chain::*; - use super::*; use crate::common::setup::{create_enum, create_table, create_table_without_asserts}; use sea_orm::{ diff --git a/tests/connection_tests.rs b/tests/connection_tests.rs index ae0304d577..83abb1f985 100644 --- a/tests/connection_tests.rs +++ b/tests/connection_tests.rs @@ -2,8 +2,7 @@ pub mod common; pub use common::{bakery_chain::*, setup::*, TestContext}; use pretty_assertions::assert_eq; -pub use sea_orm::entity::*; -pub use sea_orm::*; +use sea_orm::prelude::*; #[sea_orm_macros::test] #[cfg(any( diff --git a/tests/loader_tests.rs b/tests/loader_tests.rs index 2db148a2c9..c0cb408b12 100644 --- a/tests/loader_tests.rs +++ b/tests/loader_tests.rs @@ -1,7 +1,7 @@ pub mod common; pub use common::{bakery_chain::*, setup::*, TestContext}; -pub use sea_orm::{entity::*, query::*, DbConn, DbErr, FromQueryResult}; +use sea_orm::{entity::*, query::*, DbConn, DbErr}; #[sea_orm_macros::test] #[cfg(any( diff --git a/tests/relational_tests.rs b/tests/relational_tests.rs index 4992753421..959f055778 100644 --- a/tests/relational_tests.rs +++ b/tests/relational_tests.rs @@ -4,8 +4,8 @@ pub use chrono::offset::Utc; pub use common::{bakery_chain::*, setup::*, TestContext}; pub use rust_decimal::prelude::*; pub use rust_decimal_macros::dec; -pub use sea_orm::{entity::*, query::*, DbErr, DerivePartialModel, FromQueryResult}; -pub use sea_query::{Alias, Expr, Func, SimpleExpr}; +use sea_orm::{entity::*, query::*, DbErr, DerivePartialModel, FromQueryResult}; +use sea_query::{Expr, Func, SimpleExpr}; pub use uuid::Uuid; // Run the test locally: diff --git a/tests/returning_tests.rs b/tests/returning_tests.rs index 4f8389317f..f6025c740d 100644 --- a/tests/returning_tests.rs +++ b/tests/returning_tests.rs @@ -1,8 +1,8 @@ pub mod common; pub use common::{bakery_chain::*, setup::*, TestContext}; -pub use sea_orm::{entity::prelude::*, *}; -pub use sea_query::Query; +use sea_orm::entity::prelude::*; +use sea_query::Query; #[sea_orm_macros::test] #[cfg(any( diff --git a/tests/self_join_tests.rs b/tests/self_join_tests.rs index a9c8fc0eda..afe4443651 100644 --- a/tests/self_join_tests.rs +++ b/tests/self_join_tests.rs @@ -2,7 +2,7 @@ pub mod common; pub use common::{features::*, setup::*, TestContext}; use pretty_assertions::assert_eq; -use sea_orm::{entity::prelude::*, *}; +use sea_orm::{entity::prelude::*, IntoActiveModel, QueryOrder}; #[sea_orm_macros::test] #[cfg(any( diff --git a/tests/sequential_op_tests.rs b/tests/sequential_op_tests.rs index 2b0ef97bcd..208b388c6e 100644 --- a/tests/sequential_op_tests.rs +++ b/tests/sequential_op_tests.rs @@ -4,9 +4,11 @@ pub use chrono::offset::Utc; pub use common::{bakery_chain::*, setup::*, TestContext}; pub use rust_decimal::prelude::*; pub use rust_decimal_macros::dec; -pub use sea_orm::{entity::*, query::*, DatabaseConnection, FromQueryResult}; pub use uuid::Uuid; +#[cfg(any(feature = "sqlx-mysql", feature = "sqlx-postgres"))] +use sea_orm::{entity::*, query::*, DatabaseConnection, FromQueryResult}; + // Run the test locally: // DATABASE_URL="mysql://root:@localhost" cargo test --features sqlx-mysql,runtime-async-std --test sequential_op_tests #[sea_orm_macros::test] diff --git a/tests/transaction_tests.rs b/tests/transaction_tests.rs index 98643ef1c8..ffc937a6d7 100644 --- a/tests/transaction_tests.rs +++ b/tests/transaction_tests.rs @@ -2,8 +2,7 @@ pub mod common; pub use common::{bakery_chain::*, setup::*, TestContext}; use pretty_assertions::assert_eq; -pub use sea_orm::entity::*; -pub use sea_orm::*; +use sea_orm::{prelude::*, AccessMode, DatabaseTransaction, IsolationLevel, Set, TransactionTrait}; #[sea_orm_macros::test] #[cfg(any( From adc467f22857c7e16f941ecb81cbc3b23768a7ed Mon Sep 17 00:00:00 2001 From: Alvaro Parker <64918109+AlvaroParker@users.noreply.github.com> Date: Tue, 6 Jun 2023 05:38:12 -0400 Subject: [PATCH 0011/1371] Fix a small typo (#1685) --- src/entity/model.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/entity/model.rs b/src/entity/model.rs index 0a66e7545b..d23e9f541e 100644 --- a/src/entity/model.rs +++ b/src/entity/model.rs @@ -37,7 +37,7 @@ pub trait ModelTrait: Clone + Send + Debug { l.find_linked().belongs_to_tbl_alias(self, tbl_alias) } - /// Delete an model + /// Delete a model async fn delete<'a, A, C>(self, db: &'a C) -> Result where Self: IntoActiveModel, From 1431d8027c425ab8e04e9b9314fefb67fe0173a8 Mon Sep 17 00:00:00 2001 From: Benji Grant Date: Sun, 11 Jun 2023 20:13:08 +1000 Subject: [PATCH 0012/1371] COMMUNITY.md (#1697) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Crab Fit 🦀 --- COMMUNITY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/COMMUNITY.md b/COMMUNITY.md index 85b9289a05..d4af315fee 100644 --- a/COMMUNITY.md +++ b/COMMUNITY.md @@ -39,6 +39,7 @@ If you have built an app using SeaORM and want to showcase it, feel free to open - [Kitsune](https://github.com/kitsune-soc/kitsune) | 🦊 ActivityPub-federated microblogging | DB: Postgres, SQLite - [THUBurrow](https://github.com/BobAnkh/THUBurrow) | A campus forum built by Next.js and Rocket | DB: Postgres - [Portfolio](https://github.com/admisio/Portfolio) | Encrypted high school 🏫 admissions service | DB: Postgres, SQLite +- [Crab Fit](https://crab.fit) [![GitHub stars](https://img.shields.io/github/stars/GRA0007/crab.fit.svg?style=social)](https://github.com/GRA0007/crab.fit) | Align your schedules to find the perfect time that works for everyone. | DB: MySQL, Postgres, SQLite #### Bots - [bulbbot-gw](https://github.com/TeamBulbbot/bulbbot-gw) | The gateway code for the Discord bot Bulbbot | DB: Postgres From d35cd6aa3bd1ab4cdcff72a9f6937ca2acb9a869 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Mon, 12 Jun 2023 17:19:30 +0800 Subject: [PATCH 0013/1371] Test suit load environment variable from env files (#1701) --- .gitignore | 3 ++- Cargo.toml | 1 + tests/basic.rs | 3 +++ tests/common/mod.rs | 3 +++ 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 558044adf4..74509beb96 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ Cargo.lock *.sublime* .vscode .idea/* -*/.idea/* \ No newline at end of file +*/.idea/* +.env.local \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index f5aaecd688..8e18215140 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,6 +61,7 @@ time = { version = "0.3", features = ["macros"] } uuid = { version = "1", features = ["v4"] } once_cell = "1.8" arraystring = "0.3" +dotenv = "0.15" [features] debug-print = [] diff --git a/tests/basic.rs b/tests/basic.rs index 8c466ea604..54a9f47c01 100644 --- a/tests/basic.rs +++ b/tests/basic.rs @@ -8,6 +8,9 @@ pub use sea_orm::{entity::*, error::*, query::*, sea_query, tests_cfg::*, Databa #[sea_orm_macros::test] #[cfg(feature = "sqlx-sqlite")] async fn main() -> Result<(), DbErr> { + dotenv::from_filename(".env.local").ok(); + dotenv::from_filename(".env").ok(); + let base_url = std::env::var("DATABASE_URL").unwrap_or_else(|_| "sqlite::memory:".to_owned()); let db: DbConn = Database::connect(&base_url).await?; diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 68d986b60a..359bc9dbd8 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -13,6 +13,9 @@ pub struct TestContext { impl TestContext { pub async fn new(test_name: &str) -> Self { + dotenv::from_filename(".env.local").ok(); + dotenv::from_filename(".env").ok(); + let base_url = std::env::var("DATABASE_URL").expect("Enviroment variable 'DATABASE_URL' not set"); let db: DatabaseConnection = setup::setup(&base_url, test_name).await; From 0816faa7f17f952484c4a5516cfe7f1df28b1489 Mon Sep 17 00:00:00 2001 From: darkmmon Date: Tue, 13 Jun 2023 21:01:15 +0800 Subject: [PATCH 0014/1371] #1696 fix (#1702) * Test suit load environment variable from env files * Added expr() and exprs() for QuerySelect trait and Minor typo fix * fmt * Added doc testing for the new functions * Remove excess comment for doc test Co-authored-by: Chris Tsang * Remove excess comment for doc test Co-authored-by: Chris Tsang * updated doc tests to make example more realistic * changed doc test again for more realistic query and added expr_as() * aligned expr_as() alias input with column_as() input * update doc test for expr_as() according to previous changes --------- Co-authored-by: Billy Chan Co-authored-by: Chris Tsang --- build-tools/docker-create.sh | 2 +- src/query/helper.rs | 75 +++++++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/build-tools/docker-create.sh b/build-tools/docker-create.sh index cf5aac4ea0..8d22ee1e7b 100644 --- a/build-tools/docker-create.sh +++ b/build-tools/docker-create.sh @@ -3,7 +3,7 @@ # Delete all containers # $ docker rm -f $(docker ps -a -q) # -# Delete all volumns +# Delete all volumes # $ docker volume rm $(docker volume ls -q) # # Delete all images diff --git a/src/query/helper.rs b/src/query/helper.rs index ef754dcf92..43bc9da9ff 100644 --- a/src/query/helper.rs +++ b/src/query/helper.rs @@ -4,7 +4,7 @@ use crate::{ }; use sea_query::{ Alias, ConditionType, Expr, Iden, IntoCondition, IntoIden, LockType, SeaRc, SelectExpr, - SelectStatement, TableRef, + SelectStatement, SimpleExpr, TableRef, }; pub use sea_query::{Condition, ConditionalStatement, DynIden, JoinType, Order, OrderedStatement}; @@ -446,6 +446,79 @@ pub trait QuerySelect: Sized { self.query().lock_exclusive(); self } + + /// Add an expression to the select expression list. + /// ``` + /// use sea_orm::sea_query::Expr; + /// use sea_orm::{entity::*, tests_cfg::cake, DbBackend, QuerySelect, QueryTrait}; + /// + /// assert_eq!( + /// cake::Entity::find() + /// .select_only() + /// .expr(Expr::col((cake::Entity, cake::Column::Id))) + /// .build(DbBackend::MySql) + /// .to_string(), + /// "SELECT `cake`.`id` FROM `cake`" + /// ); + /// ``` + fn expr(mut self, expr: T) -> Self + where + T: Into, + { + self.query().expr(expr); + self + } + + /// Add select expressions from vector of [`SelectExpr`]. + /// ``` + /// use sea_orm::sea_query::Expr; + /// use sea_orm::{entity::*, tests_cfg::cake, DbBackend, QuerySelect, QueryTrait}; + /// + /// assert_eq!( + /// cake::Entity::find() + /// .select_only() + /// .exprs([ + /// Expr::col((cake::Entity, cake::Column::Id)), + /// Expr::col((cake::Entity, cake::Column::Name)), + /// ]) + /// .build(DbBackend::MySql) + /// .to_string(), + /// "SELECT `cake`.`id`, `cake`.`name` FROM `cake`" + /// ); + /// ``` + fn exprs(mut self, exprs: I) -> Self + where + T: Into, + I: IntoIterator, + { + self.query().exprs(exprs); + self + } + + /// Select column. + /// ``` + /// use sea_orm::sea_query::{Alias, Expr, Func}; + /// use sea_orm::{entity::*, tests_cfg::cake, DbBackend, QuerySelect, QueryTrait}; + /// + /// assert_eq!( + /// cake::Entity::find() + /// .expr_as( + /// Func::upper(Expr::col((cake::Entity, cake::Column::Name))), + /// "name_upper" + /// ) + /// .build(DbBackend::MySql) + /// .to_string(), + /// "SELECT `cake`.`id`, `cake`.`name`, UPPER(`cake`.`name`) AS `name_upper` FROM `cake`" + /// ); + /// ``` + fn expr_as(&mut self, expr: T, alias: A) -> &mut Self + where + T: Into, + A: IntoIdentity, + { + self.query().expr_as(expr, alias.into_identity()); + self + } } // LINT: when the column does not appear in tables selected from From fc1a5475b4c2208e3ebafd80c00b334f0805c575 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Tue, 13 Jun 2023 21:07:20 +0800 Subject: [PATCH 0015/1371] CHANGELOG --- CHANGELOG.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b462236328..7ebc21005d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -214,6 +214,44 @@ pub enum RelatedEntity { The DeriveRelatedEntity derive macro will implement `seaography::RelationBuilder` for `RelatedEntity` enumeration when the `seaography` feature is enabled +* Add `expr`, `exprs` and `expr_as` methods to `QuerySelect` trait +```rs +use sea_orm::sea_query::Expr; +use sea_orm::{entity::*, tests_cfg::cake, DbBackend, QuerySelect, QueryTrait}; + +assert_eq!( + cake::Entity::find() + .select_only() + .expr(Expr::col((cake::Entity, cake::Column::Id))) + .build(DbBackend::MySql) + .to_string(), + "SELECT `cake`.`id` FROM `cake`" +); + +assert_eq!( + cake::Entity::find() + .select_only() + .exprs([ + Expr::col((cake::Entity, cake::Column::Id)), + Expr::col((cake::Entity, cake::Column::Name)), + ]) + .build(DbBackend::MySql) + .to_string(), + "SELECT `cake`.`id`, `cake`.`name` FROM `cake`" +); + +assert_eq!( + cake::Entity::find() + .expr_as( + Func::upper(Expr::col((cake::Entity, cake::Column::Name))), + "name_upper" + ) + .build(DbBackend::MySql) + .to_string(), + "SELECT `cake`.`id`, `cake`.`name`, UPPER(`cake`.`name`) AS `name_upper` FROM `cake`" +); +``` + ### Enhancements * Added `Migration::name()` and `Migration::status()` getters for the name and status of `sea_orm_migration::Migration` https://github.com/SeaQL/sea-orm/pull/1519 From c477f67190b3f813500832411e2ac58b887d1623 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Tue, 13 Jun 2023 21:14:59 +0800 Subject: [PATCH 0016/1371] COMMUNITY --- COMMUNITY.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/COMMUNITY.md b/COMMUNITY.md index d4af315fee..509b63a7f2 100644 --- a/COMMUNITY.md +++ b/COMMUNITY.md @@ -35,11 +35,11 @@ If you have built an app using SeaORM and want to showcase it, feel free to open #### Social - [aeroFans](https://github.com/naryand/aerofans) | Full stack forum-like social media platform in Rust and WebAssembly | DB: Postgres +- [Crab Fit](https://crab.fit) [![GitHub stars](https://img.shields.io/github/stars/GRA0007/crab.fit.svg?style=social)](https://github.com/GRA0007/crab.fit) | Align your schedules to find the perfect time that works for everyone. | DB: MySQL, Postgres, SQLite - [JinShu](https://github.com/gengteng/jinshu) | A cross-platform **I**nstant **M**essaging system written in 🦀 | DB: MySQL, Postgres - [Kitsune](https://github.com/kitsune-soc/kitsune) | 🦊 ActivityPub-federated microblogging | DB: Postgres, SQLite -- [THUBurrow](https://github.com/BobAnkh/THUBurrow) | A campus forum built by Next.js and Rocket | DB: Postgres - [Portfolio](https://github.com/admisio/Portfolio) | Encrypted high school 🏫 admissions service | DB: Postgres, SQLite -- [Crab Fit](https://crab.fit) [![GitHub stars](https://img.shields.io/github/stars/GRA0007/crab.fit.svg?style=social)](https://github.com/GRA0007/crab.fit) | Align your schedules to find the perfect time that works for everyone. | DB: MySQL, Postgres, SQLite +- [THUBurrow](https://github.com/BobAnkh/THUBurrow) | A campus forum built by Next.js and Rocket | DB: Postgres #### Bots - [bulbbot-gw](https://github.com/TeamBulbbot/bulbbot-gw) | The gateway code for the Discord bot Bulbbot | DB: Postgres @@ -60,9 +60,9 @@ If you have built an app using SeaORM and want to showcase it, feel free to open - [Email view tracker](https://github.com/friedemannsommer/email-view-tracker) | Simple web UI to create tracking URLs for HTML emails. | DB: MySQL, Postgres - [LLDAP](https://github.com/nitnelave/lldap) ![GitHub stars](https://img.shields.io/github/stars/nitnelave/lldap.svg?style=social) | A light LDAP server for user management | DB: MySQL, Postgres, SQLite - [RSS aggregator](https://github.com/fistons/rss-aggregator)| A small RSS aggregator and API using Actix Web and SeaORM | DB: Postgres +- [ruuth](https://github.com/outurnate/ruuth) ([repository](https://github.com/outurnate/ruuth)) | A simple nginx auth_request backend providing MFA and lockout mechanisms | DB: MySQL, Postgres, SQLite - [snmp-sim-rust](https://github.com/sonalake/snmp-sim-rust) | SNMP Simulator | DB: SQLite - [Wikipedia Speedrun](https://wikipediaspeedrun.com) ([repository](https://github.com/hut8/wikipedia-speedrun)) | Finds shortest paths between Wikipedia articles | DB: SQLite -- [ruuth](https://github.com/outurnate/ruuth) ([repository](https://github.com/outurnate/ruuth)) | A simple nginx auth_request backend providing MFA and lockout mechanisms | DB: MySQL, Postgres, SQLite #### Url Shortener - [Dinoly](https://github.com/ippsav/Dinoly) | An url shortener using Axum web framework and SeaORM | DB: Postgres @@ -101,9 +101,9 @@ If you have built an app using SeaORM and want to showcase it, feel free to open ### Scaffolding -- [Axum Book Management](https://github.com/lz1998/axum-book-management) | CRUD system of book-management with ORM and JWT for educational purposes | DB: MySQL - [actix-react-starter-template](https://github.com/aslamplr/actix-react-starter-template) | Actix Web + SeaORM + React + Redux + Redux Saga project starter template | DB: Postgres - [Adta](https://github.com/aaronleopold/adta) | Adta is **A**nother **D**amn **T**odo **A**pp, fun little side project | DB: MySQL, Postgres, SQLite +- [Axum Book Management](https://github.com/lz1998/axum-book-management) | CRUD system of book-management with ORM and JWT for educational purposes | DB: MySQL - [BookStore](https://github.com/AfaanBilal/bookstore) | A bookstore manegement system HTTP REST API using Rocket, SeaORM and JWT. | DB: MySQL - [crud-rs](https://github.com/onichandame/crud-rs) | A framework combining async-graphql and SeaORM - [http-api-rs](https://github.com/daniel-samson/http-api-rs) | Template project for creating REST API's in rust with swagger ui From b37e7655f85d3353a8853d1156c098a618f517fd Mon Sep 17 00:00:00 2001 From: darkmmon Date: Mon, 19 Jun 2023 17:19:08 +0800 Subject: [PATCH 0017/1371] Added enum for SQLx Error and basic functions for it (#1707) * Added enum for SQL Error and basic functions for it * fmt * Restructured code to allow multiple database system simultaneously * updated code error and moved Eq to derive by compiler * added ForeignKeyViolation implementation * fmt * added type param * changed type param and used unwrap_or_default instead of unwrap * fmt * refactor code and update documentation * updated some error code and fixed formatting * create SQL Err test for UniqueConstraintViolation * fmt * updated error codes for mysql errors * added test for ForeignKeyConstraintViolation and removed unused imports/codes * fmt * updated doc and error message * added static str in SqlErr * changed to add error message into SqlErr as String * updated test file to check database type through connection * fmt * updated test for SqlErr to match the error type only * Removed comment and fixed grammar mistake * fmt * Update src/error.rs * Refactoring --------- Co-authored-by: Chris Tsang Co-authored-by: Billy Chan --- src/error.rs | 90 ++++++++++++++++++++++++++++++++++++++++++ tests/sql_err_tests.rs | 67 +++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 tests/sql_err_tests.rs diff --git a/src/error.rs b/src/error.rs index e470bb6a9a..125da4f84c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -139,3 +139,93 @@ where { DbErr::Json(s.to_string()) } + +/// An error from unsuccessful SQL query +#[derive(Error, Debug, Clone, PartialEq, Eq)] +#[non_exhaustive] +pub enum SqlErr { + /// Error for duplicate record in unique field or primary key field + #[error("Unique Constraint Violated: {0}")] + UniqueConstraintViolation(String), + /// Error for Foreign key constraint + #[error("Foreign Key Constraint Violated: {0}")] + ForeignKeyConstraintViolation(String), +} + +#[allow(dead_code)] +impl DbErr { + /// Convert generic DbErr by sqlx to SqlErr, return none if the error is not any type of SqlErr + pub fn sql_err(&self) -> Option { + #[cfg(any( + feature = "sqlx-mysql", + feature = "sqlx-postgres", + feature = "sqlx-sqlite" + ))] + { + use std::ops::Deref; + if let DbErr::Exec(RuntimeErr::SqlxError(sqlx::Error::Database(e))) + | DbErr::Query(RuntimeErr::SqlxError(sqlx::Error::Database(e))) = self + { + let error_code = e.code().unwrap_or_default(); + let _error_code_expanded = error_code.deref(); + #[cfg(feature = "sqlx-mysql")] + if e.try_downcast_ref::() + .is_some() + { + let error_number = e + .try_downcast_ref::()? + .number(); + match error_number { + // 1022 Can't write; duplicate key in table '%s' + // 1062 Duplicate entry '%s' for key %d + // 1169 Can't write, because of unique constraint, to table '%s' + // 1586 Duplicate entry '%s' for key '%s' + 1022 | 1062 | 1169 | 1586 => { + return Some(SqlErr::UniqueConstraintViolation(e.message().into())) + } + // 1216 Cannot add or update a child row: a foreign key constraint fails + // 1217 Cannot delete or update a parent row: a foreign key constraint fails + // 1451 Cannot delete or update a parent row: a foreign key constraint fails (%s) + // 1452 Cannot add or update a child row: a foreign key constraint fails (%s) + // 1557 Upholding foreign key constraints for table '%s', entry '%s', key %d would lead to a duplicate entry + // 1761 Foreign key constraint for table '%s', record '%s' would lead to a duplicate entry in table '%s', key '%s' + // 1762 Foreign key constraint for table '%s', record '%s' would lead to a duplicate entry in a child table + 1216 | 1217 | 1451 | 1452 | 1557 | 1761 | 1762 => { + return Some(SqlErr::ForeignKeyConstraintViolation(e.message().into())) + } + _ => return None, + } + } + #[cfg(feature = "sqlx-postgres")] + if e.try_downcast_ref::() + .is_some() + { + match _error_code_expanded { + "23505" => { + return Some(SqlErr::UniqueConstraintViolation(e.message().into())) + } + "23503" => { + return Some(SqlErr::ForeignKeyConstraintViolation(e.message().into())) + } + _ => return None, + } + } + #[cfg(feature = "sqlx-sqlite")] + if e.try_downcast_ref::().is_some() { + match _error_code_expanded { + // error code 1555 refers to the primary key's unique constraint violation + // error code 2067 refers to the UNIQUE unique constraint violation + "1555" | "2067" => { + return Some(SqlErr::UniqueConstraintViolation(e.message().into())) + } + "787" => { + return Some(SqlErr::ForeignKeyConstraintViolation(e.message().into())) + } + _ => return None, + } + } + } + } + None + } +} diff --git a/tests/sql_err_tests.rs b/tests/sql_err_tests.rs new file mode 100644 index 0000000000..e73b3afe5d --- /dev/null +++ b/tests/sql_err_tests.rs @@ -0,0 +1,67 @@ +pub mod common; +pub use common::{bakery_chain::*, setup::*, TestContext}; +use rust_decimal_macros::dec; +pub use sea_orm::{ + entity::*, error::DbErr, error::SqlErr, tests_cfg, DatabaseConnection, DbBackend, EntityName, + ExecResult, +}; +use uuid::Uuid; + +#[sea_orm_macros::test] +#[cfg(any( + feature = "sqlx-mysql", + feature = "sqlx-sqlite", + feature = "sqlx-postgres" +))] +async fn main() { + let ctx = TestContext::new("bakery_chain_sql_err_tests").await; + create_tables(&ctx.db).await.unwrap(); + test_error(&ctx.db).await; + ctx.delete().await; +} + +pub async fn test_error(db: &DatabaseConnection) { + let mud_cake = cake::ActiveModel { + name: Set("Moldy Cake".to_owned()), + price: Set(dec!(10.25)), + gluten_free: Set(false), + serial: Set(Uuid::new_v4()), + bakery_id: Set(None), + ..Default::default() + }; + + let cake = mud_cake.save(db).await.expect("could not insert cake"); + + // if compiling without sqlx, this assignment will complain, + // but the whole test is useless in that case anyway. + #[allow(unused_variables)] + let error: DbErr = cake + .into_active_model() + .insert(db) + .await + .expect_err("inserting should fail due to duplicate primary key"); + + assert!(matches!( + error.sql_err(), + Some(SqlErr::UniqueConstraintViolation(_)) + )); + + let fk_cake = cake::ActiveModel { + name: Set("fk error Cake".to_owned()), + price: Set(dec!(10.25)), + gluten_free: Set(false), + serial: Set(Uuid::new_v4()), + bakery_id: Set(Some(1000)), + ..Default::default() + }; + + let fk_error = fk_cake + .insert(db) + .await + .expect_err("create foreign key should fail with non-primary key"); + + assert!(matches!( + fk_error.sql_err(), + Some(SqlErr::ForeignKeyConstraintViolation(_)) + )); +} From 5c75a53614132a6506f3bbed76fbcccc66bbf753 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Mon, 19 Jun 2023 17:19:28 +0800 Subject: [PATCH 0018/1371] CHANGELOG --- CHANGELOG.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ebc21005d..8c97c31455 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -251,6 +251,27 @@ assert_eq!( "SELECT `cake`.`id`, `cake`.`name`, UPPER(`cake`.`name`) AS `name_upper` FROM `cake`" ); ``` +* Add `DbErr::sql_err()` method to convert error into common database errors `SqlErr`, such as unique constraint or foreign key violation errors. https://github.com/SeaQL/sea-orm/pull/1707 +```rs +assert!(matches!( + cake + .into_active_model() + .insert(db) + .await + .expect_err("Insert a row with duplicated primary key") + .sql_err(), + Some(SqlErr::UniqueConstraintViolation(_)) +)); + +assert!(matches!( + fk_cake + .insert(db) + .await + .expect_err("Insert a row with invalid foreign key") + .sql_err(), + Some(SqlErr::ForeignKeyConstraintViolation(_)) +)); +``` ### Enhancements From 7c6ab8fa2d1e8f8d464726b4b67ee51fb01aa1c6 Mon Sep 17 00:00:00 2001 From: Tsiry Sandratraina Date: Mon, 19 Jun 2023 12:43:27 +0300 Subject: [PATCH 0019/1371] Update COMMUNITY.md (#1710) --- COMMUNITY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/COMMUNITY.md b/COMMUNITY.md index 509b63a7f2..2d94ea3733 100644 --- a/COMMUNITY.md +++ b/COMMUNITY.md @@ -55,6 +55,7 @@ If you have built an app using SeaORM and want to showcase it, feel free to open #### Dev Tools - [nitro_repo](https://github.com/wyatt-herkamp/nitro_repo) | An OpenSource, lightweight, and fast artifact manager. | DB: MySQL, SQLite - [Orca](https://github.com/workfoxes/orca) | An No-code Test Automation platform using Actix, SeaORM, React. runs on the desktop and cloud | DB: Postgres +- [Crosup](https://github.com/tsirysndr/crosup) | Quickly setup your development environment on your Chromebook/ChromeOS or any Linux distribution 🐧 ❄️ 💻 🚀 ✨ | DB: SQLite #### System - [Email view tracker](https://github.com/friedemannsommer/email-view-tracker) | Simple web UI to create tracking URLs for HTML emails. | DB: MySQL, Postgres From 92ea837cdfed6d6789697710f18743e733a32125 Mon Sep 17 00:00:00 2001 From: darkmmon Date: Tue, 20 Jun 2023 17:17:18 +0800 Subject: [PATCH 0020/1371] Make insert statement not panic when inserting nothing (#1708) * end-of-day commit (WIP) * progress commit (WIP) * refactored and added InsertAttempt * async asjusting * completed implementation for insertAttempt in execution Added in tests for insertAttempt * updated wording for new INSERT type * removed InsertTrait --- src/executor/insert.rs | 66 ++++++++++++++++++++++ src/query/insert.rs | 110 +++++++++++++++++++++++++++++++++++- tests/empty_insert_tests.rs | 54 ++++++++++++++++++ 3 files changed, 228 insertions(+), 2 deletions(-) create mode 100644 tests/empty_insert_tests.rs diff --git a/src/executor/insert.rs b/src/executor/insert.rs index 96ad56cc18..be00dbed30 100644 --- a/src/executor/insert.rs +++ b/src/executor/insert.rs @@ -1,6 +1,7 @@ use crate::{ error::*, ActiveModelTrait, ColumnTrait, ConnectionTrait, EntityTrait, Insert, IntoActiveModel, Iterable, PrimaryKeyToColumn, PrimaryKeyTrait, SelectModel, SelectorRaw, Statement, TryFromU64, + TryInsert, }; use sea_query::{Expr, FromValueTuple, Iden, InsertStatement, IntoColumnRef, Query, ValueTuple}; use std::{future::Future, marker::PhantomData}; @@ -26,6 +27,71 @@ where pub last_insert_id: <<::Entity as EntityTrait>::PrimaryKey as PrimaryKeyTrait>::ValueType, } +/// The types of results for an INSERT operation +#[derive(Debug)] +pub enum TryInsertResult { + /// The INSERT operation did not insert any value + Empty, + /// Reserved + Conflicted, + /// Successfully inserted + Inserted(T), +} + +impl TryInsert +where + A: ActiveModelTrait, +{ + /// Execute an insert operation + #[allow(unused_mut)] + pub async fn exec<'a, C>(self, db: &'a C) -> TryInsertResult, DbErr>> + where + C: ConnectionTrait, + A: 'a, + { + if self.insert_struct.columns.is_empty() { + TryInsertResult::Empty + } else { + TryInsertResult::Inserted(self.insert_struct.exec(db).await) + } + } + + /// Execute an insert operation without returning (don't use `RETURNING` syntax) + /// Number of rows affected is returned + pub async fn exec_without_returning<'a, C>( + self, + db: &'a C, + ) -> TryInsertResult> + where + ::Model: IntoActiveModel, + C: ConnectionTrait, + A: 'a, + { + if self.insert_struct.columns.is_empty() { + TryInsertResult::Empty + } else { + TryInsertResult::Inserted(self.insert_struct.exec_without_returning(db).await) + } + } + + /// Execute an insert operation and return the inserted model (use `RETURNING` syntax if database supported) + pub async fn exec_with_returning<'a, C>( + self, + db: &'a C, + ) -> TryInsertResult::Model, DbErr>> + where + ::Model: IntoActiveModel, + C: ConnectionTrait, + A: 'a, + { + if self.insert_struct.columns.is_empty() { + TryInsertResult::Empty + } else { + TryInsertResult::Inserted(self.insert_struct.exec_with_returning(db).await) + } + } +} + impl Insert where A: ActiveModelTrait, diff --git a/src/query/insert.rs b/src/query/insert.rs index 64dc248b17..3fdb343c33 100644 --- a/src/query/insert.rs +++ b/src/query/insert.rs @@ -72,7 +72,7 @@ where /// r#"INSERT INTO "cake" ("name") VALUES ('Apple Pie')"#, /// ); /// ``` - pub fn one(m: M) -> Insert + pub fn one(m: M) -> Self where M: IntoActiveModel, { @@ -208,6 +208,15 @@ where self.query.on_conflict(on_conflict); self } + + /// Allow insert statement return safely if inserting nothing. + /// The database will not be affected. + pub fn on_empty_do_nothing(self) -> TryInsert + where + A: ActiveModelTrait, + { + TryInsert::from_insert(self) + } } impl QueryTrait for Insert @@ -229,11 +238,108 @@ where } } +/// Performs INSERT operations on a ActiveModel, will do nothing if input is empty. +/// +/// All functions works the same as if it is Insert. Please refer to Insert page for more information +#[derive(Debug)] +pub struct TryInsert +where + A: ActiveModelTrait, +{ + pub(crate) insert_struct: Insert, +} + +impl Default for TryInsert +where + A: ActiveModelTrait, +{ + fn default() -> Self { + Self::new() + } +} + +#[allow(missing_docs)] +impl TryInsert +where + A: ActiveModelTrait, +{ + pub(crate) fn new() -> Self { + Self { + insert_struct: Insert::new(), + } + } + + pub fn one(m: M) -> Self + where + M: IntoActiveModel, + { + Self::new().add(m) + } + + pub fn many(models: I) -> Self + where + M: IntoActiveModel, + I: IntoIterator, + { + Self::new().add_many(models) + } + + #[allow(clippy::should_implement_trait)] + pub fn add(mut self, m: M) -> Self + where + M: IntoActiveModel, + { + self.insert_struct = self.insert_struct.add(m); + self + } + + pub fn add_many(mut self, models: I) -> Self + where + M: IntoActiveModel, + I: IntoIterator, + { + for model in models.into_iter() { + self.insert_struct = self.insert_struct.add(model); + } + self + } + + pub fn on_conflict(mut self, on_conflict: OnConflict) -> Self { + self.insert_struct.query.on_conflict(on_conflict); + self + } + + // helper function for on_empty_do_nothing in Insert + pub fn from_insert(insert: Insert) -> Self { + Self { + insert_struct: insert, + } + } +} + +impl QueryTrait for TryInsert +where + A: ActiveModelTrait, +{ + type QueryStatement = InsertStatement; + + fn query(&mut self) -> &mut InsertStatement { + &mut self.insert_struct.query + } + + fn as_query(&self) -> &InsertStatement { + &self.insert_struct.query + } + + fn into_query(self) -> InsertStatement { + self.insert_struct.query + } +} #[cfg(test)] mod tests { use sea_query::OnConflict; - use crate::tests_cfg::cake; + use crate::tests_cfg::cake::{self, ActiveModel}; use crate::{ActiveValue, DbBackend, DbErr, EntityTrait, Insert, IntoActiveModel, QueryTrait}; #[test] diff --git a/tests/empty_insert_tests.rs b/tests/empty_insert_tests.rs new file mode 100644 index 0000000000..962947a9b6 --- /dev/null +++ b/tests/empty_insert_tests.rs @@ -0,0 +1,54 @@ +pub mod common; +mod crud; + +pub use common::{bakery_chain::*, setup::*, TestContext}; +pub use sea_orm::{ + entity::*, error::DbErr, tests_cfg, DatabaseConnection, DbBackend, EntityName, ExecResult, +}; + +pub use crud::*; +// use common::bakery_chain::*; +use sea_orm::{DbConn, TryInsertResult}; + +#[sea_orm_macros::test] +#[cfg(any( + feature = "sqlx-mysql", + feature = "sqlx-sqlite", + feature = "sqlx-postgres" +))] +async fn main() { + let ctx = TestContext::new("bakery_chain_empty_insert_tests").await; + create_tables(&ctx.db).await.unwrap(); + test(&ctx.db).await; + ctx.delete().await; +} + +pub async fn test(db: &DbConn) { + let seaside_bakery = bakery::ActiveModel { + name: Set("SeaSide Bakery".to_owned()), + profit_margin: Set(10.4), + ..Default::default() + }; + + let res = Bakery::insert(seaside_bakery) + .on_empty_do_nothing() + .exec(db) + .await; + + assert!(matches!(res, TryInsertResult::Inserted(_))); + + let empty_iterator = [bakery::ActiveModel { + name: Set("SeaSide Bakery".to_owned()), + profit_margin: Set(10.4), + ..Default::default() + }] + .into_iter() + .filter(|_| false); + + let empty_insert = Bakery::insert_many(empty_iterator) + .on_empty_do_nothing() + .exec(db) + .await; + + assert!(matches!(empty_insert, TryInsertResult::Empty)); +} From 5e5aa7cbd184db141446960e7960d12c9a61363a Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Tue, 20 Jun 2023 17:23:05 +0800 Subject: [PATCH 0021/1371] Changelog --- CHANGELOG.md | 11 +++++++++++ .../duplicated_many_to_many_paths/bills.rs | 4 ++-- .../users_saved_bills.rs | 4 ++-- .../duplicated_many_to_many_paths/users_votes.rs | 4 ++-- .../src/tests_cfg/many_to_many/bills.rs | 4 ++-- .../src/tests_cfg/many_to_many/users_votes.rs | 4 ++-- .../src/tests_cfg/many_to_many_multiple/bills.rs | 4 ++-- .../many_to_many_multiple/users_votes.rs | 12 ++++++------ .../src/tests_cfg/self_referencing/bills.rs | 8 ++------ .../src/tests_cfg/self_referencing/users.rs | 16 ++++------------ tests/empty_insert_tests.rs | 10 +--------- 11 files changed, 36 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c97c31455..5a39edf5e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -536,6 +536,16 @@ impl ColumnTrait for Column { * Added the `sea-orm-internal` feature flag to expose some SQLx types * Added `DatabaseConnection::get_*_connection_pool()` for accessing the inner SQLx connection pool https://github.com/SeaQL/sea-orm/pull/1297 * Re-exporting SQLx errors https://github.com/SeaQL/sea-orm/pull/1434 +* Added `TryInsert` that does not panic on empty inserts https://github.com/SeaQL/sea-orm/pull/1708 +```rust +// now, you can do: +let empty_insert = Bakery::insert_many(std::iter::empty()) + .on_empty_do_nothing() + .exec(db) + .await; + +assert!(matches!(empty_insert, TryInsertResult::Empty)); +``` ### Upgrades @@ -564,6 +574,7 @@ impl ColumnTrait for Column { * Fixes hitting 'negative last_insert_rowid' panic with Sqlite https://github.com/SeaQL/sea-orm/issues/1357 * Noop when update without providing any values https://github.com/SeaQL/sea-orm/pull/1384 * Fixes Syntax Error when saving active model that sets nothing https://github.com/SeaQL/sea-orm/pull/1376 +* Resolved `insert_many` failing if the models iterator is empty https://github.com/SeaQL/sea-orm/issues/873 ### Breaking Changes diff --git a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/bills.rs b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/bills.rs index a3a6911b98..baf33426eb 100644 --- a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/bills.rs +++ b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/bills.rs @@ -5,7 +5,7 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub user_id: Option , + pub user_id: Option, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] @@ -15,7 +15,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "NoAction", - on_delete = "NoAction", + on_delete = "NoAction" )] Users, #[sea_orm(has_many = "super::users_saved_bills::Entity")] diff --git a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_saved_bills.rs b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_saved_bills.rs index 4ef0281cd2..575bac58f1 100644 --- a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_saved_bills.rs +++ b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_saved_bills.rs @@ -16,7 +16,7 @@ pub enum Relation { from = "Column::BillId", to = "super::bills::Column::Id", on_update = "Cascade", - on_delete = "Cascade", + on_delete = "Cascade" )] Bills, #[sea_orm( @@ -24,7 +24,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "Cascade", - on_delete = "Cascade", + on_delete = "Cascade" )] Users, } diff --git a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_votes.rs b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_votes.rs index a04ad7fa73..62f9cf7d00 100644 --- a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_votes.rs +++ b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_votes.rs @@ -17,7 +17,7 @@ pub enum Relation { from = "Column::BillId", to = "super::bills::Column::Id", on_update = "Cascade", - on_delete = "Cascade", + on_delete = "Cascade" )] Bills, #[sea_orm( @@ -25,7 +25,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "Cascade", - on_delete = "Cascade", + on_delete = "Cascade" )] Users, } diff --git a/sea-orm-codegen/src/tests_cfg/many_to_many/bills.rs b/sea-orm-codegen/src/tests_cfg/many_to_many/bills.rs index 5a3acef9cb..f2de469f0a 100644 --- a/sea-orm-codegen/src/tests_cfg/many_to_many/bills.rs +++ b/sea-orm-codegen/src/tests_cfg/many_to_many/bills.rs @@ -5,7 +5,7 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub user_id: Option , + pub user_id: Option, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] @@ -15,7 +15,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "NoAction", - on_delete = "NoAction", + on_delete = "NoAction" )] Users, #[sea_orm(has_many = "super::users_votes::Entity")] diff --git a/sea-orm-codegen/src/tests_cfg/many_to_many/users_votes.rs b/sea-orm-codegen/src/tests_cfg/many_to_many/users_votes.rs index a04ad7fa73..62f9cf7d00 100644 --- a/sea-orm-codegen/src/tests_cfg/many_to_many/users_votes.rs +++ b/sea-orm-codegen/src/tests_cfg/many_to_many/users_votes.rs @@ -17,7 +17,7 @@ pub enum Relation { from = "Column::BillId", to = "super::bills::Column::Id", on_update = "Cascade", - on_delete = "Cascade", + on_delete = "Cascade" )] Bills, #[sea_orm( @@ -25,7 +25,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "Cascade", - on_delete = "Cascade", + on_delete = "Cascade" )] Users, } diff --git a/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/bills.rs b/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/bills.rs index 5094e5885f..6874aeed0e 100644 --- a/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/bills.rs +++ b/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/bills.rs @@ -5,7 +5,7 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub user_id: Option , + pub user_id: Option, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] @@ -15,7 +15,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "NoAction", - on_delete = "NoAction", + on_delete = "NoAction" )] Users, } diff --git a/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/users_votes.rs b/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/users_votes.rs index 87187f4c0b..29fb173cb5 100644 --- a/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/users_votes.rs +++ b/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/users_votes.rs @@ -7,8 +7,8 @@ pub struct Model { pub user_id: i32, #[sea_orm(primary_key, auto_increment = false)] pub bill_id: i32, - pub user_idd: Option , - pub bill_idd: Option , + pub user_idd: Option, + pub bill_idd: Option, pub vote: bool, } @@ -17,25 +17,25 @@ pub enum Relation { #[sea_orm( belongs_to = "super::bills::Entity", from = "Column::BillIdd", - to = "super::bills::Column::Id", + to = "super::bills::Column::Id" )] Bills2, #[sea_orm( belongs_to = "super::bills::Entity", from = "Column::BillId", - to = "super::bills::Column::Id", + to = "super::bills::Column::Id" )] Bills1, #[sea_orm( belongs_to = "super::users::Entity", from = "Column::UserIdd", - to = "super::users::Column::Id", + to = "super::users::Column::Id" )] Users2, #[sea_orm( belongs_to = "super::users::Entity", from = "Column::UserId", - to = "super::users::Column::Id", + to = "super::users::Column::Id" )] Users1, } diff --git a/sea-orm-codegen/src/tests_cfg/self_referencing/bills.rs b/sea-orm-codegen/src/tests_cfg/self_referencing/bills.rs index 583655f673..22cfc46908 100644 --- a/sea-orm-codegen/src/tests_cfg/self_referencing/bills.rs +++ b/sea-orm-codegen/src/tests_cfg/self_referencing/bills.rs @@ -5,16 +5,12 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub self_id: Option , + pub self_id: Option, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { - #[sea_orm( - belongs_to = "Entity", - from = "Column::SelfId", - to = "Column::Id", - )] + #[sea_orm(belongs_to = "Entity", from = "Column::SelfId", to = "Column::Id")] SelfRef, } diff --git a/sea-orm-codegen/src/tests_cfg/self_referencing/users.rs b/sea-orm-codegen/src/tests_cfg/self_referencing/users.rs index b54f3e5e2d..487ba9409a 100644 --- a/sea-orm-codegen/src/tests_cfg/self_referencing/users.rs +++ b/sea-orm-codegen/src/tests_cfg/self_referencing/users.rs @@ -5,23 +5,15 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub self_id: Option , - pub self_idd: Option , + pub self_id: Option, + pub self_idd: Option, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { - #[sea_orm( - belongs_to = "Entity", - from = "Column::SelfId", - to = "Column::Id", - )] + #[sea_orm(belongs_to = "Entity", from = "Column::SelfId", to = "Column::Id")] SelfRef2, - #[sea_orm( - belongs_to = "Entity", - from = "Column::SelfIdd", - to = "Column::Id", - )] + #[sea_orm(belongs_to = "Entity", from = "Column::SelfIdd", to = "Column::Id")] SelfRef1, } diff --git a/tests/empty_insert_tests.rs b/tests/empty_insert_tests.rs index 962947a9b6..bc97855a7c 100644 --- a/tests/empty_insert_tests.rs +++ b/tests/empty_insert_tests.rs @@ -37,15 +37,7 @@ pub async fn test(db: &DbConn) { assert!(matches!(res, TryInsertResult::Inserted(_))); - let empty_iterator = [bakery::ActiveModel { - name: Set("SeaSide Bakery".to_owned()), - profit_margin: Set(10.4), - ..Default::default() - }] - .into_iter() - .filter(|_| false); - - let empty_insert = Bakery::insert_many(empty_iterator) + let empty_insert = Bakery::insert_many(std::iter::empty()) .on_empty_do_nothing() .exec(db) .await; From 40ee2b29eed10c5bb7b4f34c9379420b823f5fd0 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Tue, 20 Jun 2023 17:30:16 +0800 Subject: [PATCH 0022/1371] Changelog --- CHANGELOG.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a39edf5e1..1261e77fed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -303,6 +303,16 @@ assert_eq!(migration.status(), MigrationStatus::Pending); assert!(matches!(db.ping().await, Err(DbErr::ConnectionAcquire))); } ``` +* Added `TryInsert` that does not panic on empty inserts https://github.com/SeaQL/sea-orm/pull/1708 +```rust +// now, you can do: +let empty_insert = Bakery::insert_many(std::iter::empty()) + .on_empty_do_nothing() + .exec(db) + .await; + +assert!(matches!(empty_insert, TryInsertResult::Empty)); +``` ### Upgrades @@ -398,6 +408,7 @@ impl ColumnTrait for Column { } } ``` +* Resolved `insert_many` failing if the models iterator is empty https://github.com/SeaQL/sea-orm/issues/873 ### Breaking changes @@ -536,16 +547,6 @@ impl ColumnTrait for Column { * Added the `sea-orm-internal` feature flag to expose some SQLx types * Added `DatabaseConnection::get_*_connection_pool()` for accessing the inner SQLx connection pool https://github.com/SeaQL/sea-orm/pull/1297 * Re-exporting SQLx errors https://github.com/SeaQL/sea-orm/pull/1434 -* Added `TryInsert` that does not panic on empty inserts https://github.com/SeaQL/sea-orm/pull/1708 -```rust -// now, you can do: -let empty_insert = Bakery::insert_many(std::iter::empty()) - .on_empty_do_nothing() - .exec(db) - .await; - -assert!(matches!(empty_insert, TryInsertResult::Empty)); -``` ### Upgrades @@ -574,7 +575,6 @@ assert!(matches!(empty_insert, TryInsertResult::Empty)); * Fixes hitting 'negative last_insert_rowid' panic with Sqlite https://github.com/SeaQL/sea-orm/issues/1357 * Noop when update without providing any values https://github.com/SeaQL/sea-orm/pull/1384 * Fixes Syntax Error when saving active model that sets nothing https://github.com/SeaQL/sea-orm/pull/1376 -* Resolved `insert_many` failing if the models iterator is empty https://github.com/SeaQL/sea-orm/issues/873 ### Breaking Changes From 20312b776d35af4fcddb1fa93453b0c1e4a4b4c4 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Tue, 20 Jun 2023 17:31:25 +0800 Subject: [PATCH 0023/1371] Revert "Changelog" This reverts commit 5e5aa7cbd184db141446960e7960d12c9a61363a. --- .../duplicated_many_to_many_paths/bills.rs | 4 ++-- .../users_saved_bills.rs | 4 ++-- .../duplicated_many_to_many_paths/users_votes.rs | 4 ++-- .../src/tests_cfg/many_to_many/bills.rs | 4 ++-- .../src/tests_cfg/many_to_many/users_votes.rs | 4 ++-- .../src/tests_cfg/many_to_many_multiple/bills.rs | 4 ++-- .../many_to_many_multiple/users_votes.rs | 12 ++++++------ .../src/tests_cfg/self_referencing/bills.rs | 8 ++++++-- .../src/tests_cfg/self_referencing/users.rs | 16 ++++++++++++---- 9 files changed, 36 insertions(+), 24 deletions(-) diff --git a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/bills.rs b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/bills.rs index baf33426eb..a3a6911b98 100644 --- a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/bills.rs +++ b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/bills.rs @@ -5,7 +5,7 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub user_id: Option, + pub user_id: Option , } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] @@ -15,7 +15,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "NoAction", - on_delete = "NoAction" + on_delete = "NoAction", )] Users, #[sea_orm(has_many = "super::users_saved_bills::Entity")] diff --git a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_saved_bills.rs b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_saved_bills.rs index 575bac58f1..4ef0281cd2 100644 --- a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_saved_bills.rs +++ b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_saved_bills.rs @@ -16,7 +16,7 @@ pub enum Relation { from = "Column::BillId", to = "super::bills::Column::Id", on_update = "Cascade", - on_delete = "Cascade" + on_delete = "Cascade", )] Bills, #[sea_orm( @@ -24,7 +24,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "Cascade", - on_delete = "Cascade" + on_delete = "Cascade", )] Users, } diff --git a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_votes.rs b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_votes.rs index 62f9cf7d00..a04ad7fa73 100644 --- a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_votes.rs +++ b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_votes.rs @@ -17,7 +17,7 @@ pub enum Relation { from = "Column::BillId", to = "super::bills::Column::Id", on_update = "Cascade", - on_delete = "Cascade" + on_delete = "Cascade", )] Bills, #[sea_orm( @@ -25,7 +25,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "Cascade", - on_delete = "Cascade" + on_delete = "Cascade", )] Users, } diff --git a/sea-orm-codegen/src/tests_cfg/many_to_many/bills.rs b/sea-orm-codegen/src/tests_cfg/many_to_many/bills.rs index f2de469f0a..5a3acef9cb 100644 --- a/sea-orm-codegen/src/tests_cfg/many_to_many/bills.rs +++ b/sea-orm-codegen/src/tests_cfg/many_to_many/bills.rs @@ -5,7 +5,7 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub user_id: Option, + pub user_id: Option , } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] @@ -15,7 +15,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "NoAction", - on_delete = "NoAction" + on_delete = "NoAction", )] Users, #[sea_orm(has_many = "super::users_votes::Entity")] diff --git a/sea-orm-codegen/src/tests_cfg/many_to_many/users_votes.rs b/sea-orm-codegen/src/tests_cfg/many_to_many/users_votes.rs index 62f9cf7d00..a04ad7fa73 100644 --- a/sea-orm-codegen/src/tests_cfg/many_to_many/users_votes.rs +++ b/sea-orm-codegen/src/tests_cfg/many_to_many/users_votes.rs @@ -17,7 +17,7 @@ pub enum Relation { from = "Column::BillId", to = "super::bills::Column::Id", on_update = "Cascade", - on_delete = "Cascade" + on_delete = "Cascade", )] Bills, #[sea_orm( @@ -25,7 +25,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "Cascade", - on_delete = "Cascade" + on_delete = "Cascade", )] Users, } diff --git a/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/bills.rs b/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/bills.rs index 6874aeed0e..5094e5885f 100644 --- a/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/bills.rs +++ b/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/bills.rs @@ -5,7 +5,7 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub user_id: Option, + pub user_id: Option , } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] @@ -15,7 +15,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "NoAction", - on_delete = "NoAction" + on_delete = "NoAction", )] Users, } diff --git a/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/users_votes.rs b/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/users_votes.rs index 29fb173cb5..87187f4c0b 100644 --- a/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/users_votes.rs +++ b/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/users_votes.rs @@ -7,8 +7,8 @@ pub struct Model { pub user_id: i32, #[sea_orm(primary_key, auto_increment = false)] pub bill_id: i32, - pub user_idd: Option, - pub bill_idd: Option, + pub user_idd: Option , + pub bill_idd: Option , pub vote: bool, } @@ -17,25 +17,25 @@ pub enum Relation { #[sea_orm( belongs_to = "super::bills::Entity", from = "Column::BillIdd", - to = "super::bills::Column::Id" + to = "super::bills::Column::Id", )] Bills2, #[sea_orm( belongs_to = "super::bills::Entity", from = "Column::BillId", - to = "super::bills::Column::Id" + to = "super::bills::Column::Id", )] Bills1, #[sea_orm( belongs_to = "super::users::Entity", from = "Column::UserIdd", - to = "super::users::Column::Id" + to = "super::users::Column::Id", )] Users2, #[sea_orm( belongs_to = "super::users::Entity", from = "Column::UserId", - to = "super::users::Column::Id" + to = "super::users::Column::Id", )] Users1, } diff --git a/sea-orm-codegen/src/tests_cfg/self_referencing/bills.rs b/sea-orm-codegen/src/tests_cfg/self_referencing/bills.rs index 22cfc46908..583655f673 100644 --- a/sea-orm-codegen/src/tests_cfg/self_referencing/bills.rs +++ b/sea-orm-codegen/src/tests_cfg/self_referencing/bills.rs @@ -5,12 +5,16 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub self_id: Option, + pub self_id: Option , } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { - #[sea_orm(belongs_to = "Entity", from = "Column::SelfId", to = "Column::Id")] + #[sea_orm( + belongs_to = "Entity", + from = "Column::SelfId", + to = "Column::Id", + )] SelfRef, } diff --git a/sea-orm-codegen/src/tests_cfg/self_referencing/users.rs b/sea-orm-codegen/src/tests_cfg/self_referencing/users.rs index 487ba9409a..b54f3e5e2d 100644 --- a/sea-orm-codegen/src/tests_cfg/self_referencing/users.rs +++ b/sea-orm-codegen/src/tests_cfg/self_referencing/users.rs @@ -5,15 +5,23 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub self_id: Option, - pub self_idd: Option, + pub self_id: Option , + pub self_idd: Option , } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { - #[sea_orm(belongs_to = "Entity", from = "Column::SelfId", to = "Column::Id")] + #[sea_orm( + belongs_to = "Entity", + from = "Column::SelfId", + to = "Column::Id", + )] SelfRef2, - #[sea_orm(belongs_to = "Entity", from = "Column::SelfIdd", to = "Column::Id")] + #[sea_orm( + belongs_to = "Entity", + from = "Column::SelfIdd", + to = "Column::Id", + )] SelfRef1, } From f7398d1c5cba06f2923617b288ff61dd54375dc0 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Tue, 20 Jun 2023 17:59:58 +0800 Subject: [PATCH 0024/1371] Fixup --- tests/empty_insert_tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/empty_insert_tests.rs b/tests/empty_insert_tests.rs index bc97855a7c..5c17b5e5f7 100644 --- a/tests/empty_insert_tests.rs +++ b/tests/empty_insert_tests.rs @@ -37,7 +37,7 @@ pub async fn test(db: &DbConn) { assert!(matches!(res, TryInsertResult::Inserted(_))); - let empty_insert = Bakery::insert_many(std::iter::empty()) + let empty_insert = Bakery::insert_many(std::iter::empty::()) .on_empty_do_nothing() .exec(db) .await; From 6e7950158a9ac34e5a236efea53ccaf38901712e Mon Sep 17 00:00:00 2001 From: darkmmon Date: Wed, 21 Jun 2023 22:47:17 +0800 Subject: [PATCH 0025/1371] Do nothing on conflict (#1712) * added Conflicted to TryInsertResult clippy changes fmt change from basic if statement to matches * changed to early return --- src/executor/insert.rs | 31 ++++++++++++++++++++----------- src/query/insert.rs | 10 +++++++++- tests/upsert_tests.rs | 16 +++++++++++++++- 3 files changed, 44 insertions(+), 13 deletions(-) diff --git a/src/executor/insert.rs b/src/executor/insert.rs index be00dbed30..1af3c33840 100644 --- a/src/executor/insert.rs +++ b/src/executor/insert.rs @@ -30,9 +30,9 @@ where /// The types of results for an INSERT operation #[derive(Debug)] pub enum TryInsertResult { - /// The INSERT operation did not insert any value + /// The INSERT statement did not have any value to insert Empty, - /// Reserved + /// The INSERT operation did not insert any valid value Conflicted, /// Successfully inserted Inserted(T), @@ -50,10 +50,13 @@ where A: 'a, { if self.insert_struct.columns.is_empty() { - TryInsertResult::Empty - } else { - TryInsertResult::Inserted(self.insert_struct.exec(db).await) + return TryInsertResult::Empty; } + let temp = self.insert_struct.exec(db).await; + if matches!(temp, Err(DbErr::RecordNotInserted)) { + return TryInsertResult::Conflicted; + } + TryInsertResult::Inserted(temp) } /// Execute an insert operation without returning (don't use `RETURNING` syntax) @@ -68,10 +71,13 @@ where A: 'a, { if self.insert_struct.columns.is_empty() { - TryInsertResult::Empty - } else { - TryInsertResult::Inserted(self.insert_struct.exec_without_returning(db).await) + return TryInsertResult::Empty; + } + let temp = self.insert_struct.exec_without_returning(db).await; + if matches!(temp, Err(DbErr::RecordNotInserted)) { + return TryInsertResult::Conflicted; } + TryInsertResult::Inserted(temp) } /// Execute an insert operation and return the inserted model (use `RETURNING` syntax if database supported) @@ -85,10 +91,13 @@ where A: 'a, { if self.insert_struct.columns.is_empty() { - TryInsertResult::Empty - } else { - TryInsertResult::Inserted(self.insert_struct.exec_with_returning(db).await) + return TryInsertResult::Empty; + } + let temp = self.insert_struct.exec_with_returning(db).await; + if matches!(temp, Err(DbErr::RecordNotInserted)) { + return TryInsertResult::Conflicted; } + TryInsertResult::Inserted(temp) } } diff --git a/src/query/insert.rs b/src/query/insert.rs index 3fdb343c33..cf878dcd15 100644 --- a/src/query/insert.rs +++ b/src/query/insert.rs @@ -211,6 +211,14 @@ where /// Allow insert statement return safely if inserting nothing. /// The database will not be affected. + pub fn do_nothing(self) -> TryInsert + where + A: ActiveModelTrait, + { + TryInsert::from_insert(self) + } + + /// alias to do_nothing pub fn on_empty_do_nothing(self) -> TryInsert where A: ActiveModelTrait, @@ -309,7 +317,7 @@ where self } - // helper function for on_empty_do_nothing in Insert + // helper function for do_nothing in Insert pub fn from_insert(insert: Insert) -> Self { Self { insert_struct: insert, diff --git a/tests/upsert_tests.rs b/tests/upsert_tests.rs index 748d5b0a33..05009b5b92 100644 --- a/tests/upsert_tests.rs +++ b/tests/upsert_tests.rs @@ -3,6 +3,7 @@ pub mod common; pub use common::{features::*, setup::*, TestContext}; use pretty_assertions::assert_eq; use sea_orm::entity::prelude::*; +use sea_orm::TryInsertResult; use sea_orm::{sea_query::OnConflict, Set}; #[sea_orm_macros::test] @@ -50,11 +51,24 @@ pub async fn create_insert_default(db: &DatabaseConnection) -> Result<(), DbErr> ActiveModel { id: Set(3) }, ActiveModel { id: Set(4) }, ]) - .on_conflict(on_conflict) + .on_conflict(on_conflict.clone()) .exec(db) .await; assert_eq!(res.err(), Some(DbErr::RecordNotInserted)); + let res = Entity::insert_many([ + ActiveModel { id: Set(1) }, + ActiveModel { id: Set(2) }, + ActiveModel { id: Set(3) }, + ActiveModel { id: Set(4) }, + ]) + .on_conflict(on_conflict) + .do_nothing() + .exec(db) + .await; + + assert!(matches!(res, TryInsertResult::Conflicted)); + Ok(()) } From 1f3fa1dc19e0ec6e495e43677c047284e5e84b86 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Wed, 21 Jun 2023 23:14:57 +0800 Subject: [PATCH 0026/1371] Changelog with Tweaks --- CHANGELOG.md | 16 +++++++++++++-- src/executor/insert.rs | 39 ++++++++++++++++++++----------------- tests/empty_insert_tests.rs | 4 ++-- tests/upsert_tests.rs | 4 ++-- 4 files changed, 39 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1261e77fed..324cddf2df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -306,12 +306,24 @@ assert_eq!(migration.status(), MigrationStatus::Pending); * Added `TryInsert` that does not panic on empty inserts https://github.com/SeaQL/sea-orm/pull/1708 ```rust // now, you can do: -let empty_insert = Bakery::insert_many(std::iter::empty()) +let res = Bakery::insert_many(std::iter::empty()) .on_empty_do_nothing() .exec(db) .await; -assert!(matches!(empty_insert, TryInsertResult::Empty)); +assert!(matches!(res, Ok(TryInsertResult::Empty))); +``` +* On conflict do nothing not resulting in Err https://github.com/SeaQL/sea-orm/pull/1712 +```rust +let on = OnConflict::column(Column::Id).do_nothing().to_owned(); + +// Existing behaviour +let res = Entity::insert_many([..]).on_conflict(on).exec(db).await; +assert!(matches!(res, Err(DbErr::RecordNotInserted))); + +// New API; now you can: +let res = Entity::insert_many([..]).on_conflict(on).do_nothing().exec(db).await; +assert!(matches!(res, Ok(TryInsertResult::Conflicted))); ``` ### Upgrades diff --git a/src/executor/insert.rs b/src/executor/insert.rs index 1af3c33840..cf6f320fd4 100644 --- a/src/executor/insert.rs +++ b/src/executor/insert.rs @@ -44,19 +44,20 @@ where { /// Execute an insert operation #[allow(unused_mut)] - pub async fn exec<'a, C>(self, db: &'a C) -> TryInsertResult, DbErr>> + pub async fn exec<'a, C>(self, db: &'a C) -> Result>, DbErr> where C: ConnectionTrait, A: 'a, { if self.insert_struct.columns.is_empty() { - return TryInsertResult::Empty; + return Ok(TryInsertResult::Empty); } - let temp = self.insert_struct.exec(db).await; - if matches!(temp, Err(DbErr::RecordNotInserted)) { - return TryInsertResult::Conflicted; + let res = self.insert_struct.exec(db).await; + match res { + Ok(res) => Ok(TryInsertResult::Inserted(res)), + Err(DbErr::RecordNotInserted) => Ok(TryInsertResult::Conflicted), + Err(err) => Err(err), } - TryInsertResult::Inserted(temp) } /// Execute an insert operation without returning (don't use `RETURNING` syntax) @@ -64,40 +65,42 @@ where pub async fn exec_without_returning<'a, C>( self, db: &'a C, - ) -> TryInsertResult> + ) -> Result, DbErr> where ::Model: IntoActiveModel, C: ConnectionTrait, A: 'a, { if self.insert_struct.columns.is_empty() { - return TryInsertResult::Empty; + return Ok(TryInsertResult::Empty); } - let temp = self.insert_struct.exec_without_returning(db).await; - if matches!(temp, Err(DbErr::RecordNotInserted)) { - return TryInsertResult::Conflicted; + let res = self.insert_struct.exec_without_returning(db).await; + match res { + Ok(res) => Ok(TryInsertResult::Inserted(res)), + Err(DbErr::RecordNotInserted) => Ok(TryInsertResult::Conflicted), + Err(err) => Err(err), } - TryInsertResult::Inserted(temp) } /// Execute an insert operation and return the inserted model (use `RETURNING` syntax if database supported) pub async fn exec_with_returning<'a, C>( self, db: &'a C, - ) -> TryInsertResult::Model, DbErr>> + ) -> Result::Model>, DbErr> where ::Model: IntoActiveModel, C: ConnectionTrait, A: 'a, { if self.insert_struct.columns.is_empty() { - return TryInsertResult::Empty; + return Ok(TryInsertResult::Empty); } - let temp = self.insert_struct.exec_with_returning(db).await; - if matches!(temp, Err(DbErr::RecordNotInserted)) { - return TryInsertResult::Conflicted; + let res = self.insert_struct.exec_with_returning(db).await; + match res { + Ok(res) => Ok(TryInsertResult::Inserted(res)), + Err(DbErr::RecordNotInserted) => Ok(TryInsertResult::Conflicted), + Err(err) => Err(err), } - TryInsertResult::Inserted(temp) } } diff --git a/tests/empty_insert_tests.rs b/tests/empty_insert_tests.rs index 5c17b5e5f7..e3a8bfa762 100644 --- a/tests/empty_insert_tests.rs +++ b/tests/empty_insert_tests.rs @@ -35,12 +35,12 @@ pub async fn test(db: &DbConn) { .exec(db) .await; - assert!(matches!(res, TryInsertResult::Inserted(_))); + assert!(matches!(res, Ok(TryInsertResult::Inserted(_)))); let empty_insert = Bakery::insert_many(std::iter::empty::()) .on_empty_do_nothing() .exec(db) .await; - assert!(matches!(empty_insert, TryInsertResult::Empty)); + assert!(matches!(empty_insert, Ok(TryInsertResult::Empty))); } diff --git a/tests/upsert_tests.rs b/tests/upsert_tests.rs index 05009b5b92..ff3e381fce 100644 --- a/tests/upsert_tests.rs +++ b/tests/upsert_tests.rs @@ -55,7 +55,7 @@ pub async fn create_insert_default(db: &DatabaseConnection) -> Result<(), DbErr> .exec(db) .await; - assert_eq!(res.err(), Some(DbErr::RecordNotInserted)); + assert!(matches!(res, Err(DbErr::RecordNotInserted))); let res = Entity::insert_many([ ActiveModel { id: Set(1) }, @@ -68,7 +68,7 @@ pub async fn create_insert_default(db: &DatabaseConnection) -> Result<(), DbErr> .exec(db) .await; - assert!(matches!(res, TryInsertResult::Conflicted)); + assert!(matches!(res, Ok(TryInsertResult::Conflicted))); Ok(()) } From ec198060c38d1c901538ba9fc3d2103b91d76d9f Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 22 Jun 2023 06:48:39 +0800 Subject: [PATCH 0027/1371] Prepare for rc.3 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 324cddf2df..781bcb2e1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## 0.12.0 - Pending ++ Yanked: `0.12.0-rc.1` + 2023-05-19: `0.12.0-rc.2` ++ 2023-06-22: `0.12.0-rc.3` ### New Features From 320cbf5b75c6a1d698d92fbe8e3e7f8b568efbae Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 22 Jun 2023 16:08:09 +0800 Subject: [PATCH 0028/1371] Remove nightly from Examples --- .github/workflows/rust.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 4fb3e4a412..dc544a952f 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -251,15 +251,11 @@ jobs: with: toolchain: stable components: rustfmt + - run: cargo fmt --manifest-path ${{ matrix.path }} --all -- --check - run: cargo build --manifest-path ${{ matrix.path }} - run: cargo test --manifest-path ${{ matrix.path }} - if: ${{ contains(matrix.path, 'core/Cargo.toml') }} run: cargo test --manifest-path ${{ matrix.path }} --features mock - - uses: dtolnay/rust-toolchain@master - with: - toolchain: nightly - components: rustfmt - - run: cargo fmt --manifest-path ${{ matrix.path }} --all -- --check issues-matrix: name: Issues Matrix From b45534c8c2d8d2d79b11e682ab9f0358e770375e Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 22 Jun 2023 07:00:42 +0800 Subject: [PATCH 0029/1371] 0.12.0-rc.3 --- Cargo.toml | 4 ++-- sea-orm-cli/Cargo.toml | 4 ++-- sea-orm-codegen/Cargo.toml | 2 +- sea-orm-macros/Cargo.toml | 2 +- sea-orm-migration/Cargo.toml | 6 +++--- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8e18215140..fc5dffa022 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [".", "sea-orm-macros", "sea-orm-codegen"] [package] name = "sea-orm" -version = "0.12.0" +version = "0.12.0-rc.3" authors = ["Chris Tsang "] edition = "2021" description = "🐚 An async & dynamic ORM for Rust" @@ -33,7 +33,7 @@ log = { version = "0.4", default-features = false } tracing = { version = "0.1", default-features = false, features = ["attributes", "log"] } rust_decimal = { version = "1", default-features = false, optional = true } bigdecimal = { version = "0.3", default-features = false, optional = true } -sea-orm-macros = { version = "0.12.0", path = "sea-orm-macros", default-features = false, features = ["strum"] } +sea-orm-macros = { version = "0.12.0-rc.3", path = "sea-orm-macros", default-features = false, features = ["strum"] } sea-query = { version = "0.29.0-rc.2", features = ["thread-safe"] } sea-query-binder = { version = "0.4.0-rc.2", default-features = false, optional = true } strum = { version = "0.24", default-features = false } diff --git a/sea-orm-cli/Cargo.toml b/sea-orm-cli/Cargo.toml index 57dd5fee3b..25770b8757 100644 --- a/sea-orm-cli/Cargo.toml +++ b/sea-orm-cli/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "sea-orm-cli" -version = "0.12.0" +version = "0.12.0-rc.3" authors = [ "Chris Tsang ", "Billy Chan ", @@ -37,7 +37,7 @@ required-features = ["cli", "codegen"] clap = { version = "4.3", features = ["env", "derive"], optional = true } dotenvy = { version = "0.15", default-features = false, optional = true } async-std = { version = "1.9", default-features = false, features = ["attributes", "tokio1"], optional = true } -sea-orm-codegen = { version = "=0.12.0", path = "../sea-orm-codegen", default-features = false, optional = true } +sea-orm-codegen = { version = "=0.12.0-rc.3", path = "../sea-orm-codegen", default-features = false, optional = true } sea-schema = { version = "0.12.0-rc.2" } sqlx = { version = "0.6", default-features = false, features = ["mysql", "postgres"], optional = true } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["env-filter", "fmt"] } diff --git a/sea-orm-codegen/Cargo.toml b/sea-orm-codegen/Cargo.toml index c3efc08da9..d6c27276fd 100644 --- a/sea-orm-codegen/Cargo.toml +++ b/sea-orm-codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sea-orm-codegen" -version = "0.12.0" +version = "0.12.0-rc.3" authors = ["Billy Chan "] edition = "2021" description = "Code Generator for SeaORM" diff --git a/sea-orm-macros/Cargo.toml b/sea-orm-macros/Cargo.toml index ee4c65b125..4091a83435 100644 --- a/sea-orm-macros/Cargo.toml +++ b/sea-orm-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sea-orm-macros" -version = "0.12.0" +version = "0.12.0-rc.3" authors = [ "Billy Chan " ] edition = "2021" description = "Derive macros for SeaORM" diff --git a/sea-orm-migration/Cargo.toml b/sea-orm-migration/Cargo.toml index 73ab20084f..ca423948bb 100644 --- a/sea-orm-migration/Cargo.toml +++ b/sea-orm-migration/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "sea-orm-migration" -version = "0.12.0" +version = "0.12.0-rc.3" authors = ["Billy Chan "] edition = "2021" description = "Migration utility for SeaORM" @@ -23,8 +23,8 @@ path = "src/lib.rs" async-trait = { version = "0.1", default-features = false } clap = { version = "4.3", features = ["env", "derive"], optional = true } dotenvy = { version = "0.15", default-features = false, optional = true } -sea-orm = { version = "0.12.0", path = "../", default-features = false, features = ["macros"] } -sea-orm-cli = { version = "0.12.0", path = "../sea-orm-cli", default-features = false, optional = true } +sea-orm = { version = "0.12.0-rc.3", path = "../", default-features = false, features = ["macros"] } +sea-orm-cli = { version = "0.12.0-rc.3", path = "../sea-orm-cli", default-features = false, optional = true } sea-schema = { version = "0.12.0-rc.2" } tracing = { version = "0.1", default-features = false, features = ["log"] } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["env-filter", "fmt"] } From 6bb7bb30fa3b96ce4e0527774f184776aaee5111 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 22 Jun 2023 07:06:48 +0800 Subject: [PATCH 0030/1371] update examples --- examples/actix3_example/entity/Cargo.toml | 2 +- examples/actix3_example/migration/Cargo.toml | 2 +- examples/actix3_example/service/Cargo.toml | 2 +- examples/actix_example/entity/Cargo.toml | 2 +- examples/actix_example/migration/Cargo.toml | 2 +- examples/actix_example/service/Cargo.toml | 2 +- examples/axum_example/entity/Cargo.toml | 2 +- examples/axum_example/migration/Cargo.toml | 2 +- examples/axum_example/service/Cargo.toml | 2 +- examples/graphql_example/entity/Cargo.toml | 2 +- examples/graphql_example/migration/Cargo.toml | 2 +- examples/graphql_example/service/Cargo.toml | 2 +- examples/jsonrpsee_example/entity/Cargo.toml | 2 +- examples/jsonrpsee_example/migration/Cargo.toml | 2 +- examples/jsonrpsee_example/service/Cargo.toml | 2 +- examples/poem_example/entity/Cargo.toml | 2 +- examples/poem_example/migration/Cargo.toml | 2 +- examples/poem_example/service/Cargo.toml | 2 +- examples/rocket_example/entity/Cargo.toml | 2 +- examples/rocket_example/migration/Cargo.toml | 2 +- examples/rocket_example/service/Cargo.toml | 2 +- examples/rocket_okapi_example/entity/Cargo.toml | 2 +- examples/rocket_okapi_example/migration/Cargo.toml | 2 +- examples/rocket_okapi_example/service/Cargo.toml | 2 +- examples/salvo_example/entity/Cargo.toml | 2 +- examples/salvo_example/migration/Cargo.toml | 2 +- examples/salvo_example/service/Cargo.toml | 2 +- examples/tonic_example/entity/Cargo.toml | 2 +- examples/tonic_example/migration/Cargo.toml | 2 +- examples/tonic_example/service/Cargo.toml | 2 +- 30 files changed, 30 insertions(+), 30 deletions(-) diff --git a/examples/actix3_example/entity/Cargo.toml b/examples/actix3_example/entity/Cargo.toml index 7dea367337..18787b4f95 100644 --- a/examples/actix3_example/entity/Cargo.toml +++ b/examples/actix3_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0" # sea-orm version +version = "0.12.0-rc.3" # sea-orm version diff --git a/examples/actix3_example/migration/Cargo.toml b/examples/actix3_example/migration/Cargo.toml index 1078662187..bd8ff82081 100644 --- a/examples/actix3_example/migration/Cargo.toml +++ b/examples/actix3_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0" # sea-orm-migration version +version = "0.12.0-rc.3" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-async-std-native-tls", diff --git a/examples/actix3_example/service/Cargo.toml b/examples/actix3_example/service/Cargo.toml index bdd960cc9f..b2665b487f 100644 --- a/examples/actix3_example/service/Cargo.toml +++ b/examples/actix3_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0" # sea-orm version +version = "0.12.0-rc.3" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/actix_example/entity/Cargo.toml b/examples/actix_example/entity/Cargo.toml index 7dea367337..18787b4f95 100644 --- a/examples/actix_example/entity/Cargo.toml +++ b/examples/actix_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0" # sea-orm version +version = "0.12.0-rc.3" # sea-orm version diff --git a/examples/actix_example/migration/Cargo.toml b/examples/actix_example/migration/Cargo.toml index 61187c6468..08a2b5c3e4 100644 --- a/examples/actix_example/migration/Cargo.toml +++ b/examples/actix_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0" # sea-orm-migration version +version = "0.12.0-rc.3" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-actix-native-tls", diff --git a/examples/actix_example/service/Cargo.toml b/examples/actix_example/service/Cargo.toml index f9972b5102..a65e440bb1 100644 --- a/examples/actix_example/service/Cargo.toml +++ b/examples/actix_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0" # sea-orm version +version = "0.12.0-rc.3" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/axum_example/entity/Cargo.toml b/examples/axum_example/entity/Cargo.toml index 7dea367337..18787b4f95 100644 --- a/examples/axum_example/entity/Cargo.toml +++ b/examples/axum_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0" # sea-orm version +version = "0.12.0-rc.3" # sea-orm version diff --git a/examples/axum_example/migration/Cargo.toml b/examples/axum_example/migration/Cargo.toml index a59e5d0a04..253e2afe0c 100644 --- a/examples/axum_example/migration/Cargo.toml +++ b/examples/axum_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0" # sea-orm-migration version +version = "0.12.0-rc.3" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/axum_example/service/Cargo.toml b/examples/axum_example/service/Cargo.toml index 551f127c4d..be94b06fdb 100644 --- a/examples/axum_example/service/Cargo.toml +++ b/examples/axum_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0" # sea-orm version +version = "0.12.0-rc.3" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/graphql_example/entity/Cargo.toml b/examples/graphql_example/entity/Cargo.toml index 21a849b904..d1a68a9962 100644 --- a/examples/graphql_example/entity/Cargo.toml +++ b/examples/graphql_example/entity/Cargo.toml @@ -16,4 +16,4 @@ version = "4.0.6" [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0" # sea-orm version +version = "0.12.0-rc.3" # sea-orm version diff --git a/examples/graphql_example/migration/Cargo.toml b/examples/graphql_example/migration/Cargo.toml index 6661c56568..bc6841a92a 100644 --- a/examples/graphql_example/migration/Cargo.toml +++ b/examples/graphql_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0" # sea-orm-migration version +version = "0.12.0-rc.3" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/graphql_example/service/Cargo.toml b/examples/graphql_example/service/Cargo.toml index 0cf7539e7e..67f03c7363 100644 --- a/examples/graphql_example/service/Cargo.toml +++ b/examples/graphql_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0" # sea-orm version +version = "0.12.0-rc.3" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/jsonrpsee_example/entity/Cargo.toml b/examples/jsonrpsee_example/entity/Cargo.toml index 7dea367337..18787b4f95 100644 --- a/examples/jsonrpsee_example/entity/Cargo.toml +++ b/examples/jsonrpsee_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0" # sea-orm version +version = "0.12.0-rc.3" # sea-orm version diff --git a/examples/jsonrpsee_example/migration/Cargo.toml b/examples/jsonrpsee_example/migration/Cargo.toml index 37cd12fc79..a4e60c877a 100644 --- a/examples/jsonrpsee_example/migration/Cargo.toml +++ b/examples/jsonrpsee_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0" # sea-orm-migration version +version = "0.12.0-rc.3" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/jsonrpsee_example/service/Cargo.toml b/examples/jsonrpsee_example/service/Cargo.toml index 22d8b32998..fcf658c763 100644 --- a/examples/jsonrpsee_example/service/Cargo.toml +++ b/examples/jsonrpsee_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0" # sea-orm version +version = "0.12.0-rc.3" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/poem_example/entity/Cargo.toml b/examples/poem_example/entity/Cargo.toml index 7dea367337..18787b4f95 100644 --- a/examples/poem_example/entity/Cargo.toml +++ b/examples/poem_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0" # sea-orm version +version = "0.12.0-rc.3" # sea-orm version diff --git a/examples/poem_example/migration/Cargo.toml b/examples/poem_example/migration/Cargo.toml index 37cd12fc79..a4e60c877a 100644 --- a/examples/poem_example/migration/Cargo.toml +++ b/examples/poem_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0" # sea-orm-migration version +version = "0.12.0-rc.3" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/poem_example/service/Cargo.toml b/examples/poem_example/service/Cargo.toml index 8f76a2f209..48c79eba17 100644 --- a/examples/poem_example/service/Cargo.toml +++ b/examples/poem_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0" # sea-orm version +version = "0.12.0-rc.3" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/rocket_example/entity/Cargo.toml b/examples/rocket_example/entity/Cargo.toml index 666471bb3c..5d62d37bfb 100644 --- a/examples/rocket_example/entity/Cargo.toml +++ b/examples/rocket_example/entity/Cargo.toml @@ -15,4 +15,4 @@ rocket = { version = "0.5.0-rc.1", features = [ [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0" # sea-orm version +version = "0.12.0-rc.3" # sea-orm version diff --git a/examples/rocket_example/migration/Cargo.toml b/examples/rocket_example/migration/Cargo.toml index 63672d5b1a..142fe48afb 100644 --- a/examples/rocket_example/migration/Cargo.toml +++ b/examples/rocket_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0" # sea-orm-migration version +version = "0.12.0-rc.3" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/rocket_example/service/Cargo.toml b/examples/rocket_example/service/Cargo.toml index 957ad5268c..c586d93f56 100644 --- a/examples/rocket_example/service/Cargo.toml +++ b/examples/rocket_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0" # sea-orm version +version = "0.12.0-rc.3" # sea-orm version features = [ "runtime-tokio-native-tls", "sqlx-postgres", diff --git a/examples/rocket_okapi_example/entity/Cargo.toml b/examples/rocket_okapi_example/entity/Cargo.toml index ea666d840c..8c5bd1074b 100644 --- a/examples/rocket_okapi_example/entity/Cargo.toml +++ b/examples/rocket_okapi_example/entity/Cargo.toml @@ -15,7 +15,7 @@ rocket = { version = "0.5.0-rc.1", features = [ [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0" # sea-orm version +version = "0.12.0-rc.3" # sea-orm version [dependencies.rocket_okapi] version = "0.8.0-rc.2" diff --git a/examples/rocket_okapi_example/migration/Cargo.toml b/examples/rocket_okapi_example/migration/Cargo.toml index 63672d5b1a..142fe48afb 100644 --- a/examples/rocket_okapi_example/migration/Cargo.toml +++ b/examples/rocket_okapi_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0" # sea-orm-migration version +version = "0.12.0-rc.3" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/rocket_okapi_example/service/Cargo.toml b/examples/rocket_okapi_example/service/Cargo.toml index 957ad5268c..c586d93f56 100644 --- a/examples/rocket_okapi_example/service/Cargo.toml +++ b/examples/rocket_okapi_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0" # sea-orm version +version = "0.12.0-rc.3" # sea-orm version features = [ "runtime-tokio-native-tls", "sqlx-postgres", diff --git a/examples/salvo_example/entity/Cargo.toml b/examples/salvo_example/entity/Cargo.toml index 7dea367337..18787b4f95 100644 --- a/examples/salvo_example/entity/Cargo.toml +++ b/examples/salvo_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0" # sea-orm version +version = "0.12.0-rc.3" # sea-orm version diff --git a/examples/salvo_example/migration/Cargo.toml b/examples/salvo_example/migration/Cargo.toml index 37cd12fc79..a4e60c877a 100644 --- a/examples/salvo_example/migration/Cargo.toml +++ b/examples/salvo_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0" # sea-orm-migration version +version = "0.12.0-rc.3" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/salvo_example/service/Cargo.toml b/examples/salvo_example/service/Cargo.toml index 87888a1413..4572b26ca9 100644 --- a/examples/salvo_example/service/Cargo.toml +++ b/examples/salvo_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0" # sea-orm version +version = "0.12.0-rc.3" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/tonic_example/entity/Cargo.toml b/examples/tonic_example/entity/Cargo.toml index 7dea367337..18787b4f95 100644 --- a/examples/tonic_example/entity/Cargo.toml +++ b/examples/tonic_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0" # sea-orm version +version = "0.12.0-rc.3" # sea-orm version diff --git a/examples/tonic_example/migration/Cargo.toml b/examples/tonic_example/migration/Cargo.toml index 8f8f42ca78..6308041913 100644 --- a/examples/tonic_example/migration/Cargo.toml +++ b/examples/tonic_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0" # sea-orm-migration version +version = "0.12.0-rc.3" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-rustls", diff --git a/examples/tonic_example/service/Cargo.toml b/examples/tonic_example/service/Cargo.toml index fb06f9d919..5730cf8e6a 100644 --- a/examples/tonic_example/service/Cargo.toml +++ b/examples/tonic_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0" # sea-orm version +version = "0.12.0-rc.3" # sea-orm version features = [ "debug-print", "runtime-tokio-rustls", From 6d40569164f5fb44a4ead7dc69433c6a63c0601c Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 22 Jun 2023 07:07:53 +0800 Subject: [PATCH 0031/1371] Update build script --- build-tools/bump-version.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build-tools/bump-version.sh b/build-tools/bump-version.sh index ba9657c7a1..82371e8bcf 100644 --- a/build-tools/bump-version.sh +++ b/build-tools/bump-version.sh @@ -34,4 +34,5 @@ git commit -am "$1" cd examples find . -depth -type f -name '*.toml' -exec sed -i 's/^version = ".*" # sea-orm version$/version = "'$1'" # sea-orm version/' {} \; find . -depth -type f -name '*.toml' -exec sed -i 's/^version = ".*" # sea-orm-migration version$/version = "'$1'" # sea-orm-migration version/' {} \; -git commit -am "update examples" \ No newline at end of file +git add . +git commit -m "update examples" \ No newline at end of file From 27af2c9c056be580d22d3a32fd0f766cf4443e3c Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 22 Jun 2023 07:42:48 +0800 Subject: [PATCH 0032/1371] cargo fmt check --- .github/workflows/rust.yml | 2 +- .../duplicated_many_to_many_paths/bills.rs | 4 ++-- .../users_saved_bills.rs | 4 ++-- .../duplicated_many_to_many_paths/users_votes.rs | 4 ++-- .../src/tests_cfg/many_to_many/bills.rs | 4 ++-- .../src/tests_cfg/many_to_many/users_votes.rs | 4 ++-- .../src/tests_cfg/many_to_many_multiple/bills.rs | 4 ++-- .../many_to_many_multiple/users_votes.rs | 12 ++++++------ .../src/tests_cfg/self_referencing/bills.rs | 8 ++------ .../src/tests_cfg/self_referencing/users.rs | 16 ++++------------ 10 files changed, 25 insertions(+), 37 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index dc544a952f..2005f07447 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -251,7 +251,7 @@ jobs: with: toolchain: stable components: rustfmt - - run: cargo fmt --manifest-path ${{ matrix.path }} --all -- --check + - run: cargo fmt --manifest-path ${{ matrix.path }} -- --check - run: cargo build --manifest-path ${{ matrix.path }} - run: cargo test --manifest-path ${{ matrix.path }} - if: ${{ contains(matrix.path, 'core/Cargo.toml') }} diff --git a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/bills.rs b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/bills.rs index a3a6911b98..baf33426eb 100644 --- a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/bills.rs +++ b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/bills.rs @@ -5,7 +5,7 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub user_id: Option , + pub user_id: Option, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] @@ -15,7 +15,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "NoAction", - on_delete = "NoAction", + on_delete = "NoAction" )] Users, #[sea_orm(has_many = "super::users_saved_bills::Entity")] diff --git a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_saved_bills.rs b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_saved_bills.rs index 4ef0281cd2..575bac58f1 100644 --- a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_saved_bills.rs +++ b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_saved_bills.rs @@ -16,7 +16,7 @@ pub enum Relation { from = "Column::BillId", to = "super::bills::Column::Id", on_update = "Cascade", - on_delete = "Cascade", + on_delete = "Cascade" )] Bills, #[sea_orm( @@ -24,7 +24,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "Cascade", - on_delete = "Cascade", + on_delete = "Cascade" )] Users, } diff --git a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_votes.rs b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_votes.rs index a04ad7fa73..62f9cf7d00 100644 --- a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_votes.rs +++ b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_votes.rs @@ -17,7 +17,7 @@ pub enum Relation { from = "Column::BillId", to = "super::bills::Column::Id", on_update = "Cascade", - on_delete = "Cascade", + on_delete = "Cascade" )] Bills, #[sea_orm( @@ -25,7 +25,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "Cascade", - on_delete = "Cascade", + on_delete = "Cascade" )] Users, } diff --git a/sea-orm-codegen/src/tests_cfg/many_to_many/bills.rs b/sea-orm-codegen/src/tests_cfg/many_to_many/bills.rs index 5a3acef9cb..f2de469f0a 100644 --- a/sea-orm-codegen/src/tests_cfg/many_to_many/bills.rs +++ b/sea-orm-codegen/src/tests_cfg/many_to_many/bills.rs @@ -5,7 +5,7 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub user_id: Option , + pub user_id: Option, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] @@ -15,7 +15,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "NoAction", - on_delete = "NoAction", + on_delete = "NoAction" )] Users, #[sea_orm(has_many = "super::users_votes::Entity")] diff --git a/sea-orm-codegen/src/tests_cfg/many_to_many/users_votes.rs b/sea-orm-codegen/src/tests_cfg/many_to_many/users_votes.rs index a04ad7fa73..62f9cf7d00 100644 --- a/sea-orm-codegen/src/tests_cfg/many_to_many/users_votes.rs +++ b/sea-orm-codegen/src/tests_cfg/many_to_many/users_votes.rs @@ -17,7 +17,7 @@ pub enum Relation { from = "Column::BillId", to = "super::bills::Column::Id", on_update = "Cascade", - on_delete = "Cascade", + on_delete = "Cascade" )] Bills, #[sea_orm( @@ -25,7 +25,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "Cascade", - on_delete = "Cascade", + on_delete = "Cascade" )] Users, } diff --git a/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/bills.rs b/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/bills.rs index 5094e5885f..6874aeed0e 100644 --- a/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/bills.rs +++ b/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/bills.rs @@ -5,7 +5,7 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub user_id: Option , + pub user_id: Option, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] @@ -15,7 +15,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "NoAction", - on_delete = "NoAction", + on_delete = "NoAction" )] Users, } diff --git a/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/users_votes.rs b/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/users_votes.rs index 87187f4c0b..29fb173cb5 100644 --- a/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/users_votes.rs +++ b/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/users_votes.rs @@ -7,8 +7,8 @@ pub struct Model { pub user_id: i32, #[sea_orm(primary_key, auto_increment = false)] pub bill_id: i32, - pub user_idd: Option , - pub bill_idd: Option , + pub user_idd: Option, + pub bill_idd: Option, pub vote: bool, } @@ -17,25 +17,25 @@ pub enum Relation { #[sea_orm( belongs_to = "super::bills::Entity", from = "Column::BillIdd", - to = "super::bills::Column::Id", + to = "super::bills::Column::Id" )] Bills2, #[sea_orm( belongs_to = "super::bills::Entity", from = "Column::BillId", - to = "super::bills::Column::Id", + to = "super::bills::Column::Id" )] Bills1, #[sea_orm( belongs_to = "super::users::Entity", from = "Column::UserIdd", - to = "super::users::Column::Id", + to = "super::users::Column::Id" )] Users2, #[sea_orm( belongs_to = "super::users::Entity", from = "Column::UserId", - to = "super::users::Column::Id", + to = "super::users::Column::Id" )] Users1, } diff --git a/sea-orm-codegen/src/tests_cfg/self_referencing/bills.rs b/sea-orm-codegen/src/tests_cfg/self_referencing/bills.rs index 583655f673..22cfc46908 100644 --- a/sea-orm-codegen/src/tests_cfg/self_referencing/bills.rs +++ b/sea-orm-codegen/src/tests_cfg/self_referencing/bills.rs @@ -5,16 +5,12 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub self_id: Option , + pub self_id: Option, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { - #[sea_orm( - belongs_to = "Entity", - from = "Column::SelfId", - to = "Column::Id", - )] + #[sea_orm(belongs_to = "Entity", from = "Column::SelfId", to = "Column::Id")] SelfRef, } diff --git a/sea-orm-codegen/src/tests_cfg/self_referencing/users.rs b/sea-orm-codegen/src/tests_cfg/self_referencing/users.rs index b54f3e5e2d..487ba9409a 100644 --- a/sea-orm-codegen/src/tests_cfg/self_referencing/users.rs +++ b/sea-orm-codegen/src/tests_cfg/self_referencing/users.rs @@ -5,23 +5,15 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub self_id: Option , - pub self_idd: Option , + pub self_id: Option, + pub self_idd: Option, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { - #[sea_orm( - belongs_to = "Entity", - from = "Column::SelfId", - to = "Column::Id", - )] + #[sea_orm(belongs_to = "Entity", from = "Column::SelfId", to = "Column::Id")] SelfRef2, - #[sea_orm( - belongs_to = "Entity", - from = "Column::SelfIdd", - to = "Column::Id", - )] + #[sea_orm(belongs_to = "Entity", from = "Column::SelfIdd", to = "Column::Id")] SelfRef1, } From c4f1a3c4af2a08745abbc46f6797cd4f600aa6d2 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 22 Jun 2023 17:05:22 +0800 Subject: [PATCH 0033/1371] Give up This reverts commit 27af2c9c056be580d22d3a32fd0f766cf4443e3c. --- .github/workflows/rust.yml | 2 +- .../duplicated_many_to_many_paths/bills.rs | 4 ++-- .../users_saved_bills.rs | 4 ++-- .../duplicated_many_to_many_paths/users_votes.rs | 4 ++-- .../src/tests_cfg/many_to_many/bills.rs | 4 ++-- .../src/tests_cfg/many_to_many/users_votes.rs | 4 ++-- .../src/tests_cfg/many_to_many_multiple/bills.rs | 4 ++-- .../many_to_many_multiple/users_votes.rs | 12 ++++++------ .../src/tests_cfg/self_referencing/bills.rs | 8 ++++++-- .../src/tests_cfg/self_referencing/users.rs | 16 ++++++++++++---- 10 files changed, 37 insertions(+), 25 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 2005f07447..4edf09f419 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -251,7 +251,7 @@ jobs: with: toolchain: stable components: rustfmt - - run: cargo fmt --manifest-path ${{ matrix.path }} -- --check + - run: cargo fmt --manifest-path ${{ matrix.path }} --all --check - run: cargo build --manifest-path ${{ matrix.path }} - run: cargo test --manifest-path ${{ matrix.path }} - if: ${{ contains(matrix.path, 'core/Cargo.toml') }} diff --git a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/bills.rs b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/bills.rs index baf33426eb..a3a6911b98 100644 --- a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/bills.rs +++ b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/bills.rs @@ -5,7 +5,7 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub user_id: Option, + pub user_id: Option , } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] @@ -15,7 +15,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "NoAction", - on_delete = "NoAction" + on_delete = "NoAction", )] Users, #[sea_orm(has_many = "super::users_saved_bills::Entity")] diff --git a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_saved_bills.rs b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_saved_bills.rs index 575bac58f1..4ef0281cd2 100644 --- a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_saved_bills.rs +++ b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_saved_bills.rs @@ -16,7 +16,7 @@ pub enum Relation { from = "Column::BillId", to = "super::bills::Column::Id", on_update = "Cascade", - on_delete = "Cascade" + on_delete = "Cascade", )] Bills, #[sea_orm( @@ -24,7 +24,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "Cascade", - on_delete = "Cascade" + on_delete = "Cascade", )] Users, } diff --git a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_votes.rs b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_votes.rs index 62f9cf7d00..a04ad7fa73 100644 --- a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_votes.rs +++ b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_votes.rs @@ -17,7 +17,7 @@ pub enum Relation { from = "Column::BillId", to = "super::bills::Column::Id", on_update = "Cascade", - on_delete = "Cascade" + on_delete = "Cascade", )] Bills, #[sea_orm( @@ -25,7 +25,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "Cascade", - on_delete = "Cascade" + on_delete = "Cascade", )] Users, } diff --git a/sea-orm-codegen/src/tests_cfg/many_to_many/bills.rs b/sea-orm-codegen/src/tests_cfg/many_to_many/bills.rs index f2de469f0a..5a3acef9cb 100644 --- a/sea-orm-codegen/src/tests_cfg/many_to_many/bills.rs +++ b/sea-orm-codegen/src/tests_cfg/many_to_many/bills.rs @@ -5,7 +5,7 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub user_id: Option, + pub user_id: Option , } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] @@ -15,7 +15,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "NoAction", - on_delete = "NoAction" + on_delete = "NoAction", )] Users, #[sea_orm(has_many = "super::users_votes::Entity")] diff --git a/sea-orm-codegen/src/tests_cfg/many_to_many/users_votes.rs b/sea-orm-codegen/src/tests_cfg/many_to_many/users_votes.rs index 62f9cf7d00..a04ad7fa73 100644 --- a/sea-orm-codegen/src/tests_cfg/many_to_many/users_votes.rs +++ b/sea-orm-codegen/src/tests_cfg/many_to_many/users_votes.rs @@ -17,7 +17,7 @@ pub enum Relation { from = "Column::BillId", to = "super::bills::Column::Id", on_update = "Cascade", - on_delete = "Cascade" + on_delete = "Cascade", )] Bills, #[sea_orm( @@ -25,7 +25,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "Cascade", - on_delete = "Cascade" + on_delete = "Cascade", )] Users, } diff --git a/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/bills.rs b/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/bills.rs index 6874aeed0e..5094e5885f 100644 --- a/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/bills.rs +++ b/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/bills.rs @@ -5,7 +5,7 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub user_id: Option, + pub user_id: Option , } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] @@ -15,7 +15,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "NoAction", - on_delete = "NoAction" + on_delete = "NoAction", )] Users, } diff --git a/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/users_votes.rs b/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/users_votes.rs index 29fb173cb5..87187f4c0b 100644 --- a/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/users_votes.rs +++ b/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/users_votes.rs @@ -7,8 +7,8 @@ pub struct Model { pub user_id: i32, #[sea_orm(primary_key, auto_increment = false)] pub bill_id: i32, - pub user_idd: Option, - pub bill_idd: Option, + pub user_idd: Option , + pub bill_idd: Option , pub vote: bool, } @@ -17,25 +17,25 @@ pub enum Relation { #[sea_orm( belongs_to = "super::bills::Entity", from = "Column::BillIdd", - to = "super::bills::Column::Id" + to = "super::bills::Column::Id", )] Bills2, #[sea_orm( belongs_to = "super::bills::Entity", from = "Column::BillId", - to = "super::bills::Column::Id" + to = "super::bills::Column::Id", )] Bills1, #[sea_orm( belongs_to = "super::users::Entity", from = "Column::UserIdd", - to = "super::users::Column::Id" + to = "super::users::Column::Id", )] Users2, #[sea_orm( belongs_to = "super::users::Entity", from = "Column::UserId", - to = "super::users::Column::Id" + to = "super::users::Column::Id", )] Users1, } diff --git a/sea-orm-codegen/src/tests_cfg/self_referencing/bills.rs b/sea-orm-codegen/src/tests_cfg/self_referencing/bills.rs index 22cfc46908..583655f673 100644 --- a/sea-orm-codegen/src/tests_cfg/self_referencing/bills.rs +++ b/sea-orm-codegen/src/tests_cfg/self_referencing/bills.rs @@ -5,12 +5,16 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub self_id: Option, + pub self_id: Option , } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { - #[sea_orm(belongs_to = "Entity", from = "Column::SelfId", to = "Column::Id")] + #[sea_orm( + belongs_to = "Entity", + from = "Column::SelfId", + to = "Column::Id", + )] SelfRef, } diff --git a/sea-orm-codegen/src/tests_cfg/self_referencing/users.rs b/sea-orm-codegen/src/tests_cfg/self_referencing/users.rs index 487ba9409a..b54f3e5e2d 100644 --- a/sea-orm-codegen/src/tests_cfg/self_referencing/users.rs +++ b/sea-orm-codegen/src/tests_cfg/self_referencing/users.rs @@ -5,15 +5,23 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub self_id: Option, - pub self_idd: Option, + pub self_id: Option , + pub self_idd: Option , } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { - #[sea_orm(belongs_to = "Entity", from = "Column::SelfId", to = "Column::Id")] + #[sea_orm( + belongs_to = "Entity", + from = "Column::SelfId", + to = "Column::Id", + )] SelfRef2, - #[sea_orm(belongs_to = "Entity", from = "Column::SelfIdd", to = "Column::Id")] + #[sea_orm( + belongs_to = "Entity", + from = "Column::SelfIdd", + to = "Column::Id", + )] SelfRef1, } From d464cbc9be7556bc0c9d629f91976ce45d7ca2f8 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Fri, 23 Jun 2023 18:36:53 +0800 Subject: [PATCH 0034/1371] COMMUNITY --- COMMUNITY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/COMMUNITY.md b/COMMUNITY.md index 2d94ea3733..f4fb6469a2 100644 --- a/COMMUNITY.md +++ b/COMMUNITY.md @@ -53,9 +53,9 @@ If you have built an app using SeaORM and want to showcase it, feel free to open - [RGB Lib](https://github.com/RGB-Tools/rgb-lib) | A library to manage wallets for RGB assets | DB: MySQL, Postgres, SQLite #### Dev Tools +- [Crosup](https://github.com/tsirysndr/crosup) | Quickly setup your development environment on your Chromebook/ChromeOS or any Linux distribution 🐧 ❄️ 💻 🚀 ✨ | DB: SQLite - [nitro_repo](https://github.com/wyatt-herkamp/nitro_repo) | An OpenSource, lightweight, and fast artifact manager. | DB: MySQL, SQLite - [Orca](https://github.com/workfoxes/orca) | An No-code Test Automation platform using Actix, SeaORM, React. runs on the desktop and cloud | DB: Postgres -- [Crosup](https://github.com/tsirysndr/crosup) | Quickly setup your development environment on your Chromebook/ChromeOS or any Linux distribution 🐧 ❄️ 💻 🚀 ✨ | DB: SQLite #### System - [Email view tracker](https://github.com/friedemannsommer/email-view-tracker) | Simple web UI to create tracking URLs for HTML emails. | DB: MySQL, Postgres From faf222698c6b710f7d7e2a30924f9c2decfbb493 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Fri, 23 Jun 2023 19:07:49 +0800 Subject: [PATCH 0035/1371] [CI] Fix example fmt (#1719) * [CI] Fix example fmt * CI should failed, try the fmt check still works * Revert "CI should failed, try the fmt check still works" This reverts commit 2fea8a236b94e677a32ca89415153e53eb600279. --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 4edf09f419..2005f07447 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -251,7 +251,7 @@ jobs: with: toolchain: stable components: rustfmt - - run: cargo fmt --manifest-path ${{ matrix.path }} --all --check + - run: cargo fmt --manifest-path ${{ matrix.path }} -- --check - run: cargo build --manifest-path ${{ matrix.path }} - run: cargo test --manifest-path ${{ matrix.path }} - if: ${{ contains(matrix.path, 'core/Cargo.toml') }} From e8eac61c3f02066129f5b68c81552b404d487ce9 Mon Sep 17 00:00:00 2001 From: ccQpein Date: Sun, 25 Jun 2023 23:06:04 -0400 Subject: [PATCH 0036/1371] Update readme temp (#1723) --- sea-orm-cli/template/migration/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sea-orm-cli/template/migration/README.md b/sea-orm-cli/template/migration/README.md index b3ea53eb44..3b438d89e3 100644 --- a/sea-orm-cli/template/migration/README.md +++ b/sea-orm-cli/template/migration/README.md @@ -2,7 +2,7 @@ - Generate a new migration file ```sh - cargo run -- migrate generate MIGRATION_NAME + cargo run -- generate MIGRATION_NAME ``` - Apply all pending migrations ```sh From 35c274818b7bb79889f903f7001f3288a24f2622 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Mon, 26 Jun 2023 11:08:09 +0800 Subject: [PATCH 0037/1371] CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 781bcb2e1b..96467ca3ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -423,6 +423,7 @@ impl ColumnTrait for Column { } ``` * Resolved `insert_many` failing if the models iterator is empty https://github.com/SeaQL/sea-orm/issues/873 +* Update the template MD file of `migration/README.md`, fix a faulty sample `migrate init` shell script https://github.com/SeaQL/sea-orm/pull/1723 ### Breaking changes From 64342b105c6eddda3da55e7413e708773b42adaa Mon Sep 17 00:00:00 2001 From: darkmmon Date: Mon, 26 Jun 2023 20:15:37 +0800 Subject: [PATCH 0038/1371] Upgrading to syn v2 (#1713) * resetted and re-did the changes * fmt * upgraded to syn2 for sea-orm-codegen * Rename `bae2` * Drop the use of `#[sea_orm(table_name = "col_name")]` in `DeriveColumn` * unified derived input metadata parsing filter * Propagate errors --------- Co-authored-by: Billy Chan --- sea-orm-codegen/Cargo.toml | 2 +- sea-orm-macros/Cargo.toml | 4 +- sea-orm-macros/src/derives/active_enum.rs | 120 +++++----- sea-orm-macros/src/derives/active_model.rs | 50 ++--- sea-orm-macros/src/derives/column.rs | 40 ++-- sea-orm-macros/src/derives/entity_model.rs | 223 ++++++++----------- sea-orm-macros/src/derives/model.rs | 48 ++-- sea-orm-macros/src/derives/partial_model.rs | 16 +- sea-orm-macros/src/derives/primary_key.rs | 37 ++- sea-orm-macros/src/derives/util.rs | 2 +- sea-orm-macros/src/strum/helpers/metadata.rs | 67 ++---- 11 files changed, 252 insertions(+), 357 deletions(-) diff --git a/sea-orm-codegen/Cargo.toml b/sea-orm-codegen/Cargo.toml index d6c27276fd..cb97dbf981 100644 --- a/sea-orm-codegen/Cargo.toml +++ b/sea-orm-codegen/Cargo.toml @@ -18,7 +18,7 @@ path = "src/lib.rs" [dependencies] sea-query = { version = "0.29.0-rc.2", default-features = false, features = ["thread-safe"] } -syn = { version = "1", default-features = false, features = ["parsing", "proc-macro", "derive", "printing"] } +syn = { version = "2", default-features = false, features = ["parsing", "proc-macro", "derive", "printing"] } quote = { version = "1", default-features = false } heck = { version = "0.4", default-features = false } proc-macro2 = { version = "1", default-features = false } diff --git a/sea-orm-macros/Cargo.toml b/sea-orm-macros/Cargo.toml index 4091a83435..18de557601 100644 --- a/sea-orm-macros/Cargo.toml +++ b/sea-orm-macros/Cargo.toml @@ -18,8 +18,8 @@ path = "src/lib.rs" proc-macro = true [dependencies] -bae = { version = "0.1", default-features = false, optional = true } -syn = { version = "1", default-features = false, features = ["parsing", "proc-macro", "derive", "printing"] } +bae = { version = "1", package = "bae2", default-features = false, optional = true } +syn = { version = "2", default-features = false, features = ["parsing", "proc-macro", "derive", "printing"] } quote = { version = "1", default-features = false } heck = { version = "0.4", default-features = false } proc-macro2 = { version = "1", default-features = false } diff --git a/sea-orm-macros/src/derives/active_enum.rs b/sea-orm-macros/src/derives/active_enum.rs index aa85f8a95e..77e30196ae 100644 --- a/sea-orm-macros/src/derives/active_enum.rs +++ b/sea-orm-macros/src/derives/active_enum.rs @@ -2,7 +2,7 @@ use super::util::camel_case_with_escaped_non_uax31; use heck::ToUpperCamelCase; use proc_macro2::TokenStream; use quote::{format_ident, quote, quote_spanned}; -use syn::{parse, punctuated::Punctuated, token::Comma, Expr, Lit, LitInt, LitStr, Meta, UnOp}; +use syn::{parse, Expr, Lit, LitInt, LitStr, UnOp}; enum Error { InputNotEnum, @@ -37,51 +37,46 @@ impl ActiveEnum { let mut db_type = Err(Error::TT(quote_spanned! { ident_span => compile_error!("Missing macro attribute `db_type`"); })); - for attr in input.attrs.iter() { - if let Some(ident) = attr.path.get_ident() { - if ident != "sea_orm" { - continue; - } - } else { - continue; - } - if let Ok(list) = attr.parse_args_with(Punctuated::::parse_terminated) { - for meta in list.iter() { - if let Meta::NameValue(nv) = meta { - if let Some(name) = nv.path.get_ident() { - if name == "rs_type" { - if let Lit::Str(litstr) = &nv.lit { - rs_type = syn::parse_str::(&litstr.value()) - .map_err(Error::Syn); - } - } else if name == "db_type" { - if let Lit::Str(litstr) = &nv.lit { - let s = litstr.value(); - match s.as_ref() { - "Enum" => { - db_type = Ok(quote! { - Enum { - name: Self::name(), - variants: Self::iden_values(), - } - }) - } - _ => { - db_type = syn::parse_str::(&s) - .map_err(Error::Syn); - } + + input + .attrs + .iter() + .filter(|attr| attr.path().is_ident("sea_orm")) + .try_for_each(|attr| { + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("rs_type") { + let litstr: LitStr = meta.value()?.parse()?; + rs_type = + syn::parse_str::(&litstr.value()).map_err(Error::Syn); + } else if meta.path.is_ident("db_type") { + let litstr: LitStr = meta.value()?.parse()?; + let s = litstr.value(); + match s.as_ref() { + "Enum" => { + db_type = Ok(quote! { + Enum { + name: Self::name(), + variants: Self::iden_values(), } - } - } else if name == "enum_name" { - if let Lit::Str(litstr) = &nv.lit { - enum_name = litstr.value(); - } + }) + } + _ => { + db_type = syn::parse_str::(&s).map_err(Error::Syn); } } + } else if meta.path.is_ident("enum_name") { + let litstr: LitStr = meta.value()?.parse()?; + enum_name = litstr.value(); + } else { + return Err(meta.error(format!( + "Unknown attribute parameter found: {:?}", + meta.path.get_ident() + ))); } - } - } - } + Ok(()) + }) + .map_err(Error::Syn) + })?; let variant_vec = match input.data { syn::Data::Enum(syn::DataEnum { variants, .. }) => variants, @@ -96,33 +91,26 @@ impl ActiveEnum { let mut string_value = None; let mut num_value = None; for attr in variant.attrs.iter() { - if let Some(ident) = attr.path.get_ident() { - if ident != "sea_orm" { - continue; - } - } else { + if !attr.path().is_ident("sea_orm") { continue; } - if let Ok(list) = attr.parse_args_with(Punctuated::::parse_terminated) - { - for meta in list { - if let Meta::NameValue(nv) = meta { - if let Some(name) = nv.path.get_ident() { - if name == "string_value" { - if let Lit::Str(lit) = nv.lit { - is_string = true; - string_value = Some(lit); - } - } else if name == "num_value" { - if let Lit::Int(lit) = nv.lit { - is_int = true; - num_value = Some(lit); - } - } - } - } + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("string_value") { + is_string = true; + string_value = Some(meta.value()?.parse::()?); + } else if meta.path.is_ident("num_value") { + is_int = true; + num_value = Some(meta.value()?.parse::()?); + } else { + return Err(meta.error(format!( + "Unknown attribute parameter found: {:?}", + meta.path.get_ident() + ))); } - } + + Ok(()) + }) + .map_err(Error::Syn)?; } if is_string && is_int { diff --git a/sea-orm-macros/src/derives/active_model.rs b/sea-orm-macros/src/derives/active_model.rs index dbea86f1ac..975eabfd7a 100644 --- a/sea-orm-macros/src/derives/active_model.rs +++ b/sea-orm-macros/src/derives/active_model.rs @@ -4,11 +4,7 @@ use super::util::{ use heck::ToUpperCamelCase; use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote, quote_spanned}; -use syn::{ - punctuated::{IntoIter, Punctuated}, - token::Comma, - Data, DataStruct, Field, Fields, Lit, Meta, Type, -}; +use syn::{punctuated::IntoIter, Data, DataStruct, Expr, Field, Fields, LitStr, Type}; /// Method to derive an [ActiveModel](sea_orm::ActiveModel) pub fn expand_derive_active_model(ident: Ident, data: Data) -> syn::Result { @@ -47,32 +43,28 @@ fn derive_active_model(all_fields: IntoIter) -> syn::Result let ident = trim_starting_raw_identifier(ident).to_upper_camel_case(); let ident = escape_rust_keyword(ident); let mut ident = format_ident!("{}", &ident); - for attr in field.attrs.iter() { - if let Some(ident) = attr.path.get_ident() { - if ident != "sea_orm" { - continue; - } - } else { - continue; - } - if let Ok(list) = attr.parse_args_with(Punctuated::::parse_terminated) - { - for meta in list.iter() { - if let Meta::NameValue(nv) = meta { - if let Some(name) = nv.path.get_ident() { - if name == "enum_name" { - if let Lit::Str(litstr) = &nv.lit { - ident = syn::parse_str(&litstr.value()).unwrap(); - } - } - } + field + .attrs + .iter() + .filter(|attr| attr.path().is_ident("sea_orm")) + .try_for_each(|attr| { + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("enum_name") { + let litstr: LitStr = meta.value()?.parse()?; + ident = syn::parse_str(&litstr.value()).unwrap(); + } else { + // Reads the value expression to advance the parse stream. + // Some parameters, such as `primary_key`, do not have any value, + // so ignoring an error occurred here. + let _: Option = meta.value().and_then(|v| v.parse()).ok(); } - } - } - } - ident + + Ok(()) + }) + })?; + Ok::(ident) }) - .collect(); + .collect::>()?; let ty: Vec = fields.into_iter().map(|Field { ty, .. }| ty).collect(); diff --git a/sea-orm-macros/src/derives/column.rs b/sea-orm-macros/src/derives/column.rs index dbc9017ada..f91cf48eba 100644 --- a/sea-orm-macros/src/derives/column.rs +++ b/sea-orm-macros/src/derives/column.rs @@ -1,7 +1,7 @@ use heck::{ToLowerCamelCase, ToSnakeCase}; use proc_macro2::{Ident, TokenStream}; use quote::{quote, quote_spanned}; -use syn::{punctuated::Punctuated, token::Comma, Data, DataEnum, Fields, Lit, Meta, Variant}; +use syn::{Data, DataEnum, Expr, Fields, LitStr, Variant}; /// Derive a Column name for an enum type pub fn impl_default_as_str(ident: &Ident, data: &Data) -> syn::Result { @@ -28,36 +28,24 @@ pub fn impl_default_as_str(ident: &Ident, data: &Data) -> syn::Result::parse_terminated) - { - for meta in list.iter() { - if let Meta::NameValue(nv) = meta { - if let Some(name) = nv.path.get_ident() { - if name == "column_name" { - if let Lit::Str(litstr) = &nv.lit { - column_name = litstr.value(); - } - } - if name == "table_name" { - if let Lit::Str(litstr) = &nv.lit { - column_name = litstr.value(); - } - } - } - } + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("column_name") { + column_name = meta.value()?.parse::()?.value(); + } else { + // Reads the value expression to advance the parse stream. + // Some parameters, such as `primary_key`, do not have any value, + // so ignoring an error occurred here. + let _: Option = meta.value().and_then(|v| v.parse()).ok(); } - } + Ok(()) + })?; } - quote! { #column_name } + Ok::(quote! { #column_name }) }) - .collect(); + .collect::>()?; Ok(quote!( #[automatically_derived] diff --git a/sea-orm-macros/src/derives/entity_model.rs b/sea-orm-macros/src/derives/entity_model.rs index e50b8a66d4..67cb27e875 100644 --- a/sea-orm-macros/src/derives/entity_model.rs +++ b/sea-orm-macros/src/derives/entity_model.rs @@ -3,8 +3,8 @@ use heck::{ToSnakeCase, ToUpperCamelCase}; use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote, quote_spanned}; use syn::{ - parse::Error, punctuated::Punctuated, spanned::Spanned, token::Comma, Attribute, Data, Fields, - Lit, LitStr, Meta, Type, + punctuated::Punctuated, spanned::Spanned, token::Comma, Attribute, Data, Expr, Fields, Lit, + LitStr, Type, }; /// Method to derive an Model @@ -13,32 +13,28 @@ pub fn expand_derive_entity_model(data: Data, attrs: Vec) -> syn::Res let mut table_name = None; let mut schema_name = quote! { None }; let mut table_iden = false; - attrs.iter().for_each(|attr| { - if attr.path.get_ident().map(|i| i == "sea_orm") != Some(true) { - return; - } - - if let Ok(list) = attr.parse_args_with(Punctuated::::parse_terminated) { - for meta in list.iter() { - if let Meta::NameValue(nv) = meta { - if let Some(ident) = nv.path.get_ident() { - if ident == "table_name" { - table_name = Some(nv.lit.clone()); - } else if ident == "schema_name" { - let name = &nv.lit; - schema_name = quote! { Some(#name) }; - } - } - } else if let Meta::Path(path) = meta { - if let Some(ident) = path.get_ident() { - if ident == "table_iden" { - table_iden = true; - } - } + attrs + .iter() + .filter(|attr| attr.path().is_ident("sea_orm")) + .try_for_each(|attr| { + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("table_name") { + table_name = Some(meta.value()?.parse::()?); + } else if meta.path.is_ident("schema_name") { + let name: Lit = meta.value()?.parse()?; + schema_name = quote! { Some(#name) }; + } else if meta.path.is_ident("table_iden") { + table_iden = true; + } else { + // Reads the value expression to advance the parse stream. + // Some parameters, such as `primary_key`, do not have any value, + // so ignoring an error occurred here. + let _: Option = meta.value().and_then(|v| v.parse()).ok(); } - } - } - }); + + Ok(()) + }) + })?; let entity_def = table_name .as_ref() .map(|table_name| { @@ -114,111 +110,86 @@ pub fn expand_derive_entity_model(data: Data, attrs: Vec) -> syn::Res let mut is_primary_key = false; // search for #[sea_orm(primary_key, auto_increment = false, column_type = "String(Some(255))", default_value = "new user", default_expr = "gen_random_uuid()", column_name = "name", enum_name = "Name", nullable, indexed, unique)] for attr in field.attrs.iter() { - if let Some(ident) = attr.path.get_ident() { - if ident != "sea_orm" { - continue; - } - } else { + if !attr.path().is_ident("sea_orm") { continue; } // single param - if let Ok(list) = - attr.parse_args_with(Punctuated::::parse_terminated) - { - for meta in list.iter() { - match meta { - Meta::NameValue(nv) => { - if let Some(name) = nv.path.get_ident() { - if name == "column_type" { - if let Lit::Str(litstr) = &nv.lit { - let ty: TokenStream = - syn::parse_str(&litstr.value())?; - sql_type = Some(ty); - } else { - return Err(Error::new( - field.span(), - format!("Invalid column_type {:?}", nv.lit), - )); - } - } else if name == "auto_increment" { - if let Lit::Bool(litbool) = &nv.lit { - auto_increment = litbool.value(); - } else { - return Err(Error::new( - field.span(), - format!( - "Invalid auto_increment = {:?}", - nv.lit - ), - )); - } - } else if name == "default_value" { - default_value = Some(nv.lit.to_owned()); - } else if name == "default_expr" { - default_expr = Some(nv.lit.to_owned()); - } else if name == "column_name" { - if let Lit::Str(litstr) = &nv.lit { - column_name = Some(litstr.value()); - } else { - return Err(Error::new( - field.span(), - format!("Invalid column_name {:?}", nv.lit), - )); - } - } else if name == "enum_name" { - if let Lit::Str(litstr) = &nv.lit { - let ty: Ident = - syn::parse_str(&litstr.value())?; - enum_name = Some(ty); - } else { - return Err(Error::new( - field.span(), - format!("Invalid enum_name {:?}", nv.lit), - )); - } - } else if name == "select_as" { - if let Lit::Str(litstr) = &nv.lit { - select_as = Some(litstr.value()); - } else { - return Err(Error::new( - field.span(), - format!("Invalid select_as {:?}", nv.lit), - )); - } - } else if name == "save_as" { - if let Lit::Str(litstr) = &nv.lit { - save_as = Some(litstr.value()); - } else { - return Err(Error::new( - field.span(), - format!("Invalid save_as {:?}", nv.lit), - )); - } - } - } - } - Meta::Path(p) => { - if let Some(name) = p.get_ident() { - if name == "ignore" { - ignore = true; - break; - } else if name == "primary_key" { - is_primary_key = true; - primary_key_types.push(field.ty.clone()); - } else if name == "nullable" { - nullable = true; - } else if name == "indexed" { - indexed = true; - } else if name == "unique" { - unique = true; - } - } - } - _ => {} + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("column_type") { + let lit = meta.value()?.parse()?; + if let Lit::Str(litstr) = lit { + let ty: TokenStream = syn::parse_str(&litstr.value())?; + sql_type = Some(ty); + } else { + return Err( + meta.error(format!("Invalid column_type {:?}", lit)) + ); + } + } else if meta.path.is_ident("auto_increment") { + let lit = meta.value()?.parse()?; + if let Lit::Bool(litbool) = lit { + auto_increment = litbool.value(); + } else { + return Err( + meta.error(format!("Invalid auto_increment = {:?}", lit)) + ); + } + } else if meta.path.is_ident("default_value") { + default_value = Some(meta.value()?.parse::()?); + } else if meta.path.is_ident("default_expr") { + default_expr = Some(meta.value()?.parse::()?); + } else if meta.path.is_ident("column_name") { + let lit = meta.value()?.parse()?; + if let Lit::Str(litstr) = lit { + column_name = Some(litstr.value()); + } else { + return Err( + meta.error(format!("Invalid column_name {:?}", lit)) + ); + } + } else if meta.path.is_ident("enum_name") { + let lit = meta.value()?.parse()?; + if let Lit::Str(litstr) = lit { + let ty: Ident = syn::parse_str(&litstr.value())?; + enum_name = Some(ty); + } else { + return Err(meta.error(format!("Invalid enum_name {:?}", lit))); + } + } else if meta.path.is_ident("select_as") { + let lit = meta.value()?.parse()?; + if let Lit::Str(litstr) = lit { + select_as = Some(litstr.value()); + } else { + return Err(meta.error(format!("Invalid select_as {:?}", lit))); } + } else if meta.path.is_ident("save_as") { + let lit = meta.value()?.parse()?; + if let Lit::Str(litstr) = lit { + save_as = Some(litstr.value()); + } else { + return Err(meta.error(format!("Invalid save_as {:?}", lit))); + } + } else if meta.path.is_ident("ignore") { + ignore = true; + } else if meta.path.is_ident("primary_key") { + is_primary_key = true; + primary_key_types.push(field.ty.clone()); + } else if meta.path.is_ident("nullable") { + nullable = true; + } else if meta.path.is_ident("indexed") { + indexed = true; + } else if meta.path.is_ident("unique") { + unique = true; + } else { + // Reads the value expression to advance the parse stream. + // Some parameters, such as `primary_key`, do not have any value, + // so ignoring an error occurred here. + let _: Option = meta.value().and_then(|v| v.parse()).ok(); } - } + + Ok(()) + })?; } if let Some(enum_name) = enum_name { diff --git a/sea-orm-macros/src/derives/model.rs b/sea-orm-macros/src/derives/model.rs index d3813f28b7..b28c62cf2d 100644 --- a/sea-orm-macros/src/derives/model.rs +++ b/sea-orm-macros/src/derives/model.rs @@ -6,7 +6,7 @@ use heck::ToUpperCamelCase; use proc_macro2::TokenStream; use quote::{format_ident, quote, quote_spanned}; use std::iter::FromIterator; -use syn::{punctuated::Punctuated, token::Comma, Ident, Lit, Meta}; +use syn::{Expr, Ident, LitStr}; enum Error { InputNotStruct, @@ -50,33 +50,29 @@ impl DeriveModel { let ident = trim_starting_raw_identifier(ident).to_upper_camel_case(); let ident = escape_rust_keyword(ident); let mut ident = format_ident!("{}", &ident); - for attr in field.attrs.iter() { - if let Some(ident) = attr.path.get_ident() { - if ident != "sea_orm" { - continue; - } - } else { - continue; - } - if let Ok(list) = - attr.parse_args_with(Punctuated::::parse_terminated) - { - for meta in list.iter() { - if let Meta::NameValue(nv) = meta { - if let Some(name) = nv.path.get_ident() { - if name == "enum_name" { - if let Lit::Str(litstr) = &nv.lit { - ident = syn::parse_str(&litstr.value()).unwrap(); - } - } - } + field + .attrs + .iter() + .filter(|attr| attr.path().is_ident("sea_orm")) + .try_for_each(|attr| { + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("enum_name") { + ident = syn::parse_str(&meta.value()?.parse::()?.value()) + .unwrap(); + } else { + // Reads the value expression to advance the parse stream. + // Some parameters, such as `primary_key`, do not have any value, + // so ignoring an error occurred here. + let _: Option = meta.value().and_then(|v| v.parse()).ok(); } - } - } - } - ident + + Ok(()) + }) + .map_err(Error::Syn) + })?; + Ok(ident) }) - .collect(); + .collect::>()?; let ignore_attrs = fields .iter() diff --git a/sea-orm-macros/src/derives/partial_model.rs b/sea-orm-macros/src/derives/partial_model.rs index 3d3703582b..567e633ac5 100644 --- a/sea-orm-macros/src/derives/partial_model.rs +++ b/sea-orm-macros/src/derives/partial_model.rs @@ -50,11 +50,7 @@ impl DerivePartialModel { let mut entity_ident = None; for attr in input.attrs.iter() { - if let Some(ident) = attr.path.get_ident() { - if ident != "sea_orm" { - continue; - } - } else { + if !attr.path().is_ident("sea_orm") { continue; } @@ -77,7 +73,7 @@ impl DerivePartialModel { let mut from_expr = None; for attr in field.attrs.iter() { - if !attr.path.is_ident("sea_orm") { + if !attr.path().is_ident("sea_orm") { continue; } @@ -192,7 +188,7 @@ pub fn expand_derive_partial_model(input: syn::DeriveInput) -> syn::Result Option; @@ -200,12 +196,14 @@ mod util { impl GetAsKVMeta for Meta { fn get_as_kv(&self, k: &str) -> Option { - let Meta::NameValue(MetaNameValue{path, lit:Lit::Str(lit), ..}) = self else { + let Meta::NameValue(MetaNameValue{path, value: syn::Expr::Lit(exprlit), ..}) = self else { return None; }; + let syn::Lit::Str(litstr) = &exprlit.lit else { return None; }; + if path.is_ident(k) { - Some(lit.value()) + Some(litstr.value()) } else { None } diff --git a/sea-orm-macros/src/derives/primary_key.rs b/sea-orm-macros/src/derives/primary_key.rs index 34e183fb04..f7ca14f4c2 100644 --- a/sea-orm-macros/src/derives/primary_key.rs +++ b/sea-orm-macros/src/derives/primary_key.rs @@ -1,7 +1,7 @@ use heck::ToSnakeCase; use proc_macro2::{Ident, TokenStream}; use quote::{quote, quote_spanned}; -use syn::{punctuated::Punctuated, token::Comma, Data, DataEnum, Fields, Lit, Meta, Variant}; +use syn::{Data, DataEnum, Expr, Fields, LitStr, Variant}; /// Method to derive a Primary Key for a Model using the [PrimaryKeyTrait](sea_orm::PrimaryKeyTrait) pub fn expand_derive_primary_key(ident: Ident, data: Data) -> syn::Result { @@ -34,31 +34,26 @@ pub fn expand_derive_primary_key(ident: Ident, data: Data) -> syn::Result::parse_terminated) - { - for meta in list.iter() { - if let Meta::NameValue(nv) = meta { - if let Some(name) = nv.path.get_ident() { - if name == "column_name" { - if let Lit::Str(litstr) = &nv.lit { - column_name = litstr.value(); - } - } - } - } + + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("column_name") { + column_name = meta.value()?.parse::()?.value(); + } else { + // Reads the value expression to advance the parse stream. + // Some parameters, such as `primary_key`, do not have any value, + // so ignoring an error occurred here. + let _: Option = meta.value().and_then(|v| v.parse()).ok(); } - } + + Ok(()) + })?; } - quote! { #column_name } + Ok::(quote! { #column_name }) }) - .collect(); + .collect::>()?; Ok(quote!( #[automatically_derived] diff --git a/sea-orm-macros/src/derives/util.rs b/sea-orm-macros/src/derives/util.rs index 7de4b79cd9..e59203f6f5 100644 --- a/sea-orm-macros/src/derives/util.rs +++ b/sea-orm-macros/src/derives/util.rs @@ -4,7 +4,7 @@ use syn::{punctuated::Punctuated, token::Comma, Field, Ident, Meta}; pub(crate) fn field_not_ignored(field: &Field) -> bool { for attr in field.attrs.iter() { - if let Some(ident) = attr.path.get_ident() { + if let Some(ident) = attr.path().get_ident() { if ident != "sea_orm" { continue; } diff --git a/sea-orm-macros/src/strum/helpers/metadata.rs b/sea-orm-macros/src/strum/helpers/metadata.rs index 56e4c78bd8..d638ae3c5f 100644 --- a/sea-orm-macros/src/strum/helpers/metadata.rs +++ b/sea-orm-macros/src/strum/helpers/metadata.rs @@ -1,12 +1,11 @@ -use proc_macro2::{Span, TokenStream}; +use proc_macro2::TokenStream; use syn::{ parenthesized, parse::{Parse, ParseStream}, parse2, parse_str, punctuated::Punctuated, - spanned::Spanned, - Attribute, DeriveInput, Ident, Lit, LitBool, LitStr, Meta, MetaNameValue, Path, Token, Variant, - Visibility, + Attribute, DeriveInput, Expr, ExprLit, Ident, Lit, LitBool, LitStr, Meta, MetaNameValue, Path, + Token, Variant, Visibility, }; use super::case_style::CaseStyle; @@ -76,17 +75,6 @@ impl Parse for EnumMeta { } } -impl Spanned for EnumMeta { - fn span(&self) -> Span { - match self { - EnumMeta::SerializeAll { kw, .. } => kw.span(), - EnumMeta::AsciiCaseInsensitive(kw) => kw.span(), - EnumMeta::Crate { kw, .. } => kw.span(), - EnumMeta::UsePhf(use_phf) => use_phf.span(), - } - } -} - pub enum EnumDiscriminantsMeta { Derive { kw: kw::derive, paths: Vec }, Name { kw: kw::name, name: Ident }, @@ -100,7 +88,7 @@ impl Parse for EnumDiscriminantsMeta { let kw = input.parse()?; let content; parenthesized!(content in input); - let paths = content.parse_terminated::<_, Token![,]>(Path::parse)?; + let paths = content.parse_terminated(Path::parse, Token![,])?; Ok(EnumDiscriminantsMeta::Derive { kw, paths: paths.into_iter().collect(), @@ -127,17 +115,6 @@ impl Parse for EnumDiscriminantsMeta { } } -impl Spanned for EnumDiscriminantsMeta { - fn span(&self) -> Span { - match self { - EnumDiscriminantsMeta::Derive { kw, .. } => kw.span, - EnumDiscriminantsMeta::Name { kw, .. } => kw.span, - EnumDiscriminantsMeta::Vis { kw, .. } => kw.span, - EnumDiscriminantsMeta::Other { path, .. } => path.span(), - } - } -} - pub trait DeriveInputExt { /// Get all the strum metadata associated with an enum. fn get_metadata(&self) -> syn::Result>; @@ -228,7 +205,7 @@ impl Parse for VariantMeta { let kw = input.parse()?; let content; parenthesized!(content in input); - let props = content.parse_terminated::<_, Token![,]>(Prop::parse)?; + let props = content.parse_terminated(Prop::parse, Token![,])?; Ok(VariantMeta::Props { kw, props: props @@ -256,22 +233,6 @@ impl Parse for Prop { } } -impl Spanned for VariantMeta { - fn span(&self) -> Span { - match self { - VariantMeta::Message { kw, .. } => kw.span, - VariantMeta::DetailedMessage { kw, .. } => kw.span, - VariantMeta::Documentation { value } => value.span(), - VariantMeta::Serialize { kw, .. } => kw.span, - VariantMeta::ToString { kw, .. } => kw.span, - VariantMeta::Disabled(kw) => kw.span, - VariantMeta::Default(kw) => kw.span, - VariantMeta::AsciiCaseInsensitive { kw, .. } => kw.span, - VariantMeta::Props { kw, .. } => kw.span, - } - } -} - pub trait VariantExt { /// Get all the metadata associated with an enum variant. fn get_metadata(&self) -> syn::Result>; @@ -282,26 +243,32 @@ impl VariantExt for Variant { let result = get_metadata_inner("strum", &self.attrs)?; self.attrs .iter() - .filter(|attr| attr.path.is_ident("doc")) + .filter(|attr| attr.path().is_ident("doc")) .try_fold(result, |mut vec, attr| { if let Meta::NameValue(MetaNameValue { - lit: Lit::Str(value), + value: + Expr::Lit(ExprLit { + lit: Lit::Str(value), + .. + }), .. - }) = attr.parse_meta()? + }) = &attr.meta { - vec.push(VariantMeta::Documentation { value }) + vec.push(VariantMeta::Documentation { + value: value.clone(), + }) } Ok(vec) }) } } -fn get_metadata_inner<'a, T: Parse + Spanned>( +fn get_metadata_inner<'a, T: Parse>( ident: &str, it: impl IntoIterator, ) -> syn::Result> { it.into_iter() - .filter(|attr| attr.path.is_ident(ident)) + .filter(|attr| attr.path().is_ident(ident)) .try_fold(Vec::new(), |mut vec, attr| { vec.extend(attr.parse_args_with(Punctuated::::parse_terminated)?); Ok(vec) From 4905dc5fde00a319ca64e63bee43b0eba900c6ca Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Mon, 26 Jun 2023 20:30:30 +0800 Subject: [PATCH 0039/1371] Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96467ca3ba..9dcba442e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -577,6 +577,7 @@ impl ColumnTrait for Column { * Disabled default features and enabled only the needed ones https://github.com/SeaQL/sea-orm/pull/1300 * Cleanup panic and unwrap https://github.com/SeaQL/sea-orm/pull/1231 * Cleanup the use of `vec!` macro https://github.com/SeaQL/sea-orm/pull/1367 +* Upgrade `syn` to v2 https://github.com/SeaQL/sea-orm/pull/1713 ### Bug Fixes From 986f0c2d49ef8db33a8238eaccc64278011c428a Mon Sep 17 00:00:00 2001 From: joelhy Date: Thu, 29 Jun 2023 23:44:52 +0800 Subject: [PATCH 0040/1371] fix(doc): fix salvo framework name (#1731) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 黄景祥 --- examples/salvo_example/README.md | 2 +- examples/salvo_example/api/templates/layout.html.tera | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/salvo_example/README.md b/examples/salvo_example/README.md index f38f6e0ad2..0601671464 100644 --- a/examples/salvo_example/README.md +++ b/examples/salvo_example/README.md @@ -1,6 +1,6 @@ ![screenshot](Screenshot.png) -# Poem with SeaORM example app +# Salvo with SeaORM example app 1. Modify the `DATABASE_URL` var in `.env` to point to your chosen database diff --git a/examples/salvo_example/api/templates/layout.html.tera b/examples/salvo_example/api/templates/layout.html.tera index 50ae58856c..abebef2818 100644 --- a/examples/salvo_example/api/templates/layout.html.tera +++ b/examples/salvo_example/api/templates/layout.html.tera @@ -2,7 +2,7 @@ - Poem Example + Salvo Example From 325e73468e214c49061ea6a8803f39775d50dcfe Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Wed, 5 Jul 2023 13:03:46 +0800 Subject: [PATCH 0041/1371] fmt --- sea-orm-macros/src/derives/partial_model.rs | 22 +++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/sea-orm-macros/src/derives/partial_model.rs b/sea-orm-macros/src/derives/partial_model.rs index 567e633ac5..79ad9ea698 100644 --- a/sea-orm-macros/src/derives/partial_model.rs +++ b/sea-orm-macros/src/derives/partial_model.rs @@ -43,7 +43,14 @@ impl DerivePartialModel { return Err(Error::NotSupportGeneric(input.generics.params.span())); } - let syn::Data::Struct(syn::DataStruct{fields:syn::Fields::Named(syn::FieldsNamed{named:fields,..}),..},..) = input.data else{ + let syn::Data::Struct( + syn::DataStruct { + fields: syn::Fields::Named(syn::FieldsNamed { named: fields, .. }), + .. + }, + .., + ) = input.data + else { return Err(Error::InputNotStruct); }; @@ -196,11 +203,18 @@ mod util { impl GetAsKVMeta for Meta { fn get_as_kv(&self, k: &str) -> Option { - let Meta::NameValue(MetaNameValue{path, value: syn::Expr::Lit(exprlit), ..}) = self else { - return None; + let Meta::NameValue(MetaNameValue { + path, + value: syn::Expr::Lit(exprlit), + .. + }) = self + else { + return None; }; - let syn::Lit::Str(litstr) = &exprlit.lit else { return None; }; + let syn::Lit::Str(litstr) = &exprlit.lit else { + return None; + }; if path.is_ident(k) { Some(litstr.value()) From 832fabf8ff296896231c0d00bc42ecde779c6d5f Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Fri, 7 Jul 2023 16:30:21 +0800 Subject: [PATCH 0042/1371] Upgrade SQLx to v0.7 --- Cargo.toml | 12 +++++------ sea-orm-cli/Cargo.toml | 2 +- sea-orm-codegen/Cargo.toml | 2 +- src/database/transaction.rs | 42 ++++++++++++++++++------------------- src/driver/sqlx_mysql.rs | 20 +++++++++--------- src/driver/sqlx_postgres.rs | 20 +++++++++--------- src/driver/sqlx_sqlite.rs | 20 +++++++++--------- src/query/insert.rs | 2 +- 8 files changed, 60 insertions(+), 60 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fc5dffa022..1bf905b9a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,12 +34,12 @@ tracing = { version = "0.1", default-features = false, features = ["attributes", rust_decimal = { version = "1", default-features = false, optional = true } bigdecimal = { version = "0.3", default-features = false, optional = true } sea-orm-macros = { version = "0.12.0-rc.3", path = "sea-orm-macros", default-features = false, features = ["strum"] } -sea-query = { version = "0.29.0-rc.2", features = ["thread-safe"] } -sea-query-binder = { version = "0.4.0-rc.2", default-features = false, optional = true } +sea-query = { version = "0.29.0", git = "https://github.com/seaql/sea-query", branch = "sqlx-v0.7", features = ["thread-safe"] } +sea-query-binder = { version = "0.4.0", git = "https://github.com/seaql/sea-query", branch = "sqlx-v0.7", default-features = false, optional = true } strum = { version = "0.24", default-features = false } serde = { version = "1.0", default-features = false } serde_json = { version = "1.0", default-features = false, optional = true } -sqlx = { version = "0.6", default-features = false, optional = true } +sqlx = { version = "0.7", default-features = false, optional = true } uuid = { version = "1", default-features = false, optional = true } ouroboros = { version = "0.15", default-features = false } url = { version = "2.2", default-features = false } @@ -78,7 +78,7 @@ macros = ["sea-orm-macros/derive", "sea-query/derive"] mock = [] with-json = ["serde_json", "sea-query/with-json", "chrono?/serde", "time?/serde", "uuid?/serde", "sea-query-binder?/with-json", "sqlx?/json"] with-chrono = ["chrono", "sea-query/with-chrono", "sea-query-binder?/with-chrono", "sqlx?/chrono"] -with-rust_decimal = ["rust_decimal", "sea-query/with-rust_decimal", "sea-query-binder?/with-rust_decimal", "sqlx?/decimal"] +with-rust_decimal = ["rust_decimal", "sea-query/with-rust_decimal", "sea-query-binder?/with-rust_decimal", "sqlx?/rust_decimal"] with-bigdecimal = ["bigdecimal", "sea-query/with-bigdecimal", "sea-query-binder?/with-bigdecimal", "sqlx?/bigdecimal"] with-uuid = ["uuid", "sea-query/with-uuid", "sea-query-binder?/with-uuid", "sqlx?/uuid"] with-time = ["time", "sea-query/with-time", "sea-query-binder?/with-time", "sqlx?/time"] @@ -102,12 +102,12 @@ runtime-async-std-rustls = [ ] runtime-actix = [] runtime-actix-native-tls = [ - "sqlx?/runtime-actix-native-tls", + "sqlx?/runtime-tokio-native-tls", "sea-query-binder?/runtime-actix-native-tls", "runtime-actix", ] runtime-actix-rustls = [ - "sqlx?/runtime-actix-rustls", + "sqlx?/runtime-tokio-rustls", "sea-query-binder?/runtime-actix-rustls", "runtime-actix", ] diff --git a/sea-orm-cli/Cargo.toml b/sea-orm-cli/Cargo.toml index 25770b8757..7a6fa08117 100644 --- a/sea-orm-cli/Cargo.toml +++ b/sea-orm-cli/Cargo.toml @@ -39,7 +39,7 @@ dotenvy = { version = "0.15", default-features = false, optional = true } async-std = { version = "1.9", default-features = false, features = ["attributes", "tokio1"], optional = true } sea-orm-codegen = { version = "=0.12.0-rc.3", path = "../sea-orm-codegen", default-features = false, optional = true } sea-schema = { version = "0.12.0-rc.2" } -sqlx = { version = "0.6", default-features = false, features = ["mysql", "postgres"], optional = true } +sqlx = { version = "0.7", default-features = false, features = ["mysql", "postgres"], optional = true } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["env-filter", "fmt"] } tracing = { version = "0.1", default-features = false } url = { version = "2.2", default-features = false } diff --git a/sea-orm-codegen/Cargo.toml b/sea-orm-codegen/Cargo.toml index cb97dbf981..52757d6759 100644 --- a/sea-orm-codegen/Cargo.toml +++ b/sea-orm-codegen/Cargo.toml @@ -17,7 +17,7 @@ name = "sea_orm_codegen" path = "src/lib.rs" [dependencies] -sea-query = { version = "0.29.0-rc.2", default-features = false, features = ["thread-safe"] } +sea-query = { version = "0.29.0", git = "https://github.com/seaql/sea-query", branch = "sqlx-v0.7", default-features = false, features = ["thread-safe"] } syn = { version = "2", default-features = false, features = ["parsing", "proc-macro", "derive", "printing"] } quote = { version = "1", default-features = false } heck = { version = "0.4", default-features = false } diff --git a/src/database/transaction.rs b/src/database/transaction.rs index e228a638a1..040b300e52 100644 --- a/src/database/transaction.rs +++ b/src/database/transaction.rs @@ -310,26 +310,26 @@ impl ConnectionTrait for DatabaseTransaction { match &mut *self.conn.lock().await { #[cfg(feature = "sqlx-mysql")] - InnerConnection::MySql(conn) => { + InnerConnection::MySql(ref mut conn) => { let query = crate::driver::sqlx_mysql::sqlx_query(&stmt); crate::metric::metric!(self.metric_callback, &stmt, { - query.execute(conn).await.map(Into::into) + query.execute(&mut **conn).await.map(Into::into) }) .map_err(sqlx_error_to_exec_err) } #[cfg(feature = "sqlx-postgres")] - InnerConnection::Postgres(conn) => { + InnerConnection::Postgres(ref mut conn) => { let query = crate::driver::sqlx_postgres::sqlx_query(&stmt); crate::metric::metric!(self.metric_callback, &stmt, { - query.execute(conn).await.map(Into::into) + query.execute(&mut **conn).await.map(Into::into) }) .map_err(sqlx_error_to_exec_err) } #[cfg(feature = "sqlx-sqlite")] - InnerConnection::Sqlite(conn) => { + InnerConnection::Sqlite(ref mut conn) => { let query = crate::driver::sqlx_sqlite::sqlx_query(&stmt); crate::metric::metric!(self.metric_callback, &stmt, { - query.execute(conn).await.map(Into::into) + query.execute(&mut **conn).await.map(Into::into) }) .map_err(sqlx_error_to_exec_err) } @@ -347,17 +347,17 @@ impl ConnectionTrait for DatabaseTransaction { match &mut *self.conn.lock().await { #[cfg(feature = "sqlx-mysql")] - InnerConnection::MySql(conn) => sqlx::Executor::execute(conn, sql) + InnerConnection::MySql(ref mut conn) => sqlx::Executor::execute(&mut **conn, sql) .await .map(Into::into) .map_err(sqlx_error_to_exec_err), #[cfg(feature = "sqlx-postgres")] - InnerConnection::Postgres(conn) => sqlx::Executor::execute(conn, sql) + InnerConnection::Postgres(ref mut conn) => sqlx::Executor::execute(&mut **conn, sql) .await .map(Into::into) .map_err(sqlx_error_to_exec_err), #[cfg(feature = "sqlx-sqlite")] - InnerConnection::Sqlite(conn) => sqlx::Executor::execute(conn, sql) + InnerConnection::Sqlite(ref mut conn) => sqlx::Executor::execute(&mut **conn, sql) .await .map(Into::into) .map_err(sqlx_error_to_exec_err), @@ -379,29 +379,29 @@ impl ConnectionTrait for DatabaseTransaction { match &mut *self.conn.lock().await { #[cfg(feature = "sqlx-mysql")] - InnerConnection::MySql(conn) => { + InnerConnection::MySql(ref mut conn) => { let query = crate::driver::sqlx_mysql::sqlx_query(&stmt); crate::metric::metric!(self.metric_callback, &stmt, { Self::map_err_ignore_not_found( - query.fetch_one(conn).await.map(|row| Some(row.into())), + query.fetch_one(&mut **conn).await.map(|row| Some(row.into())), ) }) } #[cfg(feature = "sqlx-postgres")] - InnerConnection::Postgres(conn) => { + InnerConnection::Postgres(ref mut conn) => { let query = crate::driver::sqlx_postgres::sqlx_query(&stmt); crate::metric::metric!(self.metric_callback, &stmt, { Self::map_err_ignore_not_found( - query.fetch_one(conn).await.map(|row| Some(row.into())), + query.fetch_one(&mut **conn).await.map(|row| Some(row.into())), ) }) } #[cfg(feature = "sqlx-sqlite")] - InnerConnection::Sqlite(conn) => { + InnerConnection::Sqlite(ref mut conn) => { let query = crate::driver::sqlx_sqlite::sqlx_query(&stmt); crate::metric::metric!(self.metric_callback, &stmt, { Self::map_err_ignore_not_found( - query.fetch_one(conn).await.map(|row| Some(row.into())), + query.fetch_one(&mut **conn).await.map(|row| Some(row.into())), ) }) } @@ -419,33 +419,33 @@ impl ConnectionTrait for DatabaseTransaction { match &mut *self.conn.lock().await { #[cfg(feature = "sqlx-mysql")] - InnerConnection::MySql(conn) => { + InnerConnection::MySql(ref mut conn) => { let query = crate::driver::sqlx_mysql::sqlx_query(&stmt); crate::metric::metric!(self.metric_callback, &stmt, { query - .fetch_all(conn) + .fetch_all(&mut **conn) .await .map(|rows| rows.into_iter().map(|r| r.into()).collect()) .map_err(sqlx_error_to_query_err) }) } #[cfg(feature = "sqlx-postgres")] - InnerConnection::Postgres(conn) => { + InnerConnection::Postgres(ref mut conn) => { let query = crate::driver::sqlx_postgres::sqlx_query(&stmt); crate::metric::metric!(self.metric_callback, &stmt, { query - .fetch_all(conn) + .fetch_all(&mut **conn) .await .map(|rows| rows.into_iter().map(|r| r.into()).collect()) .map_err(sqlx_error_to_query_err) }) } #[cfg(feature = "sqlx-sqlite")] - InnerConnection::Sqlite(conn) => { + InnerConnection::Sqlite(ref mut conn) => { let query = crate::driver::sqlx_sqlite::sqlx_query(&stmt); crate::metric::metric!(self.metric_callback, &stmt, { query - .fetch_all(conn) + .fetch_all(&mut **conn) .await .map(|rows| rows.into_iter().map(|r| r.into()).collect()) .map_err(sqlx_error_to_query_err) diff --git a/src/driver/sqlx_mysql.rs b/src/driver/sqlx_mysql.rs index ebea1b837d..627dc1e72c 100644 --- a/src/driver/sqlx_mysql.rs +++ b/src/driver/sqlx_mysql.rs @@ -49,9 +49,9 @@ impl SqlxMySqlConnector { .map_err(sqlx_error_to_conn_err)?; use sqlx::ConnectOptions; if !options.sqlx_logging { - opt.disable_statement_logging(); + opt = opt.disable_statement_logging(); } else { - opt.log_statements(options.sqlx_logging_level); + opt = opt.log_statements(options.sqlx_logging_level); } match options.pool_options().connect_with(opt).await { Ok(pool) => Ok(DatabaseConnection::SqlxMySqlPoolConnection( @@ -82,9 +82,9 @@ impl SqlxMySqlPoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - if let Ok(conn) = &mut self.pool.acquire().await { + if let Ok(mut conn) = self.pool.acquire().await { crate::metric::metric!(self.metric_callback, &stmt, { - match query.execute(conn).await { + match query.execute(&mut *conn).await { Ok(res) => Ok(res.into()), Err(err) => Err(sqlx_error_to_exec_err(err)), } @@ -99,7 +99,7 @@ impl SqlxMySqlPoolConnection { pub async fn execute_unprepared(&self, sql: &str) -> Result { debug_print!("{}", sql); - if let Ok(conn) = &mut self.pool.acquire().await { + if let Ok(mut conn) = self.pool.acquire().await { match conn.execute(sql).await { Ok(res) => Ok(res.into()), Err(err) => Err(sqlx_error_to_exec_err(err)), @@ -115,9 +115,9 @@ impl SqlxMySqlPoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - if let Ok(conn) = &mut self.pool.acquire().await { + if let Ok(mut conn) = self.pool.acquire().await { crate::metric::metric!(self.metric_callback, &stmt, { - match query.fetch_one(conn).await { + match query.fetch_one(&mut *conn).await { Ok(row) => Ok(Some(row.into())), Err(err) => match err { sqlx::Error::RowNotFound => Ok(None), @@ -136,9 +136,9 @@ impl SqlxMySqlPoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - if let Ok(conn) = &mut self.pool.acquire().await { + if let Ok(mut conn) = self.pool.acquire().await { crate::metric::metric!(self.metric_callback, &stmt, { - match query.fetch_all(conn).await { + match query.fetch_all(&mut *conn).await { Ok(rows) => Ok(rows.into_iter().map(|r| r.into()).collect()), Err(err) => Err(sqlx_error_to_query_err(err)), } @@ -224,7 +224,7 @@ impl SqlxMySqlPoolConnection { /// Checks if a connection to the database is still valid. pub async fn ping(&self) -> Result<(), DbErr> { - if let Ok(conn) = &mut self.pool.acquire().await { + if let Ok(mut conn) = self.pool.acquire().await { match conn.ping().await { Ok(_) => Ok(()), Err(err) => Err(sqlx_error_to_conn_err(err)), diff --git a/src/driver/sqlx_postgres.rs b/src/driver/sqlx_postgres.rs index e78aa8d842..0030763fc6 100644 --- a/src/driver/sqlx_postgres.rs +++ b/src/driver/sqlx_postgres.rs @@ -49,9 +49,9 @@ impl SqlxPostgresConnector { .map_err(sqlx_error_to_conn_err)?; use sqlx::ConnectOptions; if !options.sqlx_logging { - opt.disable_statement_logging(); + opt = opt.disable_statement_logging(); } else { - opt.log_statements(options.sqlx_logging_level); + opt = opt.log_statements(options.sqlx_logging_level); } let set_search_path_sql = options .schema_search_path @@ -97,9 +97,9 @@ impl SqlxPostgresPoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - if let Ok(conn) = &mut self.pool.acquire().await { + if let Ok(mut conn) = self.pool.acquire().await { crate::metric::metric!(self.metric_callback, &stmt, { - match query.execute(conn).await { + match query.execute(&mut *conn).await { Ok(res) => Ok(res.into()), Err(err) => Err(sqlx_error_to_exec_err(err)), } @@ -114,7 +114,7 @@ impl SqlxPostgresPoolConnection { pub async fn execute_unprepared(&self, sql: &str) -> Result { debug_print!("{}", sql); - if let Ok(conn) = &mut self.pool.acquire().await { + if let Ok(mut conn) = self.pool.acquire().await { match conn.execute(sql).await { Ok(res) => Ok(res.into()), Err(err) => Err(sqlx_error_to_exec_err(err)), @@ -130,9 +130,9 @@ impl SqlxPostgresPoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - if let Ok(conn) = &mut self.pool.acquire().await { + if let Ok(mut conn) = self.pool.acquire().await { crate::metric::metric!(self.metric_callback, &stmt, { - match query.fetch_one(conn).await { + match query.fetch_one(&mut *conn).await { Ok(row) => Ok(Some(row.into())), Err(err) => match err { sqlx::Error::RowNotFound => Ok(None), @@ -151,9 +151,9 @@ impl SqlxPostgresPoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - if let Ok(conn) = &mut self.pool.acquire().await { + if let Ok(mut conn) = self.pool.acquire().await { crate::metric::metric!(self.metric_callback, &stmt, { - match query.fetch_all(conn).await { + match query.fetch_all(&mut *conn).await { Ok(rows) => Ok(rows.into_iter().map(|r| r.into()).collect()), Err(err) => Err(sqlx_error_to_query_err(err)), } @@ -239,7 +239,7 @@ impl SqlxPostgresPoolConnection { /// Checks if a connection to the database is still valid. pub async fn ping(&self) -> Result<(), DbErr> { - if let Ok(conn) = &mut self.pool.acquire().await { + if let Ok(mut conn) = self.pool.acquire().await { match conn.ping().await { Ok(_) => Ok(()), Err(err) => Err(sqlx_error_to_conn_err(err)), diff --git a/src/driver/sqlx_sqlite.rs b/src/driver/sqlx_sqlite.rs index 5e1875495a..1a4419904e 100644 --- a/src/driver/sqlx_sqlite.rs +++ b/src/driver/sqlx_sqlite.rs @@ -53,9 +53,9 @@ impl SqlxSqliteConnector { } use sqlx::ConnectOptions; if !options.sqlx_logging { - opt.disable_statement_logging(); + opt = opt.disable_statement_logging(); } else { - opt.log_statements(options.sqlx_logging_level); + opt = opt.log_statements(options.sqlx_logging_level); } if options.get_max_connections().is_none() { options.max_connections(1); @@ -89,9 +89,9 @@ impl SqlxSqlitePoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - if let Ok(conn) = &mut self.pool.acquire().await { + if let Ok(mut conn) = self.pool.acquire().await { crate::metric::metric!(self.metric_callback, &stmt, { - match query.execute(conn).await { + match query.execute(&mut *conn).await { Ok(res) => Ok(res.into()), Err(err) => Err(sqlx_error_to_exec_err(err)), } @@ -106,7 +106,7 @@ impl SqlxSqlitePoolConnection { pub async fn execute_unprepared(&self, sql: &str) -> Result { debug_print!("{}", sql); - if let Ok(conn) = &mut self.pool.acquire().await { + if let Ok(mut conn) = self.pool.acquire().await { match conn.execute(sql).await { Ok(res) => Ok(res.into()), Err(err) => Err(sqlx_error_to_exec_err(err)), @@ -122,9 +122,9 @@ impl SqlxSqlitePoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - if let Ok(conn) = &mut self.pool.acquire().await { + if let Ok(mut conn) = self.pool.acquire().await { crate::metric::metric!(self.metric_callback, &stmt, { - match query.fetch_one(conn).await { + match query.fetch_one(&mut *conn).await { Ok(row) => Ok(Some(row.into())), Err(err) => match err { sqlx::Error::RowNotFound => Ok(None), @@ -143,9 +143,9 @@ impl SqlxSqlitePoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - if let Ok(conn) = &mut self.pool.acquire().await { + if let Ok(mut conn) = self.pool.acquire().await { crate::metric::metric!(self.metric_callback, &stmt, { - match query.fetch_all(conn).await { + match query.fetch_all(&mut *conn).await { Ok(rows) => Ok(rows.into_iter().map(|r| r.into()).collect()), Err(err) => Err(sqlx_error_to_query_err(err)), } @@ -231,7 +231,7 @@ impl SqlxSqlitePoolConnection { /// Checks if a connection to the database is still valid. pub async fn ping(&self) -> Result<(), DbErr> { - if let Ok(conn) = &mut self.pool.acquire().await { + if let Ok(mut conn) = self.pool.acquire().await { match conn.ping().await { Ok(_) => Ok(()), Err(err) => Err(sqlx_error_to_conn_err(err)), diff --git a/src/query/insert.rs b/src/query/insert.rs index cf878dcd15..3e26e0110e 100644 --- a/src/query/insert.rs +++ b/src/query/insert.rs @@ -347,7 +347,7 @@ where mod tests { use sea_query::OnConflict; - use crate::tests_cfg::cake::{self, ActiveModel}; + use crate::tests_cfg::cake::{self}; use crate::{ActiveValue, DbBackend, DbErr, EntityTrait, Insert, IntoActiveModel, QueryTrait}; #[test] From acca634e0370cb0c87577c6147b3a9d76ef9cc5b Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Fri, 7 Jul 2023 16:31:15 +0800 Subject: [PATCH 0043/1371] Revert "Upgrade SQLx to v0.7" This reverts commit 832fabf8ff296896231c0d00bc42ecde779c6d5f. --- Cargo.toml | 12 +++++------ sea-orm-cli/Cargo.toml | 2 +- sea-orm-codegen/Cargo.toml | 2 +- src/database/transaction.rs | 42 ++++++++++++++++++------------------- src/driver/sqlx_mysql.rs | 20 +++++++++--------- src/driver/sqlx_postgres.rs | 20 +++++++++--------- src/driver/sqlx_sqlite.rs | 20 +++++++++--------- src/query/insert.rs | 2 +- 8 files changed, 60 insertions(+), 60 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1bf905b9a7..fc5dffa022 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,12 +34,12 @@ tracing = { version = "0.1", default-features = false, features = ["attributes", rust_decimal = { version = "1", default-features = false, optional = true } bigdecimal = { version = "0.3", default-features = false, optional = true } sea-orm-macros = { version = "0.12.0-rc.3", path = "sea-orm-macros", default-features = false, features = ["strum"] } -sea-query = { version = "0.29.0", git = "https://github.com/seaql/sea-query", branch = "sqlx-v0.7", features = ["thread-safe"] } -sea-query-binder = { version = "0.4.0", git = "https://github.com/seaql/sea-query", branch = "sqlx-v0.7", default-features = false, optional = true } +sea-query = { version = "0.29.0-rc.2", features = ["thread-safe"] } +sea-query-binder = { version = "0.4.0-rc.2", default-features = false, optional = true } strum = { version = "0.24", default-features = false } serde = { version = "1.0", default-features = false } serde_json = { version = "1.0", default-features = false, optional = true } -sqlx = { version = "0.7", default-features = false, optional = true } +sqlx = { version = "0.6", default-features = false, optional = true } uuid = { version = "1", default-features = false, optional = true } ouroboros = { version = "0.15", default-features = false } url = { version = "2.2", default-features = false } @@ -78,7 +78,7 @@ macros = ["sea-orm-macros/derive", "sea-query/derive"] mock = [] with-json = ["serde_json", "sea-query/with-json", "chrono?/serde", "time?/serde", "uuid?/serde", "sea-query-binder?/with-json", "sqlx?/json"] with-chrono = ["chrono", "sea-query/with-chrono", "sea-query-binder?/with-chrono", "sqlx?/chrono"] -with-rust_decimal = ["rust_decimal", "sea-query/with-rust_decimal", "sea-query-binder?/with-rust_decimal", "sqlx?/rust_decimal"] +with-rust_decimal = ["rust_decimal", "sea-query/with-rust_decimal", "sea-query-binder?/with-rust_decimal", "sqlx?/decimal"] with-bigdecimal = ["bigdecimal", "sea-query/with-bigdecimal", "sea-query-binder?/with-bigdecimal", "sqlx?/bigdecimal"] with-uuid = ["uuid", "sea-query/with-uuid", "sea-query-binder?/with-uuid", "sqlx?/uuid"] with-time = ["time", "sea-query/with-time", "sea-query-binder?/with-time", "sqlx?/time"] @@ -102,12 +102,12 @@ runtime-async-std-rustls = [ ] runtime-actix = [] runtime-actix-native-tls = [ - "sqlx?/runtime-tokio-native-tls", + "sqlx?/runtime-actix-native-tls", "sea-query-binder?/runtime-actix-native-tls", "runtime-actix", ] runtime-actix-rustls = [ - "sqlx?/runtime-tokio-rustls", + "sqlx?/runtime-actix-rustls", "sea-query-binder?/runtime-actix-rustls", "runtime-actix", ] diff --git a/sea-orm-cli/Cargo.toml b/sea-orm-cli/Cargo.toml index 7a6fa08117..25770b8757 100644 --- a/sea-orm-cli/Cargo.toml +++ b/sea-orm-cli/Cargo.toml @@ -39,7 +39,7 @@ dotenvy = { version = "0.15", default-features = false, optional = true } async-std = { version = "1.9", default-features = false, features = ["attributes", "tokio1"], optional = true } sea-orm-codegen = { version = "=0.12.0-rc.3", path = "../sea-orm-codegen", default-features = false, optional = true } sea-schema = { version = "0.12.0-rc.2" } -sqlx = { version = "0.7", default-features = false, features = ["mysql", "postgres"], optional = true } +sqlx = { version = "0.6", default-features = false, features = ["mysql", "postgres"], optional = true } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["env-filter", "fmt"] } tracing = { version = "0.1", default-features = false } url = { version = "2.2", default-features = false } diff --git a/sea-orm-codegen/Cargo.toml b/sea-orm-codegen/Cargo.toml index 52757d6759..cb97dbf981 100644 --- a/sea-orm-codegen/Cargo.toml +++ b/sea-orm-codegen/Cargo.toml @@ -17,7 +17,7 @@ name = "sea_orm_codegen" path = "src/lib.rs" [dependencies] -sea-query = { version = "0.29.0", git = "https://github.com/seaql/sea-query", branch = "sqlx-v0.7", default-features = false, features = ["thread-safe"] } +sea-query = { version = "0.29.0-rc.2", default-features = false, features = ["thread-safe"] } syn = { version = "2", default-features = false, features = ["parsing", "proc-macro", "derive", "printing"] } quote = { version = "1", default-features = false } heck = { version = "0.4", default-features = false } diff --git a/src/database/transaction.rs b/src/database/transaction.rs index 040b300e52..e228a638a1 100644 --- a/src/database/transaction.rs +++ b/src/database/transaction.rs @@ -310,26 +310,26 @@ impl ConnectionTrait for DatabaseTransaction { match &mut *self.conn.lock().await { #[cfg(feature = "sqlx-mysql")] - InnerConnection::MySql(ref mut conn) => { + InnerConnection::MySql(conn) => { let query = crate::driver::sqlx_mysql::sqlx_query(&stmt); crate::metric::metric!(self.metric_callback, &stmt, { - query.execute(&mut **conn).await.map(Into::into) + query.execute(conn).await.map(Into::into) }) .map_err(sqlx_error_to_exec_err) } #[cfg(feature = "sqlx-postgres")] - InnerConnection::Postgres(ref mut conn) => { + InnerConnection::Postgres(conn) => { let query = crate::driver::sqlx_postgres::sqlx_query(&stmt); crate::metric::metric!(self.metric_callback, &stmt, { - query.execute(&mut **conn).await.map(Into::into) + query.execute(conn).await.map(Into::into) }) .map_err(sqlx_error_to_exec_err) } #[cfg(feature = "sqlx-sqlite")] - InnerConnection::Sqlite(ref mut conn) => { + InnerConnection::Sqlite(conn) => { let query = crate::driver::sqlx_sqlite::sqlx_query(&stmt); crate::metric::metric!(self.metric_callback, &stmt, { - query.execute(&mut **conn).await.map(Into::into) + query.execute(conn).await.map(Into::into) }) .map_err(sqlx_error_to_exec_err) } @@ -347,17 +347,17 @@ impl ConnectionTrait for DatabaseTransaction { match &mut *self.conn.lock().await { #[cfg(feature = "sqlx-mysql")] - InnerConnection::MySql(ref mut conn) => sqlx::Executor::execute(&mut **conn, sql) + InnerConnection::MySql(conn) => sqlx::Executor::execute(conn, sql) .await .map(Into::into) .map_err(sqlx_error_to_exec_err), #[cfg(feature = "sqlx-postgres")] - InnerConnection::Postgres(ref mut conn) => sqlx::Executor::execute(&mut **conn, sql) + InnerConnection::Postgres(conn) => sqlx::Executor::execute(conn, sql) .await .map(Into::into) .map_err(sqlx_error_to_exec_err), #[cfg(feature = "sqlx-sqlite")] - InnerConnection::Sqlite(ref mut conn) => sqlx::Executor::execute(&mut **conn, sql) + InnerConnection::Sqlite(conn) => sqlx::Executor::execute(conn, sql) .await .map(Into::into) .map_err(sqlx_error_to_exec_err), @@ -379,29 +379,29 @@ impl ConnectionTrait for DatabaseTransaction { match &mut *self.conn.lock().await { #[cfg(feature = "sqlx-mysql")] - InnerConnection::MySql(ref mut conn) => { + InnerConnection::MySql(conn) => { let query = crate::driver::sqlx_mysql::sqlx_query(&stmt); crate::metric::metric!(self.metric_callback, &stmt, { Self::map_err_ignore_not_found( - query.fetch_one(&mut **conn).await.map(|row| Some(row.into())), + query.fetch_one(conn).await.map(|row| Some(row.into())), ) }) } #[cfg(feature = "sqlx-postgres")] - InnerConnection::Postgres(ref mut conn) => { + InnerConnection::Postgres(conn) => { let query = crate::driver::sqlx_postgres::sqlx_query(&stmt); crate::metric::metric!(self.metric_callback, &stmt, { Self::map_err_ignore_not_found( - query.fetch_one(&mut **conn).await.map(|row| Some(row.into())), + query.fetch_one(conn).await.map(|row| Some(row.into())), ) }) } #[cfg(feature = "sqlx-sqlite")] - InnerConnection::Sqlite(ref mut conn) => { + InnerConnection::Sqlite(conn) => { let query = crate::driver::sqlx_sqlite::sqlx_query(&stmt); crate::metric::metric!(self.metric_callback, &stmt, { Self::map_err_ignore_not_found( - query.fetch_one(&mut **conn).await.map(|row| Some(row.into())), + query.fetch_one(conn).await.map(|row| Some(row.into())), ) }) } @@ -419,33 +419,33 @@ impl ConnectionTrait for DatabaseTransaction { match &mut *self.conn.lock().await { #[cfg(feature = "sqlx-mysql")] - InnerConnection::MySql(ref mut conn) => { + InnerConnection::MySql(conn) => { let query = crate::driver::sqlx_mysql::sqlx_query(&stmt); crate::metric::metric!(self.metric_callback, &stmt, { query - .fetch_all(&mut **conn) + .fetch_all(conn) .await .map(|rows| rows.into_iter().map(|r| r.into()).collect()) .map_err(sqlx_error_to_query_err) }) } #[cfg(feature = "sqlx-postgres")] - InnerConnection::Postgres(ref mut conn) => { + InnerConnection::Postgres(conn) => { let query = crate::driver::sqlx_postgres::sqlx_query(&stmt); crate::metric::metric!(self.metric_callback, &stmt, { query - .fetch_all(&mut **conn) + .fetch_all(conn) .await .map(|rows| rows.into_iter().map(|r| r.into()).collect()) .map_err(sqlx_error_to_query_err) }) } #[cfg(feature = "sqlx-sqlite")] - InnerConnection::Sqlite(ref mut conn) => { + InnerConnection::Sqlite(conn) => { let query = crate::driver::sqlx_sqlite::sqlx_query(&stmt); crate::metric::metric!(self.metric_callback, &stmt, { query - .fetch_all(&mut **conn) + .fetch_all(conn) .await .map(|rows| rows.into_iter().map(|r| r.into()).collect()) .map_err(sqlx_error_to_query_err) diff --git a/src/driver/sqlx_mysql.rs b/src/driver/sqlx_mysql.rs index 627dc1e72c..ebea1b837d 100644 --- a/src/driver/sqlx_mysql.rs +++ b/src/driver/sqlx_mysql.rs @@ -49,9 +49,9 @@ impl SqlxMySqlConnector { .map_err(sqlx_error_to_conn_err)?; use sqlx::ConnectOptions; if !options.sqlx_logging { - opt = opt.disable_statement_logging(); + opt.disable_statement_logging(); } else { - opt = opt.log_statements(options.sqlx_logging_level); + opt.log_statements(options.sqlx_logging_level); } match options.pool_options().connect_with(opt).await { Ok(pool) => Ok(DatabaseConnection::SqlxMySqlPoolConnection( @@ -82,9 +82,9 @@ impl SqlxMySqlPoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - if let Ok(mut conn) = self.pool.acquire().await { + if let Ok(conn) = &mut self.pool.acquire().await { crate::metric::metric!(self.metric_callback, &stmt, { - match query.execute(&mut *conn).await { + match query.execute(conn).await { Ok(res) => Ok(res.into()), Err(err) => Err(sqlx_error_to_exec_err(err)), } @@ -99,7 +99,7 @@ impl SqlxMySqlPoolConnection { pub async fn execute_unprepared(&self, sql: &str) -> Result { debug_print!("{}", sql); - if let Ok(mut conn) = self.pool.acquire().await { + if let Ok(conn) = &mut self.pool.acquire().await { match conn.execute(sql).await { Ok(res) => Ok(res.into()), Err(err) => Err(sqlx_error_to_exec_err(err)), @@ -115,9 +115,9 @@ impl SqlxMySqlPoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - if let Ok(mut conn) = self.pool.acquire().await { + if let Ok(conn) = &mut self.pool.acquire().await { crate::metric::metric!(self.metric_callback, &stmt, { - match query.fetch_one(&mut *conn).await { + match query.fetch_one(conn).await { Ok(row) => Ok(Some(row.into())), Err(err) => match err { sqlx::Error::RowNotFound => Ok(None), @@ -136,9 +136,9 @@ impl SqlxMySqlPoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - if let Ok(mut conn) = self.pool.acquire().await { + if let Ok(conn) = &mut self.pool.acquire().await { crate::metric::metric!(self.metric_callback, &stmt, { - match query.fetch_all(&mut *conn).await { + match query.fetch_all(conn).await { Ok(rows) => Ok(rows.into_iter().map(|r| r.into()).collect()), Err(err) => Err(sqlx_error_to_query_err(err)), } @@ -224,7 +224,7 @@ impl SqlxMySqlPoolConnection { /// Checks if a connection to the database is still valid. pub async fn ping(&self) -> Result<(), DbErr> { - if let Ok(mut conn) = self.pool.acquire().await { + if let Ok(conn) = &mut self.pool.acquire().await { match conn.ping().await { Ok(_) => Ok(()), Err(err) => Err(sqlx_error_to_conn_err(err)), diff --git a/src/driver/sqlx_postgres.rs b/src/driver/sqlx_postgres.rs index 0030763fc6..e78aa8d842 100644 --- a/src/driver/sqlx_postgres.rs +++ b/src/driver/sqlx_postgres.rs @@ -49,9 +49,9 @@ impl SqlxPostgresConnector { .map_err(sqlx_error_to_conn_err)?; use sqlx::ConnectOptions; if !options.sqlx_logging { - opt = opt.disable_statement_logging(); + opt.disable_statement_logging(); } else { - opt = opt.log_statements(options.sqlx_logging_level); + opt.log_statements(options.sqlx_logging_level); } let set_search_path_sql = options .schema_search_path @@ -97,9 +97,9 @@ impl SqlxPostgresPoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - if let Ok(mut conn) = self.pool.acquire().await { + if let Ok(conn) = &mut self.pool.acquire().await { crate::metric::metric!(self.metric_callback, &stmt, { - match query.execute(&mut *conn).await { + match query.execute(conn).await { Ok(res) => Ok(res.into()), Err(err) => Err(sqlx_error_to_exec_err(err)), } @@ -114,7 +114,7 @@ impl SqlxPostgresPoolConnection { pub async fn execute_unprepared(&self, sql: &str) -> Result { debug_print!("{}", sql); - if let Ok(mut conn) = self.pool.acquire().await { + if let Ok(conn) = &mut self.pool.acquire().await { match conn.execute(sql).await { Ok(res) => Ok(res.into()), Err(err) => Err(sqlx_error_to_exec_err(err)), @@ -130,9 +130,9 @@ impl SqlxPostgresPoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - if let Ok(mut conn) = self.pool.acquire().await { + if let Ok(conn) = &mut self.pool.acquire().await { crate::metric::metric!(self.metric_callback, &stmt, { - match query.fetch_one(&mut *conn).await { + match query.fetch_one(conn).await { Ok(row) => Ok(Some(row.into())), Err(err) => match err { sqlx::Error::RowNotFound => Ok(None), @@ -151,9 +151,9 @@ impl SqlxPostgresPoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - if let Ok(mut conn) = self.pool.acquire().await { + if let Ok(conn) = &mut self.pool.acquire().await { crate::metric::metric!(self.metric_callback, &stmt, { - match query.fetch_all(&mut *conn).await { + match query.fetch_all(conn).await { Ok(rows) => Ok(rows.into_iter().map(|r| r.into()).collect()), Err(err) => Err(sqlx_error_to_query_err(err)), } @@ -239,7 +239,7 @@ impl SqlxPostgresPoolConnection { /// Checks if a connection to the database is still valid. pub async fn ping(&self) -> Result<(), DbErr> { - if let Ok(mut conn) = self.pool.acquire().await { + if let Ok(conn) = &mut self.pool.acquire().await { match conn.ping().await { Ok(_) => Ok(()), Err(err) => Err(sqlx_error_to_conn_err(err)), diff --git a/src/driver/sqlx_sqlite.rs b/src/driver/sqlx_sqlite.rs index 1a4419904e..5e1875495a 100644 --- a/src/driver/sqlx_sqlite.rs +++ b/src/driver/sqlx_sqlite.rs @@ -53,9 +53,9 @@ impl SqlxSqliteConnector { } use sqlx::ConnectOptions; if !options.sqlx_logging { - opt = opt.disable_statement_logging(); + opt.disable_statement_logging(); } else { - opt = opt.log_statements(options.sqlx_logging_level); + opt.log_statements(options.sqlx_logging_level); } if options.get_max_connections().is_none() { options.max_connections(1); @@ -89,9 +89,9 @@ impl SqlxSqlitePoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - if let Ok(mut conn) = self.pool.acquire().await { + if let Ok(conn) = &mut self.pool.acquire().await { crate::metric::metric!(self.metric_callback, &stmt, { - match query.execute(&mut *conn).await { + match query.execute(conn).await { Ok(res) => Ok(res.into()), Err(err) => Err(sqlx_error_to_exec_err(err)), } @@ -106,7 +106,7 @@ impl SqlxSqlitePoolConnection { pub async fn execute_unprepared(&self, sql: &str) -> Result { debug_print!("{}", sql); - if let Ok(mut conn) = self.pool.acquire().await { + if let Ok(conn) = &mut self.pool.acquire().await { match conn.execute(sql).await { Ok(res) => Ok(res.into()), Err(err) => Err(sqlx_error_to_exec_err(err)), @@ -122,9 +122,9 @@ impl SqlxSqlitePoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - if let Ok(mut conn) = self.pool.acquire().await { + if let Ok(conn) = &mut self.pool.acquire().await { crate::metric::metric!(self.metric_callback, &stmt, { - match query.fetch_one(&mut *conn).await { + match query.fetch_one(conn).await { Ok(row) => Ok(Some(row.into())), Err(err) => match err { sqlx::Error::RowNotFound => Ok(None), @@ -143,9 +143,9 @@ impl SqlxSqlitePoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - if let Ok(mut conn) = self.pool.acquire().await { + if let Ok(conn) = &mut self.pool.acquire().await { crate::metric::metric!(self.metric_callback, &stmt, { - match query.fetch_all(&mut *conn).await { + match query.fetch_all(conn).await { Ok(rows) => Ok(rows.into_iter().map(|r| r.into()).collect()), Err(err) => Err(sqlx_error_to_query_err(err)), } @@ -231,7 +231,7 @@ impl SqlxSqlitePoolConnection { /// Checks if a connection to the database is still valid. pub async fn ping(&self) -> Result<(), DbErr> { - if let Ok(mut conn) = self.pool.acquire().await { + if let Ok(conn) = &mut self.pool.acquire().await { match conn.ping().await { Ok(_) => Ok(()), Err(err) => Err(sqlx_error_to_conn_err(err)), diff --git a/src/query/insert.rs b/src/query/insert.rs index 3e26e0110e..cf878dcd15 100644 --- a/src/query/insert.rs +++ b/src/query/insert.rs @@ -347,7 +347,7 @@ where mod tests { use sea_query::OnConflict; - use crate::tests_cfg::cake::{self}; + use crate::tests_cfg::cake::{self, ActiveModel}; use crate::{ActiveValue, DbBackend, DbErr, EntityTrait, Insert, IntoActiveModel, QueryTrait}; #[test] From 50a7048deb09a5bcced20474470b04c6656e8c93 Mon Sep 17 00:00:00 2001 From: darkmmon Date: Fri, 7 Jul 2023 22:52:41 +0800 Subject: [PATCH 0044/1371] Adding find_with_linked (#1728) --- Cargo.toml | 2 +- src/executor/select.rs | 59 +++++++++++++++-------------- src/query/combine.rs | 8 +++- src/query/join.rs | 46 ++++++++++++++++++++++ tests/relational_tests.rs | 80 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 164 insertions(+), 31 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fc5dffa022..a10d74ae68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ tracing = { version = "0.1", default-features = false, features = ["attributes", rust_decimal = { version = "1", default-features = false, optional = true } bigdecimal = { version = "0.3", default-features = false, optional = true } sea-orm-macros = { version = "0.12.0-rc.3", path = "sea-orm-macros", default-features = false, features = ["strum"] } -sea-query = { version = "0.29.0-rc.2", features = ["thread-safe"] } +sea-query = { version = "0.29.0-rc.2", features = ["thread-safe", "hashable-value"] } sea-query-binder = { version = "0.4.0-rc.2", default-features = false, optional = true } strum = { version = "0.24", default-features = false } serde = { version = "1.0", default-features = false } diff --git a/src/executor/select.rs b/src/executor/select.rs index 73815f1e2b..42c994dcc8 100644 --- a/src/executor/select.rs +++ b/src/executor/select.rs @@ -4,7 +4,8 @@ use crate::{ SelectB, SelectTwo, SelectTwoMany, Statement, StreamTrait, TryGetableMany, }; use futures::{Stream, TryStreamExt}; -use sea_query::SelectStatement; +use sea_query::{SelectStatement, Value}; +use std::collections::HashMap; use std::marker::PhantomData; use std::pin::Pin; @@ -991,37 +992,39 @@ where } fn consolidate_query_result( - rows: Vec<(L::Model, Option)>, + mut rows: Vec<(L::Model, Option)>, ) -> Vec<(L::Model, Vec)> where L: EntityTrait, R: EntityTrait, { - let mut acc: Vec<(L::Model, Vec)> = Vec::new(); - for (l, r) in rows { - if let Some((last_l, last_r)) = acc.last_mut() { - let mut same_l = true; - for pk_col in ::iter() { - let col = pk_col.into_column(); - let val = l.get(col); - let last_val = last_l.get(col); - if !val.eq(&last_val) { - same_l = false; - break; - } - } - if same_l { - if let Some(r) = r { - last_r.push(r); - continue; - } + //todo: could take not iter + let pkcol = ::iter() + .next() + .expect("should have primary key") + .into_column(); + + let mut hashmap: HashMap> = rows.iter_mut().fold( + HashMap::>::new(), + |mut acc: HashMap>, row: &mut (L::Model, Option)| { + let key = row.0.get(pkcol); + let value = row.1.take().expect("should have a linked entity"); + let vec: Option<&mut Vec> = acc.get_mut(&key); + if let Some(vec) = vec { + vec.push(value) + } else { + acc.insert(key, vec![value]); } - } - let rows = match r { - Some(r) => vec![r], - None => vec![], - }; - acc.push((l, rows)); - } - acc + + acc + }, + ); + + rows.into_iter() + .filter_map(|(l_model, _)| { + let l_pk = l_model.get(pkcol); + let r_models = hashmap.remove(&l_pk); + r_models.map(|r_models| (l_model, r_models)) + }) + .collect() } diff --git a/src/query/combine.rs b/src/query/combine.rs index 9ffb8e6ed5..61d957f74f 100644 --- a/src/query/combine.rs +++ b/src/query/combine.rs @@ -119,12 +119,16 @@ where F: EntityTrait, { pub(crate) fn new(query: SelectStatement) -> Self { + Self::new_without_prepare(query) + .prepare_select() + .prepare_order_by() + } + + pub(crate) fn new_without_prepare(query: SelectStatement) -> Self { Self { query, entity: PhantomData, } - .prepare_select() - .prepare_order_by() } fn prepare_select(mut self) -> Self { diff --git a/src/query/join.rs b/src/query/join.rs index 09f2e38574..6ed56de0c8 100644 --- a/src/query/join.rs +++ b/src/query/join.rs @@ -107,6 +107,52 @@ where } select_two } + + /// Left Join with a Linked Entity and select Entity as a `Vec`. + pub fn find_with_linked(self, l: L) -> SelectTwoMany + where + L: Linked, + T: EntityTrait, + { + let mut slf = self; + for (i, mut rel) in l.link().into_iter().enumerate() { + let to_tbl = Alias::new(format!("r{i}")).into_iden(); + let from_tbl = if i > 0 { + Alias::new(format!("r{}", i - 1)).into_iden() + } else { + unpack_table_ref(&rel.from_tbl) + }; + let table_ref = rel.to_tbl; + + let mut condition = Condition::all().add(join_tbl_on_condition( + SeaRc::clone(&from_tbl), + SeaRc::clone(&to_tbl), + rel.from_col, + rel.to_col, + )); + if let Some(f) = rel.on_condition.take() { + condition = condition.add(f(SeaRc::clone(&from_tbl), SeaRc::clone(&to_tbl))); + } + + slf.query() + .join_as(JoinType::LeftJoin, table_ref, to_tbl, condition); + } + slf = slf.apply_alias(SelectA.as_str()); + let mut select_two_many = SelectTwoMany::new_without_prepare(slf.query); + for col in ::iter() { + let alias = format!("{}{}", SelectB.as_str(), col.as_str()); + let expr = Expr::col(( + Alias::new(format!("r{}", l.link().len() - 1)).into_iden(), + col.into_iden(), + )); + select_two_many.query().expr(SelectExpr { + expr: col.select_as(expr), + alias: Some(SeaRc::new(Alias::new(alias))), + window: None, + }); + } + select_two_many + } } #[cfg(test)] diff --git a/tests/relational_tests.rs b/tests/relational_tests.rs index 959f055778..6b0c6950b4 100644 --- a/tests/relational_tests.rs +++ b/tests/relational_tests.rs @@ -2,6 +2,7 @@ pub mod common; pub use chrono::offset::Utc; pub use common::{bakery_chain::*, setup::*, TestContext}; +use pretty_assertions::assert_eq; pub use rust_decimal::prelude::*; pub use rust_decimal_macros::dec; use sea_orm::{entity::*, query::*, DbErr, DerivePartialModel, FromQueryResult}; @@ -747,6 +748,85 @@ pub async fn linked() -> Result<(), DbErr> { }] ); + let select_baker_with_customer = Baker::find() + .find_with_linked(baker::BakedForCustomer) + .order_by_asc(baker::Column::Id) + .order_by_asc(Expr::col((Alias::new("r4"), customer::Column::Id))); + + assert_eq!( + select_baker_with_customer + .build(sea_orm::DatabaseBackend::MySql) + .to_string(), + [ + // FIXME: This might be faulty! + "SELECT `baker`.`id` AS `A_id`,", + "`baker`.`name` AS `A_name`,", + "`baker`.`contact_details` AS `A_contact_details`,", + "`baker`.`bakery_id` AS `A_bakery_id`,", + "`r4`.`id` AS `B_id`,", + "`r4`.`name` AS `B_name`,", + "`r4`.`notes` AS `B_notes`", + "FROM `baker`", + "LEFT JOIN `cakes_bakers` AS `r0` ON `baker`.`id` = `r0`.`baker_id`", + "LEFT JOIN `cake` AS `r1` ON `r0`.`cake_id` = `r1`.`id`", + "LEFT JOIN `lineitem` AS `r2` ON `r1`.`id` = `r2`.`cake_id`", + "LEFT JOIN `order` AS `r3` ON `r2`.`order_id` = `r3`.`id`", + "LEFT JOIN `customer` AS `r4` ON `r3`.`customer_id` = `r4`.`id`", + "ORDER BY `baker`.`id` ASC, `r4`.`id` ASC" + ] + .join(" ") + ); + + assert_eq!( + select_baker_with_customer.all(&ctx.db).await?, + [ + ( + baker::Model { + id: 1, + name: "Baker Bob".into(), + contact_details: serde_json::json!({ + "mobile": "+61424000000", + "home": "0395555555", + "address": "12 Test St, Testville, Vic, Australia", + }), + bakery_id: Some(1), + }, + vec![customer::Model { + id: 2, + name: "Kara".into(), + notes: Some("Loves all cakes".into()), + }] + ), + ( + baker::Model { + id: 2, + name: "Baker Bobby".into(), + contact_details: serde_json::json!({ + "mobile": "+85212345678", + }), + bakery_id: Some(1), + }, + vec![ + customer::Model { + id: 1, + name: "Kate".into(), + notes: Some("Loves cheese cake".into()), + }, + customer::Model { + id: 1, + name: "Kate".into(), + notes: Some("Loves cheese cake".into()), + }, + customer::Model { + id: 2, + name: "Kara".into(), + notes: Some("Loves all cakes".into()), + }, + ] + ), + ] + ); + ctx.delete().await; Ok(()) From 6dc19d97bd462f6dd108c04ce920d15b71c9602f Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 7 Jul 2023 23:05:59 +0800 Subject: [PATCH 0045/1371] Support tuple primary key (as the old impl does) Also, r_model is optional (same as the old impl) --- src/executor/select.rs | 119 +++++++++++++++++++++++++++++++++-------- 1 file changed, 98 insertions(+), 21 deletions(-) diff --git a/src/executor/select.rs b/src/executor/select.rs index 42c994dcc8..32f633f8ea 100644 --- a/src/executor/select.rs +++ b/src/executor/select.rs @@ -6,8 +6,7 @@ use crate::{ use futures::{Stream, TryStreamExt}; use sea_query::{SelectStatement, Value}; use std::collections::HashMap; -use std::marker::PhantomData; -use std::pin::Pin; +use std::{hash::Hash, marker::PhantomData, pin::Pin}; #[cfg(feature = "with-json")] use crate::JsonValue; @@ -992,39 +991,117 @@ where } fn consolidate_query_result( + rows: Vec<(L::Model, Option)>, +) -> Vec<(L::Model, Vec)> +where + L: EntityTrait, + R: EntityTrait, +{ + // This is a strong point to consider adding a trait associated constant + // to PrimaryKeyTrait to indicate the arity + let pkcol: Vec<_> = ::iter() + .map(|pk| pk.into_column()) + .collect(); + if pkcol.len() == 1 { + consolidate_query_result_of::>(rows, UnitPk(pkcol[0])) + } else { + consolidate_query_result_of::>(rows, TuplePk(pkcol)) + } +} + +trait ModelKey { + type Type: Hash + PartialEq + Eq; + fn get(&self, model: &E::Model) -> Self::Type; +} + +// This could have been an array of [E::Column; ::ARITY] +struct UnitPk(E::Column); +struct TuplePk(Vec); + +impl ModelKey for UnitPk { + type Type = Value; + fn get(&self, model: &E::Model) -> Self::Type { + model.get(self.0) + } +} + +impl ModelKey for TuplePk { + type Type = Vec; + fn get(&self, model: &E::Model) -> Self::Type { + let mut key = Vec::new(); + for col in self.0.iter() { + key.push(model.get(*col)); + } + key + } +} + +fn consolidate_query_result_of>( mut rows: Vec<(L::Model, Option)>, + model_key: KEY, ) -> Vec<(L::Model, Vec)> where L: EntityTrait, R: EntityTrait, { - //todo: could take not iter - let pkcol = ::iter() - .next() - .expect("should have primary key") - .into_column(); - - let mut hashmap: HashMap> = rows.iter_mut().fold( - HashMap::>::new(), - |mut acc: HashMap>, row: &mut (L::Model, Option)| { - let key = row.0.get(pkcol); - let value = row.1.take().expect("should have a linked entity"); - let vec: Option<&mut Vec> = acc.get_mut(&key); - if let Some(vec) = vec { - vec.push(value) - } else { - acc.insert(key, vec![value]); + let mut hashmap: HashMap> = + rows.iter_mut().fold(HashMap::new(), |mut acc, row| { + let key = model_key.get(&row.0); + if let Some(value) = row.1.take() { + let vec: Option<&mut Vec> = acc.get_mut(&key); + if let Some(vec) = vec { + vec.push(value) + } else { + acc.insert(key, vec![value]); + } } acc - }, - ); + }); rows.into_iter() .filter_map(|(l_model, _)| { - let l_pk = l_model.get(pkcol); + let l_pk = model_key.get(&l_model); let r_models = hashmap.remove(&l_pk); r_models.map(|r_models| (l_model, r_models)) }) .collect() } + +/// This is the legacy consolidate algorithm. Kept for reference +#[allow(dead_code)] +fn consolidate_query_result_of_ordered_rows( + rows: Vec<(L::Model, Option)>, +) -> Vec<(L::Model, Vec)> +where + L: EntityTrait, + R: EntityTrait, +{ + let mut acc: Vec<(L::Model, Vec)> = Vec::new(); + for (l, r) in rows { + if let Some((last_l, last_r)) = acc.last_mut() { + let mut same_l = true; + for pk_col in ::iter() { + let col = pk_col.into_column(); + let val = l.get(col); + let last_val = last_l.get(col); + if !val.eq(&last_val) { + same_l = false; + break; + } + } + if same_l { + if let Some(r) = r { + last_r.push(r); + continue; + } + } + } + let rows = match r { + Some(r) => vec![r], + None => vec![], + }; + acc.push((l, rows)); + } + acc +} From eacd71f3a6fc30b4bce5ebf1d83fb7c561012617 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sat, 8 Jul 2023 00:10:42 +0800 Subject: [PATCH 0046/1371] Changelog --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9dcba442e1..19d1393f3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -274,6 +274,15 @@ assert!(matches!( Some(SqlErr::ForeignKeyConstraintViolation(_)) )); ``` +* Add `Select::find_with_linked`, similar to `find_with_related`: +```rust +fn find_with_related(self, r: R) -> SelectTwoMany + where R: EntityTrait, E: Related; +fn find_with_linked(self, l: L) -> SelectTwoMany + where L: Linked, T: EntityTrait; + +// boths yields `Vec<(E::Model, Vec)>` +``` ### Enhancements @@ -433,6 +442,7 @@ impl ColumnTrait for Column { * The derive macro `EnumIter` is now shipped by `sea-orm-macros` * Added a new variant `Many` to `Identity` https://github.com/SeaQL/sea-orm/pull/1508 * Replace the use of `SeaRc` where `T` isn't `dyn Iden` with `RcOrArc` https://github.com/SeaQL/sea-orm/pull/1661 +* Enabled `hashable-value` feature in SeaQuery, thus `Value::Float(NaN) == Value::Float(NaN)` would be true ## 0.11.3 - 2023-04-24 From 4e8d0b0c0c7a61a5285a549d2af55bd2fd212600 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sat, 8 Jul 2023 00:14:02 +0800 Subject: [PATCH 0047/1371] 0.12.0-rc.4 --- CHANGELOG.md | 3 ++- Cargo.toml | 4 ++-- sea-orm-cli/Cargo.toml | 4 ++-- sea-orm-codegen/Cargo.toml | 2 +- sea-orm-macros/Cargo.toml | 2 +- sea-orm-migration/Cargo.toml | 6 +++--- 6 files changed, 11 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19d1393f3e..35f4a6e639 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,9 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## 0.12.0 - Pending -+ Yanked: `0.12.0-rc.1` ++ Yanked : `0.12.0-rc.1` + 2023-05-19: `0.12.0-rc.2` + 2023-06-22: `0.12.0-rc.3` ++ 2023-07-08: `0.12.0-rc.4` ### New Features diff --git a/Cargo.toml b/Cargo.toml index a10d74ae68..7b5ca9ccf6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [".", "sea-orm-macros", "sea-orm-codegen"] [package] name = "sea-orm" -version = "0.12.0-rc.3" +version = "0.12.0-rc.4" authors = ["Chris Tsang "] edition = "2021" description = "🐚 An async & dynamic ORM for Rust" @@ -33,7 +33,7 @@ log = { version = "0.4", default-features = false } tracing = { version = "0.1", default-features = false, features = ["attributes", "log"] } rust_decimal = { version = "1", default-features = false, optional = true } bigdecimal = { version = "0.3", default-features = false, optional = true } -sea-orm-macros = { version = "0.12.0-rc.3", path = "sea-orm-macros", default-features = false, features = ["strum"] } +sea-orm-macros = { version = "0.12.0-rc.4", path = "sea-orm-macros", default-features = false, features = ["strum"] } sea-query = { version = "0.29.0-rc.2", features = ["thread-safe", "hashable-value"] } sea-query-binder = { version = "0.4.0-rc.2", default-features = false, optional = true } strum = { version = "0.24", default-features = false } diff --git a/sea-orm-cli/Cargo.toml b/sea-orm-cli/Cargo.toml index 25770b8757..9f393a95a1 100644 --- a/sea-orm-cli/Cargo.toml +++ b/sea-orm-cli/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "sea-orm-cli" -version = "0.12.0-rc.3" +version = "0.12.0-rc.4" authors = [ "Chris Tsang ", "Billy Chan ", @@ -37,7 +37,7 @@ required-features = ["cli", "codegen"] clap = { version = "4.3", features = ["env", "derive"], optional = true } dotenvy = { version = "0.15", default-features = false, optional = true } async-std = { version = "1.9", default-features = false, features = ["attributes", "tokio1"], optional = true } -sea-orm-codegen = { version = "=0.12.0-rc.3", path = "../sea-orm-codegen", default-features = false, optional = true } +sea-orm-codegen = { version = "=0.12.0-rc.4", path = "../sea-orm-codegen", default-features = false, optional = true } sea-schema = { version = "0.12.0-rc.2" } sqlx = { version = "0.6", default-features = false, features = ["mysql", "postgres"], optional = true } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["env-filter", "fmt"] } diff --git a/sea-orm-codegen/Cargo.toml b/sea-orm-codegen/Cargo.toml index cb97dbf981..1a0a2c7da3 100644 --- a/sea-orm-codegen/Cargo.toml +++ b/sea-orm-codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sea-orm-codegen" -version = "0.12.0-rc.3" +version = "0.12.0-rc.4" authors = ["Billy Chan "] edition = "2021" description = "Code Generator for SeaORM" diff --git a/sea-orm-macros/Cargo.toml b/sea-orm-macros/Cargo.toml index 18de557601..23199c85bb 100644 --- a/sea-orm-macros/Cargo.toml +++ b/sea-orm-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sea-orm-macros" -version = "0.12.0-rc.3" +version = "0.12.0-rc.4" authors = [ "Billy Chan " ] edition = "2021" description = "Derive macros for SeaORM" diff --git a/sea-orm-migration/Cargo.toml b/sea-orm-migration/Cargo.toml index ca423948bb..22b279e5cd 100644 --- a/sea-orm-migration/Cargo.toml +++ b/sea-orm-migration/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "sea-orm-migration" -version = "0.12.0-rc.3" +version = "0.12.0-rc.4" authors = ["Billy Chan "] edition = "2021" description = "Migration utility for SeaORM" @@ -23,8 +23,8 @@ path = "src/lib.rs" async-trait = { version = "0.1", default-features = false } clap = { version = "4.3", features = ["env", "derive"], optional = true } dotenvy = { version = "0.15", default-features = false, optional = true } -sea-orm = { version = "0.12.0-rc.3", path = "../", default-features = false, features = ["macros"] } -sea-orm-cli = { version = "0.12.0-rc.3", path = "../sea-orm-cli", default-features = false, optional = true } +sea-orm = { version = "0.12.0-rc.4", path = "../", default-features = false, features = ["macros"] } +sea-orm-cli = { version = "0.12.0-rc.4", path = "../sea-orm-cli", default-features = false, optional = true } sea-schema = { version = "0.12.0-rc.2" } tracing = { version = "0.1", default-features = false, features = ["log"] } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["env-filter", "fmt"] } From a42a9a1d3df056bd6a1cc2dbbfcc798cfd754b33 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sat, 8 Jul 2023 00:14:02 +0800 Subject: [PATCH 0048/1371] update examples --- examples/actix3_example/entity/Cargo.toml | 2 +- examples/actix3_example/migration/Cargo.toml | 2 +- examples/actix3_example/service/Cargo.toml | 2 +- examples/actix_example/entity/Cargo.toml | 2 +- examples/actix_example/migration/Cargo.toml | 2 +- examples/actix_example/service/Cargo.toml | 2 +- examples/axum_example/entity/Cargo.toml | 2 +- examples/axum_example/migration/Cargo.toml | 2 +- examples/axum_example/service/Cargo.toml | 2 +- examples/graphql_example/entity/Cargo.toml | 2 +- examples/graphql_example/migration/Cargo.toml | 2 +- examples/graphql_example/service/Cargo.toml | 2 +- examples/jsonrpsee_example/entity/Cargo.toml | 2 +- examples/jsonrpsee_example/migration/Cargo.toml | 2 +- examples/jsonrpsee_example/service/Cargo.toml | 2 +- examples/poem_example/entity/Cargo.toml | 2 +- examples/poem_example/migration/Cargo.toml | 2 +- examples/poem_example/service/Cargo.toml | 2 +- examples/rocket_example/entity/Cargo.toml | 2 +- examples/rocket_example/migration/Cargo.toml | 2 +- examples/rocket_example/service/Cargo.toml | 2 +- examples/rocket_okapi_example/entity/Cargo.toml | 2 +- examples/rocket_okapi_example/migration/Cargo.toml | 2 +- examples/rocket_okapi_example/service/Cargo.toml | 2 +- examples/salvo_example/entity/Cargo.toml | 2 +- examples/salvo_example/migration/Cargo.toml | 2 +- examples/salvo_example/service/Cargo.toml | 2 +- examples/tonic_example/entity/Cargo.toml | 2 +- examples/tonic_example/migration/Cargo.toml | 2 +- examples/tonic_example/service/Cargo.toml | 2 +- 30 files changed, 30 insertions(+), 30 deletions(-) diff --git a/examples/actix3_example/entity/Cargo.toml b/examples/actix3_example/entity/Cargo.toml index 18787b4f95..5171c39b8b 100644 --- a/examples/actix3_example/entity/Cargo.toml +++ b/examples/actix3_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.3" # sea-orm version +version = "0.12.0-rc.4" # sea-orm version diff --git a/examples/actix3_example/migration/Cargo.toml b/examples/actix3_example/migration/Cargo.toml index bd8ff82081..f016bf4352 100644 --- a/examples/actix3_example/migration/Cargo.toml +++ b/examples/actix3_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0-rc.3" # sea-orm-migration version +version = "0.12.0-rc.4" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-async-std-native-tls", diff --git a/examples/actix3_example/service/Cargo.toml b/examples/actix3_example/service/Cargo.toml index b2665b487f..63182fa2f8 100644 --- a/examples/actix3_example/service/Cargo.toml +++ b/examples/actix3_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.3" # sea-orm version +version = "0.12.0-rc.4" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/actix_example/entity/Cargo.toml b/examples/actix_example/entity/Cargo.toml index 18787b4f95..5171c39b8b 100644 --- a/examples/actix_example/entity/Cargo.toml +++ b/examples/actix_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.3" # sea-orm version +version = "0.12.0-rc.4" # sea-orm version diff --git a/examples/actix_example/migration/Cargo.toml b/examples/actix_example/migration/Cargo.toml index 08a2b5c3e4..c3747823ac 100644 --- a/examples/actix_example/migration/Cargo.toml +++ b/examples/actix_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0-rc.3" # sea-orm-migration version +version = "0.12.0-rc.4" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-actix-native-tls", diff --git a/examples/actix_example/service/Cargo.toml b/examples/actix_example/service/Cargo.toml index a65e440bb1..d785922627 100644 --- a/examples/actix_example/service/Cargo.toml +++ b/examples/actix_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.3" # sea-orm version +version = "0.12.0-rc.4" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/axum_example/entity/Cargo.toml b/examples/axum_example/entity/Cargo.toml index 18787b4f95..5171c39b8b 100644 --- a/examples/axum_example/entity/Cargo.toml +++ b/examples/axum_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.3" # sea-orm version +version = "0.12.0-rc.4" # sea-orm version diff --git a/examples/axum_example/migration/Cargo.toml b/examples/axum_example/migration/Cargo.toml index 253e2afe0c..b7da1873b6 100644 --- a/examples/axum_example/migration/Cargo.toml +++ b/examples/axum_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0-rc.3" # sea-orm-migration version +version = "0.12.0-rc.4" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/axum_example/service/Cargo.toml b/examples/axum_example/service/Cargo.toml index be94b06fdb..7fe9c2f179 100644 --- a/examples/axum_example/service/Cargo.toml +++ b/examples/axum_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.3" # sea-orm version +version = "0.12.0-rc.4" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/graphql_example/entity/Cargo.toml b/examples/graphql_example/entity/Cargo.toml index d1a68a9962..1c6b5a5b53 100644 --- a/examples/graphql_example/entity/Cargo.toml +++ b/examples/graphql_example/entity/Cargo.toml @@ -16,4 +16,4 @@ version = "4.0.6" [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.3" # sea-orm version +version = "0.12.0-rc.4" # sea-orm version diff --git a/examples/graphql_example/migration/Cargo.toml b/examples/graphql_example/migration/Cargo.toml index bc6841a92a..5347fce8e3 100644 --- a/examples/graphql_example/migration/Cargo.toml +++ b/examples/graphql_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0-rc.3" # sea-orm-migration version +version = "0.12.0-rc.4" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/graphql_example/service/Cargo.toml b/examples/graphql_example/service/Cargo.toml index 67f03c7363..9451909f4e 100644 --- a/examples/graphql_example/service/Cargo.toml +++ b/examples/graphql_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.3" # sea-orm version +version = "0.12.0-rc.4" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/jsonrpsee_example/entity/Cargo.toml b/examples/jsonrpsee_example/entity/Cargo.toml index 18787b4f95..5171c39b8b 100644 --- a/examples/jsonrpsee_example/entity/Cargo.toml +++ b/examples/jsonrpsee_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.3" # sea-orm version +version = "0.12.0-rc.4" # sea-orm version diff --git a/examples/jsonrpsee_example/migration/Cargo.toml b/examples/jsonrpsee_example/migration/Cargo.toml index a4e60c877a..5fa11fc400 100644 --- a/examples/jsonrpsee_example/migration/Cargo.toml +++ b/examples/jsonrpsee_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0-rc.3" # sea-orm-migration version +version = "0.12.0-rc.4" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/jsonrpsee_example/service/Cargo.toml b/examples/jsonrpsee_example/service/Cargo.toml index fcf658c763..d0d5e6306e 100644 --- a/examples/jsonrpsee_example/service/Cargo.toml +++ b/examples/jsonrpsee_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.3" # sea-orm version +version = "0.12.0-rc.4" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/poem_example/entity/Cargo.toml b/examples/poem_example/entity/Cargo.toml index 18787b4f95..5171c39b8b 100644 --- a/examples/poem_example/entity/Cargo.toml +++ b/examples/poem_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.3" # sea-orm version +version = "0.12.0-rc.4" # sea-orm version diff --git a/examples/poem_example/migration/Cargo.toml b/examples/poem_example/migration/Cargo.toml index a4e60c877a..5fa11fc400 100644 --- a/examples/poem_example/migration/Cargo.toml +++ b/examples/poem_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0-rc.3" # sea-orm-migration version +version = "0.12.0-rc.4" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/poem_example/service/Cargo.toml b/examples/poem_example/service/Cargo.toml index 48c79eba17..b0287a3373 100644 --- a/examples/poem_example/service/Cargo.toml +++ b/examples/poem_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.3" # sea-orm version +version = "0.12.0-rc.4" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/rocket_example/entity/Cargo.toml b/examples/rocket_example/entity/Cargo.toml index 5d62d37bfb..e56b23b7af 100644 --- a/examples/rocket_example/entity/Cargo.toml +++ b/examples/rocket_example/entity/Cargo.toml @@ -15,4 +15,4 @@ rocket = { version = "0.5.0-rc.1", features = [ [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.3" # sea-orm version +version = "0.12.0-rc.4" # sea-orm version diff --git a/examples/rocket_example/migration/Cargo.toml b/examples/rocket_example/migration/Cargo.toml index 142fe48afb..eab9a6b52a 100644 --- a/examples/rocket_example/migration/Cargo.toml +++ b/examples/rocket_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0-rc.3" # sea-orm-migration version +version = "0.12.0-rc.4" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/rocket_example/service/Cargo.toml b/examples/rocket_example/service/Cargo.toml index c586d93f56..7dfb3cc8ea 100644 --- a/examples/rocket_example/service/Cargo.toml +++ b/examples/rocket_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.3" # sea-orm version +version = "0.12.0-rc.4" # sea-orm version features = [ "runtime-tokio-native-tls", "sqlx-postgres", diff --git a/examples/rocket_okapi_example/entity/Cargo.toml b/examples/rocket_okapi_example/entity/Cargo.toml index 8c5bd1074b..58dcc8336a 100644 --- a/examples/rocket_okapi_example/entity/Cargo.toml +++ b/examples/rocket_okapi_example/entity/Cargo.toml @@ -15,7 +15,7 @@ rocket = { version = "0.5.0-rc.1", features = [ [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.3" # sea-orm version +version = "0.12.0-rc.4" # sea-orm version [dependencies.rocket_okapi] version = "0.8.0-rc.2" diff --git a/examples/rocket_okapi_example/migration/Cargo.toml b/examples/rocket_okapi_example/migration/Cargo.toml index 142fe48afb..eab9a6b52a 100644 --- a/examples/rocket_okapi_example/migration/Cargo.toml +++ b/examples/rocket_okapi_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0-rc.3" # sea-orm-migration version +version = "0.12.0-rc.4" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/rocket_okapi_example/service/Cargo.toml b/examples/rocket_okapi_example/service/Cargo.toml index c586d93f56..7dfb3cc8ea 100644 --- a/examples/rocket_okapi_example/service/Cargo.toml +++ b/examples/rocket_okapi_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.3" # sea-orm version +version = "0.12.0-rc.4" # sea-orm version features = [ "runtime-tokio-native-tls", "sqlx-postgres", diff --git a/examples/salvo_example/entity/Cargo.toml b/examples/salvo_example/entity/Cargo.toml index 18787b4f95..5171c39b8b 100644 --- a/examples/salvo_example/entity/Cargo.toml +++ b/examples/salvo_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.3" # sea-orm version +version = "0.12.0-rc.4" # sea-orm version diff --git a/examples/salvo_example/migration/Cargo.toml b/examples/salvo_example/migration/Cargo.toml index a4e60c877a..5fa11fc400 100644 --- a/examples/salvo_example/migration/Cargo.toml +++ b/examples/salvo_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0-rc.3" # sea-orm-migration version +version = "0.12.0-rc.4" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/salvo_example/service/Cargo.toml b/examples/salvo_example/service/Cargo.toml index 4572b26ca9..e5a1fe9c45 100644 --- a/examples/salvo_example/service/Cargo.toml +++ b/examples/salvo_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.3" # sea-orm version +version = "0.12.0-rc.4" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/tonic_example/entity/Cargo.toml b/examples/tonic_example/entity/Cargo.toml index 18787b4f95..5171c39b8b 100644 --- a/examples/tonic_example/entity/Cargo.toml +++ b/examples/tonic_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.3" # sea-orm version +version = "0.12.0-rc.4" # sea-orm version diff --git a/examples/tonic_example/migration/Cargo.toml b/examples/tonic_example/migration/Cargo.toml index 6308041913..252f0aaa59 100644 --- a/examples/tonic_example/migration/Cargo.toml +++ b/examples/tonic_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0-rc.3" # sea-orm-migration version +version = "0.12.0-rc.4" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-rustls", diff --git a/examples/tonic_example/service/Cargo.toml b/examples/tonic_example/service/Cargo.toml index 5730cf8e6a..5319688b2d 100644 --- a/examples/tonic_example/service/Cargo.toml +++ b/examples/tonic_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.3" # sea-orm version +version = "0.12.0-rc.4" # sea-orm version features = [ "debug-print", "runtime-tokio-rustls", From 26405650829c53a618872604797f55cf531af133 Mon Sep 17 00:00:00 2001 From: darkmmon Date: Sat, 8 Jul 2023 00:50:12 +0800 Subject: [PATCH 0049/1371] upgrade version for ouroboros (#1724) --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 7b5ca9ccf6..037de27a82 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,7 @@ serde = { version = "1.0", default-features = false } serde_json = { version = "1.0", default-features = false, optional = true } sqlx = { version = "0.6", default-features = false, optional = true } uuid = { version = "1", default-features = false, optional = true } -ouroboros = { version = "0.15", default-features = false } +ouroboros = { version = "0.17", default-features = false } url = { version = "2.2", default-features = false } thiserror = { version = "1", default-features = false } From 7c57020cf5e7f9b69e5ea04ea8dfcec28f78d1dd Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sat, 8 Jul 2023 00:16:52 +0800 Subject: [PATCH 0050/1371] Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35f4a6e639..db9310c2d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -589,6 +589,7 @@ impl ColumnTrait for Column { * Cleanup panic and unwrap https://github.com/SeaQL/sea-orm/pull/1231 * Cleanup the use of `vec!` macro https://github.com/SeaQL/sea-orm/pull/1367 * Upgrade `syn` to v2 https://github.com/SeaQL/sea-orm/pull/1713 +* Upgrade `ouroboros` to `0.17` https://github.com/SeaQL/sea-orm/pull/1724 ### Bug Fixes From 207c008e5b790583897e76175ef806cbf4d16619 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Sat, 8 Jul 2023 08:23:05 +0800 Subject: [PATCH 0051/1371] Unused import --- src/query/insert.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/query/insert.rs b/src/query/insert.rs index cf878dcd15..3e26e0110e 100644 --- a/src/query/insert.rs +++ b/src/query/insert.rs @@ -347,7 +347,7 @@ where mod tests { use sea_query::OnConflict; - use crate::tests_cfg::cake::{self, ActiveModel}; + use crate::tests_cfg::cake::{self}; use crate::{ActiveValue, DbBackend, DbErr, EntityTrait, Insert, IntoActiveModel, QueryTrait}; #[test] From f5a73117946f4c598b2731da8622d073de3d0088 Mon Sep 17 00:00:00 2001 From: darkmmon Date: Mon, 10 Jul 2023 10:48:20 +0800 Subject: [PATCH 0052/1371] Derive value type (#1720) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * progress (WIP) * WIP * WIP, finished structure except impl programming * WIP * revert event_trigger modification * adding tests and mods * completed derive value type * fixed tests, adjusted code position and completed error messages * column type commit * added attribute array_type and column_type for specification * renamed items and files, and removed debug messages * move attributes outside of the wrapper struct * refactored code for type matching, and restructured code in test cases * clippy fix * fix(doc): fix salvo framework name (#1731) Co-authored-by: 黄景祥 * fmt * changed json_vec_test to use DeriveValueType * fmt * Revert "changed json_vec_test to use DeriveValueType" This reverts commit 92bbf3b6e4eca72e0af0af35776aeec3ee035602. * added test cases for inserting StringVec in a model * fmt * Try non-public wrapped type * Refactoring --------- Co-authored-by: joelhy Co-authored-by: 黄景祥 Co-authored-by: Billy Chan --- sea-orm-macros/src/derives/entity_model.rs | 58 +------- sea-orm-macros/src/derives/mod.rs | 3 + sea-orm-macros/src/derives/sql_type_match.rs | 116 +++++++++++++++ sea-orm-macros/src/derives/value_type.rs | 144 +++++++++++++++++++ sea-orm-macros/src/lib.rs | 10 ++ tests/common/features/mod.rs | 1 + tests/common/features/schema.rs | 46 ++++++ tests/common/features/value_type.rs | 59 ++++++++ tests/value_type_tests.rs | 110 ++++++++++++++ 9 files changed, 495 insertions(+), 52 deletions(-) create mode 100644 sea-orm-macros/src/derives/sql_type_match.rs create mode 100644 sea-orm-macros/src/derives/value_type.rs create mode 100644 tests/common/features/value_type.rs create mode 100644 tests/value_type_tests.rs diff --git a/sea-orm-macros/src/derives/entity_model.rs b/sea-orm-macros/src/derives/entity_model.rs index 67cb27e875..3ce2801e45 100644 --- a/sea-orm-macros/src/derives/entity_model.rs +++ b/sea-orm-macros/src/derives/entity_model.rs @@ -1,10 +1,9 @@ use super::util::{escape_rust_keyword, trim_starting_raw_identifier}; use heck::{ToSnakeCase, ToUpperCamelCase}; use proc_macro2::{Ident, Span, TokenStream}; -use quote::{quote, quote_spanned}; +use quote::quote; use syn::{ punctuated::Punctuated, spanned::Spanned, token::Comma, Attribute, Data, Expr, Fields, Lit, - LitStr, Type, }; /// Method to derive an Model @@ -245,57 +244,12 @@ pub fn expand_derive_entity_model(data: Data, attrs: Vec) -> syn::Res } else { field_type.as_str() }; + let field_span = field.span(); + + let sea_query_col_type = crate::derives::sql_type_match::col_type_match( + sql_type, field_type, field_span, + ); - let sea_query_col_type = match sql_type { - Some(t) => quote! { sea_orm::prelude::ColumnType::#t }, - None => { - let col_type = match field_type { - "char" => quote! { Char(None) }, - "String" | "&str" => quote! { String(None) }, - "i8" => quote! { TinyInteger }, - "u8" => quote! { TinyUnsigned }, - "i16" => quote! { SmallInteger }, - "u16" => quote! { SmallUnsigned }, - "i32" => quote! { Integer }, - "u32" => quote! { Unsigned }, - "i64" => quote! { BigInteger }, - "u64" => quote! { BigUnsigned }, - "f32" => quote! { Float }, - "f64" => quote! { Double }, - "bool" => quote! { Boolean }, - "Date" | "NaiveDate" => quote! { Date }, - "Time" | "NaiveTime" => quote! { Time }, - "DateTime" | "NaiveDateTime" => { - quote! { DateTime } - } - "DateTimeUtc" | "DateTimeLocal" | "DateTimeWithTimeZone" => { - quote! { TimestampWithTimeZone } - } - "Uuid" => quote! { Uuid }, - "Json" => quote! { Json }, - "Decimal" => quote! { Decimal(None) }, - "Vec" => { - quote! { Binary(sea_orm::sea_query::BlobSize::Blob(None)) } - } - _ => { - // Assumed it's ActiveEnum if none of the above type matches - quote! {} - } - }; - if col_type.is_empty() { - let field_span = field.span(); - let ty: Type = LitStr::new(field_type, field_span).parse()?; - let def = quote_spanned! { field_span => - std::convert::Into::::into( - <#ty as sea_orm::sea_query::ValueType>::column_type() - ) - }; - quote! { #def } - } else { - quote! { sea_orm::prelude::ColumnType::#col_type } - } - } - }; let col_def = quote! { sea_orm::prelude::ColumnTypeTrait::def(#sea_query_col_type) }; diff --git a/sea-orm-macros/src/derives/mod.rs b/sea-orm-macros/src/derives/mod.rs index 29f69b053a..6d71d1a516 100644 --- a/sea-orm-macros/src/derives/mod.rs +++ b/sea-orm-macros/src/derives/mod.rs @@ -13,8 +13,10 @@ mod partial_model; mod primary_key; mod related_entity; mod relation; +mod sql_type_match; mod try_getable_from_json; mod util; +mod value_type; pub use active_enum::*; pub use active_model::*; @@ -31,3 +33,4 @@ pub use primary_key::*; pub use related_entity::*; pub use relation::*; pub use try_getable_from_json::*; +pub use value_type::*; diff --git a/sea-orm-macros/src/derives/sql_type_match.rs b/sea-orm-macros/src/derives/sql_type_match.rs new file mode 100644 index 0000000000..65465d12ab --- /dev/null +++ b/sea-orm-macros/src/derives/sql_type_match.rs @@ -0,0 +1,116 @@ +use proc_macro2::{Span, TokenStream}; +use quote::{quote, quote_spanned}; +use syn::{LitStr, Type}; + +pub fn col_type_match( + col_type: Option, + field_type: &str, + field_span: Span, +) -> TokenStream { + match col_type { + Some(t) => quote! { sea_orm::prelude::ColumnType::#t }, + None => { + let col_type = match field_type { + "char" => quote! { Char(None) }, + "String" | "&str" => quote! { String(None) }, + "i8" => quote! { TinyInteger }, + "u8" => quote! { TinyUnsigned }, + "i16" => quote! { SmallInteger }, + "u16" => quote! { SmallUnsigned }, + "i32" => quote! { Integer }, + "u32" => quote! { Unsigned }, + "i64" => quote! { BigInteger }, + "u64" => quote! { BigUnsigned }, + "f32" => quote! { Float }, + "f64" => quote! { Double }, + "bool" => quote! { Boolean }, + "Date" | "NaiveDate" => quote! { Date }, + "Time" | "NaiveTime" => quote! { Time }, + "DateTime" | "NaiveDateTime" => { + quote! { DateTime } + } + "DateTimeUtc" | "DateTimeLocal" | "DateTimeWithTimeZone" => { + quote! { TimestampWithTimeZone } + } + "Uuid" => quote! { Uuid }, + "Json" => quote! { Json }, + "Decimal" => quote! { Decimal(None) }, + "Vec" => { + quote! { Binary(sea_orm::sea_query::BlobSize::Blob(None)) } + } + _ => { + // Assumed it's ActiveEnum if none of the above type matches + quote! {} + } + }; + if col_type.is_empty() { + let ty: Type = LitStr::new(field_type, field_span) + .parse() + .expect("field type error"); + let def = quote_spanned! { field_span => + std::convert::Into::::into( + <#ty as sea_orm::sea_query::ValueType>::column_type() + ) + }; + quote! { #def } + } else { + quote! { sea_orm::prelude::ColumnType::#col_type } + } + } + } +} + +pub fn arr_type_match( + arr_type: Option, + field_type: &str, + field_span: Span, +) -> TokenStream { + match arr_type { + Some(t) => quote! { sea_orm::sea_query::ArrayType::#t }, + None => { + let arr_type = match field_type { + "char" => quote! { Char }, + "String" | "&str" => quote! { String }, + "i8" => quote! { TinyInt }, + "u8" => quote! { TinyUnsigned }, + "i16" => quote! { SmallInt }, + "u16" => quote! { SmallUnsigned }, + "i32" => quote! { Int }, + "u32" => quote! { Unsigned }, + "i64" => quote! { BigInt }, + "u64" => quote! { BigUnsigned }, + "f32" => quote! { Float }, + "f64" => quote! { Double }, + "bool" => quote! { Bool }, + "Date" | "NaiveDate" => quote! { ChronoDate }, + "Time" | "NaiveTime" => quote! { ChronoTime }, + "DateTime" | "NaiveDateTime" => { + quote! { ChronoDateTime } + } + "DateTimeUtc" | "DateTimeLocal" | "DateTimeWithTimeZone" => { + quote! { ChronoDateTimeWithTimeZone } + } + "Uuid" => quote! { Uuid }, + "Json" => quote! { Json }, + "Decimal" => quote! { Decimal }, + _ => { + // Assumed it's ActiveEnum if none of the above type matches + quote! {} + } + }; + if arr_type.is_empty() { + let ty: Type = LitStr::new(field_type, field_span) + .parse() + .expect("field type error"); + let def = quote_spanned! { field_span => + std::convert::Into::::into( + <#ty as sea_orm::sea_query::ValueType>::array_type() + ) + }; + quote! { #def } + } else { + quote! { sea_orm::sea_query::ArrayType::#arr_type } + } + } + } +} diff --git a/sea-orm-macros/src/derives/value_type.rs b/sea-orm-macros/src/derives/value_type.rs new file mode 100644 index 0000000000..2a1ee606ec --- /dev/null +++ b/sea-orm-macros/src/derives/value_type.rs @@ -0,0 +1,144 @@ +use proc_macro2::TokenStream; +use quote::quote; +use syn::{spanned::Spanned, Lit, Type}; + +struct DeriveValueType { + name: syn::Ident, + ty: Type, + column_type: TokenStream, + array_type: TokenStream, +} + +impl DeriveValueType { + pub fn new(input: syn::DeriveInput) -> Option { + let dat = input.data; + let fields: Option> = match dat { + syn::Data::Struct(syn::DataStruct { + fields: syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed, .. }), + .. + }) => Some(unnamed), + _ => None, + }; + + let field = fields + .expect("This derive accept only struct") + .first() + .expect("The struct should contain one value field") + .to_owned(); + + let name = input.ident; + let mut col_type = None; + let mut arr_type = None; + + for attr in input.attrs.iter() { + if !attr.path().is_ident("sea_orm") { + continue; + } + + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("column_type") { + let lit = meta.value()?.parse()?; + if let Lit::Str(litstr) = lit { + let ty: TokenStream = syn::parse_str(&litstr.value())?; + col_type = Some(ty); + } else { + return Err(meta.error(format!("Invalid column_type {:?}", lit))); + } + } else if meta.path.is_ident("array_type") { + let lit = meta.value()?.parse()?; + if let Lit::Str(litstr) = lit { + let ty: TokenStream = syn::parse_str(&litstr.value())?; + arr_type = Some(ty); + } else { + return Err(meta.error(format!("Invalid array_type {:?}", lit))); + } + } else { + // received other attribute + return Err(meta.error(format!("Invalid attribute {:?}", meta.path))); + } + + Ok(()) + }) + .unwrap_or(()); + } + + let ty = field.clone().ty; + let field_type = quote! { #ty } + .to_string() //E.g.: "Option < String >" + .replace(' ', ""); // Remove spaces + let field_type = if field_type.starts_with("Option<") { + &field_type[7..(field_type.len() - 1)] // Extract `T` out of `Option` + } else { + field_type.as_str() + }; + let field_span = field.span(); + + let column_type = + crate::derives::sql_type_match::col_type_match(col_type, field_type, field_span); + + let array_type = + crate::derives::sql_type_match::arr_type_match(arr_type, field_type, field_span); + + Some(DeriveValueType { + name, + ty, + column_type, + array_type, + }) + } + + fn expand(&self) -> syn::Result { + let expanded_impl_value_type: TokenStream = self.impl_value_type(); + Ok(expanded_impl_value_type) + } + + fn impl_value_type(&self) -> TokenStream { + let name = &self.name; + let field_type = &self.ty; + let column_type = &self.column_type; + let array_type = &self.array_type; + + quote!( + #[automatically_derived] + impl From<#name> for Value { + fn from(source: #name) -> Self { + source.0.into() + } + } + + #[automatically_derived] + impl sea_orm::TryGetable for #name { + fn try_get_by(res: &QueryResult, idx: I) -> Result { + <#field_type as sea_orm::TryGetable>::try_get_by(res, idx).map(|v| #name(v)) + } + } + + #[automatically_derived] + impl sea_orm::sea_query::ValueType for #name { + fn try_from(v: Value) -> Result { + <#field_type as sea_orm::sea_query::ValueType>::try_from(v).map(|v| #name(v)) + } + + fn type_name() -> String { + stringify!(#name).to_owned() + } + + fn array_type() -> sea_orm::sea_query::ArrayType { + #array_type + } + + fn column_type() -> sea_orm::sea_query::ColumnType { + #column_type + } + } + ) + } +} + +pub fn expand_derive_value_type(input: syn::DeriveInput) -> syn::Result { + let input_span = input.span(); + match DeriveValueType::new(input) { + Some(model) => model.expand(), + None => Err(syn::Error::new(input_span, "error")), + } +} diff --git a/sea-orm-macros/src/lib.rs b/sea-orm-macros/src/lib.rs index 0e9c7b1b8b..07a26f053c 100644 --- a/sea-orm-macros/src/lib.rs +++ b/sea-orm-macros/src/lib.rs @@ -832,3 +832,13 @@ pub fn enum_iter(input: TokenStream) -> TokenStream { .unwrap_or_else(Error::into_compile_error) .into() } + +#[cfg(feature = "derive")] +#[proc_macro_derive(DeriveValueType, attributes(sea_orm))] +pub fn derive_value_type(input: TokenStream) -> TokenStream { + let derive_input = parse_macro_input!(input as DeriveInput); + match derives::expand_derive_value_type(derive_input) { + Ok(token_stream) => token_stream.into(), + Err(e) => e.to_compile_error().into(), + } +} diff --git a/tests/common/features/mod.rs b/tests/common/features/mod.rs index 8507b4e382..407a599171 100644 --- a/tests/common/features/mod.rs +++ b/tests/common/features/mod.rs @@ -23,6 +23,7 @@ pub mod self_join; pub mod teas; pub mod transaction_log; pub mod uuid_fmt; +pub mod value_type; pub use active_enum::Entity as ActiveEnum; pub use active_enum_child::Entity as ActiveEnumChild; diff --git a/tests/common/features/schema.rs b/tests/common/features/schema.rs index 12897ca896..058474e0ed 100644 --- a/tests/common/features/schema.rs +++ b/tests/common/features/schema.rs @@ -48,8 +48,10 @@ pub async fn create_tables(db: &DatabaseConnection) -> Result<(), DbErr> { create_binary_table(db).await?; create_bits_table(db).await?; create_dyn_table_name_lazy_static_table(db).await?; + create_value_type_table(db).await?; if DbBackend::Postgres == db_backend { + create_value_type_postgres_table(db).await?; create_collection_table(db).await?; create_event_trigger_table(db).await?; } @@ -634,3 +636,47 @@ pub async fn create_dyn_table_name_lazy_static_table(db: &DbConn) -> Result<(), Ok(()) } + +pub async fn create_value_type_table(db: &DbConn) -> Result { + let general_stmt = sea_query::Table::create() + .table(value_type::value_type_general::Entity) + .col( + ColumnDef::new(value_type::value_type_general::Column::Id) + .integer() + .not_null() + .auto_increment() + .primary_key(), + ) + .col( + ColumnDef::new(value_type::value_type_general::Column::Number) + .integer() + .not_null(), + ) + .to_owned(); + + create_table(db, &general_stmt, value_type::value_type_general::Entity).await +} +pub async fn create_value_type_postgres_table(db: &DbConn) -> Result { + let postgres_stmt = sea_query::Table::create() + .table(value_type::value_type_pg::Entity) + .col( + ColumnDef::new(value_type::value_type_pg::Column::Id) + .integer() + .not_null() + .auto_increment() + .primary_key(), + ) + .col( + ColumnDef::new(value_type::value_type_pg::Column::Number) + .integer() + .not_null(), + ) + .col( + ColumnDef::new(json_vec::Column::StrVec) + .array(sea_query::ColumnType::String(None)) + .not_null(), + ) + .to_owned(); + + create_table(db, &postgres_stmt, value_type::value_type_pg::Entity).await +} diff --git a/tests/common/features/value_type.rs b/tests/common/features/value_type.rs new file mode 100644 index 0000000000..f0efb9dc27 --- /dev/null +++ b/tests/common/features/value_type.rs @@ -0,0 +1,59 @@ +pub mod value_type_general { + use super::*; + use sea_orm::entity::prelude::*; + + #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] + #[sea_orm(table_name = "value_type")] + pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub number: Integer, + } + + #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] + pub enum Relation {} + + impl ActiveModelBehavior for ActiveModel {} +} + +pub mod value_type_pg { + use super::*; + use sea_orm::entity::prelude::*; + + #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] + #[sea_orm(table_name = "value_type_postgres")] + pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub number: Integer, + pub str_vec: StringVec, + } + + #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] + pub enum Relation {} + + impl ActiveModelBehavior for ActiveModel {} +} + +use sea_orm::entity::prelude::*; +use sea_orm_macros::DeriveValueType; + +#[derive(Clone, Debug, PartialEq, Eq, DeriveValueType)] +#[sea_orm(array_type = "Int")] +pub struct Integer(i32); + +impl From for Integer +where + T: Into, +{ + fn from(v: T) -> Integer { + Integer(v.into()) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, DeriveValueType)] +#[sea_orm(column_type = "Boolean", array_type = "Bool")] +pub struct Boolbean(pub String); + +#[derive(Clone, Debug, PartialEq, Eq, DeriveValueType)] +pub struct StringVec(pub Vec); diff --git a/tests/value_type_tests.rs b/tests/value_type_tests.rs new file mode 100644 index 0000000000..2271866063 --- /dev/null +++ b/tests/value_type_tests.rs @@ -0,0 +1,110 @@ +pub mod common; + +use std::sync::Arc; +use std::vec; + +pub use common::{ + features::{ + value_type::{value_type_general, value_type_pg, Boolbean, Integer, StringVec}, + *, + }, + setup::*, + TestContext, +}; +use pretty_assertions::assert_eq; +use sea_orm::{entity::prelude::*, entity::*, DatabaseConnection}; +use sea_query::{ArrayType, ColumnType, Value, ValueType, ValueTypeErr}; + +#[sea_orm_macros::test] +#[cfg(any( + feature = "sqlx-mysql", + feature = "sqlx-sqlite", + feature = "sqlx-postgres" +))] +async fn main() -> Result<(), DbErr> { + let ctx = TestContext::new("value_type_tests").await; + create_tables(&ctx.db).await?; + insert_value(&ctx.db).await?; + ctx.delete().await; + + if cfg!(feature = "sqlx-postgres") { + let ctx = TestContext::new("value_type_postgres_tests").await; + create_tables(&ctx.db).await?; + postgres_insert_value(&ctx.db).await?; + ctx.delete().await; + } + + type_test(); + conversion_test(); + + Ok(()) +} + +pub async fn insert_value(db: &DatabaseConnection) -> Result<(), DbErr> { + let model = value_type_general::Model { + id: 1, + number: 48.into(), + }; + let result = model.clone().into_active_model().insert(db).await?; + assert_eq!(result, model); + + Ok(()) +} + +pub async fn postgres_insert_value(db: &DatabaseConnection) -> Result<(), DbErr> { + let model = value_type_pg::Model { + id: 1, + number: 48.into(), + str_vec: StringVec(vec!["ab".to_string(), "cd".to_string()]), + }; + let result = model.clone().into_active_model().insert(db).await?; + assert_eq!(result, model); + + Ok(()) +} + +pub fn type_test() { + assert_eq!(StringVec::type_name(), "StringVec"); + + // custom types + assert_eq!(Integer::array_type(), ArrayType::Int); + assert_eq!(Integer::array_type(), ArrayType::Int); + assert_eq!(Boolbean::column_type(), ColumnType::Boolean); + assert_eq!(Boolbean::array_type(), ArrayType::Bool); + // self implied + assert_eq!( + StringVec::column_type(), + ColumnType::Array(Arc::new(ColumnType::String(None))) + ); + assert_eq!(StringVec::array_type(), ArrayType::String); +} + +pub fn conversion_test() { + let stringvec = StringVec(vec!["ab".to_string(), "cd".to_string()]); + let string: Value = stringvec.into(); + assert_eq!( + string, + Value::Array( + ArrayType::String, + Some(Box::new(vec![ + "ab".to_string().into(), + "cd".to_string().into() + ])) + ) + ); + + let value_random_int = Value::Int(Some(523)); + let unwrap_int = Integer::unwrap(value_random_int.clone()); + let try_from_int = + ::try_from(value_random_int).expect("should be ok to convert"); + + // tests for unwrap and try_from + let direct_int: Integer = 523.into(); + assert_eq!(direct_int, unwrap_int); + assert_eq!(direct_int, try_from_int); + + // test for error + let try_from_string_vec = ::try_from(Value::Char(Some('a'))) + .expect_err("should not be ok to convert char to stringvec"); + assert_eq!(try_from_string_vec.to_string(), ValueTypeErr.to_string()); +} From ef8946325016522994904be3047c26f969be13f5 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Mon, 10 Jul 2023 10:49:16 +0800 Subject: [PATCH 0053/1371] CHANGELOG --- CHANGELOG.md | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index db9310c2d1..8984dca4f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -284,6 +284,60 @@ fn find_with_linked(self, l: L) -> SelectTwoMany // boths yields `Vec<(E::Model, Vec)>` ``` +* Add `DeriveValueType` derive macro for custom wrapper types, implementations of the required traits will be provided, you can customize the `column_type` and `array_type` if needed https://github.com/SeaQL/sea-orm/pull/1720 +```rs +#[derive(DeriveValueType)] +#[sea_orm(array_type = "Int")] +pub struct Integer(i32); + +#[derive(DeriveValueType)] +#[sea_orm(column_type = "Boolean", array_type = "Bool")] +pub struct Boolbean(pub String); + +#[derive(DeriveValueType)] +pub struct StringVec(pub Vec); +``` +The expanded code of `DeriveValueType` looks like. +```rs +#[derive(DeriveValueType)] +pub struct StringVec(pub Vec); + +// The `DeriveValueType` will be expanded into... + +impl From for Value { + fn from(source: StringVec) -> Self { + source.0.into() + } +} + +impl sea_orm::TryGetable for StringVec { + fn try_get_by(res: &QueryResult, idx: I) -> Result { + as sea_orm::TryGetable>::try_get_by(res, idx).map(|v| StringVec(v)) + } +} + +impl sea_orm::sea_query::ValueType for StringVec { + fn try_from(v: Value) -> Result { + as sea_orm::sea_query::ValueType>::try_from(v).map(|v| StringVec(v)) + } + + fn type_name() -> String { + stringify!(StringVec).to_owned() + } + + fn array_type() -> sea_orm::sea_query::ArrayType { + std::convert::Into::::into( + as sea_orm::sea_query::ValueType>::array_type() + ) + } + + fn column_type() -> sea_orm::sea_query::ColumnType { + std::convert::Into::::into( + as sea_orm::sea_query::ValueType>::column_type() + ) + } +} +``` ### Enhancements From 902c0ed5ff2d14cc7934303d2690f11fb3e5d9d5 Mon Sep 17 00:00:00 2001 From: darkmmon Date: Mon, 10 Jul 2023 11:51:19 +0800 Subject: [PATCH 0054/1371] DeriveDisplay macro for enum (#1726) * WIP * WIP * WIP * changed from storing ident to storing TokenStream internally * Push for code sharing * fmt * changing test target to string * updated Display not showing extra single quote, fixed clippy * removed DeriveDisplay for test case not using display trait * added test cases for display_value attribute * Remove useless clone * Add comment * Remove useless clone * Rename method * Refactoring --------- Co-authored-by: Billy Chan --- sea-orm-macros/src/derives/active_enum.rs | 12 +- .../src/derives/active_enum_display.rs | 104 ++++++++++++++++++ sea-orm-macros/src/derives/mod.rs | 2 + sea-orm-macros/src/lib.rs | 10 ++ src/entity/active_enum.rs | 30 ++--- src/entity/prelude.rs | 4 +- src/lib.rs | 2 +- tests/active_enum_tests.rs | 8 ++ tests/common/features/sea_orm_active_enums.rs | 11 +- 9 files changed, 156 insertions(+), 27 deletions(-) create mode 100644 sea-orm-macros/src/derives/active_enum_display.rs diff --git a/sea-orm-macros/src/derives/active_enum.rs b/sea-orm-macros/src/derives/active_enum.rs index 77e30196ae..591da6f331 100644 --- a/sea-orm-macros/src/derives/active_enum.rs +++ b/sea-orm-macros/src/derives/active_enum.rs @@ -101,6 +101,10 @@ impl ActiveEnum { } else if meta.path.is_ident("num_value") { is_int = true; num_value = Some(meta.value()?.parse::()?); + } else if meta.path.is_ident("display_value") { + // This is a placeholder to prevent the `display_value` proc_macro attribute of `DeriveDisplay` + // to be considered unknown attribute parameter + meta.value()?.parse::()?; } else { return Err(meta.error(format!( "Unknown attribute parameter found: {:?}", @@ -379,14 +383,6 @@ impl ActiveEnum { } } - #[automatically_derived] - impl std::fmt::Display for #ident { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let v: sea_orm::sea_query::Value = ::to_value(&self).into(); - write!(f, "{}", v) - } - } - #impl_not_u8 ) } diff --git a/sea-orm-macros/src/derives/active_enum_display.rs b/sea-orm-macros/src/derives/active_enum_display.rs new file mode 100644 index 0000000000..565e41933f --- /dev/null +++ b/sea-orm-macros/src/derives/active_enum_display.rs @@ -0,0 +1,104 @@ +use proc_macro2::TokenStream; +use quote::{quote, quote_spanned, ToTokens}; +use syn::{LitInt, LitStr}; + +enum Error { + InputNotEnum, + Syn(syn::Error), +} + +struct Display { + ident: syn::Ident, + variants: Vec, +} + +struct DisplayVariant { + ident: syn::Ident, + display_value: TokenStream, +} + +impl Display { + fn new(input: syn::DeriveInput) -> Result { + let ident = input.ident; + + let variant_vec = match input.data { + syn::Data::Enum(syn::DataEnum { variants, .. }) => variants, + _ => return Err(Error::InputNotEnum), + }; + + let mut variants = Vec::new(); + for variant in variant_vec { + let mut display_value = variant.ident.to_string().to_token_stream(); + for attr in variant.attrs.iter() { + if !attr.path().is_ident("sea_orm") { + continue; + } + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("string_value") { + meta.value()?.parse::()?; + } else if meta.path.is_ident("num_value") { + meta.value()?.parse::()?; + } else if meta.path.is_ident("display_value") { + display_value = meta.value()?.parse::()?.to_token_stream(); + } else { + return Err(meta.error(format!( + "Unknown attribute parameter found: {:?}", + meta.path.get_ident() + ))); + } + + Ok(()) + }) + .map_err(Error::Syn)?; + } + variants.push(DisplayVariant { + ident: variant.ident, + display_value, + }); + } + Ok(Display { ident, variants }) + } + + fn expand(&self) -> syn::Result { + let expanded_impl_active_enum_display = self.impl_active_enum_display(); + + Ok(expanded_impl_active_enum_display) + } + + fn impl_active_enum_display(&self) -> TokenStream { + let Self { ident, variants } = self; + + let variant_idents: Vec<_> = variants + .iter() + .map(|variant| variant.ident.clone()) + .collect(); + + let variant_display: Vec<_> = variants + .iter() + .map(|variant| variant.display_value.to_owned()) + .collect(); + + quote!( + #[automatically_derived] + impl std::fmt::Display for #ident { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", match self { + #( Self::#variant_idents => #variant_display, )* + }) + } + } + ) + } +} + +pub fn expand_derive_active_enum_display(input: syn::DeriveInput) -> syn::Result { + let ident_span = input.ident.span(); + + match Display::new(input) { + Ok(model) => model.expand(), + Err(Error::InputNotEnum) => Ok(quote_spanned! { + ident_span => compile_error!("you can only derive EnumDisplay on enums"); + }), + Err(Error::Syn(e)) => Err(e), + } +} diff --git a/sea-orm-macros/src/derives/mod.rs b/sea-orm-macros/src/derives/mod.rs index 6d71d1a516..168f94eeb8 100644 --- a/sea-orm-macros/src/derives/mod.rs +++ b/sea-orm-macros/src/derives/mod.rs @@ -1,4 +1,5 @@ mod active_enum; +mod active_enum_display; mod active_model; mod active_model_behavior; mod attributes; @@ -19,6 +20,7 @@ mod util; mod value_type; pub use active_enum::*; +pub use active_enum_display::*; pub use active_model::*; pub use active_model_behavior::*; pub use column::*; diff --git a/sea-orm-macros/src/lib.rs b/sea-orm-macros/src/lib.rs index 07a26f053c..c9d84d4a3c 100644 --- a/sea-orm-macros/src/lib.rs +++ b/sea-orm-macros/src/lib.rs @@ -842,3 +842,13 @@ pub fn derive_value_type(input: TokenStream) -> TokenStream { Err(e) => e.to_compile_error().into(), } } + +#[cfg(feature = "derive")] +#[proc_macro_derive(DeriveDisplay, attributes(sea_orm))] +pub fn derive_active_enum_display(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + match derives::expand_derive_active_enum_display(input) { + Ok(ts) => ts.into(), + Err(e) => e.to_compile_error().into(), + } +} diff --git a/src/entity/active_enum.rs b/src/entity/active_enum.rs index 27bc894d08..99e6351dac 100644 --- a/src/entity/active_enum.rs +++ b/src/entity/active_enum.rs @@ -17,7 +17,7 @@ use sea_query::{DynIden, Expr, Nullable, SimpleExpr, Value, ValueType}; /// use sea_orm::entity::prelude::*; /// /// // Using the derive macro -/// #[derive(Debug, PartialEq, EnumIter, DeriveActiveEnum)] +/// #[derive(Debug, PartialEq, EnumIter, DeriveActiveEnum, DeriveDisplay)] /// #[sea_orm( /// rs_type = "String", /// db_type = "String(Some(1))", @@ -85,7 +85,7 @@ use sea_query::{DynIden, Expr, Nullable, SimpleExpr, Value, ValueType}; /// use sea_orm::entity::prelude::*; /// /// // Define the `Category` active enum -/// #[derive(Debug, Clone, PartialEq, EnumIter, DeriveActiveEnum)] +/// #[derive(Debug, Clone, PartialEq, EnumIter, DeriveActiveEnum, DeriveDisplay)] /// #[sea_orm(rs_type = "String", db_type = "String(Some(1))")] /// pub enum Category { /// #[sea_orm(string_value = "B")] @@ -216,7 +216,7 @@ mod tests { } } - #[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)] + #[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, DeriveDisplay)] #[sea_orm( rs_type = "String", db_type = "String(Some(1))", @@ -268,15 +268,15 @@ mod tests { ); assert_eq!(Category::values(), DeriveCategory::values()); - assert_eq!(format!("{}", DeriveCategory::Big), "'B'"); - assert_eq!(format!("{}", DeriveCategory::Small), "'S'"); + assert_eq!(format!("{}", DeriveCategory::Big), "Big"); + assert_eq!(format!("{}", DeriveCategory::Small), "Small"); } #[test] fn active_enum_derive_signed_integers() { macro_rules! test_num_value_int { ($ident: ident, $rs_type: expr, $db_type: expr, $col_def: ident) => { - #[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)] + #[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, DeriveDisplay)] #[sea_orm(rs_type = $rs_type, db_type = $db_type)] pub enum $ident { #[sea_orm(num_value = -10)] @@ -293,7 +293,7 @@ mod tests { macro_rules! test_fallback_int { ($ident: ident, $fallback_type: ident, $rs_type: expr, $db_type: expr, $col_def: ident) => { - #[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)] + #[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, DeriveDisplay)] #[sea_orm(rs_type = $rs_type, db_type = $db_type)] #[repr(i32)] pub enum $ident { @@ -325,9 +325,9 @@ mod tests { assert_eq!($ident::db_type(), ColumnType::$col_def.def()); - assert_eq!(format!("{}", $ident::Big), "1"); - assert_eq!(format!("{}", $ident::Small), "0"); - assert_eq!(format!("{}", $ident::Negative), "-10"); + assert_eq!(format!("{}", $ident::Big), "Big"); + assert_eq!(format!("{}", $ident::Small), "Small"); + assert_eq!(format!("{}", $ident::Negative), "Negative"); }; } @@ -346,7 +346,7 @@ mod tests { fn active_enum_derive_unsigned_integers() { macro_rules! test_num_value_uint { ($ident: ident, $rs_type: expr, $db_type: expr, $col_def: ident) => { - #[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)] + #[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, DeriveDisplay)] #[sea_orm(rs_type = $rs_type, db_type = $db_type)] pub enum $ident { #[sea_orm(num_value = 1)] @@ -361,7 +361,7 @@ mod tests { macro_rules! test_fallback_uint { ($ident: ident, $fallback_type: ident, $rs_type: expr, $db_type: expr, $col_def: ident) => { - #[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)] + #[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, DeriveDisplay)] #[sea_orm(rs_type = $rs_type, db_type = $db_type)] #[repr($fallback_type)] pub enum $ident { @@ -390,8 +390,8 @@ mod tests { assert_eq!($ident::db_type(), ColumnType::$col_def.def()); - assert_eq!(format!("{}", $ident::Big), "1"); - assert_eq!(format!("{}", $ident::Small), "0"); + assert_eq!(format!("{}", $ident::Big), "Big"); + assert_eq!(format!("{}", $ident::Small), "Small"); }; } @@ -459,7 +459,7 @@ mod tests { assert_eq!(PopOSTypos::try_from_value(&val.to_owned()), Ok(variant)); } - #[derive(Clone, Debug, PartialEq, EnumIter, DeriveActiveEnum)] + #[derive(Clone, Debug, PartialEq, EnumIter, DeriveActiveEnum, DeriveDisplay)] #[sea_orm( rs_type = "String", db_type = "String(None)", diff --git a/src/entity/prelude.rs b/src/entity/prelude.rs index 394ab47d24..f335c9c82e 100644 --- a/src/entity/prelude.rs +++ b/src/entity/prelude.rs @@ -11,8 +11,8 @@ pub use crate::{ #[cfg(feature = "macros")] pub use crate::{ DeriveActiveEnum, DeriveActiveModel, DeriveActiveModelBehavior, DeriveColumn, - DeriveCustomColumn, DeriveEntity, DeriveEntityModel, DeriveIntoActiveModel, DeriveModel, - DerivePrimaryKey, DeriveRelatedEntity, DeriveRelation, FromJsonQueryResult, + DeriveCustomColumn, DeriveDisplay, DeriveEntity, DeriveEntityModel, DeriveIntoActiveModel, + DeriveModel, DerivePrimaryKey, DeriveRelatedEntity, DeriveRelation, FromJsonQueryResult, }; pub use async_trait; diff --git a/src/lib.rs b/src/lib.rs index 15e13e5954..c01c2fe75f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -349,7 +349,7 @@ pub use schema::*; #[cfg(feature = "macros")] pub use sea_orm_macros::{ DeriveActiveEnum, DeriveActiveModel, DeriveActiveModelBehavior, DeriveColumn, - DeriveCustomColumn, DeriveEntity, DeriveEntityModel, DeriveIntoActiveModel, + DeriveCustomColumn, DeriveDisplay, DeriveEntity, DeriveEntityModel, DeriveIntoActiveModel, DeriveMigrationName, DeriveModel, DerivePartialModel, DerivePrimaryKey, DeriveRelatedEntity, DeriveRelation, FromJsonQueryResult, FromQueryResult, }; diff --git a/tests/active_enum_tests.rs b/tests/active_enum_tests.rs index c6bf47d4ac..14ebb436b8 100644 --- a/tests/active_enum_tests.rs +++ b/tests/active_enum_tests.rs @@ -798,4 +798,12 @@ mod tests { ) ); } + + #[test] + fn display_test() { + assert_eq!(format!("{}", Tea::BreakfastTea), "BreakfastTea"); + assert_eq!(format!("{}", DisplayTea::BreakfastTea), "Breakfast"); + assert_eq!(format!("{}", Tea::EverydayTea), "EverydayTea"); + assert_eq!(format!("{}", DisplayTea::EverydayTea), "Everyday"); + } } diff --git a/tests/common/features/sea_orm_active_enums.rs b/tests/common/features/sea_orm_active_enums.rs index 94289dfce3..e5fe2981ae 100644 --- a/tests/common/features/sea_orm_active_enums.rs +++ b/tests/common/features/sea_orm_active_enums.rs @@ -18,7 +18,7 @@ pub enum Color { White, } -#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum)] +#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, DeriveDisplay)] #[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "tea")] pub enum Tea { #[sea_orm(string_value = "EverydayTea")] @@ -53,3 +53,12 @@ pub enum MediaType { #[sea_orm(string_value = "3D")] _3D, } + +#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, DeriveDisplay)] +#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "tea")] +pub enum DisplayTea { + #[sea_orm(string_value = "EverydayTea", display_value = "Everyday")] + EverydayTea, + #[sea_orm(string_value = "BreakfastTea", display_value = "Breakfast")] + BreakfastTea, +} From be83092be99a62fbd3d935faf176380c9db55d1c Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Mon, 10 Jul 2023 12:12:55 +0800 Subject: [PATCH 0055/1371] CHANGELOG --- CHANGELOG.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8984dca4f3..013b78d9fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -338,6 +338,47 @@ impl sea_orm::sea_query::ValueType for StringVec { } } ``` +* Add `DeriveDisplay` derive macro to implements `std::fmt::Display` for active enum https://github.com/SeaQL/sea-orm/pull/1726 +```rs +// String enum +#[derive(EnumIter, DeriveActiveEnum, DeriveDisplay)] +#[sea_orm(rs_type = "String", db_type = "String(Some(1))", enum_name = "category")] +pub enum DeriveCategory { + #[sea_orm(string_value = "B")] + Big, + #[sea_orm(string_value = "S")] + Small, +} +assert_eq!(format!("{}", DeriveCategory::Big), "Big"); +assert_eq!(format!("{}", DeriveCategory::Small), "Small"); + +// Numeric enum +#[derive(EnumIter, DeriveActiveEnum, DeriveDisplay)] +#[sea_orm(rs_type = "i32", db_type = "Integer")] +pub enum $ident { + #[sea_orm(num_value = -10)] + Negative, + #[sea_orm(num_value = 1)] + Big, + #[sea_orm(num_value = 0)] + Small, +} +assert_eq!(format!("{}", $ident::Big), "Big"); +assert_eq!(format!("{}", $ident::Small), "Small"); +assert_eq!(format!("{}", $ident::Negative), "Negative"); + +// String enum with `display_value` overrides +#[derive(EnumIter, DeriveActiveEnum, DeriveDisplay)] +#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "tea")] +pub enum DisplayTea { + #[sea_orm(string_value = "EverydayTea", display_value = "Everyday")] + EverydayTea, + #[sea_orm(string_value = "BreakfastTea", display_value = "Breakfast")] + BreakfastTea, +} +assert_eq!(format!("{}", DisplayTea::BreakfastTea), "Breakfast"); +assert_eq!(format!("{}", DisplayTea::EverydayTea), "Everyday"); +``` ### Enhancements @@ -498,6 +539,7 @@ impl ColumnTrait for Column { * Added a new variant `Many` to `Identity` https://github.com/SeaQL/sea-orm/pull/1508 * Replace the use of `SeaRc` where `T` isn't `dyn Iden` with `RcOrArc` https://github.com/SeaQL/sea-orm/pull/1661 * Enabled `hashable-value` feature in SeaQuery, thus `Value::Float(NaN) == Value::Float(NaN)` would be true +* The `DeriveActiveEnum` derive macro no longer provide `std::fmt::Display` implementation for the enum. You need to derive an extra `DeriveDisplay` macro alongside with `DeriveActiveEnum` derive macro. https://github.com/SeaQL/sea-orm/pull/1726 ## 0.11.3 - 2023-04-24 From 19e761e73dfd3e02b8d485525fd08a11677cf50f Mon Sep 17 00:00:00 2001 From: darkmmon Date: Mon, 10 Jul 2023 16:11:58 +0800 Subject: [PATCH 0056/1371] moving bae2 into sea-orm local system (#1739) * imported bae2 into sea-orm local system * doc update * fix doc * Remove sea-bae from the workspace * Use sea-bae --------- Co-authored-by: Billy Chan --- sea-orm-macros/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sea-orm-macros/Cargo.toml b/sea-orm-macros/Cargo.toml index 23199c85bb..49b71a38b1 100644 --- a/sea-orm-macros/Cargo.toml +++ b/sea-orm-macros/Cargo.toml @@ -18,7 +18,7 @@ path = "src/lib.rs" proc-macro = true [dependencies] -bae = { version = "1", package = "bae2", default-features = false, optional = true } +bae = { version = "0.2", package = "sea-bae", default-features = false, optional = true } syn = { version = "2", default-features = false, features = ["parsing", "proc-macro", "derive", "printing"] } quote = { version = "1", default-features = false } heck = { version = "0.4", default-features = false } From 1b72b35319b4dbffc8c713234e9efe497ec80ee0 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Mon, 10 Jul 2023 17:40:28 +0800 Subject: [PATCH 0057/1371] CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 013b78d9fa..b21f736622 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -441,6 +441,7 @@ assert!(matches!(res, Ok(TryInsertResult::Conflicted))); * Upgrade `sea-query-binder` to `0.4` https://github.com/SeaQL/sea-orm/pull/1562 * Upgrade `sea-schema` to `0.12` https://github.com/SeaQL/sea-orm/pull/1562 * Upgrade `clap` to `4.3` https://github.com/SeaQL/sea-orm/pull/1468 +* Replace `bae` with `sea-bae` https://github.com/SeaQL/sea-orm/pull/1739 ### Bug Fixes From 53caf94af95aa72063bcca1b0b9331417830b54e Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Tue, 30 May 2023 11:29:02 +0800 Subject: [PATCH 0058/1371] Update many with returning --- src/executor/update.rs | 40 +++++++++++++ tests/returning_tests.rs | 123 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 161 insertions(+), 2 deletions(-) diff --git a/src/executor/update.rs b/src/executor/update.rs index 26fe58a834..d2d772ec92 100644 --- a/src/executor/update.rs +++ b/src/executor/update.rs @@ -45,6 +45,16 @@ where { Updater::new(self.query).exec(db).await } + + /// Execute an update operation and return the updated model (use `RETURNING` syntax if database supported) + pub async fn exec_with_returning(self, db: &'a C) -> Result, DbErr> + where + C: ConnectionTrait, + { + Updater::new(self.query) + .exec_update_with_returning::(db) + .await + } } impl Updater { @@ -123,6 +133,36 @@ impl Updater { } } + async fn exec_update_with_returning(mut self, db: &C) -> Result, DbErr> + where + E: EntityTrait, + C: ConnectionTrait, + { + if self.is_noop() { + return Ok(vec![]); + } + + match db.support_returning() { + true => { + let returning = + Query::returning().exprs(E::Column::iter().map(|c| c.select_as(Expr::col(c)))); + self.query.returning(returning); + let db_backend = db.get_database_backend(); + let models: Vec = SelectorRaw::>::from_statement( + db_backend.build(&self.query), + ) + .all(db) + .await?; + // If we got an empty Vec then we are updating a row that does not exist. + match models.is_empty() { + true => Err(DbErr::RecordNotUpdated), + false => Ok(models), + } + } + false => unimplemented!("Database backend doesn't support RETURNING"), + } + } + fn is_noop(&self) -> bool { self.query.get_values().is_empty() } diff --git a/tests/returning_tests.rs b/tests/returning_tests.rs index f6025c740d..fe0ca45833 100644 --- a/tests/returning_tests.rs +++ b/tests/returning_tests.rs @@ -1,8 +1,9 @@ pub mod common; pub use common::{bakery_chain::*, setup::*, TestContext}; -use sea_orm::entity::prelude::*; -use sea_query::Query; +pub use sea_orm::{entity::prelude::*, *}; +pub use sea_query::{Expr, Query}; +use serde_json::json; #[sea_orm_macros::test] #[cfg(any( @@ -66,3 +67,121 @@ async fn main() -> Result<(), DbErr> { Ok(()) } + +#[sea_orm_macros::test] +#[cfg(any( + feature = "sqlx-mysql", + feature = "sqlx-sqlite", + feature = "sqlx-postgres" +))] +#[cfg_attr( + any(feature = "sqlx-mysql", feature = "sqlx-sqlite"), + should_panic(expected = "Database backend doesn't support RETURNING") +)] +async fn update_many() { + pub use common::{features::*, setup::*, TestContext}; + use edit_log::*; + + let run = || async { + let ctx = TestContext::new("returning_tests_update_many").await; + let db = &ctx.db; + + create_tables(db).await?; + + Entity::insert( + Model { + id: 1, + action: "before_save".into(), + values: json!({ "id": "unique-id-001" }), + } + .into_active_model(), + ) + .exec(db) + .await?; + + Entity::insert( + Model { + id: 2, + action: "before_save".into(), + values: json!({ "id": "unique-id-002" }), + } + .into_active_model(), + ) + .exec(db) + .await?; + + Entity::insert( + Model { + id: 3, + action: "before_save".into(), + values: json!({ "id": "unique-id-003" }), + } + .into_active_model(), + ) + .exec(db) + .await?; + + assert_eq!( + Entity::find().all(db).await?, + [ + Model { + id: 1, + action: "before_save".into(), + values: json!({ "id": "unique-id-001" }), + }, + Model { + id: 2, + action: "before_save".into(), + values: json!({ "id": "unique-id-002" }), + }, + Model { + id: 3, + action: "before_save".into(), + values: json!({ "id": "unique-id-003" }), + }, + ] + ); + + // Update many with returning + assert_eq!( + Entity::update_many() + .col_expr( + Column::Values, + Expr::value(json!({ "remarks": "save log" })) + ) + .filter(Column::Action.eq("before_save")) + .exec_with_returning(db) + .await?, + [ + Model { + id: 1, + action: "before_save".into(), + values: json!({ "remarks": "save log" }), + }, + Model { + id: 2, + action: "before_save".into(), + values: json!({ "remarks": "save log" }), + }, + Model { + id: 3, + action: "before_save".into(), + values: json!({ "remarks": "save log" }), + }, + ] + ); + + // No-op + assert_eq!( + Entity::update_many() + .filter(Column::Action.eq("before_save")) + .exec_with_returning(db) + .await?, + [] + ); + + Result::<(), DbErr>::Ok(()) + }; + + run().await.unwrap(); +} From d7a978642ef60253414cf3b5f5c599a26ebfa373 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Mon, 10 Jul 2023 19:51:33 +0800 Subject: [PATCH 0059/1371] Do not throw RecordNotUpdated error --- src/executor/update.rs | 6 +----- tests/returning_tests.rs | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/executor/update.rs b/src/executor/update.rs index d2d772ec92..aef8b4d5e8 100644 --- a/src/executor/update.rs +++ b/src/executor/update.rs @@ -153,11 +153,7 @@ impl Updater { ) .all(db) .await?; - // If we got an empty Vec then we are updating a row that does not exist. - match models.is_empty() { - true => Err(DbErr::RecordNotUpdated), - false => Ok(models), - } + Ok(models) } false => unimplemented!("Database backend doesn't support RETURNING"), } diff --git a/tests/returning_tests.rs b/tests/returning_tests.rs index fe0ca45833..bcdcc38d6b 100644 --- a/tests/returning_tests.rs +++ b/tests/returning_tests.rs @@ -1,7 +1,7 @@ pub mod common; pub use common::{bakery_chain::*, setup::*, TestContext}; -pub use sea_orm::{entity::prelude::*, *}; +use sea_orm::{entity::prelude::*, IntoActiveModel}; pub use sea_query::{Expr, Query}; use serde_json::json; From 727d83c9d7fcf510b7d499ca4ced0f33c31ed48f Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Mon, 10 Jul 2023 21:55:26 +0800 Subject: [PATCH 0060/1371] Docs --- src/executor/update.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/executor/update.rs b/src/executor/update.rs index aef8b4d5e8..cf8938a579 100644 --- a/src/executor/update.rs +++ b/src/executor/update.rs @@ -47,6 +47,10 @@ where } /// Execute an update operation and return the updated model (use `RETURNING` syntax if database supported) + /// + /// # Panics + /// + /// Panics if the database backend does not support `UPDATE RETURNING`. pub async fn exec_with_returning(self, db: &'a C) -> Result, DbErr> where C: ConnectionTrait, From bdddb3163d3fe86707e42ecabd3b67f7dcd4c82b Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Mon, 10 Jul 2023 21:57:52 +0800 Subject: [PATCH 0061/1371] Changelog --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b21f736622..4d0c6fc0ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -432,6 +432,13 @@ assert!(matches!(res, Err(DbErr::RecordNotInserted))); let res = Entity::insert_many([..]).on_conflict(on).do_nothing().exec(db).await; assert!(matches!(res, Ok(TryInsertResult::Conflicted))); ``` +* Added `UpdateMany::exec_with_returning()` https://github.com/SeaQL/sea-orm/pull/1677 +```rust +Entity::update_many() + .col_expr(Column::Values, Expr::expr(..)) + .exec_with_returning(db) + .await +``` ### Upgrades From df390ff7ced22a9e9496dfdb4a83720cac31be26 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Wed, 12 Jul 2023 15:21:39 +0800 Subject: [PATCH 0062/1371] CHANGELOG --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d0c6fc0ea..e7564158d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -217,7 +217,7 @@ pub enum RelatedEntity { The DeriveRelatedEntity derive macro will implement `seaography::RelationBuilder` for `RelatedEntity` enumeration when the `seaography` feature is enabled -* Add `expr`, `exprs` and `expr_as` methods to `QuerySelect` trait +* Add `expr`, `exprs` and `expr_as` methods to `QuerySelect` trait https://github.com/SeaQL/sea-orm/pull/1702 ```rs use sea_orm::sea_query::Expr; use sea_orm::{entity::*, tests_cfg::cake, DbBackend, QuerySelect, QueryTrait}; @@ -275,7 +275,7 @@ assert!(matches!( Some(SqlErr::ForeignKeyConstraintViolation(_)) )); ``` -* Add `Select::find_with_linked`, similar to `find_with_related`: +* Add `Select::find_with_linked`, similar to `find_with_related`: https://github.com/SeaQL/sea-orm/pull/1728, https://github.com/SeaQL/sea-orm/pull/1743 ```rust fn find_with_related(self, r: R) -> SelectTwoMany where R: EntityTrait, E: Related; From 4c682539b6728b8b4310334c14b65a0f1b3a5678 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Wed, 12 Jul 2023 17:03:28 +0800 Subject: [PATCH 0063/1371] CHANGELOG --- CHANGELOG.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7564158d4..fa78e1286d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### New Features * Supports for partial select of `Option` model field. A `None` value will be filled when the select result does not contain the `Option` field without throwing an error. https://github.com/SeaQL/sea-orm/pull/1513 -```rs +```rust customer::ActiveModel { name: Set("Alice".to_owned()), notes: Set(Some("Want to communicate with Bob".to_owned())), @@ -39,7 +39,7 @@ assert_eq!(customers.notes, None); ``` * [sea-orm-cli] the `migrate init` command will create a `.gitignore` file when the migration folder reside in a Git repository https://github.com/SeaQL/sea-orm/pull/1334 * Added `MigratorTrait::migration_table_name()` method to configure the name of migration table https://github.com/SeaQL/sea-orm/pull/1511 -```rs +```rust #[async_trait::async_trait] impl MigratorTrait for Migrator { fn migrations() -> Vec> { @@ -56,7 +56,7 @@ impl MigratorTrait for Migrator { } ``` * Added option to construct chained AND / OR join on condition https://github.com/SeaQL/sea-orm/pull/1433 -```rs +```rust use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] @@ -90,7 +90,7 @@ pub enum Relation { impl ActiveModelBehavior for ActiveModel {} ``` You can also override it in custom join. -```rs +```rust assert_eq!( cake::Entity::find() .column_as( @@ -128,7 +128,7 @@ assert_eq!( * Implemented `IdentityOf` for tuple of `ColumnTrait` with length up to 12 https://github.com/SeaQL/sea-orm/pull/1508 * Implemented `TryGetableMany` for tuple of `TryGetable` with length up to 12 https://github.com/SeaQL/sea-orm/pull/1508 * Implemented `TryFromU64` for tuple of `TryFromU64` with length up to 12 https://github.com/SeaQL/sea-orm/pull/1508 -```rs +```rust use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, DeriveEntityModel)] @@ -169,7 +169,7 @@ pub enum Relation {} impl ActiveModelBehavior for ActiveModel {} ``` * Added macro `DerivePartialModel` https://github.com/SeaQL/sea-orm/pull/1597 -```rs +```rust #[derive(DerivePartialModel, FromQueryResult)] #[sea_orm(entity = "Cake")] struct PartialCake { @@ -218,7 +218,7 @@ pub enum RelatedEntity { The DeriveRelatedEntity derive macro will implement `seaography::RelationBuilder` for `RelatedEntity` enumeration when the `seaography` feature is enabled * Add `expr`, `exprs` and `expr_as` methods to `QuerySelect` trait https://github.com/SeaQL/sea-orm/pull/1702 -```rs +```rust use sea_orm::sea_query::Expr; use sea_orm::{entity::*, tests_cfg::cake, DbBackend, QuerySelect, QueryTrait}; @@ -255,7 +255,7 @@ assert_eq!( ); ``` * Add `DbErr::sql_err()` method to convert error into common database errors `SqlErr`, such as unique constraint or foreign key violation errors. https://github.com/SeaQL/sea-orm/pull/1707 -```rs +```rust assert!(matches!( cake .into_active_model() @@ -285,7 +285,7 @@ fn find_with_linked(self, l: L) -> SelectTwoMany // boths yields `Vec<(E::Model, Vec)>` ``` * Add `DeriveValueType` derive macro for custom wrapper types, implementations of the required traits will be provided, you can customize the `column_type` and `array_type` if needed https://github.com/SeaQL/sea-orm/pull/1720 -```rs +```rust #[derive(DeriveValueType)] #[sea_orm(array_type = "Int")] pub struct Integer(i32); @@ -298,7 +298,7 @@ pub struct Boolbean(pub String); pub struct StringVec(pub Vec); ``` The expanded code of `DeriveValueType` looks like. -```rs +```rust #[derive(DeriveValueType)] pub struct StringVec(pub Vec); @@ -339,7 +339,7 @@ impl sea_orm::sea_query::ValueType for StringVec { } ``` * Add `DeriveDisplay` derive macro to implements `std::fmt::Display` for active enum https://github.com/SeaQL/sea-orm/pull/1726 -```rs +```rust // String enum #[derive(EnumIter, DeriveActiveEnum, DeriveDisplay)] #[sea_orm(rs_type = "String", db_type = "String(Some(1))", enum_name = "category")] @@ -383,7 +383,7 @@ assert_eq!(format!("{}", DisplayTea::EverydayTea), "Everyday"); ### Enhancements * Added `Migration::name()` and `Migration::status()` getters for the name and status of `sea_orm_migration::Migration` https://github.com/SeaQL/sea-orm/pull/1519 -```rs +```rust let migrations = Migrator::get_pending_migrations(db).await?; assert_eq!(migrations.len(), 5); @@ -522,7 +522,7 @@ CREATE TABLE users_saved_bills ``` * [sea-orm-cli] fixed entity generation includes partitioned tables https://github.com/SeaQL/sea-orm/issues/1582, https://github.com/SeaQL/sea-schema/pull/105 * Fixed `ActiveEnum::db_type()` return type does not implement `ColumnTypeTrait` https://github.com/SeaQL/sea-orm/pull/1576 -```rs +```rust impl ColumnTrait for Column { type EntityName = Entity; fn def(&self) -> ColumnDef { @@ -555,14 +555,14 @@ impl ColumnTrait for Column { * Re-export `sea_orm::ConnectionTrait` in `sea_orm_migration::prelude` https://github.com/SeaQL/sea-orm/pull/1577 * Support generic structs in `FromQueryResult` derive macro https://github.com/SeaQL/sea-orm/pull/1464, https://github.com/SeaQL/sea-orm/pull/1603 -```rs +```rust #[derive(FromQueryResult)] struct GenericTest { foo: i32, bar: T, } ``` -```rs +```rust trait MyTrait { type Item: TryGetable; } @@ -593,7 +593,7 @@ where * Fixes `DeriveActiveEnum` (by qualifying `ColumnTypeTrait::def`) https://github.com/SeaQL/sea-orm/issues/1478 * The CLI command `sea-orm-cli generate entity -u ''` will now generate the following code for each `Binary` or `VarBinary` columns in compact format https://github.com/SeaQL/sea-orm/pull/1529 -```rs +```rust #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] #[sea_orm(table_name = "binary")] pub struct Model { @@ -614,7 +614,7 @@ pub struct Model { } ``` * The CLI command `sea-orm-cli generate entity -u '' --expanded-format` will now generate the following code for each `Binary` or `VarBinary` columns in expanded format https://github.com/SeaQL/sea-orm/pull/1529 -```rs +```rust impl ColumnTrait for Column { type EntityName = Entity; fn def(&self) -> ColumnDef { From 1b903f73d81ba03be516b2e0fd9076dc5f12a0c3 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Wed, 12 Jul 2023 17:37:25 +0800 Subject: [PATCH 0064/1371] CHANGELOG --- CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa78e1286d..50db6679f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -444,10 +444,10 @@ Entity::update_many() * Upgrade `heck` dependency in `sea-orm-macros` and `sea-orm-codegen` to 0.4 https://github.com/SeaQL/sea-orm/pull/1520, https://github.com/SeaQL/sea-orm/pull/1544 * Upgrade `strum` to 0.24 -* Upgrade `sea-query` to `0.29` https://github.com/SeaQL/sea-orm/pull/1562 -* Upgrade `sea-query-binder` to `0.4` https://github.com/SeaQL/sea-orm/pull/1562 -* Upgrade `sea-schema` to `0.12` https://github.com/SeaQL/sea-orm/pull/1562 -* Upgrade `clap` to `4.3` https://github.com/SeaQL/sea-orm/pull/1468 +* Upgrade `sea-query` to 0.29 https://github.com/SeaQL/sea-orm/pull/1562 +* Upgrade `sea-query-binder` to 0.4 https://github.com/SeaQL/sea-orm/pull/1562 +* Upgrade `sea-schema` to 0.12 https://github.com/SeaQL/sea-orm/pull/1562 +* Upgrade `clap` to 4.3 https://github.com/SeaQL/sea-orm/pull/1468 * Replace `bae` with `sea-bae` https://github.com/SeaQL/sea-orm/pull/1739 ### Bug Fixes From a55162e3b89541958f543175b1af0da529cb5b3d Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Wed, 12 Jul 2023 18:41:19 +0800 Subject: [PATCH 0065/1371] CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50db6679f8..25d52147ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -546,7 +546,7 @@ impl ColumnTrait for Column { * The derive macro `EnumIter` is now shipped by `sea-orm-macros` * Added a new variant `Many` to `Identity` https://github.com/SeaQL/sea-orm/pull/1508 * Replace the use of `SeaRc` where `T` isn't `dyn Iden` with `RcOrArc` https://github.com/SeaQL/sea-orm/pull/1661 -* Enabled `hashable-value` feature in SeaQuery, thus `Value::Float(NaN) == Value::Float(NaN)` would be true +* Enabled `hashable-value` feature in SeaQuery, thus `Value::Float(NaN) == Value::Float(NaN)` would be true https://github.com/SeaQL/sea-orm/pull/1728, https://github.com/SeaQL/sea-orm/pull/1743 * The `DeriveActiveEnum` derive macro no longer provide `std::fmt::Display` implementation for the enum. You need to derive an extra `DeriveDisplay` macro alongside with `DeriveActiveEnum` derive macro. https://github.com/SeaQL/sea-orm/pull/1726 ## 0.11.3 - 2023-04-24 From 288f73de21c36d40968740a7c78b85c0c150205d Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Wed, 12 Jul 2023 19:05:58 +0800 Subject: [PATCH 0066/1371] Upgrade `strum` to v0.25 (#1752) * Upgrade `strum` to v0.25 * CHANGELOG --------- Co-authored-by: Chris Tsang --- CHANGELOG.md | 10 +++++----- Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 25d52147ac..b044d4343a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -443,11 +443,11 @@ Entity::update_many() ### Upgrades * Upgrade `heck` dependency in `sea-orm-macros` and `sea-orm-codegen` to 0.4 https://github.com/SeaQL/sea-orm/pull/1520, https://github.com/SeaQL/sea-orm/pull/1544 -* Upgrade `strum` to 0.24 -* Upgrade `sea-query` to 0.29 https://github.com/SeaQL/sea-orm/pull/1562 -* Upgrade `sea-query-binder` to 0.4 https://github.com/SeaQL/sea-orm/pull/1562 -* Upgrade `sea-schema` to 0.12 https://github.com/SeaQL/sea-orm/pull/1562 -* Upgrade `clap` to 4.3 https://github.com/SeaQL/sea-orm/pull/1468 +* Upgrade `strum` to 0.25 https://github.com/SeaQL/sea-orm/pull/1752 +* Upgrade `sea-query` to `0.29` https://github.com/SeaQL/sea-orm/pull/1562 +* Upgrade `sea-query-binder` to `0.4` https://github.com/SeaQL/sea-orm/pull/1562 +* Upgrade `sea-schema` to `0.12` https://github.com/SeaQL/sea-orm/pull/1562 +* Upgrade `clap` to `4.3` https://github.com/SeaQL/sea-orm/pull/1468 * Replace `bae` with `sea-bae` https://github.com/SeaQL/sea-orm/pull/1739 ### Bug Fixes diff --git a/Cargo.toml b/Cargo.toml index 037de27a82..bc04951e09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ bigdecimal = { version = "0.3", default-features = false, optional = true } sea-orm-macros = { version = "0.12.0-rc.4", path = "sea-orm-macros", default-features = false, features = ["strum"] } sea-query = { version = "0.29.0-rc.2", features = ["thread-safe", "hashable-value"] } sea-query-binder = { version = "0.4.0-rc.2", default-features = false, optional = true } -strum = { version = "0.24", default-features = false } +strum = { version = "0.25", default-features = false } serde = { version = "1.0", default-features = false } serde_json = { version = "1.0", default-features = false, optional = true } sqlx = { version = "0.6", default-features = false, optional = true } From d09ca3ea778ef708e9dcd828d162bc22ff7fddc9 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Wed, 12 Jul 2023 19:10:37 +0800 Subject: [PATCH 0067/1371] CHANGELOG --- CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b044d4343a..915008b78f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -444,10 +444,10 @@ Entity::update_many() * Upgrade `heck` dependency in `sea-orm-macros` and `sea-orm-codegen` to 0.4 https://github.com/SeaQL/sea-orm/pull/1520, https://github.com/SeaQL/sea-orm/pull/1544 * Upgrade `strum` to 0.25 https://github.com/SeaQL/sea-orm/pull/1752 -* Upgrade `sea-query` to `0.29` https://github.com/SeaQL/sea-orm/pull/1562 -* Upgrade `sea-query-binder` to `0.4` https://github.com/SeaQL/sea-orm/pull/1562 -* Upgrade `sea-schema` to `0.12` https://github.com/SeaQL/sea-orm/pull/1562 -* Upgrade `clap` to `4.3` https://github.com/SeaQL/sea-orm/pull/1468 +* Upgrade `sea-query` to 0.29 https://github.com/SeaQL/sea-orm/pull/1562 +* Upgrade `sea-query-binder` to 0.4 https://github.com/SeaQL/sea-orm/pull/1562 +* Upgrade `sea-schema` to 0.12 https://github.com/SeaQL/sea-orm/pull/1562 +* Upgrade `clap` to 4.3 https://github.com/SeaQL/sea-orm/pull/1468 * Replace `bae` with `sea-bae` https://github.com/SeaQL/sea-orm/pull/1739 ### Bug Fixes From b4c1a692f5faf224fe958c0d9ae78a48022e9a1f Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Wed, 12 Jul 2023 20:26:25 +0800 Subject: [PATCH 0068/1371] Edit --- .github/workflows/release-bot.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release-bot.yml b/.github/workflows/release-bot.yml index 0e6bfe2962..0265c4bfe8 100644 --- a/.github/workflows/release-bot.yml +++ b/.github/workflows/release-bot.yml @@ -17,6 +17,7 @@ jobs: message: | ### :tada: Released In [${releaseTag}](${releaseUrl}) :tada: - Your participation is what makes us unique; your adoption is what drives us forward. - Thank you all in the Rust community for making SeaQL what it is today. :crab: - You are also invited to shape it's future! Please star our repos, every ⭐ counts! + Thank you everyone for the contribution! + This feature is now available in the latest release. Now is a good time to upgrade! + Your participation is what makes us unique; your adoption is what drives us forward. + You can support SeaQL 🌊 by starring our repos, sharing our libraries and becoming a sponsor ⭐. From 866025a733e7a7208bb238368a6262ecb8df59b6 Mon Sep 17 00:00:00 2001 From: darkmmon Date: Thu, 13 Jul 2023 16:28:35 +0800 Subject: [PATCH 0069/1371] implement DeriveIden in sea-orm only (#1740) * WIP, implementing Iden * completed implementation for DeriveIden and added basic test cases * added feature flag to prevent sea-orm::sea-query::DeriveIden from crashing when sea-query is not used * fixed doc test and adjusted test case * enable `sea-query-derive`'s `sea-orm` feature * Bump `sea-query-derive` to v0.4 * Update Cargo.toml * Update Cargo.toml * adjusted test cases and updated so that iden attribute will not be snake cased * Update Cargo.toml * Update main.rs --------- Co-authored-by: Billy Chan Co-authored-by: Chris Tsang --- Cargo.toml | 2 +- issues/1473/Cargo.toml | 13 ++ issues/1473/src/main.rs | 17 +++ sea-orm-macros/src/derives/derive_iden.rs | 146 ++++++++++++++++++++++ sea-orm-macros/src/derives/mod.rs | 2 + sea-orm-macros/src/lib.rs | 42 +++++++ src/entity/column.rs | 2 +- src/lib.rs | 8 +- tests/derive_iden_tests.rs | 46 +++++++ 9 files changed, 271 insertions(+), 7 deletions(-) create mode 100644 issues/1473/Cargo.toml create mode 100644 issues/1473/src/main.rs create mode 100644 sea-orm-macros/src/derives/derive_iden.rs create mode 100644 tests/derive_iden_tests.rs diff --git a/Cargo.toml b/Cargo.toml index bc04951e09..d42f79e177 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ tracing = { version = "0.1", default-features = false, features = ["attributes", rust_decimal = { version = "1", default-features = false, optional = true } bigdecimal = { version = "0.3", default-features = false, optional = true } sea-orm-macros = { version = "0.12.0-rc.4", path = "sea-orm-macros", default-features = false, features = ["strum"] } -sea-query = { version = "0.29.0-rc.2", features = ["thread-safe", "hashable-value"] } +sea-query = { version = "0.29.0", features = ["thread-safe", "hashable-value"] } sea-query-binder = { version = "0.4.0-rc.2", default-features = false, optional = true } strum = { version = "0.25", default-features = false } serde = { version = "1.0", default-features = false } diff --git a/issues/1473/Cargo.toml b/issues/1473/Cargo.toml new file mode 100644 index 0000000000..ae5bdae575 --- /dev/null +++ b/issues/1473/Cargo.toml @@ -0,0 +1,13 @@ +[workspace] +# A separate workspace + +[package] +name = "sea-orm-issues-1473" +version = "0.1.0" +edition = "2021" +publish = false + +[dependencies.sea-orm] +path = "../../" +default-features = false +features = ["macros", "runtime-tokio-native-tls"] diff --git a/issues/1473/src/main.rs b/issues/1473/src/main.rs new file mode 100644 index 0000000000..ed691b8258 --- /dev/null +++ b/issues/1473/src/main.rs @@ -0,0 +1,17 @@ +use sea_orm::{sea_query::{self, Iden}}; + +#[derive(Iden)] +enum Character { + Table, + Id, +} + +#[derive(Iden)] +struct Glyph; + +fn main() { + assert_eq!(Character::Table.to_string(), "character"); + assert_eq!(Character::Id.to_string(), "id"); + + assert_eq!(Glyph.to_string(), "glyph"); +} diff --git a/sea-orm-macros/src/derives/derive_iden.rs b/sea-orm-macros/src/derives/derive_iden.rs new file mode 100644 index 0000000000..026a97b1cf --- /dev/null +++ b/sea-orm-macros/src/derives/derive_iden.rs @@ -0,0 +1,146 @@ +use heck::ToSnakeCase; +use proc_macro2::{self, TokenStream}; +use quote::{quote, quote_spanned}; +use syn::{ + punctuated::Punctuated, DataEnum, DataStruct, DeriveInput, Expr, Fields, LitStr, Variant, +}; + +fn must_be_valid_iden(name: &str) -> bool { + // can only begin with [a-z_] + name.chars() + .take(1) + .all(|c| c == '_' || c.is_ascii_alphabetic()) + && name.chars().all(|c| c == '_' || c.is_ascii_alphanumeric()) +} + +fn impl_iden_for_unit_struct( + ident: &proc_macro2::Ident, + new_iden: &str, +) -> proc_macro2::TokenStream { + let prepare = if must_be_valid_iden(new_iden) { + quote! { + fn prepare(&self, s: &mut dyn ::std::fmt::Write, q: sea_orm::sea_query::Quote) { + write!(s, "{}", q.left()).unwrap(); + self.unquoted(s); + write!(s, "{}", q.right()).unwrap(); + } + } + } else { + quote! {} + }; + quote! { + impl sea_orm::sea_query::Iden for #ident { + #prepare + + fn unquoted(&self, s: &mut dyn ::std::fmt::Write) { + write!(s, #new_iden).unwrap(); + } + } + } +} + +fn impl_iden_for_enum( + ident: &proc_macro2::Ident, + variants: Punctuated, +) -> proc_macro2::TokenStream { + let variants = variants.iter(); + let mut all_valid = true; + + let match_pair: Vec = variants + .map(|v| { + let var_ident = &v.ident; + let mut var_name = var_ident.to_string().to_snake_case(); + v.attrs + .iter() + .filter(|attr| attr.path().is_ident("sea_orm")) + .try_for_each(|attr| { + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("iden") { + let litstr: LitStr = meta.value()?.parse()?; + var_name = litstr.value(); + all_valid &= must_be_valid_iden(var_name.as_str()); + } else { + // Reads the value expression to advance the parse stream. + // Some parameters do not have any value, + // so ignoring an error occurred here. + let _: Option = meta.value().and_then(|v| v.parse()).ok(); + } + Ok(()) + }) + }) + .expect("something something"); + quote! { Self::#var_ident => write!(s, "{}", #var_name).unwrap() } + }) + .collect(); + + let match_arms: TokenStream = quote! { #(#match_pair),* }; + + let prepare = if all_valid { + quote! { + fn prepare(&self, s: &mut dyn ::std::fmt::Write, q: sea_orm::sea_query::Quote) { + write!(s, "{}", q.left()).unwrap(); + self.unquoted(s); + write!(s, "{}", q.right()).unwrap(); + } + } + } else { + quote! {} + }; + + quote! { + impl sea_orm::sea_query::Iden for #ident { + #prepare + + fn unquoted(&self, s: &mut dyn ::std::fmt::Write) { + match self { + #match_arms + }; + } + } + } +} + +pub fn expand_derive_iden(input: DeriveInput) -> syn::Result { + let DeriveInput { ident, data, .. } = input; + + let mut new_iden: TokenStream = ident.to_string().to_snake_case().parse().unwrap(); + input + .attrs + .iter() + .filter(|attr| attr.path().is_ident("sea_orm")) + .try_for_each(|attr| { + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("iden") { + let litstr: LitStr = meta.value()?.parse()?; + new_iden = syn::parse_str::(&litstr.value())?; + } else { + // Reads the value expression to advance the parse stream. + // Some parameters do not have any value, + // so ignoring an error occurred here. + let _: Option = meta.value().and_then(|v| v.parse()).ok(); + } + Ok(()) + }) + })?; + + // Currently we only support enums and unit structs + match data { + syn::Data::Enum(DataEnum { variants, .. }) => { + if variants.is_empty() { + Ok(TokenStream::new()) + } else { + Ok(impl_iden_for_enum(&ident, variants)) + } + } + syn::Data::Struct(DataStruct { + fields: Fields::Unit, + .. + }) => Ok(impl_iden_for_unit_struct( + &ident, + new_iden.to_string().as_str(), + )), + _ => Ok(quote_spanned! { + ident.span() => compile_error!("you can only derive DeriveIden on unit struct or enum"); + }), + } +} diff --git a/sea-orm-macros/src/derives/mod.rs b/sea-orm-macros/src/derives/mod.rs index 168f94eeb8..cc63ebb65f 100644 --- a/sea-orm-macros/src/derives/mod.rs +++ b/sea-orm-macros/src/derives/mod.rs @@ -4,6 +4,7 @@ mod active_model; mod active_model_behavior; mod attributes; mod column; +mod derive_iden; mod entity; mod entity_model; mod from_query_result; @@ -24,6 +25,7 @@ pub use active_enum_display::*; pub use active_model::*; pub use active_model_behavior::*; pub use column::*; +pub use derive_iden::*; pub use entity::*; pub use entity_model::*; pub use from_query_result::*; diff --git a/sea-orm-macros/src/lib.rs b/sea-orm-macros/src/lib.rs index c9d84d4a3c..85a9fee622 100644 --- a/sea-orm-macros/src/lib.rs +++ b/sea-orm-macros/src/lib.rs @@ -852,3 +852,45 @@ pub fn derive_active_enum_display(input: TokenStream) -> TokenStream { Err(e) => e.to_compile_error().into(), } } + +/// The DeriveIden derive macro will implement `sea_orm::sea_query::Iden` for simplify Iden implementation. +/// +/// ## Usage +/// +/// ```rust +/// use sea_orm::DeriveIden; +/// +/// #[derive(DeriveIden)] +/// pub enum Class { +/// Id, +/// Title, +/// Text, +/// } +/// +/// #[derive(DeriveIden)] +/// struct Glyph; +/// ``` +/// +/// You can use iden = "" to customize the name +/// ``` +/// use sea_orm::DeriveIden; +/// +/// #[derive(DeriveIden)] +/// pub enum Class { +/// Id, +/// #[sea_orm(iden = "turtle")] +/// Title, +/// #[sea_orm(iden = "TeXt")] +/// Text, +/// } +/// ``` +#[cfg(feature = "derive")] +#[proc_macro_derive(DeriveIden, attributes(sea_orm))] +pub fn derive_iden(input: TokenStream) -> TokenStream { + let derive_input = parse_macro_input!(input as DeriveInput); + + match derives::expand_derive_iden(derive_input) { + Ok(token_stream) => token_stream.into(), + Err(e) => e.to_compile_error().into(), + } +} diff --git a/src/entity/column.rs b/src/entity/column.rs index e2ade89b5b..b85bcb6127 100644 --- a/src/entity/column.rs +++ b/src/entity/column.rs @@ -1,4 +1,4 @@ -use crate::{EntityName, IdenStatic, IntoSimpleExpr, Iterable}; +use crate::{self as sea_orm, EntityName, IdenStatic, IntoSimpleExpr, Iterable}; use sea_query::{ Alias, BinOper, DynIden, Expr, Iden, IntoIden, SeaRc, SelectStatement, SimpleExpr, Value, }; diff --git a/src/lib.rs b/src/lib.rs index c01c2fe75f..cdf5bb3dae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -349,15 +349,13 @@ pub use schema::*; #[cfg(feature = "macros")] pub use sea_orm_macros::{ DeriveActiveEnum, DeriveActiveModel, DeriveActiveModelBehavior, DeriveColumn, - DeriveCustomColumn, DeriveDisplay, DeriveEntity, DeriveEntityModel, DeriveIntoActiveModel, - DeriveMigrationName, DeriveModel, DerivePartialModel, DerivePrimaryKey, DeriveRelatedEntity, - DeriveRelation, FromJsonQueryResult, FromQueryResult, + DeriveCustomColumn, DeriveDisplay, DeriveEntity, DeriveEntityModel, DeriveIden, + DeriveIntoActiveModel, DeriveMigrationName, DeriveModel, DerivePartialModel, DerivePrimaryKey, + DeriveRelatedEntity, DeriveRelation, FromJsonQueryResult, FromQueryResult, }; pub use sea_query; pub use sea_query::Iden; -#[cfg(feature = "macros")] -pub use sea_query::Iden as DeriveIden; pub use sea_orm_macros::EnumIter; pub use strum; diff --git a/tests/derive_iden_tests.rs b/tests/derive_iden_tests.rs new file mode 100644 index 0000000000..707a69f1ff --- /dev/null +++ b/tests/derive_iden_tests.rs @@ -0,0 +1,46 @@ +pub mod common; +pub use common::{features::*, setup::*, TestContext}; +use sea_orm::entity::prelude::*; +use sea_orm_macros::DeriveIden; + +#[derive(DeriveIden)] +pub enum Class { + Id, + Title, + Text, +} + +#[derive(DeriveIden)] +pub enum Book { + Id, + #[sea_orm(iden = "turtle")] + Title, + #[sea_orm(iden = "TeXt")] + Text, + #[sea_orm(iden = "ty_pe")] + Type, +} + +#[derive(DeriveIden)] +struct Glyph; + +#[derive(DeriveIden)] +#[sea_orm(iden = "weRd")] +struct Word; + +#[test] +fn main() -> Result<(), DbErr> { + assert_eq!(Class::Id.to_string(), "id"); + assert_eq!(Class::Title.to_string(), "title"); + assert_eq!(Class::Text.to_string(), "text"); + + assert_eq!(Book::Id.to_string(), "id"); + assert_eq!(Book::Title.to_string(), "turtle"); + assert_eq!(Book::Text.to_string(), "TeXt"); + assert_eq!(Book::Type.to_string(), "ty_pe"); + + assert_eq!(Glyph.to_string(), "glyph"); + + assert_eq!(Word.to_string(), "weRd"); + Ok(()) +} From ae899d67441ea54cc62aae75b757614278f9789b Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 13 Jul 2023 10:25:59 +0100 Subject: [PATCH 0070/1371] Remove sea-query's Iden macro --- CHANGELOG.md | 28 +++++++++++++++++++ Cargo.toml | 4 +-- .../src/m20220120_000001_create_post_table.rs | 3 +- .../src/m20220120_000001_create_post_table.rs | 3 +- .../src/m20220120_000001_create_post_table.rs | 3 +- .../src/m20220101_000001_create_table.rs | 3 +- .../src/m20220120_000001_create_post_table.rs | 3 +- .../src/m20220120_000001_create_post_table.rs | 3 +- .../src/m20220120_000001_create_post_table.rs | 3 +- .../src/m20220120_000001_create_post_table.rs | 3 +- .../src/m20220120_000001_create_post_table.rs | 3 +- .../src/m20220120_000001_create_post_table.rs | 3 +- issues/1473/Cargo.toml | 2 +- issues/1473/src/main.rs | 7 ++--- .../src/m20220101_000001_create_table.rs | 3 +- sea-orm-migration/src/migrator.rs | 21 +++++++------- sea-orm-migration/src/prelude.rs | 2 +- .../m20220118_000001_create_cake_table.rs | 3 +- .../m20220118_000002_create_fruit_table.rs | 3 +- .../m20220118_000004_create_tea_enum.rs | 7 ++--- .../m20220923_000001_seed_cake_table.rs | 3 +- src/entity/active_enum.rs | 6 ++-- src/entity/column.rs | 20 +++++++++---- src/entity/prelude.rs | 5 ++-- 24 files changed, 83 insertions(+), 61 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 915008b78f..0bd07bba94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -548,6 +548,34 @@ impl ColumnTrait for Column { * Replace the use of `SeaRc` where `T` isn't `dyn Iden` with `RcOrArc` https://github.com/SeaQL/sea-orm/pull/1661 * Enabled `hashable-value` feature in SeaQuery, thus `Value::Float(NaN) == Value::Float(NaN)` would be true https://github.com/SeaQL/sea-orm/pull/1728, https://github.com/SeaQL/sea-orm/pull/1743 * The `DeriveActiveEnum` derive macro no longer provide `std::fmt::Display` implementation for the enum. You need to derive an extra `DeriveDisplay` macro alongside with `DeriveActiveEnum` derive macro. https://github.com/SeaQL/sea-orm/pull/1726 +* `sea-query/derive` is no longer enabled by `sea-orm`, as such, `Iden` no longer works as a derive macro (it's still a trait). Instead, we are shipping a new macro `DeriveIden`: +```rust +// then: + +#[derive(Iden)] +#[iden = "category"] +pub struct CategoryEnum; + +#[derive(Iden)] +pub enum Tea { + Table, + #[iden = "EverydayTea"] + EverydayTea, +} + +// now: + +#[derive(DeriveIden)] +#[sea_orm(iden = "category")] +pub struct CategoryEnum; + +#[derive(DeriveIden)] +pub enum Tea { + Table, + #[sea_orm(iden = "EverydayTea")] + EverydayTea, +} +``` ## 0.11.3 - 2023-04-24 diff --git a/Cargo.toml b/Cargo.toml index d42f79e177..06f711b6ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ tracing = { version = "0.1", default-features = false, features = ["attributes", rust_decimal = { version = "1", default-features = false, optional = true } bigdecimal = { version = "0.3", default-features = false, optional = true } sea-orm-macros = { version = "0.12.0-rc.4", path = "sea-orm-macros", default-features = false, features = ["strum"] } -sea-query = { version = "0.29.0", features = ["thread-safe", "hashable-value"] } +sea-query = { version = "0.29.0", default-features = false, features = ["thread-safe", "hashable-value", "backend-mysql", "backend-postgres", "backend-sqlite"] } sea-query-binder = { version = "0.4.0-rc.2", default-features = false, optional = true } strum = { version = "0.25", default-features = false } serde = { version = "1.0", default-features = false } @@ -74,7 +74,7 @@ default = [ "with-uuid", "with-time", ] -macros = ["sea-orm-macros/derive", "sea-query/derive"] +macros = ["sea-orm-macros/derive"] mock = [] with-json = ["serde_json", "sea-query/with-json", "chrono?/serde", "time?/serde", "uuid?/serde", "sea-query-binder?/with-json", "sqlx?/json"] with-chrono = ["chrono", "sea-query/with-chrono", "sea-query-binder?/with-chrono", "sqlx?/chrono"] diff --git a/examples/actix3_example/migration/src/m20220120_000001_create_post_table.rs b/examples/actix3_example/migration/src/m20220120_000001_create_post_table.rs index a2fa0219cd..12c9fd7d27 100644 --- a/examples/actix3_example/migration/src/m20220120_000001_create_post_table.rs +++ b/examples/actix3_example/migration/src/m20220120_000001_create_post_table.rs @@ -32,8 +32,7 @@ impl MigrationTrait for Migration { } } -/// Learn more at https://docs.rs/sea-query#iden -#[derive(Iden)] +#[derive(DeriveIden)] enum Posts { Table, Id, diff --git a/examples/actix_example/migration/src/m20220120_000001_create_post_table.rs b/examples/actix_example/migration/src/m20220120_000001_create_post_table.rs index a2fa0219cd..12c9fd7d27 100644 --- a/examples/actix_example/migration/src/m20220120_000001_create_post_table.rs +++ b/examples/actix_example/migration/src/m20220120_000001_create_post_table.rs @@ -32,8 +32,7 @@ impl MigrationTrait for Migration { } } -/// Learn more at https://docs.rs/sea-query#iden -#[derive(Iden)] +#[derive(DeriveIden)] enum Posts { Table, Id, diff --git a/examples/axum_example/migration/src/m20220120_000001_create_post_table.rs b/examples/axum_example/migration/src/m20220120_000001_create_post_table.rs index a2fa0219cd..12c9fd7d27 100644 --- a/examples/axum_example/migration/src/m20220120_000001_create_post_table.rs +++ b/examples/axum_example/migration/src/m20220120_000001_create_post_table.rs @@ -32,8 +32,7 @@ impl MigrationTrait for Migration { } } -/// Learn more at https://docs.rs/sea-query#iden -#[derive(Iden)] +#[derive(DeriveIden)] enum Posts { Table, Id, diff --git a/examples/graphql_example/migration/src/m20220101_000001_create_table.rs b/examples/graphql_example/migration/src/m20220101_000001_create_table.rs index b7ec5a4d6b..22d0f82d01 100644 --- a/examples/graphql_example/migration/src/m20220101_000001_create_table.rs +++ b/examples/graphql_example/migration/src/m20220101_000001_create_table.rs @@ -32,8 +32,7 @@ impl MigrationTrait for Migration { } } -/// Learn more at https://docs.rs/sea-query#iden -#[derive(Iden)] +#[derive(DeriveIden)] enum Notes { Table, Id, diff --git a/examples/jsonrpsee_example/migration/src/m20220120_000001_create_post_table.rs b/examples/jsonrpsee_example/migration/src/m20220120_000001_create_post_table.rs index a2fa0219cd..12c9fd7d27 100644 --- a/examples/jsonrpsee_example/migration/src/m20220120_000001_create_post_table.rs +++ b/examples/jsonrpsee_example/migration/src/m20220120_000001_create_post_table.rs @@ -32,8 +32,7 @@ impl MigrationTrait for Migration { } } -/// Learn more at https://docs.rs/sea-query#iden -#[derive(Iden)] +#[derive(DeriveIden)] enum Posts { Table, Id, diff --git a/examples/poem_example/migration/src/m20220120_000001_create_post_table.rs b/examples/poem_example/migration/src/m20220120_000001_create_post_table.rs index a2fa0219cd..12c9fd7d27 100644 --- a/examples/poem_example/migration/src/m20220120_000001_create_post_table.rs +++ b/examples/poem_example/migration/src/m20220120_000001_create_post_table.rs @@ -32,8 +32,7 @@ impl MigrationTrait for Migration { } } -/// Learn more at https://docs.rs/sea-query#iden -#[derive(Iden)] +#[derive(DeriveIden)] enum Posts { Table, Id, diff --git a/examples/rocket_example/migration/src/m20220120_000001_create_post_table.rs b/examples/rocket_example/migration/src/m20220120_000001_create_post_table.rs index a2fa0219cd..12c9fd7d27 100644 --- a/examples/rocket_example/migration/src/m20220120_000001_create_post_table.rs +++ b/examples/rocket_example/migration/src/m20220120_000001_create_post_table.rs @@ -32,8 +32,7 @@ impl MigrationTrait for Migration { } } -/// Learn more at https://docs.rs/sea-query#iden -#[derive(Iden)] +#[derive(DeriveIden)] enum Posts { Table, Id, diff --git a/examples/rocket_okapi_example/migration/src/m20220120_000001_create_post_table.rs b/examples/rocket_okapi_example/migration/src/m20220120_000001_create_post_table.rs index a2fa0219cd..12c9fd7d27 100644 --- a/examples/rocket_okapi_example/migration/src/m20220120_000001_create_post_table.rs +++ b/examples/rocket_okapi_example/migration/src/m20220120_000001_create_post_table.rs @@ -32,8 +32,7 @@ impl MigrationTrait for Migration { } } -/// Learn more at https://docs.rs/sea-query#iden -#[derive(Iden)] +#[derive(DeriveIden)] enum Posts { Table, Id, diff --git a/examples/salvo_example/migration/src/m20220120_000001_create_post_table.rs b/examples/salvo_example/migration/src/m20220120_000001_create_post_table.rs index a2fa0219cd..12c9fd7d27 100644 --- a/examples/salvo_example/migration/src/m20220120_000001_create_post_table.rs +++ b/examples/salvo_example/migration/src/m20220120_000001_create_post_table.rs @@ -32,8 +32,7 @@ impl MigrationTrait for Migration { } } -/// Learn more at https://docs.rs/sea-query#iden -#[derive(Iden)] +#[derive(DeriveIden)] enum Posts { Table, Id, diff --git a/examples/tonic_example/migration/src/m20220120_000001_create_post_table.rs b/examples/tonic_example/migration/src/m20220120_000001_create_post_table.rs index a2fa0219cd..12c9fd7d27 100644 --- a/examples/tonic_example/migration/src/m20220120_000001_create_post_table.rs +++ b/examples/tonic_example/migration/src/m20220120_000001_create_post_table.rs @@ -32,8 +32,7 @@ impl MigrationTrait for Migration { } } -/// Learn more at https://docs.rs/sea-query#iden -#[derive(Iden)] +#[derive(DeriveIden)] enum Posts { Table, Id, diff --git a/issues/1473/Cargo.toml b/issues/1473/Cargo.toml index ae5bdae575..84e8f15476 100644 --- a/issues/1473/Cargo.toml +++ b/issues/1473/Cargo.toml @@ -10,4 +10,4 @@ publish = false [dependencies.sea-orm] path = "../../" default-features = false -features = ["macros", "runtime-tokio-native-tls"] +features = ["macros", "runtime-tokio-native-tls"] \ No newline at end of file diff --git a/issues/1473/src/main.rs b/issues/1473/src/main.rs index ed691b8258..235b85c5b8 100644 --- a/issues/1473/src/main.rs +++ b/issues/1473/src/main.rs @@ -1,17 +1,16 @@ -use sea_orm::{sea_query::{self, Iden}}; +use sea_orm::{DeriveIden, Iden}; -#[derive(Iden)] +#[derive(DeriveIden)] enum Character { Table, Id, } -#[derive(Iden)] +#[derive(DeriveIden)] struct Glyph; fn main() { assert_eq!(Character::Table.to_string(), "character"); assert_eq!(Character::Id.to_string(), "id"); - assert_eq!(Glyph.to_string(), "glyph"); } diff --git a/sea-orm-cli/template/migration/src/m20220101_000001_create_table.rs b/sea-orm-cli/template/migration/src/m20220101_000001_create_table.rs index b058244632..c8f2a5d161 100644 --- a/sea-orm-cli/template/migration/src/m20220101_000001_create_table.rs +++ b/sea-orm-cli/template/migration/src/m20220101_000001_create_table.rs @@ -38,8 +38,7 @@ impl MigrationTrait for Migration { } } -/// Learn more at https://docs.rs/sea-query#iden -#[derive(Iden)] +#[derive(DeriveIden)] enum Post { Table, Id, diff --git a/sea-orm-migration/src/migrator.rs b/sea-orm-migration/src/migrator.rs index 84adb17f1e..587951ed1c 100644 --- a/sea-orm-migration/src/migrator.rs +++ b/sea-orm-migration/src/migrator.rs @@ -6,12 +6,13 @@ use std::time::SystemTime; use tracing::info; use sea_orm::sea_query::{ - self, extension::postgres::Type, Alias, Expr, ForeignKey, Iden, IntoIden, JoinType, Order, - Query, SelectStatement, SimpleExpr, Table, + self, extension::postgres::Type, Alias, Expr, ForeignKey, IntoIden, JoinType, Order, Query, + SelectStatement, SimpleExpr, Table, }; use sea_orm::{ - ActiveModelTrait, ActiveValue, Condition, ConnectionTrait, DbBackend, DbErr, DynIden, - EntityTrait, FromQueryResult, Iterable, QueryFilter, Schema, Statement, TransactionTrait, + ActiveModelTrait, ActiveValue, Condition, ConnectionTrait, DbBackend, DbErr, DeriveIden, + DynIden, EntityTrait, FromQueryResult, Iterable, QueryFilter, Schema, Statement, + TransactionTrait, }; use sea_schema::{mysql::MySql, postgres::Postgres, probe::SchemaProbe, sqlite::Sqlite}; @@ -457,13 +458,13 @@ where } } -#[derive(Iden)] +#[derive(DeriveIden)] enum InformationSchema { - #[iden = "information_schema"] + #[sea_orm(iden = "information_schema")] Schema, - #[iden = "TABLE_NAME"] + #[sea_orm(iden = "TABLE_NAME")] TableName, - #[iden = "CONSTRAINT_NAME"] + #[sea_orm(iden = "CONSTRAINT_NAME")] ConstraintName, TableConstraints, TableSchema, @@ -500,7 +501,7 @@ where stmt } -#[derive(Iden)] +#[derive(DeriveIden)] enum PgType { Table, Typname, @@ -508,7 +509,7 @@ enum PgType { Typelem, } -#[derive(Iden)] +#[derive(DeriveIden)] enum PgNamespace { Table, Oid, diff --git a/sea-orm-migration/src/prelude.rs b/sea-orm-migration/src/prelude.rs index 9b1030ff3a..3267b1b16f 100644 --- a/sea-orm-migration/src/prelude.rs +++ b/sea-orm-migration/src/prelude.rs @@ -9,5 +9,5 @@ pub use async_trait; pub use sea_orm::{ self, sea_query::{self, *}, - ConnectionTrait, DbErr, DeriveMigrationName, + ConnectionTrait, DbErr, DeriveIden, DeriveMigrationName, }; diff --git a/sea-orm-migration/tests/common/migration/m20220118_000001_create_cake_table.rs b/sea-orm-migration/tests/common/migration/m20220118_000001_create_cake_table.rs index f06513aa62..370697fbfe 100644 --- a/sea-orm-migration/tests/common/migration/m20220118_000001_create_cake_table.rs +++ b/sea-orm-migration/tests/common/migration/m20220118_000001_create_cake_table.rs @@ -38,8 +38,7 @@ impl MigrationTrait for Migration { } } -/// Learn more at https://docs.rs/sea-query#iden -#[derive(Iden)] +#[derive(DeriveIden)] pub enum Cake { Table, Id, diff --git a/sea-orm-migration/tests/common/migration/m20220118_000002_create_fruit_table.rs b/sea-orm-migration/tests/common/migration/m20220118_000002_create_fruit_table.rs index 07ead83499..4b9ceb458c 100644 --- a/sea-orm-migration/tests/common/migration/m20220118_000002_create_fruit_table.rs +++ b/sea-orm-migration/tests/common/migration/m20220118_000002_create_fruit_table.rs @@ -49,8 +49,7 @@ impl MigrationTrait for Migration { } } -/// Learn more at https://docs.rs/sea-query#iden -#[derive(Iden)] +#[derive(DeriveIden)] pub enum Fruit { Table, Id, diff --git a/sea-orm-migration/tests/common/migration/m20220118_000004_create_tea_enum.rs b/sea-orm-migration/tests/common/migration/m20220118_000004_create_tea_enum.rs index ba9b68e6b1..26661d4a1e 100644 --- a/sea-orm-migration/tests/common/migration/m20220118_000004_create_tea_enum.rs +++ b/sea-orm-migration/tests/common/migration/m20220118_000004_create_tea_enum.rs @@ -42,12 +42,11 @@ impl MigrationTrait for Migration { } } -/// Learn more at https://docs.rs/sea-query#iden -#[derive(Iden)] +#[derive(DeriveIden)] pub enum Tea { Table, - #[iden = "EverydayTea"] + #[sea_orm(iden = "EverydayTea")] EverydayTea, - #[iden = "BreakfastTea"] + #[sea_orm(iden = "BreakfastTea")] BreakfastTea, } diff --git a/sea-orm-migration/tests/common/migration/m20220923_000001_seed_cake_table.rs b/sea-orm-migration/tests/common/migration/m20220923_000001_seed_cake_table.rs index 16c019bf82..5ab38ebb7c 100644 --- a/sea-orm-migration/tests/common/migration/m20220923_000001_seed_cake_table.rs +++ b/sea-orm-migration/tests/common/migration/m20220923_000001_seed_cake_table.rs @@ -29,8 +29,7 @@ impl MigrationTrait for Migration { } } -/// Learn more at https://docs.rs/sea-query#iden -#[derive(Iden)] +#[derive(DeriveIden)] pub enum Cake { Table, Name, diff --git a/src/entity/active_enum.rs b/src/entity/active_enum.rs index 99e6351dac..0f56bd7154 100644 --- a/src/entity/active_enum.rs +++ b/src/entity/active_enum.rs @@ -37,7 +37,7 @@ use sea_query::{DynIden, Expr, Nullable, SimpleExpr, Value, ValueType}; /// Small, /// } /// -/// #[derive(Debug, Iden)] +/// #[derive(Debug, DeriveIden)] /// pub struct CategoryEnum; /// /// impl ActiveEnum for Category { @@ -182,8 +182,8 @@ mod tests { Small, } - #[derive(Debug, Iden)] - #[iden = "category"] + #[derive(Debug, DeriveIden)] + #[sea_orm(iden = "category")] pub struct CategoryEnum; impl ActiveEnum for Category { diff --git a/src/entity/column.rs b/src/entity/column.rs index b85bcb6127..b263413fdd 100644 --- a/src/entity/column.rs +++ b/src/entity/column.rs @@ -1,6 +1,6 @@ -use crate::{self as sea_orm, EntityName, IdenStatic, IntoSimpleExpr, Iterable}; +use crate::{EntityName, Iden, IdenStatic, IntoSimpleExpr, Iterable}; use sea_query::{ - Alias, BinOper, DynIden, Expr, Iden, IntoIden, SeaRc, SelectStatement, SimpleExpr, Value, + Alias, BinOper, DynIden, Expr, IntoIden, SeaRc, SelectStatement, SimpleExpr, Value, }; use std::str::FromStr; @@ -381,13 +381,21 @@ impl ColumnDef { } } -#[derive(Iden)] struct Text; - -#[derive(Iden)] -#[iden = "text[]"] struct TextArray; +impl Iden for Text { + fn unquoted(&self, s: &mut dyn std::fmt::Write) { + write!(s, "text").unwrap(); + } +} + +impl Iden for TextArray { + fn unquoted(&self, s: &mut dyn std::fmt::Write) { + write!(s, "text[]").unwrap(); + } +} + fn cast_enum_as(expr: Expr, col: &C, f: F) -> SimpleExpr where C: ColumnTrait, diff --git a/src/entity/prelude.rs b/src/entity/prelude.rs index f335c9c82e..ff6d779c50 100644 --- a/src/entity/prelude.rs +++ b/src/entity/prelude.rs @@ -11,8 +11,9 @@ pub use crate::{ #[cfg(feature = "macros")] pub use crate::{ DeriveActiveEnum, DeriveActiveModel, DeriveActiveModelBehavior, DeriveColumn, - DeriveCustomColumn, DeriveDisplay, DeriveEntity, DeriveEntityModel, DeriveIntoActiveModel, - DeriveModel, DerivePrimaryKey, DeriveRelatedEntity, DeriveRelation, FromJsonQueryResult, + DeriveCustomColumn, DeriveDisplay, DeriveEntity, DeriveEntityModel, DeriveIden, + DeriveIntoActiveModel, DeriveModel, DerivePrimaryKey, DeriveRelatedEntity, DeriveRelation, + FromJsonQueryResult, }; pub use async_trait; From c2d19686279b3581f7de85ed7f0197e903afa192 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 13 Jul 2023 11:30:34 +0100 Subject: [PATCH 0071/1371] Reinstate Table => type_name semantic --- sea-orm-macros/src/derives/derive_iden.rs | 7 ++++++- tests/derive_iden_tests.rs | 16 ++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/sea-orm-macros/src/derives/derive_iden.rs b/sea-orm-macros/src/derives/derive_iden.rs index 026a97b1cf..75442b0c36 100644 --- a/sea-orm-macros/src/derives/derive_iden.rs +++ b/sea-orm-macros/src/derives/derive_iden.rs @@ -49,7 +49,12 @@ fn impl_iden_for_enum( let match_pair: Vec = variants .map(|v| { let var_ident = &v.ident; - let mut var_name = var_ident.to_string().to_snake_case(); + let var_name = if var_ident == "Table" { + ident + } else { + var_ident + }; + let mut var_name = var_name.to_string().to_snake_case(); v.attrs .iter() .filter(|attr| attr.path().is_ident("sea_orm")) diff --git a/tests/derive_iden_tests.rs b/tests/derive_iden_tests.rs index 707a69f1ff..e45adda7d4 100644 --- a/tests/derive_iden_tests.rs +++ b/tests/derive_iden_tests.rs @@ -4,7 +4,8 @@ use sea_orm::entity::prelude::*; use sea_orm_macros::DeriveIden; #[derive(DeriveIden)] -pub enum Class { +pub enum ClassName { + Table, Id, Title, Text, @@ -12,6 +13,7 @@ pub enum Class { #[derive(DeriveIden)] pub enum Book { + Table, Id, #[sea_orm(iden = "turtle")] Title, @@ -22,7 +24,7 @@ pub enum Book { } #[derive(DeriveIden)] -struct Glyph; +struct GlyphToken; #[derive(DeriveIden)] #[sea_orm(iden = "weRd")] @@ -30,16 +32,18 @@ struct Word; #[test] fn main() -> Result<(), DbErr> { - assert_eq!(Class::Id.to_string(), "id"); - assert_eq!(Class::Title.to_string(), "title"); - assert_eq!(Class::Text.to_string(), "text"); + assert_eq!(ClassName::Table.to_string(), "class_name"); + assert_eq!(ClassName::Id.to_string(), "id"); + assert_eq!(ClassName::Title.to_string(), "title"); + assert_eq!(ClassName::Text.to_string(), "text"); assert_eq!(Book::Id.to_string(), "id"); + assert_eq!(Book::Table.to_string(), "book"); assert_eq!(Book::Title.to_string(), "turtle"); assert_eq!(Book::Text.to_string(), "TeXt"); assert_eq!(Book::Type.to_string(), "ty_pe"); - assert_eq!(Glyph.to_string(), "glyph"); + assert_eq!(GlyphToken.to_string(), "glyph_token"); assert_eq!(Word.to_string(), "weRd"); Ok(()) From 1ba37b61d551985c15e478773fd46e4da0a20a23 Mon Sep 17 00:00:00 2001 From: Diwakar Gupta <39624018+Diwakar-Gupta@users.noreply.github.com> Date: Thu, 13 Jul 2023 19:39:53 +0530 Subject: [PATCH 0072/1371] Add default_expr support for ColumnDef (#1474) * Add default_expr support for ColumnDef * added test for attribute `default_value` * Implements `PartialEq` for `sea_query::SimpleExpr` * RcOrArc * Re-export `sea_query::Expr` in `sea_orm::entity::prelude` * Add default_expr support for ColumnDef * added test for attribute `default_value` * Implements `PartialEq` for `sea_query::SimpleExpr` * RcOrArc * Re-export `sea_query::Expr` in `sea_orm::entity::prelude` * Deprecate `ColumnDef::default_value()` * Add `ColumnDef::default()` --------- Co-authored-by: Billy Chan --- sea-orm-macros/src/derives/entity_model.rs | 14 ++++++-- src/entity/column.rs | 38 +++++++++++++++++++--- src/entity/prelude.rs | 2 +- src/schema/entity.rs | 4 +-- 4 files changed, 48 insertions(+), 10 deletions(-) diff --git a/sea-orm-macros/src/derives/entity_model.rs b/sea-orm-macros/src/derives/entity_model.rs index 3ce2801e45..d9abf8e941 100644 --- a/sea-orm-macros/src/derives/entity_model.rs +++ b/sea-orm-macros/src/derives/entity_model.rs @@ -137,7 +137,15 @@ pub fn expand_derive_entity_model(data: Data, attrs: Vec) -> syn::Res } else if meta.path.is_ident("default_value") { default_value = Some(meta.value()?.parse::()?); } else if meta.path.is_ident("default_expr") { - default_expr = Some(meta.value()?.parse::()?); + let lit = meta.value()?.parse()?; + if let Lit::Str(litstr) = lit { + let value_expr: TokenStream = syn::parse_str(&litstr.value())?; + default_expr = Some(value_expr); + } else { + return Err( + meta.error(format!("Invalid column_type {:?}", lit)) + ); + } } else if meta.path.is_ident("column_name") { let lit = meta.value()?.parse()?; if let Lit::Str(litstr) = lit { @@ -264,10 +272,10 @@ pub fn expand_derive_entity_model(data: Data, attrs: Vec) -> syn::Res match_row = quote! { #match_row.unique() }; } if let Some(default_value) = default_value { - match_row = quote! { #match_row.default_value(#default_value) }; + match_row = quote! { #match_row.default(#default_value) }; } if let Some(default_expr) = default_expr { - match_row = quote! { #match_row.default_expr(#default_expr) }; + match_row = quote! { #match_row.default(#default_expr) }; } columns_trait.push(match_row); } diff --git a/src/entity/column.rs b/src/entity/column.rs index b263413fdd..7569d908dc 100644 --- a/src/entity/column.rs +++ b/src/entity/column.rs @@ -15,7 +15,7 @@ pub struct ColumnDef { pub(crate) null: bool, pub(crate) unique: bool, pub(crate) indexed: bool, - pub(crate) default_value: Option, + pub(crate) default: Option, } macro_rules! bind_oper { @@ -310,7 +310,7 @@ impl ColumnTypeTrait for ColumnType { null: false, unique: false, indexed: false, - default_value: None, + default: None, } } @@ -362,11 +362,21 @@ impl ColumnDef { } /// Set the default value + #[deprecated(since = "0.12.0", note = "Please use [`ColumnDef::default`]")] pub fn default_value(mut self, value: T) -> Self where T: Into, { - self.default_value = Some(value.into()); + self.default = Some(value.into().into()); + self + } + + /// Set the default value or expression of a column + pub fn default(mut self, default: T) -> Self + where + T: Into, + { + self.default = Some(default.into()); self } @@ -493,7 +503,7 @@ mod tests { #[test] #[cfg(feature = "macros")] fn entity_model_column_1() { - use crate::entity::*; + use crate::prelude::*; mod hello { use crate as sea_orm; @@ -521,6 +531,12 @@ mod tests { pub eight: u32, #[sea_orm(unique, indexed, nullable)] pub nine: u64, + #[sea_orm(default_expr = "Expr::current_timestamp()")] + pub ten: DateTimeUtc, + #[sea_orm(default_value = 7)] + pub eleven: u8, + #[sea_orm(default_value = "twelve_value")] + pub twelve: String, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] @@ -562,6 +578,20 @@ mod tests { hello::Column::Nine.def(), ColumnType::BigUnsigned.def().unique().indexed().nullable() ); + assert_eq!( + hello::Column::Ten.def(), + ColumnType::TimestampWithTimeZone + .def() + .default(Expr::current_timestamp()) + ); + assert_eq!( + hello::Column::Eleven.def(), + ColumnType::TinyUnsigned.def().default(7) + ); + assert_eq!( + hello::Column::Twelve.def(), + ColumnType::String(None).def().default("twelve_value") + ); } #[test] diff --git a/src/entity/prelude.rs b/src/entity/prelude.rs index ff6d779c50..fb326808ae 100644 --- a/src/entity/prelude.rs +++ b/src/entity/prelude.rs @@ -1,6 +1,6 @@ pub use crate::{ error::*, - sea_query::{BlobSize, DynIden, RcOrArc, SeaRc}, + sea_query::{BlobSize, DynIden, Expr, RcOrArc, SeaRc}, ActiveEnum, ActiveModelBehavior, ActiveModelTrait, ColumnDef, ColumnTrait, ColumnType, ColumnTypeTrait, ConnectionTrait, CursorTrait, DatabaseConnection, DbConn, EntityName, EntityTrait, EnumIter, ForeignKeyAction, Iden, IdenStatic, Linked, LoaderTrait, ModelTrait, diff --git a/src/schema/entity.rs b/src/schema/entity.rs index 7aaac164d2..f6e9ead016 100644 --- a/src/schema/entity.rs +++ b/src/schema/entity.rs @@ -210,8 +210,8 @@ where if orm_column_def.unique { column_def.unique_key(); } - if let Some(value) = orm_column_def.default_value { - column_def.default(value); + if let Some(default) = orm_column_def.default { + column_def.default(default); } for primary_key in E::PrimaryKey::iter() { if column.to_string() == primary_key.into_column().to_string() { From 3629b91d01fdc733139923b1d1e9847eaf23ec27 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 13 Jul 2023 15:16:55 +0100 Subject: [PATCH 0073/1371] Do not deprecate default_value --- sea-orm-macros/src/derives/entity_model.rs | 2 +- src/entity/column.rs | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/sea-orm-macros/src/derives/entity_model.rs b/sea-orm-macros/src/derives/entity_model.rs index d9abf8e941..bea168ac3e 100644 --- a/sea-orm-macros/src/derives/entity_model.rs +++ b/sea-orm-macros/src/derives/entity_model.rs @@ -272,7 +272,7 @@ pub fn expand_derive_entity_model(data: Data, attrs: Vec) -> syn::Res match_row = quote! { #match_row.unique() }; } if let Some(default_value) = default_value { - match_row = quote! { #match_row.default(#default_value) }; + match_row = quote! { #match_row.default_value(#default_value) }; } if let Some(default_expr) = default_expr { match_row = quote! { #match_row.default(#default_expr) }; diff --git a/src/entity/column.rs b/src/entity/column.rs index 7569d908dc..a2b7d87236 100644 --- a/src/entity/column.rs +++ b/src/entity/column.rs @@ -362,7 +362,6 @@ impl ColumnDef { } /// Set the default value - #[deprecated(since = "0.12.0", note = "Please use [`ColumnDef::default`]")] pub fn default_value(mut self, value: T) -> Self where T: Into, @@ -537,6 +536,8 @@ mod tests { pub eleven: u8, #[sea_orm(default_value = "twelve_value")] pub twelve: String, + #[sea_orm(default_expr = "\"twelve_value\"")] + pub twelve_two: String, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] @@ -592,6 +593,10 @@ mod tests { hello::Column::Twelve.def(), ColumnType::String(None).def().default("twelve_value") ); + assert_eq!( + hello::Column::TwelveTwo.def(), + ColumnType::String(None).def().default("twelve_value") + ); } #[test] From 7352008e0eb285228e52541161e94b54589f7ec0 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 13 Jul 2023 15:43:23 +0100 Subject: [PATCH 0074/1371] Changelog --- CHANGELOG.md | 270 ++++++++++++++------------------------------------- 1 file changed, 73 insertions(+), 197 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bd07bba94..41ea7b3358 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,45 +14,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### New Features -* Supports for partial select of `Option` model field. A `None` value will be filled when the select result does not contain the `Option` field without throwing an error. https://github.com/SeaQL/sea-orm/pull/1513 -```rust -customer::ActiveModel { - name: Set("Alice".to_owned()), - notes: Set(Some("Want to communicate with Bob".to_owned())), - ..Default::default() -} -.save(db) -.await?; - -// The `notes` field was intentionally leaved out -let customer = Customer::find() - .select_only() - .column(customer::Column::Id) - .column(customer::Column::Name) - .one(db) - .await - .unwrap(); - -// The select result does not contain `notes` field. -// Since it's of type `Option`, it'll be `None` and no error will be thrown. -assert_eq!(customers.notes, None); -``` -* [sea-orm-cli] the `migrate init` command will create a `.gitignore` file when the migration folder reside in a Git repository https://github.com/SeaQL/sea-orm/pull/1334 * Added `MigratorTrait::migration_table_name()` method to configure the name of migration table https://github.com/SeaQL/sea-orm/pull/1511 ```rust #[async_trait::async_trait] impl MigratorTrait for Migrator { - fn migrations() -> Vec> { - vec![ - Box::new(m20220118_000001_create_cake_table::Migration), - Box::new(m20220118_000002_create_fruit_table::Migration), - ] - } - // Override the name of migration table fn migration_table_name() -> sea_orm::DynIden { Alias::new("override_migration_table_name").into_iden() } + ... } ``` * Added option to construct chained AND / OR join on condition https://github.com/SeaQL/sea-orm/pull/1433 @@ -64,7 +34,6 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - #[sea_orm(column_name = "name", enum_name = "Name")] pub name: String, } @@ -86,40 +55,6 @@ pub enum Relation { )] OrTropicalFruit, } - -impl ActiveModelBehavior for ActiveModel {} -``` -You can also override it in custom join. -```rust -assert_eq!( - cake::Entity::find() - .column_as( - Expr::col((Alias::new("cake_filling_alias"), cake_filling::Column::CakeId)), - "cake_filling_cake_id" - ) - .join(JoinType::LeftJoin, cake::Relation::OrTropicalFruit.def()) - .join_as_rev( - JoinType::LeftJoin, - cake_filling::Relation::Cake - .def() - // chained AND / OR join on condition - .condition_type(ConditionType::Any) - .on_condition(|left, _right| { - Expr::col((left, cake_filling::Column::CakeId)) - .gt(10) - .into_condition() - }), - Alias::new("cake_filling_alias") - ) - .build(DbBackend::MySql) - .to_string(), - [ - "SELECT `cake`.`id`, `cake`.`name`, `cake_filling_alias`.`cake_id` AS `cake_filling_cake_id` FROM `cake`", - "LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id` OR `fruit`.`name` LIKE '%tropical%'", - "LEFT JOIN `cake_filling` AS `cake_filling_alias` ON `cake_filling_alias`.`cake_id` = `cake`.`id` OR `cake_filling_alias`.`cake_id` > 10", - ] - .join(" ") -); ``` * Supports entity with composite primary key of length 12 https://github.com/SeaQL/sea-orm/pull/1508 * Implemented `IntoIdentity` for `Identity` https://github.com/SeaQL/sea-orm/pull/1508 @@ -136,37 +71,10 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id_1: String, - #[sea_orm(primary_key, auto_increment = false)] - pub id_2: i8, - #[sea_orm(primary_key, auto_increment = false)] - pub id_3: u8, - #[sea_orm(primary_key, auto_increment = false)] - pub id_4: i16, - #[sea_orm(primary_key, auto_increment = false)] - pub id_5: u16, - #[sea_orm(primary_key, auto_increment = false)] - pub id_6: i32, - #[sea_orm(primary_key, auto_increment = false)] - pub id_7: u32, - #[sea_orm(primary_key, auto_increment = false)] - pub id_8: i64, - #[sea_orm(primary_key, auto_increment = false)] - pub id_9: u64, - #[sea_orm(primary_key, auto_increment = false)] - pub id_10: f32, - #[sea_orm(primary_key, auto_increment = false)] - pub id_11: f64, + ... #[sea_orm(primary_key, auto_increment = false)] pub id_12: bool, - pub owner: String, - pub name: String, - pub description: String, } - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation {} - -impl ActiveModelBehavior for ActiveModel {} ``` * Added macro `DerivePartialModel` https://github.com/SeaQL/sea-orm/pull/1597 ```rust @@ -188,31 +96,6 @@ assert_eq!( r#"SELECT "cake"."name", UPPER("cake"."name") AS "name_upper" FROM "cake""# ); ``` -* [sea-orm-cli] Added support for generating migration of space separated name, for example executing `sea-orm-cli migrate generate "create accounts table"` command will create `m20230503_000000_create_accounts_table.rs` for you https://github.com/SeaQL/sea-orm/pull/1570 - -* Add `seaography` flag to `sea-orm`, `sea-orm-orm-macros` and `sea-orm-cli` https://github.com/SeaQL/sea-orm/pull/1599 -* Add generation of `seaography` related information to `sea-orm-codegen` https://github.com/SeaQL/sea-orm/pull/1599 - - The following information is added in entities files by `sea-orm-cli` when flag `seaography` is `true` -```rust -/// ... Entity File ... - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelatedEntity)] -pub enum RelatedEntity { - #[sea_orm(entity = "super::address::Entity")] - Address, - #[sea_orm(entity = "super::payment::Entity")] - Payment, - #[sea_orm(entity = "super::rental::Entity")] - Rental, - #[sea_orm(entity = "Entity", def = "Relation::SelfRef.def()")] - SelfRef, - #[sea_orm(entity = "super::store::Entity")] - Store, - #[sea_orm(entity = "Entity", def = "Relation::SelfRef.def().rev()")] - SelfRefRev, -} -``` * Add `DeriveEntityRelated` macro https://github.com/SeaQL/sea-orm/pull/1599 The DeriveRelatedEntity derive macro will implement `seaography::RelationBuilder` for `RelatedEntity` enumeration when the `seaography` feature is enabled @@ -297,91 +180,91 @@ pub struct Boolbean(pub String); #[derive(DeriveValueType)] pub struct StringVec(pub Vec); ``` -The expanded code of `DeriveValueType` looks like. +* Add `DeriveDisplay` derive macro to implements `std::fmt::Display` for active enum https://github.com/SeaQL/sea-orm/pull/1726 ```rust -#[derive(DeriveValueType)] -pub struct StringVec(pub Vec); - -// The `DeriveValueType` will be expanded into... - -impl From for Value { - fn from(source: StringVec) -> Self { - source.0.into() - } +#[derive(DeriveDisplay)] +pub enum DisplayTea { + EverydayTea, + #[sea_orm(display_value = "Breakfast")] + BreakfastTea, } - -impl sea_orm::TryGetable for StringVec { - fn try_get_by(res: &QueryResult, idx: I) -> Result { - as sea_orm::TryGetable>::try_get_by(res, idx).map(|v| StringVec(v)) - } +assert_eq!(format!("{}", DisplayTea::EverydayTea), "EverydayTea"); +assert_eq!(format!("{}", DisplayTea::BreakfastTea), "Breakfast"); +``` +* Added `UpdateMany::exec_with_returning()` https://github.com/SeaQL/sea-orm/pull/1677 +```rust +Entity::update_many() + .col_expr(Column::Values, Expr::expr(..)) + .exec_with_returning(db) + .await +``` +* Supporting `default_expr` in `DeriveEntityModel` https://github.com/SeaQL/sea-orm/pull/1474 +```rust +#[derive(DeriveEntityModel)] +#[sea_orm(table_name = "hello")] +pub struct Model { + #[sea_orm(default_expr = "Expr::current_timestamp()")] + pub timestamp: DateTimeUtc, } -impl sea_orm::sea_query::ValueType for StringVec { - fn try_from(v: Value) -> Result { - as sea_orm::sea_query::ValueType>::try_from(v).map(|v| StringVec(v)) - } +assert_eq!( + Column::Timestamp.def(), + ColumnType::TimestampWithTimeZone.def().default(Expr::current_timestamp()) +); +``` - fn type_name() -> String { - stringify!(StringVec).to_owned() - } +#### Seaography - fn array_type() -> sea_orm::sea_query::ArrayType { - std::convert::Into::::into( - as sea_orm::sea_query::ValueType>::array_type() - ) - } +* Add generation of `seaography` related information to `sea-orm-codegen` https://github.com/SeaQL/sea-orm/pull/1599 - fn column_type() -> sea_orm::sea_query::ColumnType { - std::convert::Into::::into( - as sea_orm::sea_query::ValueType>::column_type() - ) - } -} -``` -* Add `DeriveDisplay` derive macro to implements `std::fmt::Display` for active enum https://github.com/SeaQL/sea-orm/pull/1726 + The following information is added in entities files by `sea-orm-cli` when flag `seaography` is `true` ```rust -// String enum -#[derive(EnumIter, DeriveActiveEnum, DeriveDisplay)] -#[sea_orm(rs_type = "String", db_type = "String(Some(1))", enum_name = "category")] -pub enum DeriveCategory { - #[sea_orm(string_value = "B")] - Big, - #[sea_orm(string_value = "S")] - Small, -} -assert_eq!(format!("{}", DeriveCategory::Big), "Big"); -assert_eq!(format!("{}", DeriveCategory::Small), "Small"); - -// Numeric enum -#[derive(EnumIter, DeriveActiveEnum, DeriveDisplay)] -#[sea_orm(rs_type = "i32", db_type = "Integer")] -pub enum $ident { - #[sea_orm(num_value = -10)] - Negative, - #[sea_orm(num_value = 1)] - Big, - #[sea_orm(num_value = 0)] - Small, -} -assert_eq!(format!("{}", $ident::Big), "Big"); -assert_eq!(format!("{}", $ident::Small), "Small"); -assert_eq!(format!("{}", $ident::Negative), "Negative"); +/// ... Entity File ... -// String enum with `display_value` overrides -#[derive(EnumIter, DeriveActiveEnum, DeriveDisplay)] -#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "tea")] -pub enum DisplayTea { - #[sea_orm(string_value = "EverydayTea", display_value = "Everyday")] - EverydayTea, - #[sea_orm(string_value = "BreakfastTea", display_value = "Breakfast")] - BreakfastTea, +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelatedEntity)] +pub enum RelatedEntity { + #[sea_orm(entity = "super::address::Entity")] + Address, + #[sea_orm(entity = "super::payment::Entity")] + Payment, + #[sea_orm(entity = "super::rental::Entity")] + Rental, + #[sea_orm(entity = "Entity", def = "Relation::SelfRef.def()")] + SelfRef, + #[sea_orm(entity = "super::store::Entity")] + Store, + #[sea_orm(entity = "Entity", def = "Relation::SelfRef.def().rev()")] + SelfRefRev, } -assert_eq!(format!("{}", DisplayTea::BreakfastTea), "Breakfast"); -assert_eq!(format!("{}", DisplayTea::EverydayTea), "Everyday"); ``` ### Enhancements +* Supports for partial select of `Option` model field. A `None` value will be filled when the select result does not contain the `Option` field without throwing an error. https://github.com/SeaQL/sea-orm/pull/1513 +```rust +customer::ActiveModel { + name: Set("Alice".to_owned()), + notes: Set(Some("Want to communicate with Bob".to_owned())), + ..Default::default() +} +.save(db) +.await?; + +// The `notes` field was intentionally leaved out +let customer = Customer::find() + .select_only() + .column(customer::Column::Id) + .column(customer::Column::Name) + .one(db) + .await + .unwrap(); + +// The select result does not contain `notes` field. +// Since it's of type `Option`, it'll be `None` and no error will be thrown. +assert_eq!(customers.notes, None); +``` +* [sea-orm-cli] the `migrate init` command will create a `.gitignore` file when the migration folder reside in a Git repository https://github.com/SeaQL/sea-orm/pull/1334 +* [sea-orm-cli] Added support for generating migration of space separated name, for example executing `sea-orm-cli migrate generate "create accounts table"` command will create `m20230503_000000_create_accounts_table.rs` for you https://github.com/SeaQL/sea-orm/pull/1570 * Added `Migration::name()` and `Migration::status()` getters for the name and status of `sea_orm_migration::Migration` https://github.com/SeaQL/sea-orm/pull/1519 ```rust let migrations = Migrator::get_pending_migrations(db).await?; @@ -432,13 +315,6 @@ assert!(matches!(res, Err(DbErr::RecordNotInserted))); let res = Entity::insert_many([..]).on_conflict(on).do_nothing().exec(db).await; assert!(matches!(res, Ok(TryInsertResult::Conflicted))); ``` -* Added `UpdateMany::exec_with_returning()` https://github.com/SeaQL/sea-orm/pull/1677 -```rust -Entity::update_many() - .col_expr(Column::Values, Expr::expr(..)) - .exec_with_returning(db) - .await -``` ### Upgrades From 500e761a683ae721694e6f90bf8d223e76d22f7b Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 13 Jul 2023 16:00:37 +0100 Subject: [PATCH 0075/1371] Changelog --- CHANGELOG.md | 7 ++++--- src/entity/active_enum.rs | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41ea7b3358..11a1ab0560 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -183,13 +183,13 @@ pub struct StringVec(pub Vec); * Add `DeriveDisplay` derive macro to implements `std::fmt::Display` for active enum https://github.com/SeaQL/sea-orm/pull/1726 ```rust #[derive(DeriveDisplay)] -pub enum DisplayTea { +enum DisplayTea { EverydayTea, - #[sea_orm(display_value = "Breakfast")] + #[sea_orm(display_value = "Breakfast Tea")] BreakfastTea, } assert_eq!(format!("{}", DisplayTea::EverydayTea), "EverydayTea"); -assert_eq!(format!("{}", DisplayTea::BreakfastTea), "Breakfast"); +assert_eq!(format!("{}", DisplayTea::BreakfastTea), "Breakfast Tea"); ``` * Added `UpdateMany::exec_with_returning()` https://github.com/SeaQL/sea-orm/pull/1677 ```rust @@ -328,6 +328,7 @@ assert!(matches!(res, Ok(TryInsertResult::Conflicted))); ### Bug Fixes +* `DeriveActiveEnum` no longer impl `Display` * Fixed `DeriveActiveEnum` throwing errors because `string_value` consists non-UAX#31 compliant characters https://github.com/SeaQL/sea-orm/pull/1374 For example, diff --git a/src/entity/active_enum.rs b/src/entity/active_enum.rs index 0f56bd7154..6ac81f1128 100644 --- a/src/entity/active_enum.rs +++ b/src/entity/active_enum.rs @@ -490,4 +490,18 @@ mod tests { assert_eq!(EnumVariant::A0x24B.to_string(), "A$B"); assert_eq!(EnumVariant::_0x300x20123.to_string(), "0 123"); } + + #[test] + fn test_derive_display() { + use crate::DeriveDisplay; + + #[derive(DeriveDisplay)] + enum DisplayTea { + EverydayTea, + #[sea_orm(display_value = "Breakfast Tea")] + BreakfastTea, + } + assert_eq!(format!("{}", DisplayTea::EverydayTea), "EverydayTea"); + assert_eq!(format!("{}", DisplayTea::BreakfastTea), "Breakfast Tea"); + } } From fd4aab7a8cd22e0b6940c1c7ba158459b46134ea Mon Sep 17 00:00:00 2001 From: darkmmon Date: Thu, 13 Jul 2023 23:24:12 +0800 Subject: [PATCH 0076/1371] Connection acquire expand (#1737) * Try connection pool acquire timeout * expanded ConnectionAcquire Errors, and adjusted relevant functions * updated to include ClosedConnection removed unknown as variant of ConnectionAcquireError updated DbErr Eq * revert mistakenly edited code * prolonged the timeout time for connection timeout tests * Revert "prolonged the timeout time for connection timeout tests" This reverts commit 04d98cf1fdafe64dbe6168b3bbcdcc813329ac4c. * Error PartialEq & Eq * fn conn_acquire_err * refactor code with conn_acquire_err * fmt * error msg --------- Co-authored-by: Billy Chan --- src/driver/sqlx_mysql.rs | 136 +++++++++++++++--------------------- src/driver/sqlx_postgres.rs | 136 +++++++++++++++--------------------- src/driver/sqlx_sqlite.rs | 136 +++++++++++++++--------------------- src/error.rs | 25 ++++++- tests/connection_tests.rs | 99 +++++++++++++++++++++++++- 5 files changed, 287 insertions(+), 245 deletions(-) diff --git a/src/driver/sqlx_mysql.rs b/src/driver/sqlx_mysql.rs index ebea1b837d..a777ecb89a 100644 --- a/src/driver/sqlx_mysql.rs +++ b/src/driver/sqlx_mysql.rs @@ -82,16 +82,13 @@ impl SqlxMySqlPoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - if let Ok(conn) = &mut self.pool.acquire().await { - crate::metric::metric!(self.metric_callback, &stmt, { - match query.execute(conn).await { - Ok(res) => Ok(res.into()), - Err(err) => Err(sqlx_error_to_exec_err(err)), - } - }) - } else { - Err(DbErr::ConnectionAcquire) - } + let conn = &mut self.pool.acquire().await.map_err(conn_acquire_err)?; + crate::metric::metric!(self.metric_callback, &stmt, { + match query.execute(conn).await { + Ok(res) => Ok(res.into()), + Err(err) => Err(sqlx_error_to_exec_err(err)), + } + }) } /// Execute an unprepared SQL statement on a MySQL backend @@ -99,13 +96,10 @@ impl SqlxMySqlPoolConnection { pub async fn execute_unprepared(&self, sql: &str) -> Result { debug_print!("{}", sql); - if let Ok(conn) = &mut self.pool.acquire().await { - match conn.execute(sql).await { - Ok(res) => Ok(res.into()), - Err(err) => Err(sqlx_error_to_exec_err(err)), - } - } else { - Err(DbErr::ConnectionAcquire) + let conn = &mut self.pool.acquire().await.map_err(conn_acquire_err)?; + match conn.execute(sql).await { + Ok(res) => Ok(res.into()), + Err(err) => Err(sqlx_error_to_exec_err(err)), } } @@ -115,19 +109,16 @@ impl SqlxMySqlPoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - if let Ok(conn) = &mut self.pool.acquire().await { - crate::metric::metric!(self.metric_callback, &stmt, { - match query.fetch_one(conn).await { - Ok(row) => Ok(Some(row.into())), - Err(err) => match err { - sqlx::Error::RowNotFound => Ok(None), - _ => Err(sqlx_error_to_query_err(err)), - }, - } - }) - } else { - Err(DbErr::ConnectionAcquire) - } + let conn = &mut self.pool.acquire().await.map_err(conn_acquire_err)?; + crate::metric::metric!(self.metric_callback, &stmt, { + match query.fetch_one(conn).await { + Ok(row) => Ok(Some(row.into())), + Err(err) => match err { + sqlx::Error::RowNotFound => Ok(None), + _ => Err(sqlx_error_to_query_err(err)), + }, + } + }) } /// Get the results of a query returning them as a Vec<[QueryResult]> @@ -136,16 +127,13 @@ impl SqlxMySqlPoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - if let Ok(conn) = &mut self.pool.acquire().await { - crate::metric::metric!(self.metric_callback, &stmt, { - match query.fetch_all(conn).await { - Ok(rows) => Ok(rows.into_iter().map(|r| r.into()).collect()), - Err(err) => Err(sqlx_error_to_query_err(err)), - } - }) - } else { - Err(DbErr::ConnectionAcquire) - } + let conn = &mut self.pool.acquire().await.map_err(conn_acquire_err)?; + crate::metric::metric!(self.metric_callback, &stmt, { + match query.fetch_all(conn).await { + Ok(rows) => Ok(rows.into_iter().map(|r| r.into()).collect()), + Err(err) => Err(sqlx_error_to_query_err(err)), + } + }) } /// Stream the results of executing a SQL query @@ -153,15 +141,12 @@ impl SqlxMySqlPoolConnection { pub async fn stream(&self, stmt: Statement) -> Result { debug_print!("{}", stmt); - if let Ok(conn) = self.pool.acquire().await { - Ok(QueryStream::from(( - conn, - stmt, - self.metric_callback.clone(), - ))) - } else { - Err(DbErr::ConnectionAcquire) - } + let conn = self.pool.acquire().await.map_err(conn_acquire_err)?; + Ok(QueryStream::from(( + conn, + stmt, + self.metric_callback.clone(), + ))) } /// Bundle a set of SQL statements that execute together. @@ -171,17 +156,14 @@ impl SqlxMySqlPoolConnection { isolation_level: Option, access_mode: Option, ) -> Result { - if let Ok(conn) = self.pool.acquire().await { - DatabaseTransaction::new_mysql( - conn, - self.metric_callback.clone(), - isolation_level, - access_mode, - ) - .await - } else { - Err(DbErr::ConnectionAcquire) - } + let conn = self.pool.acquire().await.map_err(conn_acquire_err)?; + DatabaseTransaction::new_mysql( + conn, + self.metric_callback.clone(), + isolation_level, + access_mode, + ) + .await } /// Create a MySQL transaction @@ -200,19 +182,16 @@ impl SqlxMySqlPoolConnection { T: Send, E: std::error::Error + Send, { - if let Ok(conn) = self.pool.acquire().await { - let transaction = DatabaseTransaction::new_mysql( - conn, - self.metric_callback.clone(), - isolation_level, - access_mode, - ) - .await - .map_err(|e| TransactionError::Connection(e))?; - transaction.run(callback).await - } else { - Err(DbErr::ConnectionAcquire.into()) - } + let conn = self.pool.acquire().await.map_err(conn_acquire_err)?; + let transaction = DatabaseTransaction::new_mysql( + conn, + self.metric_callback.clone(), + isolation_level, + access_mode, + ) + .await + .map_err(|e| TransactionError::Connection(e))?; + transaction.run(callback).await } pub(crate) fn set_metric_callback(&mut self, callback: F) @@ -224,13 +203,10 @@ impl SqlxMySqlPoolConnection { /// Checks if a connection to the database is still valid. pub async fn ping(&self) -> Result<(), DbErr> { - if let Ok(conn) = &mut self.pool.acquire().await { - match conn.ping().await { - Ok(_) => Ok(()), - Err(err) => Err(sqlx_error_to_conn_err(err)), - } - } else { - Err(DbErr::ConnectionAcquire) + let conn = &mut self.pool.acquire().await.map_err(conn_acquire_err)?; + match conn.ping().await { + Ok(_) => Ok(()), + Err(err) => Err(sqlx_error_to_conn_err(err)), } } diff --git a/src/driver/sqlx_postgres.rs b/src/driver/sqlx_postgres.rs index e78aa8d842..35f65a6195 100644 --- a/src/driver/sqlx_postgres.rs +++ b/src/driver/sqlx_postgres.rs @@ -97,16 +97,13 @@ impl SqlxPostgresPoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - if let Ok(conn) = &mut self.pool.acquire().await { - crate::metric::metric!(self.metric_callback, &stmt, { - match query.execute(conn).await { - Ok(res) => Ok(res.into()), - Err(err) => Err(sqlx_error_to_exec_err(err)), - } - }) - } else { - Err(DbErr::ConnectionAcquire) - } + let conn = &mut self.pool.acquire().await.map_err(conn_acquire_err)?; + crate::metric::metric!(self.metric_callback, &stmt, { + match query.execute(conn).await { + Ok(res) => Ok(res.into()), + Err(err) => Err(sqlx_error_to_exec_err(err)), + } + }) } /// Execute an unprepared SQL statement on a PostgreSQL backend @@ -114,13 +111,10 @@ impl SqlxPostgresPoolConnection { pub async fn execute_unprepared(&self, sql: &str) -> Result { debug_print!("{}", sql); - if let Ok(conn) = &mut self.pool.acquire().await { - match conn.execute(sql).await { - Ok(res) => Ok(res.into()), - Err(err) => Err(sqlx_error_to_exec_err(err)), - } - } else { - Err(DbErr::ConnectionAcquire) + let conn = &mut self.pool.acquire().await.map_err(conn_acquire_err)?; + match conn.execute(sql).await { + Ok(res) => Ok(res.into()), + Err(err) => Err(sqlx_error_to_exec_err(err)), } } @@ -130,19 +124,16 @@ impl SqlxPostgresPoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - if let Ok(conn) = &mut self.pool.acquire().await { - crate::metric::metric!(self.metric_callback, &stmt, { - match query.fetch_one(conn).await { - Ok(row) => Ok(Some(row.into())), - Err(err) => match err { - sqlx::Error::RowNotFound => Ok(None), - _ => Err(sqlx_error_to_query_err(err)), - }, - } - }) - } else { - Err(DbErr::ConnectionAcquire) - } + let conn = &mut self.pool.acquire().await.map_err(conn_acquire_err)?; + crate::metric::metric!(self.metric_callback, &stmt, { + match query.fetch_one(conn).await { + Ok(row) => Ok(Some(row.into())), + Err(err) => match err { + sqlx::Error::RowNotFound => Ok(None), + _ => Err(sqlx_error_to_query_err(err)), + }, + } + }) } /// Get the results of a query returning them as a Vec<[QueryResult]> @@ -151,16 +142,13 @@ impl SqlxPostgresPoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - if let Ok(conn) = &mut self.pool.acquire().await { - crate::metric::metric!(self.metric_callback, &stmt, { - match query.fetch_all(conn).await { - Ok(rows) => Ok(rows.into_iter().map(|r| r.into()).collect()), - Err(err) => Err(sqlx_error_to_query_err(err)), - } - }) - } else { - Err(DbErr::ConnectionAcquire) - } + let conn = &mut self.pool.acquire().await.map_err(conn_acquire_err)?; + crate::metric::metric!(self.metric_callback, &stmt, { + match query.fetch_all(conn).await { + Ok(rows) => Ok(rows.into_iter().map(|r| r.into()).collect()), + Err(err) => Err(sqlx_error_to_query_err(err)), + } + }) } /// Stream the results of executing a SQL query @@ -168,15 +156,12 @@ impl SqlxPostgresPoolConnection { pub async fn stream(&self, stmt: Statement) -> Result { debug_print!("{}", stmt); - if let Ok(conn) = self.pool.acquire().await { - Ok(QueryStream::from(( - conn, - stmt, - self.metric_callback.clone(), - ))) - } else { - Err(DbErr::ConnectionAcquire) - } + let conn = self.pool.acquire().await.map_err(conn_acquire_err)?; + Ok(QueryStream::from(( + conn, + stmt, + self.metric_callback.clone(), + ))) } /// Bundle a set of SQL statements that execute together. @@ -186,17 +171,14 @@ impl SqlxPostgresPoolConnection { isolation_level: Option, access_mode: Option, ) -> Result { - if let Ok(conn) = self.pool.acquire().await { - DatabaseTransaction::new_postgres( - conn, - self.metric_callback.clone(), - isolation_level, - access_mode, - ) - .await - } else { - Err(DbErr::ConnectionAcquire) - } + let conn = self.pool.acquire().await.map_err(conn_acquire_err)?; + DatabaseTransaction::new_postgres( + conn, + self.metric_callback.clone(), + isolation_level, + access_mode, + ) + .await } /// Create a PostgreSQL transaction @@ -215,19 +197,16 @@ impl SqlxPostgresPoolConnection { T: Send, E: std::error::Error + Send, { - if let Ok(conn) = self.pool.acquire().await { - let transaction = DatabaseTransaction::new_postgres( - conn, - self.metric_callback.clone(), - isolation_level, - access_mode, - ) - .await - .map_err(|e| TransactionError::Connection(e))?; - transaction.run(callback).await - } else { - Err(DbErr::ConnectionAcquire.into()) - } + let conn = self.pool.acquire().await.map_err(conn_acquire_err)?; + let transaction = DatabaseTransaction::new_postgres( + conn, + self.metric_callback.clone(), + isolation_level, + access_mode, + ) + .await + .map_err(|e| TransactionError::Connection(e))?; + transaction.run(callback).await } pub(crate) fn set_metric_callback(&mut self, callback: F) @@ -239,13 +218,10 @@ impl SqlxPostgresPoolConnection { /// Checks if a connection to the database is still valid. pub async fn ping(&self) -> Result<(), DbErr> { - if let Ok(conn) = &mut self.pool.acquire().await { - match conn.ping().await { - Ok(_) => Ok(()), - Err(err) => Err(sqlx_error_to_conn_err(err)), - } - } else { - Err(DbErr::ConnectionAcquire) + let conn = &mut self.pool.acquire().await.map_err(conn_acquire_err)?; + match conn.ping().await { + Ok(_) => Ok(()), + Err(err) => Err(sqlx_error_to_conn_err(err)), } } diff --git a/src/driver/sqlx_sqlite.rs b/src/driver/sqlx_sqlite.rs index 5e1875495a..f8d351a9b8 100644 --- a/src/driver/sqlx_sqlite.rs +++ b/src/driver/sqlx_sqlite.rs @@ -89,16 +89,13 @@ impl SqlxSqlitePoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - if let Ok(conn) = &mut self.pool.acquire().await { - crate::metric::metric!(self.metric_callback, &stmt, { - match query.execute(conn).await { - Ok(res) => Ok(res.into()), - Err(err) => Err(sqlx_error_to_exec_err(err)), - } - }) - } else { - Err(DbErr::ConnectionAcquire) - } + let conn = &mut self.pool.acquire().await.map_err(conn_acquire_err)?; + crate::metric::metric!(self.metric_callback, &stmt, { + match query.execute(conn).await { + Ok(res) => Ok(res.into()), + Err(err) => Err(sqlx_error_to_exec_err(err)), + } + }) } /// Execute an unprepared SQL statement on a SQLite backend @@ -106,13 +103,10 @@ impl SqlxSqlitePoolConnection { pub async fn execute_unprepared(&self, sql: &str) -> Result { debug_print!("{}", sql); - if let Ok(conn) = &mut self.pool.acquire().await { - match conn.execute(sql).await { - Ok(res) => Ok(res.into()), - Err(err) => Err(sqlx_error_to_exec_err(err)), - } - } else { - Err(DbErr::ConnectionAcquire) + let conn = &mut self.pool.acquire().await.map_err(conn_acquire_err)?; + match conn.execute(sql).await { + Ok(res) => Ok(res.into()), + Err(err) => Err(sqlx_error_to_exec_err(err)), } } @@ -122,19 +116,16 @@ impl SqlxSqlitePoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - if let Ok(conn) = &mut self.pool.acquire().await { - crate::metric::metric!(self.metric_callback, &stmt, { - match query.fetch_one(conn).await { - Ok(row) => Ok(Some(row.into())), - Err(err) => match err { - sqlx::Error::RowNotFound => Ok(None), - _ => Err(sqlx_error_to_query_err(err)), - }, - } - }) - } else { - Err(DbErr::ConnectionAcquire) - } + let conn = &mut self.pool.acquire().await.map_err(conn_acquire_err)?; + crate::metric::metric!(self.metric_callback, &stmt, { + match query.fetch_one(conn).await { + Ok(row) => Ok(Some(row.into())), + Err(err) => match err { + sqlx::Error::RowNotFound => Ok(None), + _ => Err(sqlx_error_to_query_err(err)), + }, + } + }) } /// Get the results of a query returning them as a Vec<[QueryResult]> @@ -143,16 +134,13 @@ impl SqlxSqlitePoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - if let Ok(conn) = &mut self.pool.acquire().await { - crate::metric::metric!(self.metric_callback, &stmt, { - match query.fetch_all(conn).await { - Ok(rows) => Ok(rows.into_iter().map(|r| r.into()).collect()), - Err(err) => Err(sqlx_error_to_query_err(err)), - } - }) - } else { - Err(DbErr::ConnectionAcquire) - } + let conn = &mut self.pool.acquire().await.map_err(conn_acquire_err)?; + crate::metric::metric!(self.metric_callback, &stmt, { + match query.fetch_all(conn).await { + Ok(rows) => Ok(rows.into_iter().map(|r| r.into()).collect()), + Err(err) => Err(sqlx_error_to_query_err(err)), + } + }) } /// Stream the results of executing a SQL query @@ -160,15 +148,12 @@ impl SqlxSqlitePoolConnection { pub async fn stream(&self, stmt: Statement) -> Result { debug_print!("{}", stmt); - if let Ok(conn) = self.pool.acquire().await { - Ok(QueryStream::from(( - conn, - stmt, - self.metric_callback.clone(), - ))) - } else { - Err(DbErr::ConnectionAcquire) - } + let conn = self.pool.acquire().await.map_err(conn_acquire_err)?; + Ok(QueryStream::from(( + conn, + stmt, + self.metric_callback.clone(), + ))) } /// Bundle a set of SQL statements that execute together. @@ -178,17 +163,14 @@ impl SqlxSqlitePoolConnection { isolation_level: Option, access_mode: Option, ) -> Result { - if let Ok(conn) = self.pool.acquire().await { - DatabaseTransaction::new_sqlite( - conn, - self.metric_callback.clone(), - isolation_level, - access_mode, - ) - .await - } else { - Err(DbErr::ConnectionAcquire) - } + let conn = self.pool.acquire().await.map_err(conn_acquire_err)?; + DatabaseTransaction::new_sqlite( + conn, + self.metric_callback.clone(), + isolation_level, + access_mode, + ) + .await } /// Create a MySQL transaction @@ -207,19 +189,16 @@ impl SqlxSqlitePoolConnection { T: Send, E: std::error::Error + Send, { - if let Ok(conn) = self.pool.acquire().await { - let transaction = DatabaseTransaction::new_sqlite( - conn, - self.metric_callback.clone(), - isolation_level, - access_mode, - ) - .await - .map_err(|e| TransactionError::Connection(e))?; - transaction.run(callback).await - } else { - Err(DbErr::ConnectionAcquire.into()) - } + let conn = self.pool.acquire().await.map_err(conn_acquire_err)?; + let transaction = DatabaseTransaction::new_sqlite( + conn, + self.metric_callback.clone(), + isolation_level, + access_mode, + ) + .await + .map_err(|e| TransactionError::Connection(e))?; + transaction.run(callback).await } pub(crate) fn set_metric_callback(&mut self, callback: F) @@ -231,13 +210,10 @@ impl SqlxSqlitePoolConnection { /// Checks if a connection to the database is still valid. pub async fn ping(&self) -> Result<(), DbErr> { - if let Ok(conn) = &mut self.pool.acquire().await { - match conn.ping().await { - Ok(_) => Ok(()), - Err(err) => Err(sqlx_error_to_conn_err(err)), - } - } else { - Err(DbErr::ConnectionAcquire) + let conn = &mut self.pool.acquire().await.map_err(conn_acquire_err)?; + match conn.ping().await { + Ok(_) => Ok(()), + Err(err) => Err(sqlx_error_to_conn_err(err)), } } diff --git a/src/error.rs b/src/error.rs index 125da4f84c..a63ac70822 100644 --- a/src/error.rs +++ b/src/error.rs @@ -16,8 +16,8 @@ use thiserror::Error; #[derive(Error, Debug)] pub enum DbErr { /// This error can happen when the connection pool is fully-utilized - #[error("Failed to acquire connection from pool")] - ConnectionAcquire, + #[error("Failed to acquire connection from pool: {0}")] + ConnectionAcquire(ConnAcquireErr), /// Runtime type conversion error #[error("Error converting `{from}` into `{into}`: {source}")] TryIntoErr { @@ -75,6 +75,17 @@ pub enum DbErr { RecordNotUpdated, } +/// Connection error +#[derive(Error, Debug, PartialEq, Eq)] +pub enum ConnAcquireErr { + /// Connection Timed Out + #[error("Connection Timed out")] + Timeout, + /// Unavailable connection + #[error("Connection closed by host")] + ConnectionClosed, +} + /// Runtime error #[derive(Error, Debug)] pub enum RuntimeErr { @@ -140,6 +151,16 @@ where DbErr::Json(s.to_string()) } +#[allow(dead_code)] +#[cfg(feature = "sqlx-dep")] +pub(crate) fn conn_acquire_err(sqlx_err: sqlx::Error) -> DbErr { + match sqlx_err { + sqlx::Error::PoolTimedOut => DbErr::ConnectionAcquire(ConnAcquireErr::Timeout), + sqlx::Error::PoolClosed => DbErr::ConnectionAcquire(ConnAcquireErr::ConnectionClosed), + _ => DbErr::Conn(RuntimeErr::SqlxError(sqlx_err)), + } +} + /// An error from unsuccessful SQL query #[derive(Error, Debug, Clone, PartialEq, Eq)] #[non_exhaustive] diff --git a/tests/connection_tests.rs b/tests/connection_tests.rs index 83abb1f985..e5966d106f 100644 --- a/tests/connection_tests.rs +++ b/tests/connection_tests.rs @@ -25,7 +25,38 @@ pub async fn connection_ping_closed_mysql() { let ctx_ping = std::rc::Rc::clone(&ctx); ctx.db.get_mysql_connection_pool().close().await; - assert_eq!(ctx_ping.db.ping().await, Err(DbErr::ConnectionAcquire)); + assert_eq!( + ctx_ping.db.ping().await, + Err(DbErr::ConnectionAcquire(ConnAcquireErr::ConnectionClosed)) + ); + + let base_url = std::env::var("DATABASE_URL").unwrap(); + let mut opt = sea_orm::ConnectOptions::new(format!("{base_url}/connection_ping_closed")); + opt + // The connection pool has a single connection only + .max_connections(1) + // A controlled connection acquire timeout + .acquire_timeout(std::time::Duration::from_secs(2)); + + let db = sea_orm::Database::connect(opt).await.unwrap(); + + async fn transaction_blocked(db: &DatabaseConnection) { + let _txn = sea_orm::TransactionTrait::begin(db).await.unwrap(); + // Occupy the only connection, thus forcing others fail to acquire connection + tokio::time::sleep(std::time::Duration::from_secs(3)).await; + } + + async fn transaction(db: &DatabaseConnection) { + // Should fail to acquire + let txn = sea_orm::TransactionTrait::begin(db).await; + assert_eq!( + txn.expect_err("should be a time out"), + crate::DbErr::ConnectionAcquire(ConnAcquireErr::Timeout) + ) + } + + tokio::join!(transaction_blocked(&db), transaction(&db)); + ctx.delete().await; } @@ -36,7 +67,38 @@ pub async fn connection_ping_closed_sqlite() { let ctx_ping = std::rc::Rc::clone(&ctx); ctx.db.get_sqlite_connection_pool().close().await; - assert_eq!(ctx_ping.db.ping().await, Err(DbErr::ConnectionAcquire)); + assert_eq!( + ctx_ping.db.ping().await, + Err(DbErr::ConnectionAcquire(ConnAcquireErr::ConnectionClosed)) + ); + + let base_url = std::env::var("DATABASE_URL").unwrap(); + let mut opt = sea_orm::ConnectOptions::new(base_url); + opt + // The connection pool has a single connection only + .max_connections(1) + // A controlled connection acquire timeout + .acquire_timeout(std::time::Duration::from_secs(2)); + + let db = sea_orm::Database::connect(opt).await.unwrap(); + + async fn transaction_blocked(db: &DatabaseConnection) { + let _txn = sea_orm::TransactionTrait::begin(db).await.unwrap(); + // Occupy the only connection, thus forcing others fail to acquire connection + tokio::time::sleep(std::time::Duration::from_secs(3)).await; + } + + async fn transaction(db: &DatabaseConnection) { + // Should fail to acquire + let txn = sea_orm::TransactionTrait::begin(db).await; + assert_eq!( + txn.expect_err("should be a time out"), + crate::DbErr::ConnectionAcquire(ConnAcquireErr::Timeout) + ) + } + + tokio::join!(transaction_blocked(&db), transaction(&db)); + ctx.delete().await; } @@ -47,6 +109,37 @@ pub async fn connection_ping_closed_postgres() { let ctx_ping = std::rc::Rc::clone(&ctx); ctx.db.get_postgres_connection_pool().close().await; - assert_eq!(ctx_ping.db.ping().await, Err(DbErr::ConnectionAcquire)); + assert_eq!( + ctx_ping.db.ping().await, + Err(DbErr::ConnectionAcquire(ConnAcquireErr::ConnectionClosed)) + ); + + let base_url = std::env::var("DATABASE_URL").unwrap(); + let mut opt = sea_orm::ConnectOptions::new(format!("{base_url}/connection_ping_closed")); + opt + // The connection pool has a single connection only + .max_connections(1) + // A controlled connection acquire timeout + .acquire_timeout(std::time::Duration::from_secs(2)); + + let db = sea_orm::Database::connect(opt).await.unwrap(); + + async fn transaction_blocked(db: &DatabaseConnection) { + let _txn = sea_orm::TransactionTrait::begin(db).await.unwrap(); + // Occupy the only connection, thus forcing others fail to acquire connection + tokio::time::sleep(std::time::Duration::from_secs(3)).await; + } + + async fn transaction(db: &DatabaseConnection) { + // Should fail to acquire + let txn = sea_orm::TransactionTrait::begin(db).await; + assert_eq!( + txn.expect_err("should be a time out"), + crate::DbErr::ConnectionAcquire(ConnAcquireErr::Timeout) + ) + } + + tokio::join!(transaction_blocked(&db), transaction(&db)); + ctx.delete().await; } From d920fc3eda6ca107c524ae11b9e4289e77ee23ee Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 13 Jul 2023 16:33:03 +0100 Subject: [PATCH 0077/1371] Changelog --- CHANGELOG.md | 15 ++++++++++++++- src/error.rs | 8 ++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11a1ab0560..0f2871b909 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -212,6 +212,18 @@ assert_eq!( ColumnType::TimestampWithTimeZone.def().default(Expr::current_timestamp()) ); ``` +* Introduced new `ConnAcquireErr` https://github.com/SeaQL/sea-orm/pull/1737 +```rust +enum DbErr { + ConnectionAcquire(ConnAcquireErr), + .. +} + +enum ConnAcquireErr { + Timeout, + ConnectionClosed, +} +``` #### Seaography @@ -328,7 +340,7 @@ assert!(matches!(res, Ok(TryInsertResult::Conflicted))); ### Bug Fixes -* `DeriveActiveEnum` no longer impl `Display` +* `DeriveActiveEnum` no longer impl `Display` https://github.com/SeaQL/sea-orm/pull/1726 * Fixed `DeriveActiveEnum` throwing errors because `string_value` consists non-UAX#31 compliant characters https://github.com/SeaQL/sea-orm/pull/1374 For example, @@ -453,6 +465,7 @@ pub enum Tea { EverydayTea, } ``` +* Definition of `DbErr::ConnectionAcquire` changed to `ConnectionAcquire(ConnAcquireErr)` https://github.com/SeaQL/sea-orm/pull/1737 ## 0.11.3 - 2023-04-24 diff --git a/src/error.rs b/src/error.rs index a63ac70822..cbeda7025c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -78,11 +78,11 @@ pub enum DbErr { /// Connection error #[derive(Error, Debug, PartialEq, Eq)] pub enum ConnAcquireErr { - /// Connection Timed Out - #[error("Connection Timed out")] + /// Connection pool timed out + #[error("Connection pool timed out")] Timeout, - /// Unavailable connection - #[error("Connection closed by host")] + /// Connection closed + #[error("Connection closed")] ConnectionClosed, } From e8b1c0b975b8cf84c6a948e5030bcb22b2c04e4a Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 13 Jul 2023 16:39:41 +0100 Subject: [PATCH 0078/1371] Docs --- src/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/error.rs b/src/error.rs index cbeda7025c..1ba430dbb8 100644 --- a/src/error.rs +++ b/src/error.rs @@ -75,7 +75,7 @@ pub enum DbErr { RecordNotUpdated, } -/// Connection error +/// Connection Acquire error #[derive(Error, Debug, PartialEq, Eq)] pub enum ConnAcquireErr { /// Connection pool timed out From a5f2ae69e9f08648c83e951a2b1ad298bf134d04 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 13 Jul 2023 16:45:12 +0100 Subject: [PATCH 0079/1371] Organize entity prelude --- src/entity/prelude.rs | 4 ++-- src/lib.rs | 2 +- tests/common/features/json_struct.rs | 1 + tests/common/features/value_type.rs | 1 - tests/derive_iden_tests.rs | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/entity/prelude.rs b/src/entity/prelude.rs index fb326808ae..003ddcd02d 100644 --- a/src/entity/prelude.rs +++ b/src/entity/prelude.rs @@ -12,8 +12,8 @@ pub use crate::{ pub use crate::{ DeriveActiveEnum, DeriveActiveModel, DeriveActiveModelBehavior, DeriveColumn, DeriveCustomColumn, DeriveDisplay, DeriveEntity, DeriveEntityModel, DeriveIden, - DeriveIntoActiveModel, DeriveModel, DerivePrimaryKey, DeriveRelatedEntity, DeriveRelation, - FromJsonQueryResult, + DeriveIntoActiveModel, DeriveModel, DerivePartialModel, DerivePrimaryKey, DeriveRelatedEntity, + DeriveRelation, DeriveValueType, }; pub use async_trait; diff --git a/src/lib.rs b/src/lib.rs index cdf5bb3dae..f7367f021f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -351,7 +351,7 @@ pub use sea_orm_macros::{ DeriveActiveEnum, DeriveActiveModel, DeriveActiveModelBehavior, DeriveColumn, DeriveCustomColumn, DeriveDisplay, DeriveEntity, DeriveEntityModel, DeriveIden, DeriveIntoActiveModel, DeriveMigrationName, DeriveModel, DerivePartialModel, DerivePrimaryKey, - DeriveRelatedEntity, DeriveRelation, FromJsonQueryResult, FromQueryResult, + DeriveRelatedEntity, DeriveRelation, DeriveValueType, FromJsonQueryResult, FromQueryResult, }; pub use sea_query; diff --git a/tests/common/features/json_struct.rs b/tests/common/features/json_struct.rs index 44235d1b7d..b291a22d13 100644 --- a/tests/common/features/json_struct.rs +++ b/tests/common/features/json_struct.rs @@ -1,4 +1,5 @@ use sea_orm::entity::prelude::*; +use sea_orm::FromJsonQueryResult; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, PartialEq, DeriveEntityModel)] diff --git a/tests/common/features/value_type.rs b/tests/common/features/value_type.rs index f0efb9dc27..783a7ae2e0 100644 --- a/tests/common/features/value_type.rs +++ b/tests/common/features/value_type.rs @@ -36,7 +36,6 @@ pub mod value_type_pg { } use sea_orm::entity::prelude::*; -use sea_orm_macros::DeriveValueType; #[derive(Clone, Debug, PartialEq, Eq, DeriveValueType)] #[sea_orm(array_type = "Int")] diff --git a/tests/derive_iden_tests.rs b/tests/derive_iden_tests.rs index e45adda7d4..12dd6fe0d7 100644 --- a/tests/derive_iden_tests.rs +++ b/tests/derive_iden_tests.rs @@ -1,7 +1,6 @@ pub mod common; pub use common::{features::*, setup::*, TestContext}; use sea_orm::entity::prelude::*; -use sea_orm_macros::DeriveIden; #[derive(DeriveIden)] pub enum ClassName { @@ -13,6 +12,7 @@ pub enum ClassName { #[derive(DeriveIden)] pub enum Book { + #[sea_orm(iden = "book_table")] Table, Id, #[sea_orm(iden = "turtle")] @@ -38,7 +38,7 @@ fn main() -> Result<(), DbErr> { assert_eq!(ClassName::Text.to_string(), "text"); assert_eq!(Book::Id.to_string(), "id"); - assert_eq!(Book::Table.to_string(), "book"); + assert_eq!(Book::Table.to_string(), "book_table"); assert_eq!(Book::Title.to_string(), "turtle"); assert_eq!(Book::Text.to_string(), "TeXt"); assert_eq!(Book::Type.to_string(), "ty_pe"); From 09c416744f6854e8eaef3bfed024a57086f3761e Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 13 Jul 2023 16:55:04 +0100 Subject: [PATCH 0080/1371] Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f2871b909..c0a16dd5d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -466,6 +466,7 @@ pub enum Tea { } ``` * Definition of `DbErr::ConnectionAcquire` changed to `ConnectionAcquire(ConnAcquireErr)` https://github.com/SeaQL/sea-orm/pull/1737 +* `FromJsonQueryResult` removed from entity prelude ## 0.11.3 - 2023-04-24 From c64a46a6837d3ef3c0495e231f430c1353dbe3ea Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Thu, 20 Jul 2023 21:46:52 +0800 Subject: [PATCH 0081/1371] [CI] Compile & test each examples on a single instance (#1767) * Try * Try * Ignore examples root folder * Try * Try * Try * Try * Try * Try --- .github/workflows/rust.yml | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 2005f07447..859dd23526 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -231,7 +231,7 @@ jobs: steps: - uses: actions/checkout@v3 - id: set-matrix - run: echo "path_matrix=$(find examples -type f -name 'Cargo.toml' -printf '%P\0' | jq -Rc '[ split("\u0000") | .[] | "examples/\(.)" ]')" >> $GITHUB_OUTPUT + run: echo "path_matrix=$(find examples -mindepth 1 -maxdepth 1 -type d -printf '%P\0' | jq -Rc '[ split("\u0000") | .[] | "examples/\(.)" ]')" >> $GITHUB_OUTPUT outputs: path_matrix: ${{ steps.set-matrix.outputs.path_matrix }} @@ -239,10 +239,8 @@ jobs: name: Examples runs-on: ubuntu-latest needs: examples-matrix - timeout-minutes: 15 strategy: fail-fast: false - max-parallel: 12 matrix: path: ${{ fromJson(needs.examples-matrix.outputs.path_matrix) }} steps: @@ -251,11 +249,10 @@ jobs: with: toolchain: stable components: rustfmt - - run: cargo fmt --manifest-path ${{ matrix.path }} -- --check - - run: cargo build --manifest-path ${{ matrix.path }} - - run: cargo test --manifest-path ${{ matrix.path }} - - if: ${{ contains(matrix.path, 'core/Cargo.toml') }} - run: cargo test --manifest-path ${{ matrix.path }} --features mock + - run: find ${{ matrix.path }} -type f -name 'Cargo.toml' -printf '\ncargo fmt --manifest-path %p -- --check\n' -exec cargo fmt --manifest-path {} -- --check \; + - run: find ${{ matrix.path }} -type f -name 'Cargo.toml' -printf '\ncargo build --manifest-path %p\n' -exec cargo build --manifest-path {} \; + - run: find ${{ matrix.path }} -type f -name 'Cargo.toml' -printf '\ncargo test --manifest-path %p\n' -exec cargo test --manifest-path {} \; + - run: (${{ '[ -d "' }}${{ matrix.path }}${{ '/service" ]' }} && find ${{ matrix.path }}/service -type f -name 'Cargo.toml' -printf '\ncargo test --manifest-path %p --features mock\n' -exec cargo test --manifest-path {} --features mock \;) || true issues-matrix: name: Issues Matrix From 217894ddad9bd29767ffbe84a28231098a74d21e Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Fri, 21 Jul 2023 21:44:43 +0800 Subject: [PATCH 0082/1371] Drop `git2` dependency (#1769) * Change of API of SeaSchema 0.12 * [CLI] replace `git2` with `glob` --- sea-orm-cli/Cargo.toml | 2 +- sea-orm-cli/src/commands/generate.rs | 4 ++-- sea-orm-cli/src/commands/migrate.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sea-orm-cli/Cargo.toml b/sea-orm-cli/Cargo.toml index 9f393a95a1..864fae7163 100644 --- a/sea-orm-cli/Cargo.toml +++ b/sea-orm-cli/Cargo.toml @@ -45,7 +45,7 @@ tracing = { version = "0.1", default-features = false } url = { version = "2.2", default-features = false } chrono = { version = "0.4.20", default-features = false, features = ["clock"] } regex = { version = "1", default-features = false } -git2 = { version = "0.16", default-features = false } +glob = { version = "0.3", default-features = false } [dev-dependencies] smol = "1.2.5" diff --git a/sea-orm-cli/src/commands/generate.rs b/sea-orm-cli/src/commands/generate.rs index 7de19d9aa1..934f91a8d6 100644 --- a/sea-orm-cli/src/commands/generate.rs +++ b/sea-orm-cli/src/commands/generate.rs @@ -112,7 +112,7 @@ pub async fn run_generate_command( let connection = connect::(max_connections, url.as_str(), None).await?; let schema_discovery = SchemaDiscovery::new(connection, database_name); - let schema = schema_discovery.discover().await; + let schema = schema_discovery.discover().await?; let table_stmts = schema .tables .into_iter() @@ -148,7 +148,7 @@ pub async fn run_generate_command( let connection = connect::(max_connections, url.as_str(), Some(schema)).await?; let schema_discovery = SchemaDiscovery::new(connection, schema); - let schema = schema_discovery.discover().await; + let schema = schema_discovery.discover().await?; let table_stmts = schema .tables .into_iter() diff --git a/sea-orm-cli/src/commands/migrate.rs b/sea-orm-cli/src/commands/migrate.rs index d7eedcf553..5c86364b27 100644 --- a/sea-orm-cli/src/commands/migrate.rs +++ b/sea-orm-cli/src/commands/migrate.rs @@ -117,7 +117,7 @@ pub fn run_migrate_init(migration_dir: &str) -> Result<(), Box> { content.replace("", &ver) }); write_file!("README.md"); - if git2::Repository::discover(Path::new(&migration_dir)).is_ok() { + if glob::glob(&format!("{migration_dir}**/.git"))?.count() > 0 { write_file!(".gitignore", "_gitignore"); } println!("Done!"); From 73a57836faefa1c4fdbbee80d77626bcc606332e Mon Sep 17 00:00:00 2001 From: Niklas Korz Date: Fri, 21 Jul 2023 15:45:11 +0200 Subject: [PATCH 0083/1371] sea-orm-codegen: PostgreSQL enum arrays (#1678) * sea-orm-codegen: PostgreSQL enum arrays * Refactoring --------- Co-authored-by: Billy Chan --- sea-orm-codegen/src/entity/column.rs | 7 +++++++ sea-orm-codegen/src/entity/transformer.rs | 3 ++- sea-orm-codegen/src/entity/writer.rs | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/sea-orm-codegen/src/entity/column.rs b/sea-orm-codegen/src/entity/column.rs index 50582c27f8..f5ba9284b9 100644 --- a/sea-orm-codegen/src/entity/column.rs +++ b/sea-orm-codegen/src/entity/column.rs @@ -249,6 +249,13 @@ impl Column { quote! {} } } + + pub fn get_inner_col_type(&self) -> &ColumnType { + match &self.col_type { + ColumnType::Array(inner_col_type) => inner_col_type.as_ref(), + _ => &self.col_type, + } + } } impl From for Column { diff --git a/sea-orm-codegen/src/entity/transformer.rs b/sea-orm-codegen/src/entity/transformer.rs index 421a83bf18..20f8047cb6 100644 --- a/sea-orm-codegen/src/entity/transformer.rs +++ b/sea-orm-codegen/src/entity/transformer.rs @@ -58,7 +58,8 @@ impl EntityTransformer { col }) .map(|col| { - if let sea_query::ColumnType::Enum { name, variants } = &col.col_type { + if let sea_query::ColumnType::Enum { name, variants } = col.get_inner_col_type() + { enums.insert( name.to_string(), ActiveEnum { diff --git a/sea-orm-codegen/src/entity/writer.rs b/sea-orm-codegen/src/entity/writer.rs index 36da98910d..637e6eb28f 100644 --- a/sea-orm-codegen/src/entity/writer.rs +++ b/sea-orm-codegen/src/entity/writer.rs @@ -461,7 +461,7 @@ impl EntityWriter { .columns .iter() .fold(TokenStream::new(), |mut ts, col| { - if let sea_query::ColumnType::Enum { name, .. } = &col.col_type { + if let sea_query::ColumnType::Enum { name, .. } = col.get_inner_col_type() { let enum_name = format_ident!("{}", name.to_string().to_upper_camel_case()); ts.extend([quote! { use super::sea_orm_active_enums::#enum_name; From 9f47b88748214aa89fd78c4e2991d5b69c8a9444 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 21 Jul 2023 21:49:21 +0800 Subject: [PATCH 0084/1371] Changelog --- CHANGELOG.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0a16dd5d0..66f5face92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -330,13 +330,13 @@ assert!(matches!(res, Ok(TryInsertResult::Conflicted))); ### Upgrades -* Upgrade `heck` dependency in `sea-orm-macros` and `sea-orm-codegen` to 0.4 https://github.com/SeaQL/sea-orm/pull/1520, https://github.com/SeaQL/sea-orm/pull/1544 -* Upgrade `strum` to 0.25 https://github.com/SeaQL/sea-orm/pull/1752 -* Upgrade `sea-query` to 0.29 https://github.com/SeaQL/sea-orm/pull/1562 -* Upgrade `sea-query-binder` to 0.4 https://github.com/SeaQL/sea-orm/pull/1562 -* Upgrade `sea-schema` to 0.12 https://github.com/SeaQL/sea-orm/pull/1562 -* Upgrade `clap` to 4.3 https://github.com/SeaQL/sea-orm/pull/1468 -* Replace `bae` with `sea-bae` https://github.com/SeaQL/sea-orm/pull/1739 +* Upgrade `syn` to `2` https://github.com/SeaQL/sea-orm/pull/1713 +* Upgrade `heck` to `0.4` https://github.com/SeaQL/sea-orm/pull/1520, https://github.com/SeaQL/sea-orm/pull/1544 +* Upgrade `strum` to `0.25` https://github.com/SeaQL/sea-orm/pull/1752 +* Upgrade `sea-query` to `0.30` +* Upgrade `sea-schema` to `0.14` +* Upgrade `clap` to `4.3` https://github.com/SeaQL/sea-orm/pull/1468 +* Upgrade `ouroboros` to `0.17` https://github.com/SeaQL/sea-orm/pull/1724 ### Bug Fixes @@ -426,6 +426,7 @@ impl ColumnTrait for Column { ``` * Resolved `insert_many` failing if the models iterator is empty https://github.com/SeaQL/sea-orm/issues/873 * Update the template MD file of `migration/README.md`, fix a faulty sample `migrate init` shell script https://github.com/SeaQL/sea-orm/pull/1723 +* [sea-orm-cli] Fix Postgres enum arrays https://github.com/SeaQL/sea-orm/pull/1678 ### Breaking changes @@ -468,6 +469,10 @@ pub enum Tea { * Definition of `DbErr::ConnectionAcquire` changed to `ConnectionAcquire(ConnAcquireErr)` https://github.com/SeaQL/sea-orm/pull/1737 * `FromJsonQueryResult` removed from entity prelude +### House keeping + +* Replace `bae` with `sea-bae` https://github.com/SeaQL/sea-orm/pull/1739 + ## 0.11.3 - 2023-04-24 ### Enhancements @@ -611,8 +616,6 @@ impl ColumnTrait for Column { * Disabled default features and enabled only the needed ones https://github.com/SeaQL/sea-orm/pull/1300 * Cleanup panic and unwrap https://github.com/SeaQL/sea-orm/pull/1231 * Cleanup the use of `vec!` macro https://github.com/SeaQL/sea-orm/pull/1367 -* Upgrade `syn` to v2 https://github.com/SeaQL/sea-orm/pull/1713 -* Upgrade `ouroboros` to `0.17` https://github.com/SeaQL/sea-orm/pull/1724 ### Bug Fixes From 9b54797dd7893b7ac078b7cc8689943b2d56f2e4 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Sat, 22 Jul 2023 01:56:48 +0800 Subject: [PATCH 0085/1371] Upgrade SQLx to v0.7 (#1742) * Upgrade SQLx to v0.7 * Refactoring * Fixup * Bump SeaSchema version * Fixup * Refactoring * Refactoring * dep * Bump * Update sea-schema * Update sea-schema --------- Co-authored-by: Chris Tsang --- Cargo.toml | 12 +++++------ sea-orm-cli/Cargo.toml | 8 +++---- sea-orm-codegen/Cargo.toml | 2 +- sea-orm-migration/Cargo.toml | 2 +- src/database/transaction.rs | 42 +++++++++++++++++++++++++----------- src/driver/sqlx_mysql.rs | 16 +++++++------- src/driver/sqlx_postgres.rs | 16 +++++++------- src/driver/sqlx_sqlite.rs | 16 +++++++------- 8 files changed, 66 insertions(+), 48 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 06f711b6ca..b8c3192b66 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,12 +34,12 @@ tracing = { version = "0.1", default-features = false, features = ["attributes", rust_decimal = { version = "1", default-features = false, optional = true } bigdecimal = { version = "0.3", default-features = false, optional = true } sea-orm-macros = { version = "0.12.0-rc.4", path = "sea-orm-macros", default-features = false, features = ["strum"] } -sea-query = { version = "0.29.0", default-features = false, features = ["thread-safe", "hashable-value", "backend-mysql", "backend-postgres", "backend-sqlite"] } -sea-query-binder = { version = "0.4.0-rc.2", default-features = false, optional = true } +sea-query = { version = "0.30.0", default-features = false, features = ["thread-safe", "hashable-value", "backend-mysql", "backend-postgres", "backend-sqlite"] } +sea-query-binder = { version = "0.5.0", default-features = false, optional = true } strum = { version = "0.25", default-features = false } serde = { version = "1.0", default-features = false } serde_json = { version = "1.0", default-features = false, optional = true } -sqlx = { version = "0.6", default-features = false, optional = true } +sqlx = { version = "0.7", default-features = false, optional = true } uuid = { version = "1", default-features = false, optional = true } ouroboros = { version = "0.17", default-features = false } url = { version = "2.2", default-features = false } @@ -78,7 +78,7 @@ macros = ["sea-orm-macros/derive"] mock = [] with-json = ["serde_json", "sea-query/with-json", "chrono?/serde", "time?/serde", "uuid?/serde", "sea-query-binder?/with-json", "sqlx?/json"] with-chrono = ["chrono", "sea-query/with-chrono", "sea-query-binder?/with-chrono", "sqlx?/chrono"] -with-rust_decimal = ["rust_decimal", "sea-query/with-rust_decimal", "sea-query-binder?/with-rust_decimal", "sqlx?/decimal"] +with-rust_decimal = ["rust_decimal", "sea-query/with-rust_decimal", "sea-query-binder?/with-rust_decimal", "sqlx?/rust_decimal"] with-bigdecimal = ["bigdecimal", "sea-query/with-bigdecimal", "sea-query-binder?/with-bigdecimal", "sqlx?/bigdecimal"] with-uuid = ["uuid", "sea-query/with-uuid", "sea-query-binder?/with-uuid", "sqlx?/uuid"] with-time = ["time", "sea-query/with-time", "sea-query-binder?/with-time", "sqlx?/time"] @@ -102,12 +102,12 @@ runtime-async-std-rustls = [ ] runtime-actix = [] runtime-actix-native-tls = [ - "sqlx?/runtime-actix-native-tls", + "sqlx?/runtime-tokio-native-tls", "sea-query-binder?/runtime-actix-native-tls", "runtime-actix", ] runtime-actix-rustls = [ - "sqlx?/runtime-actix-rustls", + "sqlx?/runtime-tokio-rustls", "sea-query-binder?/runtime-actix-rustls", "runtime-actix", ] diff --git a/sea-orm-cli/Cargo.toml b/sea-orm-cli/Cargo.toml index 864fae7163..2ceafaee55 100644 --- a/sea-orm-cli/Cargo.toml +++ b/sea-orm-cli/Cargo.toml @@ -38,8 +38,8 @@ clap = { version = "4.3", features = ["env", "derive"], optional = true } dotenvy = { version = "0.15", default-features = false, optional = true } async-std = { version = "1.9", default-features = false, features = ["attributes", "tokio1"], optional = true } sea-orm-codegen = { version = "=0.12.0-rc.4", path = "../sea-orm-codegen", default-features = false, optional = true } -sea-schema = { version = "0.12.0-rc.2" } -sqlx = { version = "0.6", default-features = false, features = ["mysql", "postgres"], optional = true } +sea-schema = { version = "0.14.0" } +sqlx = { version = "0.7", default-features = false, features = ["mysql", "postgres"], optional = true } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["env-filter", "fmt"] } tracing = { version = "0.1", default-features = false } url = { version = "2.2", default-features = false } @@ -54,9 +54,9 @@ smol = "1.2.5" default = ["codegen", "cli", "runtime-async-std-native-tls", "async-std"] codegen = ["sea-schema/sqlx-all", "sea-orm-codegen"] cli = ["clap", "dotenvy"] -runtime-actix-native-tls = ["sqlx/runtime-actix-native-tls", "sea-schema/runtime-actix-native-tls"] +runtime-actix-native-tls = ["sqlx/runtime-tokio-native-tls", "sea-schema/runtime-actix-native-tls"] runtime-async-std-native-tls = ["sqlx/runtime-async-std-native-tls", "sea-schema/runtime-async-std-native-tls"] runtime-tokio-native-tls = ["sqlx/runtime-tokio-native-tls", "sea-schema/runtime-tokio-native-tls"] -runtime-actix-rustls = ["sqlx/runtime-actix-rustls", "sea-schema/runtime-actix-rustls"] +runtime-actix-rustls = ["sqlx/runtime-tokio-rustls", "sea-schema/runtime-actix-rustls"] runtime-async-std-rustls = ["sqlx/runtime-async-std-rustls", "sea-schema/runtime-async-std-rustls"] runtime-tokio-rustls = ["sqlx/runtime-tokio-rustls", "sea-schema/runtime-tokio-rustls"] diff --git a/sea-orm-codegen/Cargo.toml b/sea-orm-codegen/Cargo.toml index 1a0a2c7da3..7a0ca52f1e 100644 --- a/sea-orm-codegen/Cargo.toml +++ b/sea-orm-codegen/Cargo.toml @@ -17,7 +17,7 @@ name = "sea_orm_codegen" path = "src/lib.rs" [dependencies] -sea-query = { version = "0.29.0-rc.2", default-features = false, features = ["thread-safe"] } +sea-query = { version = "0.30.0", default-features = false, features = ["thread-safe"] } syn = { version = "2", default-features = false, features = ["parsing", "proc-macro", "derive", "printing"] } quote = { version = "1", default-features = false } heck = { version = "0.4", default-features = false } diff --git a/sea-orm-migration/Cargo.toml b/sea-orm-migration/Cargo.toml index 22b279e5cd..58c7cafeef 100644 --- a/sea-orm-migration/Cargo.toml +++ b/sea-orm-migration/Cargo.toml @@ -25,7 +25,7 @@ clap = { version = "4.3", features = ["env", "derive"], optional = true } dotenvy = { version = "0.15", default-features = false, optional = true } sea-orm = { version = "0.12.0-rc.4", path = "../", default-features = false, features = ["macros"] } sea-orm-cli = { version = "0.12.0-rc.4", path = "../sea-orm-cli", default-features = false, optional = true } -sea-schema = { version = "0.12.0-rc.2" } +sea-schema = { version = "0.14.0" } tracing = { version = "0.1", default-features = false, features = ["log"] } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["env-filter", "fmt"] } futures = { version = "0.3", default-features = false, features = ["std"] } diff --git a/src/database/transaction.rs b/src/database/transaction.rs index e228a638a1..14765b214b 100644 --- a/src/database/transaction.rs +++ b/src/database/transaction.rs @@ -312,6 +312,7 @@ impl ConnectionTrait for DatabaseTransaction { #[cfg(feature = "sqlx-mysql")] InnerConnection::MySql(conn) => { let query = crate::driver::sqlx_mysql::sqlx_query(&stmt); + let conn: &mut sqlx::MySqlConnection = &mut *conn; crate::metric::metric!(self.metric_callback, &stmt, { query.execute(conn).await.map(Into::into) }) @@ -320,6 +321,7 @@ impl ConnectionTrait for DatabaseTransaction { #[cfg(feature = "sqlx-postgres")] InnerConnection::Postgres(conn) => { let query = crate::driver::sqlx_postgres::sqlx_query(&stmt); + let conn: &mut sqlx::PgConnection = &mut *conn; crate::metric::metric!(self.metric_callback, &stmt, { query.execute(conn).await.map(Into::into) }) @@ -328,6 +330,7 @@ impl ConnectionTrait for DatabaseTransaction { #[cfg(feature = "sqlx-sqlite")] InnerConnection::Sqlite(conn) => { let query = crate::driver::sqlx_sqlite::sqlx_query(&stmt); + let conn: &mut sqlx::SqliteConnection = &mut *conn; crate::metric::metric!(self.metric_callback, &stmt, { query.execute(conn).await.map(Into::into) }) @@ -347,20 +350,29 @@ impl ConnectionTrait for DatabaseTransaction { match &mut *self.conn.lock().await { #[cfg(feature = "sqlx-mysql")] - InnerConnection::MySql(conn) => sqlx::Executor::execute(conn, sql) - .await - .map(Into::into) - .map_err(sqlx_error_to_exec_err), + InnerConnection::MySql(conn) => { + let conn: &mut sqlx::MySqlConnection = &mut *conn; + sqlx::Executor::execute(conn, sql) + .await + .map(Into::into) + .map_err(sqlx_error_to_exec_err) + } #[cfg(feature = "sqlx-postgres")] - InnerConnection::Postgres(conn) => sqlx::Executor::execute(conn, sql) - .await - .map(Into::into) - .map_err(sqlx_error_to_exec_err), + InnerConnection::Postgres(conn) => { + let conn: &mut sqlx::PgConnection = &mut *conn; + sqlx::Executor::execute(conn, sql) + .await + .map(Into::into) + .map_err(sqlx_error_to_exec_err) + } #[cfg(feature = "sqlx-sqlite")] - InnerConnection::Sqlite(conn) => sqlx::Executor::execute(conn, sql) - .await - .map(Into::into) - .map_err(sqlx_error_to_exec_err), + InnerConnection::Sqlite(conn) => { + let conn: &mut sqlx::SqliteConnection = &mut *conn; + sqlx::Executor::execute(conn, sql) + .await + .map(Into::into) + .map_err(sqlx_error_to_exec_err) + } #[cfg(feature = "mock")] InnerConnection::Mock(conn) => { let db_backend = conn.get_database_backend(); @@ -381,6 +393,7 @@ impl ConnectionTrait for DatabaseTransaction { #[cfg(feature = "sqlx-mysql")] InnerConnection::MySql(conn) => { let query = crate::driver::sqlx_mysql::sqlx_query(&stmt); + let conn: &mut sqlx::MySqlConnection = &mut *conn; crate::metric::metric!(self.metric_callback, &stmt, { Self::map_err_ignore_not_found( query.fetch_one(conn).await.map(|row| Some(row.into())), @@ -390,6 +403,7 @@ impl ConnectionTrait for DatabaseTransaction { #[cfg(feature = "sqlx-postgres")] InnerConnection::Postgres(conn) => { let query = crate::driver::sqlx_postgres::sqlx_query(&stmt); + let conn: &mut sqlx::PgConnection = &mut *conn; crate::metric::metric!(self.metric_callback, &stmt, { Self::map_err_ignore_not_found( query.fetch_one(conn).await.map(|row| Some(row.into())), @@ -399,6 +413,7 @@ impl ConnectionTrait for DatabaseTransaction { #[cfg(feature = "sqlx-sqlite")] InnerConnection::Sqlite(conn) => { let query = crate::driver::sqlx_sqlite::sqlx_query(&stmt); + let conn: &mut sqlx::SqliteConnection = &mut *conn; crate::metric::metric!(self.metric_callback, &stmt, { Self::map_err_ignore_not_found( query.fetch_one(conn).await.map(|row| Some(row.into())), @@ -421,6 +436,7 @@ impl ConnectionTrait for DatabaseTransaction { #[cfg(feature = "sqlx-mysql")] InnerConnection::MySql(conn) => { let query = crate::driver::sqlx_mysql::sqlx_query(&stmt); + let conn: &mut sqlx::MySqlConnection = &mut *conn; crate::metric::metric!(self.metric_callback, &stmt, { query .fetch_all(conn) @@ -432,6 +448,7 @@ impl ConnectionTrait for DatabaseTransaction { #[cfg(feature = "sqlx-postgres")] InnerConnection::Postgres(conn) => { let query = crate::driver::sqlx_postgres::sqlx_query(&stmt); + let conn: &mut sqlx::PgConnection = &mut *conn; crate::metric::metric!(self.metric_callback, &stmt, { query .fetch_all(conn) @@ -443,6 +460,7 @@ impl ConnectionTrait for DatabaseTransaction { #[cfg(feature = "sqlx-sqlite")] InnerConnection::Sqlite(conn) => { let query = crate::driver::sqlx_sqlite::sqlx_query(&stmt); + let conn: &mut sqlx::SqliteConnection = &mut *conn; crate::metric::metric!(self.metric_callback, &stmt, { query .fetch_all(conn) diff --git a/src/driver/sqlx_mysql.rs b/src/driver/sqlx_mysql.rs index a777ecb89a..51a1842a76 100644 --- a/src/driver/sqlx_mysql.rs +++ b/src/driver/sqlx_mysql.rs @@ -49,9 +49,9 @@ impl SqlxMySqlConnector { .map_err(sqlx_error_to_conn_err)?; use sqlx::ConnectOptions; if !options.sqlx_logging { - opt.disable_statement_logging(); + opt = opt.disable_statement_logging(); } else { - opt.log_statements(options.sqlx_logging_level); + opt = opt.log_statements(options.sqlx_logging_level); } match options.pool_options().connect_with(opt).await { Ok(pool) => Ok(DatabaseConnection::SqlxMySqlPoolConnection( @@ -82,9 +82,9 @@ impl SqlxMySqlPoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - let conn = &mut self.pool.acquire().await.map_err(conn_acquire_err)?; + let mut conn = self.pool.acquire().await.map_err(conn_acquire_err)?; crate::metric::metric!(self.metric_callback, &stmt, { - match query.execute(conn).await { + match query.execute(&mut *conn).await { Ok(res) => Ok(res.into()), Err(err) => Err(sqlx_error_to_exec_err(err)), } @@ -109,9 +109,9 @@ impl SqlxMySqlPoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - let conn = &mut self.pool.acquire().await.map_err(conn_acquire_err)?; + let mut conn = self.pool.acquire().await.map_err(conn_acquire_err)?; crate::metric::metric!(self.metric_callback, &stmt, { - match query.fetch_one(conn).await { + match query.fetch_one(&mut *conn).await { Ok(row) => Ok(Some(row.into())), Err(err) => match err { sqlx::Error::RowNotFound => Ok(None), @@ -127,9 +127,9 @@ impl SqlxMySqlPoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - let conn = &mut self.pool.acquire().await.map_err(conn_acquire_err)?; + let mut conn = self.pool.acquire().await.map_err(conn_acquire_err)?; crate::metric::metric!(self.metric_callback, &stmt, { - match query.fetch_all(conn).await { + match query.fetch_all(&mut *conn).await { Ok(rows) => Ok(rows.into_iter().map(|r| r.into()).collect()), Err(err) => Err(sqlx_error_to_query_err(err)), } diff --git a/src/driver/sqlx_postgres.rs b/src/driver/sqlx_postgres.rs index 35f65a6195..1214f6c29c 100644 --- a/src/driver/sqlx_postgres.rs +++ b/src/driver/sqlx_postgres.rs @@ -49,9 +49,9 @@ impl SqlxPostgresConnector { .map_err(sqlx_error_to_conn_err)?; use sqlx::ConnectOptions; if !options.sqlx_logging { - opt.disable_statement_logging(); + opt = opt.disable_statement_logging(); } else { - opt.log_statements(options.sqlx_logging_level); + opt = opt.log_statements(options.sqlx_logging_level); } let set_search_path_sql = options .schema_search_path @@ -97,9 +97,9 @@ impl SqlxPostgresPoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - let conn = &mut self.pool.acquire().await.map_err(conn_acquire_err)?; + let mut conn = self.pool.acquire().await.map_err(conn_acquire_err)?; crate::metric::metric!(self.metric_callback, &stmt, { - match query.execute(conn).await { + match query.execute(&mut *conn).await { Ok(res) => Ok(res.into()), Err(err) => Err(sqlx_error_to_exec_err(err)), } @@ -124,9 +124,9 @@ impl SqlxPostgresPoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - let conn = &mut self.pool.acquire().await.map_err(conn_acquire_err)?; + let mut conn = self.pool.acquire().await.map_err(conn_acquire_err)?; crate::metric::metric!(self.metric_callback, &stmt, { - match query.fetch_one(conn).await { + match query.fetch_one(&mut *conn).await { Ok(row) => Ok(Some(row.into())), Err(err) => match err { sqlx::Error::RowNotFound => Ok(None), @@ -142,9 +142,9 @@ impl SqlxPostgresPoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - let conn = &mut self.pool.acquire().await.map_err(conn_acquire_err)?; + let mut conn = self.pool.acquire().await.map_err(conn_acquire_err)?; crate::metric::metric!(self.metric_callback, &stmt, { - match query.fetch_all(conn).await { + match query.fetch_all(&mut *conn).await { Ok(rows) => Ok(rows.into_iter().map(|r| r.into()).collect()), Err(err) => Err(sqlx_error_to_query_err(err)), } diff --git a/src/driver/sqlx_sqlite.rs b/src/driver/sqlx_sqlite.rs index f8d351a9b8..8a9924cf56 100644 --- a/src/driver/sqlx_sqlite.rs +++ b/src/driver/sqlx_sqlite.rs @@ -53,9 +53,9 @@ impl SqlxSqliteConnector { } use sqlx::ConnectOptions; if !options.sqlx_logging { - opt.disable_statement_logging(); + opt = opt.disable_statement_logging(); } else { - opt.log_statements(options.sqlx_logging_level); + opt = opt.log_statements(options.sqlx_logging_level); } if options.get_max_connections().is_none() { options.max_connections(1); @@ -89,9 +89,9 @@ impl SqlxSqlitePoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - let conn = &mut self.pool.acquire().await.map_err(conn_acquire_err)?; + let mut conn = self.pool.acquire().await.map_err(conn_acquire_err)?; crate::metric::metric!(self.metric_callback, &stmt, { - match query.execute(conn).await { + match query.execute(&mut *conn).await { Ok(res) => Ok(res.into()), Err(err) => Err(sqlx_error_to_exec_err(err)), } @@ -116,9 +116,9 @@ impl SqlxSqlitePoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - let conn = &mut self.pool.acquire().await.map_err(conn_acquire_err)?; + let mut conn = self.pool.acquire().await.map_err(conn_acquire_err)?; crate::metric::metric!(self.metric_callback, &stmt, { - match query.fetch_one(conn).await { + match query.fetch_one(&mut *conn).await { Ok(row) => Ok(Some(row.into())), Err(err) => match err { sqlx::Error::RowNotFound => Ok(None), @@ -134,9 +134,9 @@ impl SqlxSqlitePoolConnection { debug_print!("{}", stmt); let query = sqlx_query(&stmt); - let conn = &mut self.pool.acquire().await.map_err(conn_acquire_err)?; + let mut conn = self.pool.acquire().await.map_err(conn_acquire_err)?; crate::metric::metric!(self.metric_callback, &stmt, { - match query.fetch_all(conn).await { + match query.fetch_all(&mut *conn).await { Ok(rows) => Ok(rows.into_iter().map(|r| r.into()).collect()), Err(err) => Err(sqlx_error_to_query_err(err)), } From 2335fe9d35e5dc3f9552f231b6c37d1be5422c41 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sat, 22 Jul 2023 00:57:54 +0800 Subject: [PATCH 0086/1371] Changelog --- CHANGELOG.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66f5face92..e4e4f4d0fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,8 +27,6 @@ impl MigratorTrait for Migrator { ``` * Added option to construct chained AND / OR join on condition https://github.com/SeaQL/sea-orm/pull/1433 ```rust -use sea_orm::entity::prelude::*; - #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] #[sea_orm(table_name = "cake")] pub struct Model { @@ -64,8 +62,6 @@ pub enum Relation { * Implemented `TryGetableMany` for tuple of `TryGetable` with length up to 12 https://github.com/SeaQL/sea-orm/pull/1508 * Implemented `TryFromU64` for tuple of `TryFromU64` with length up to 12 https://github.com/SeaQL/sea-orm/pull/1508 ```rust -use sea_orm::entity::prelude::*; - #[derive(Clone, Debug, PartialEq, DeriveEntityModel)] #[sea_orm(table_name = "primary_key_of_12")] pub struct Model { @@ -330,11 +326,12 @@ assert!(matches!(res, Ok(TryInsertResult::Conflicted))); ### Upgrades +* Upgrade `sqlx` to `0.7` https://github.com/SeaQL/sea-orm/pull/1742 +* Upgrade `sea-query` to `0.30` https://github.com/SeaQL/sea-orm/pull/1742 +* Upgrade `sea-schema` to `0.14` https://github.com/SeaQL/sea-orm/pull/1742 * Upgrade `syn` to `2` https://github.com/SeaQL/sea-orm/pull/1713 * Upgrade `heck` to `0.4` https://github.com/SeaQL/sea-orm/pull/1520, https://github.com/SeaQL/sea-orm/pull/1544 * Upgrade `strum` to `0.25` https://github.com/SeaQL/sea-orm/pull/1752 -* Upgrade `sea-query` to `0.30` -* Upgrade `sea-schema` to `0.14` * Upgrade `clap` to `4.3` https://github.com/SeaQL/sea-orm/pull/1468 * Upgrade `ouroboros` to `0.17` https://github.com/SeaQL/sea-orm/pull/1724 From b14a88f7aef2d78b339e626844f0c78687cbe0fd Mon Sep 17 00:00:00 2001 From: Aidan Gauland Date: Mon, 24 Jul 2023 08:53:04 +1200 Subject: [PATCH 0087/1371] Expose sea-orm feature flags in sea-orm-migration (#1775) Some features of the sea-query language are unavailable in migrations with the default feature flags, so we need to be able to enable them in sea-orm-migration --- sea-orm-migration/Cargo.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sea-orm-migration/Cargo.toml b/sea-orm-migration/Cargo.toml index 58c7cafeef..16d83bfe5c 100644 --- a/sea-orm-migration/Cargo.toml +++ b/sea-orm-migration/Cargo.toml @@ -45,3 +45,9 @@ runtime-tokio-native-tls = ["sea-orm/runtime-tokio-native-tls"] runtime-actix-rustls = ["sea-orm/runtime-actix-rustls"] runtime-async-std-rustls = ["sea-orm/runtime-async-std-rustls"] runtime-tokio-rustls = ["sea-orm/runtime-tokio-rustls"] +with-json = ["sea-orm/with-json"] +with-chrono = ["sea-orm/with-chrono"] +with-rust_decimal = ["sea-orm/with-rust_decimal"] +with-bigdecimal = ["sea-orm/with-bigdecimal"] +with-uuid = ["sea-orm/with-uuid"] +with-time = ["sea-orm/with-time"] From 7902177da7a91529c8b9c1b9db23417524f07501 Mon Sep 17 00:00:00 2001 From: Tree Xie Date: Mon, 24 Jul 2023 11:54:23 +0800 Subject: [PATCH 0088/1371] community: Add CyberAPI to COMMUNITY.md, #403 (#1729) --- COMMUNITY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/COMMUNITY.md b/COMMUNITY.md index f4fb6469a2..a42c2263c7 100644 --- a/COMMUNITY.md +++ b/COMMUNITY.md @@ -56,6 +56,7 @@ If you have built an app using SeaORM and want to showcase it, feel free to open - [Crosup](https://github.com/tsirysndr/crosup) | Quickly setup your development environment on your Chromebook/ChromeOS or any Linux distribution 🐧 ❄️ 💻 🚀 ✨ | DB: SQLite - [nitro_repo](https://github.com/wyatt-herkamp/nitro_repo) | An OpenSource, lightweight, and fast artifact manager. | DB: MySQL, SQLite - [Orca](https://github.com/workfoxes/orca) | An No-code Test Automation platform using Actix, SeaORM, React. runs on the desktop and cloud | DB: Postgres +- [CyberAPI](https://github.com/vicanso/cyberapi) API tool client for developer. | DB: SQLite #### System - [Email view tracker](https://github.com/friedemannsommer/email-view-tracker) | Simple web UI to create tracking URLs for HTML emails. | DB: MySQL, Postgres From abc86638d5681350f7796346476f583ea1e45b78 Mon Sep 17 00:00:00 2001 From: Manuel Gugger <72697492+mgugger@users.noreply.github.com> Date: Mon, 24 Jul 2023 05:54:57 +0200 Subject: [PATCH 0089/1371] Add actix-admin to showcases (#1761) Co-authored-by: Manuel Gugger --- COMMUNITY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/COMMUNITY.md b/COMMUNITY.md index a42c2263c7..aa34f73aff 100644 --- a/COMMUNITY.md +++ b/COMMUNITY.md @@ -96,6 +96,7 @@ If you have built an app using SeaORM and want to showcase it, feel free to open ### Frameworks - [awto](https://github.com/awto-rs/awto) | Awtomate your 🦀 microservices with awto | DB: Postgres +- [actix-admin](https://github.com/mgugger/actix-admin/) | An admin panel for Actix Web built with Tera, HTMX and SeaOrm | DB: MySQL, Postgres, SQLite - [poem_admin](https://github.com/lingdu1234/poem_admin) | An admin panel built with poems, SeaORM and Vue 3. | DB: MySQL, Postgres, SQLite - [Quasar](https://github.com/Technik97/Quasar) | Rust REST API using Actix Web, SeaORM and Postgres with Svelte Typescript Frontend | DB: Postgres - [tardis](https://github.com/ideal-world/tardis) | Elegant, Clean Rust development framework🛸 | DB: MySQL, Postgres, SQLite From 63a46f8931a8cfebc326481a94fab6663c8648ab Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Mon, 24 Jul 2023 12:06:19 +0800 Subject: [PATCH 0090/1371] COMMUNITY --- COMMUNITY.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/COMMUNITY.md b/COMMUNITY.md index aa34f73aff..c40958cefb 100644 --- a/COMMUNITY.md +++ b/COMMUNITY.md @@ -54,9 +54,9 @@ If you have built an app using SeaORM and want to showcase it, feel free to open #### Dev Tools - [Crosup](https://github.com/tsirysndr/crosup) | Quickly setup your development environment on your Chromebook/ChromeOS or any Linux distribution 🐧 ❄️ 💻 🚀 ✨ | DB: SQLite +- [CyberAPI](https://github.com/vicanso/cyberapi) API tool client for developer. | DB: SQLite - [nitro_repo](https://github.com/wyatt-herkamp/nitro_repo) | An OpenSource, lightweight, and fast artifact manager. | DB: MySQL, SQLite - [Orca](https://github.com/workfoxes/orca) | An No-code Test Automation platform using Actix, SeaORM, React. runs on the desktop and cloud | DB: Postgres -- [CyberAPI](https://github.com/vicanso/cyberapi) API tool client for developer. | DB: SQLite #### System - [Email view tracker](https://github.com/friedemannsommer/email-view-tracker) | Simple web UI to create tracking URLs for HTML emails. | DB: MySQL, Postgres @@ -95,8 +95,8 @@ If you have built an app using SeaORM and want to showcase it, feel free to open ### Frameworks -- [awto](https://github.com/awto-rs/awto) | Awtomate your 🦀 microservices with awto | DB: Postgres - [actix-admin](https://github.com/mgugger/actix-admin/) | An admin panel for Actix Web built with Tera, HTMX and SeaOrm | DB: MySQL, Postgres, SQLite +- [awto](https://github.com/awto-rs/awto) | Awtomate your 🦀 microservices with awto | DB: Postgres - [poem_admin](https://github.com/lingdu1234/poem_admin) | An admin panel built with poems, SeaORM and Vue 3. | DB: MySQL, Postgres, SQLite - [Quasar](https://github.com/Technik97/Quasar) | Rust REST API using Actix Web, SeaORM and Postgres with Svelte Typescript Frontend | DB: Postgres - [tardis](https://github.com/ideal-world/tardis) | Elegant, Clean Rust development framework🛸 | DB: MySQL, Postgres, SQLite From 49c494aa6cb0978b6ff65336b139ff0c5c30b12a Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Mon, 24 Jul 2023 18:26:44 +0800 Subject: [PATCH 0091/1371] CI Fixup (#1777) --- .github/workflows/rust.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 859dd23526..c3187dcb36 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -249,10 +249,10 @@ jobs: with: toolchain: stable components: rustfmt - - run: find ${{ matrix.path }} -type f -name 'Cargo.toml' -printf '\ncargo fmt --manifest-path %p -- --check\n' -exec cargo fmt --manifest-path {} -- --check \; - - run: find ${{ matrix.path }} -type f -name 'Cargo.toml' -printf '\ncargo build --manifest-path %p\n' -exec cargo build --manifest-path {} \; - - run: find ${{ matrix.path }} -type f -name 'Cargo.toml' -printf '\ncargo test --manifest-path %p\n' -exec cargo test --manifest-path {} \; - - run: (${{ '[ -d "' }}${{ matrix.path }}${{ '/service" ]' }} && find ${{ matrix.path }}/service -type f -name 'Cargo.toml' -printf '\ncargo test --manifest-path %p --features mock\n' -exec cargo test --manifest-path {} --features mock \;) || true + - run: find ${{ matrix.path }} -type f -name 'Cargo.toml' -print0 | xargs -t -0 -I {} cargo fmt --manifest-path {} -- --check + - run: find ${{ matrix.path }} -type f -name 'Cargo.toml' -print0 | xargs -t -0 -I {} cargo build --manifest-path {} + - run: find ${{ matrix.path }} -type f -name 'Cargo.toml' -print0 | xargs -t -0 -I {} cargo test --manifest-path {} + - run: ${{'! '}}${{ '[ -d "' }}${{ matrix.path }}${{ '/service" ]' }} || find ${{ matrix.path }}/service -type f -name 'Cargo.toml' -print0 | xargs -t -0 -I {} cargo test --manifest-path {} --features mock issues-matrix: name: Issues Matrix From 2eeb93567c1d0d64b82536d30d97527da87981e7 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Tue, 25 Jul 2023 13:49:16 +0800 Subject: [PATCH 0092/1371] [CI] run fmt checks with nightly toolchain (#1780) --- .github/workflows/rust.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index c3187dcb36..895be2c8a1 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -247,9 +247,10 @@ jobs: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@master with: - toolchain: stable + toolchain: nightly components: rustfmt - run: find ${{ matrix.path }} -type f -name 'Cargo.toml' -print0 | xargs -t -0 -I {} cargo fmt --manifest-path {} -- --check + - uses: dtolnay/rust-toolchain@stable - run: find ${{ matrix.path }} -type f -name 'Cargo.toml' -print0 | xargs -t -0 -I {} cargo build --manifest-path {} - run: find ${{ matrix.path }} -type f -name 'Cargo.toml' -print0 | xargs -t -0 -I {} cargo test --manifest-path {} - run: ${{'! '}}${{ '[ -d "' }}${{ matrix.path }}${{ '/service" ]' }} || find ${{ matrix.path }}/service -type f -name 'Cargo.toml' -print0 | xargs -t -0 -I {} cargo test --manifest-path {} --features mock From 84be56e01a418f06383aeace90ac2f6eafc83231 Mon Sep 17 00:00:00 2001 From: darkmmon Date: Tue, 25 Jul 2023 16:12:44 +0800 Subject: [PATCH 0093/1371] update example's dependency versions (#1759) * version update, all examples marked with #notcomplete does not compile with cargo run * WIP * salvo example fixed * testing, and boost a minor version * fmt * build dependency update * CI * cleanup --------- Co-authored-by: Billy Chan --- .github/workflows/rust.yml | 2 ++ examples/actix_example/api/Cargo.toml | 6 +++--- examples/axum_example/api/Cargo.toml | 16 ++++++++-------- examples/axum_example/api/src/lib.rs | 2 +- examples/basic/Cargo.toml | 4 ++-- examples/graphql_example/api/Cargo.toml | 11 ++++++----- examples/graphql_example/api/src/lib.rs | 8 +++++--- examples/graphql_example/entity/Cargo.toml | 2 +- examples/jsonrpsee_example/api/Cargo.toml | 10 +++++----- examples/jsonrpsee_example/api/src/lib.rs | 22 ++++++++++++---------- examples/poem_example/api/Cargo.toml | 6 +++--- examples/rocket_example/api/Cargo.toml | 2 +- examples/salvo_example/api/Cargo.toml | 8 ++++---- examples/salvo_example/api/src/lib.rs | 18 ++++++++---------- examples/salvo_example/entity/Cargo.toml | 1 + examples/salvo_example/entity/src/post.rs | 3 ++- examples/tonic_example/Cargo.toml | 4 ++-- examples/tonic_example/api/Cargo.toml | 8 ++++---- 18 files changed, 70 insertions(+), 63 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 895be2c8a1..763fd32372 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -245,6 +245,8 @@ jobs: path: ${{ fromJson(needs.examples-matrix.outputs.path_matrix) }} steps: - uses: actions/checkout@v3 + - if: ${{ contains(matrix.path, 'tonic_example') }} + uses: arduino/setup-protoc@v1 - uses: dtolnay/rust-toolchain@master with: toolchain: nightly diff --git a/examples/actix_example/api/Cargo.toml b/examples/actix_example/api/Cargo.toml index 18374e7073..44d4f3a03e 100644 --- a/examples/actix_example/api/Cargo.toml +++ b/examples/actix_example/api/Cargo.toml @@ -9,12 +9,12 @@ publish = false actix-example-service = { path = "../service" } actix-files = "0.6" actix-http = "3" -actix-rt = "2.7" +actix-rt = "2.8" actix-service = "2" actix-web = "4" -tera = "1.15.0" +tera = "1.19.0" dotenvy = "0.15" -listenfd = "0.5" +listenfd = "1" serde = "1" tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } entity = { path = "../entity" } diff --git a/examples/axum_example/api/Cargo.toml b/examples/axum_example/api/Cargo.toml index 541034e392..de7462e485 100644 --- a/examples/axum_example/api/Cargo.toml +++ b/examples/axum_example/api/Cargo.toml @@ -7,15 +7,15 @@ publish = false [dependencies] axum-example-service = { path = "../service" } -tokio = { version = "1.23.0", features = ["full"] } -axum = "0.6.1" +tokio = { version = "1.29.0", features = ["full"] } +axum = "0.6.19" tower = "0.4.13" -tower-http = { version = "0.3.5", features = ["fs"] } -tower-cookies = "0.8.0" -anyhow = "1.0.66" -dotenvy = "0.15.6" -serde = "1.0.149" -serde_json = "1.0.89" +tower-http = { version = "0.4.3", features = ["fs"] } +tower-cookies = "0.9.0" +anyhow = "1.0.71" +dotenvy = "0.15.7" +serde = "1.0.175" +serde_json = "1.0.102" tera = "1.17.1" tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } entity = { path = "../entity" } diff --git a/examples/axum_example/api/src/lib.rs b/examples/axum_example/api/src/lib.rs index 324840337c..28a7572b94 100644 --- a/examples/axum_example/api/src/lib.rs +++ b/examples/axum_example/api/src/lib.rs @@ -53,7 +53,7 @@ async fn start() -> anyhow::Result<()> { env!("CARGO_MANIFEST_DIR"), "/static" ))) - .handle_error(|error: std::io::Error| async move { + .handle_error(|error| async move { ( StatusCode::INTERNAL_SERVER_ERROR, format!("Unhandled internal error: {error}"), diff --git a/examples/basic/Cargo.toml b/examples/basic/Cargo.toml index 4c36d6cc9f..36f258443f 100644 --- a/examples/basic/Cargo.toml +++ b/examples/basic/Cargo.toml @@ -8,9 +8,9 @@ edition = "2021" publish = false [dependencies] -async-std = { version = "1.9", features = [ "attributes", "tokio1" ] } +async-std = { version = "1.12", features = [ "attributes", "tokio1" ] } sea-orm = { path = "../../", features = [ "sqlx-all", "runtime-async-std-native-tls" ] } serde_json = { version = "1" } futures = { version = "0.3" } async-stream = { version = "0.3" } -futures-util = { version = "0.3" } +futures-util = { version = "0.3" } \ No newline at end of file diff --git a/examples/graphql_example/api/Cargo.toml b/examples/graphql_example/api/Cargo.toml index 1115d83966..2ffb5544c6 100644 --- a/examples/graphql_example/api/Cargo.toml +++ b/examples/graphql_example/api/Cargo.toml @@ -7,9 +7,10 @@ publish = false [dependencies] graphql-example-service = { path = "../service" } -tokio = { version = "1.0", features = ["full"] } -axum = "0.5.1" -dotenvy = "0.15.0" -async-graphql-axum = "4.0.6" +tokio = { version = "1.29", features = ["full"] } +axum = "0.6.18" +axum-macros = "0.3" +dotenvy = "0.15.7" +async-graphql-axum = "5.0.10" entity = { path = "../entity" } -migration = { path = "../migration" } +migration = { path = "../migration" } \ No newline at end of file diff --git a/examples/graphql_example/api/src/lib.rs b/examples/graphql_example/api/src/lib.rs index 85275fcbe1..51cbc68a67 100644 --- a/examples/graphql_example/api/src/lib.rs +++ b/examples/graphql_example/api/src/lib.rs @@ -6,17 +6,19 @@ use entity::async_graphql; use async_graphql::http::{playground_source, GraphQLPlaygroundConfig}; use async_graphql_axum::{GraphQLRequest, GraphQLResponse}; use axum::{ - extract::Extension, + extract::State, response::{Html, IntoResponse}, routing::get, Router, }; +use axum_macros::debug_handler; use graphql::schema::{build_schema, AppSchema}; #[cfg(debug_assertions)] use dotenvy::dotenv; -async fn graphql_handler(schema: Extension, req: GraphQLRequest) -> GraphQLResponse { +#[debug_handler] +async fn graphql_handler(schema: State, req: GraphQLRequest) -> GraphQLResponse { schema.execute(req.into_inner()).await.into() } @@ -38,7 +40,7 @@ pub async fn main() { "/api/graphql", get(graphql_playground).post(graphql_handler), ) - .layer(Extension(schema)); + .with_state(schema); println!("Playground: http://localhost:3000/api/graphql"); diff --git a/examples/graphql_example/entity/Cargo.toml b/examples/graphql_example/entity/Cargo.toml index 1c6b5a5b53..74812d24c6 100644 --- a/examples/graphql_example/entity/Cargo.toml +++ b/examples/graphql_example/entity/Cargo.toml @@ -12,7 +12,7 @@ path = "src/lib.rs" serde = { version = "1", features = ["derive"] } [dependencies.async-graphql] -version = "4.0.6" +version = "5.0.10" [dependencies.sea-orm] path = "../../../" # remove this line in your own project diff --git a/examples/jsonrpsee_example/api/Cargo.toml b/examples/jsonrpsee_example/api/Cargo.toml index fc8883917b..32693934eb 100644 --- a/examples/jsonrpsee_example/api/Cargo.toml +++ b/examples/jsonrpsee_example/api/Cargo.toml @@ -6,14 +6,14 @@ publish = false [dependencies] jsonrpsee-example-service = { path = "../service" } -jsonrpsee = { version = "0.8.0", features = ["full"] } -jsonrpsee-core = "0.9.0" -tokio = { version = "1.8.0", features = ["full"] } +jsonrpsee = { version = "0.18.2", features = ["full"] } +jsonrpsee-core = "0.18.2" +tokio = { version = "1.29.1", features = ["full"] } serde = { version = "1", features = ["derive"] } dotenvy = "0.15" entity = { path = "../entity" } migration = { path = "../migration" } -anyhow = "1.0.52" -async-trait = "0.1.52" +anyhow = "1.0.71" +async-trait = "0.1.71" log = { version = "0.4", features = ["std"] } simplelog = "0.12" diff --git a/examples/jsonrpsee_example/api/src/lib.rs b/examples/jsonrpsee_example/api/src/lib.rs index de52523844..2e0e275609 100644 --- a/examples/jsonrpsee_example/api/src/lib.rs +++ b/examples/jsonrpsee_example/api/src/lib.rs @@ -1,11 +1,10 @@ use std::env; -use anyhow::anyhow; use entity::post; use jsonrpsee::core::{async_trait, RpcResult}; -use jsonrpsee::http_server::HttpServerBuilder; use jsonrpsee::proc_macros::rpc; -use jsonrpsee::types::error::CallError; +use jsonrpsee::server::ServerBuilder; +use jsonrpsee::types::error::ErrorObjectOwned; use jsonrpsee_example_service::sea_orm::{Database, DatabaseConnection}; use jsonrpsee_example_service::{Mutation, Query}; use log::info; @@ -14,7 +13,7 @@ use simplelog::*; use std::fmt::Display; use std::net::SocketAddr; use tokio::signal::ctrl_c; -use tokio::signal::unix::{signal, SignalKind}; +// use tokio::signal::unix::{signal, SignalKind}; const DEFAULT_POSTS_PER_PAGE: u64 = 5; @@ -88,7 +87,9 @@ where E: Display, { fn internal_call_error(self) -> RpcResult { - self.map_err(|e| jsonrpsee::core::Error::Call(CallError::Failed(anyhow!("{}", e)))) + // Err(ErrorObjectOwned::owned(1, "c", None::<()>)) + // self.map_err(|e| jsonrpsee::core::Error::Call(CallError::Failed(anyhow!("{}", e)))) + self.map_err(|e| ErrorObjectOwned::owned(1, format!("{}", e), None::<()>)) } } @@ -112,8 +113,9 @@ async fn start() -> std::io::Result<()> { let conn = Database::connect(&db_url).await.unwrap(); Migrator::up(&conn, None).await.unwrap(); - let server = HttpServerBuilder::default() + let server = ServerBuilder::default() .build(server_url.parse::().unwrap()) + .await .unwrap(); let rpc_impl = PpcImpl { conn }; @@ -121,12 +123,12 @@ async fn start() -> std::io::Result<()> { let handle = server.start(rpc_impl.into_rpc()).unwrap(); info!("starting listening {}", server_addr); - let mut sig_int = signal(SignalKind::interrupt()).unwrap(); - let mut sig_term = signal(SignalKind::terminate()).unwrap(); + // let mut sig_int = signal(SignalKind::interrupt()).unwrap(); + // let mut sig_term = signal(SignalKind::terminate()).unwrap(); tokio::select! { - _ = sig_int.recv() => info!("receive SIGINT"), - _ = sig_term.recv() => info!("receive SIGTERM"), + // _ = sig_int.recv() => info!("receive SIGINT"), + // _ = sig_term.recv() => info!("receive SIGTERM"), _ = ctrl_c() => info!("receive Ctrl C"), } handle.stop().unwrap(); diff --git a/examples/poem_example/api/Cargo.toml b/examples/poem_example/api/Cargo.toml index 383843f4a8..1830c52dd8 100644 --- a/examples/poem_example/api/Cargo.toml +++ b/examples/poem_example/api/Cargo.toml @@ -5,11 +5,11 @@ edition = "2021" [dependencies] poem-example-service = { path = "../service" } -tokio = { version = "1.15.0", features = ["macros", "rt-multi-thread"] } -poem = { version = "1.2.33", features = ["static-files"] } +tokio = { version = "1.29.0", features = ["macros", "rt-multi-thread"] } +poem = { version = "1.3.56", features = ["static-files"] } tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } serde = { version = "1", features = ["derive"] } -tera = "1.8.0" +tera = "1.19.0" dotenvy = "0.15" entity = { path = "../entity" } migration = { path = "../migration" } diff --git a/examples/rocket_example/api/Cargo.toml b/examples/rocket_example/api/Cargo.toml index bb2d5cdeec..26269bcf23 100644 --- a/examples/rocket_example/api/Cargo.toml +++ b/examples/rocket_example/api/Cargo.toml @@ -20,7 +20,7 @@ rocket_dyn_templates = { version = "0.1.0-rc.1", features = [ serde_json = { version = "1" } entity = { path = "../entity" } migration = { path = "../migration" } -tokio = "1.20.0" +tokio = "1.29.0" [dependencies.sea-orm-rocket] path = "../../../sea-orm-rocket/lib" # remove this line in your own project and uncomment the following line diff --git a/examples/salvo_example/api/Cargo.toml b/examples/salvo_example/api/Cargo.toml index 6635ccbe2e..460425de2d 100644 --- a/examples/salvo_example/api/Cargo.toml +++ b/examples/salvo_example/api/Cargo.toml @@ -5,11 +5,11 @@ edition = "2021" [dependencies] salvo-example-service = { path = "../service" } -tokio = { version = "1.15.0", features = ["macros", "rt-multi-thread"] } -salvo = { version = "0.27", features = ["affix", "serve-static"] } +tokio = { version = "1.29.0", features = ["macros", "rt-multi-thread"] } +salvo = { version = "0.49", features = ["affix", "serve-static"] } tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } serde = { version = "1", features = ["derive"] } -tera = "1.8.0" +tera = "1.19.0" dotenvy = "0.15" entity = { path = "../entity" } -migration = { path = "../migration" } +migration = { path = "../migration" } \ No newline at end of file diff --git a/examples/salvo_example/api/src/lib.rs b/examples/salvo_example/api/src/lib.rs index 76927c9531..6b1aa0fbd3 100644 --- a/examples/salvo_example/api/src/lib.rs +++ b/examples/salvo_example/api/src/lib.rs @@ -2,10 +2,8 @@ use std::env; use entity::post; use migration::{Migrator, MigratorTrait}; -use salvo::extra::affix; -use salvo::extra::serve_static::DirHandler; +use salvo::affix; use salvo::prelude::*; -use salvo::writer::Text; use salvo_example_service::{ sea_orm::{Database, DatabaseConnection}, Mutation, Query, @@ -29,7 +27,7 @@ async fn create(req: &mut Request, depot: &mut Depot, res: &mut Response) -> Res let conn = &state.conn; let form = req - .extract_form::() + .parse_form::() .await .map_err(|_| StatusError::bad_request())?; @@ -37,7 +35,7 @@ async fn create(req: &mut Request, depot: &mut Depot, res: &mut Response) -> Res .await .map_err(|_| StatusError::internal_server_error())?; - res.redirect_found("/"); + Redirect::found("/").render(res); Ok(()) } @@ -114,7 +112,7 @@ async fn update(req: &mut Request, depot: &mut Depot, res: &mut Response) -> Res let conn = &state.conn; let id = req.param::("id").unwrap_or_default(); let form = req - .extract_form::() + .parse_form::() .await .map_err(|_| StatusError::bad_request())?; @@ -122,7 +120,7 @@ async fn update(req: &mut Request, depot: &mut Depot, res: &mut Response) -> Res .await .map_err(|_| StatusError::internal_server_error())?; - res.redirect_found("/"); + Redirect::found("/").render(res); Ok(()) } @@ -138,7 +136,7 @@ async fn delete(req: &mut Request, depot: &mut Depot, res: &mut Response) -> Res .await .map_err(|_| StatusError::internal_server_error())?; - res.redirect_found("/"); + Redirect::found("/").render(res); Ok(()) } @@ -170,13 +168,13 @@ pub async fn main() { .push(Router::with_path("").get(edit).post(update)) .push(Router::with_path("delete/").post(delete)) .push( - Router::with_path("static/<**>").get(DirHandler::new(concat!( + Router::with_path("static/<**>").get(salvo::prelude::StaticDir::new(concat!( env!("CARGO_MANIFEST_DIR"), "/static" ))), ); - Server::new(TcpListener::bind(&format!("{host}:{port}"))) + Server::new(TcpListener::bind(TcpListener::new(format!("{host}:{port}"))).await) .serve(router) .await; } diff --git a/examples/salvo_example/entity/Cargo.toml b/examples/salvo_example/entity/Cargo.toml index 5171c39b8b..dfc8470e80 100644 --- a/examples/salvo_example/entity/Cargo.toml +++ b/examples/salvo_example/entity/Cargo.toml @@ -10,6 +10,7 @@ path = "src/lib.rs" [dependencies] serde = { version = "1", features = ["derive"] } +salvo = { version = "0.49" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project diff --git a/examples/salvo_example/entity/src/post.rs b/examples/salvo_example/entity/src/post.rs index 62cde830cf..7223abc746 100644 --- a/examples/salvo_example/entity/src/post.rs +++ b/examples/salvo_example/entity/src/post.rs @@ -1,7 +1,8 @@ +use salvo::prelude::Extractible; use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; -#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Extractible, Deserialize, Serialize)] #[sea_orm(table_name = "posts")] pub struct Model { #[sea_orm(primary_key)] diff --git a/examples/tonic_example/Cargo.toml b/examples/tonic_example/Cargo.toml index 115d1cb0f7..c23ee975d7 100644 --- a/examples/tonic_example/Cargo.toml +++ b/examples/tonic_example/Cargo.toml @@ -11,8 +11,8 @@ members = [".", "api", "service", "entity", "migration"] [dependencies] tonic-example-api = { path = "api" } -tonic = "0.7" -tokio = { version = "1.17", features = ["macros", "rt-multi-thread", "full"] } +tonic = "0.9.2" +tokio = { version = "1.29", features = ["macros", "rt-multi-thread", "full"] } [[bin]] name = "server" diff --git a/examples/tonic_example/api/Cargo.toml b/examples/tonic_example/api/Cargo.toml index 6d1e650cff..8868303479 100644 --- a/examples/tonic_example/api/Cargo.toml +++ b/examples/tonic_example/api/Cargo.toml @@ -6,15 +6,15 @@ publish = false [dependencies] tonic-example-service = { path = "../service" } -tonic = "0.7" -tokio = { version = "1.17", features = ["macros", "rt-multi-thread", "full"] } +tonic = "0.9.2" +tokio = { version = "1.29", features = ["macros", "rt-multi-thread", "full"] } entity = { path = "../entity" } migration = { path = "../migration" } -prost = "0.10.0" +prost = "0.11.9" serde = "1.0" [lib] path = "./src/lib.rs" [build-dependencies] -tonic-build = "0.7" +tonic-build = "0.9.2" From 96087291c2d1291afeeecbb5f803239d727bc005 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sat, 22 Jul 2023 20:02:17 +0800 Subject: [PATCH 0094/1371] 0.12.0-rc.5 --- Cargo.toml | 4 ++-- examples/actix3_example/entity/Cargo.toml | 2 +- examples/actix3_example/migration/Cargo.toml | 2 +- examples/actix3_example/service/Cargo.toml | 2 +- examples/actix_example/entity/Cargo.toml | 2 +- examples/actix_example/migration/Cargo.toml | 2 +- examples/actix_example/service/Cargo.toml | 2 +- examples/axum_example/entity/Cargo.toml | 2 +- examples/axum_example/migration/Cargo.toml | 2 +- examples/axum_example/service/Cargo.toml | 2 +- examples/graphql_example/entity/Cargo.toml | 2 +- examples/graphql_example/migration/Cargo.toml | 2 +- examples/graphql_example/service/Cargo.toml | 2 +- examples/jsonrpsee_example/entity/Cargo.toml | 2 +- examples/jsonrpsee_example/migration/Cargo.toml | 2 +- examples/jsonrpsee_example/service/Cargo.toml | 2 +- examples/poem_example/entity/Cargo.toml | 2 +- examples/poem_example/migration/Cargo.toml | 2 +- examples/poem_example/service/Cargo.toml | 2 +- examples/rocket_example/entity/Cargo.toml | 2 +- examples/rocket_example/migration/Cargo.toml | 2 +- examples/rocket_example/service/Cargo.toml | 2 +- examples/rocket_okapi_example/entity/Cargo.toml | 2 +- examples/rocket_okapi_example/migration/Cargo.toml | 2 +- examples/rocket_okapi_example/service/Cargo.toml | 2 +- examples/salvo_example/entity/Cargo.toml | 2 +- examples/salvo_example/migration/Cargo.toml | 2 +- examples/salvo_example/service/Cargo.toml | 2 +- examples/tonic_example/entity/Cargo.toml | 2 +- examples/tonic_example/migration/Cargo.toml | 2 +- examples/tonic_example/service/Cargo.toml | 2 +- sea-orm-cli/Cargo.toml | 4 ++-- sea-orm-codegen/Cargo.toml | 2 +- sea-orm-macros/Cargo.toml | 2 +- sea-orm-migration/Cargo.toml | 6 +++--- 35 files changed, 39 insertions(+), 39 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b8c3192b66..aa626aa342 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [".", "sea-orm-macros", "sea-orm-codegen"] [package] name = "sea-orm" -version = "0.12.0-rc.4" +version = "0.12.0-rc.5" authors = ["Chris Tsang "] edition = "2021" description = "🐚 An async & dynamic ORM for Rust" @@ -33,7 +33,7 @@ log = { version = "0.4", default-features = false } tracing = { version = "0.1", default-features = false, features = ["attributes", "log"] } rust_decimal = { version = "1", default-features = false, optional = true } bigdecimal = { version = "0.3", default-features = false, optional = true } -sea-orm-macros = { version = "0.12.0-rc.4", path = "sea-orm-macros", default-features = false, features = ["strum"] } +sea-orm-macros = { version = "0.12.0-rc.5", path = "sea-orm-macros", default-features = false, features = ["strum"] } sea-query = { version = "0.30.0", default-features = false, features = ["thread-safe", "hashable-value", "backend-mysql", "backend-postgres", "backend-sqlite"] } sea-query-binder = { version = "0.5.0", default-features = false, optional = true } strum = { version = "0.25", default-features = false } diff --git a/examples/actix3_example/entity/Cargo.toml b/examples/actix3_example/entity/Cargo.toml index 5171c39b8b..acd3010b8e 100644 --- a/examples/actix3_example/entity/Cargo.toml +++ b/examples/actix3_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.4" # sea-orm version +version = "0.12.0-rc.5" # sea-orm version diff --git a/examples/actix3_example/migration/Cargo.toml b/examples/actix3_example/migration/Cargo.toml index f016bf4352..6c5ae11317 100644 --- a/examples/actix3_example/migration/Cargo.toml +++ b/examples/actix3_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0-rc.4" # sea-orm-migration version +version = "0.12.0-rc.5" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-async-std-native-tls", diff --git a/examples/actix3_example/service/Cargo.toml b/examples/actix3_example/service/Cargo.toml index 63182fa2f8..f8e61a5045 100644 --- a/examples/actix3_example/service/Cargo.toml +++ b/examples/actix3_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.4" # sea-orm version +version = "0.12.0-rc.5" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/actix_example/entity/Cargo.toml b/examples/actix_example/entity/Cargo.toml index 5171c39b8b..acd3010b8e 100644 --- a/examples/actix_example/entity/Cargo.toml +++ b/examples/actix_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.4" # sea-orm version +version = "0.12.0-rc.5" # sea-orm version diff --git a/examples/actix_example/migration/Cargo.toml b/examples/actix_example/migration/Cargo.toml index c3747823ac..0c075677a0 100644 --- a/examples/actix_example/migration/Cargo.toml +++ b/examples/actix_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0-rc.4" # sea-orm-migration version +version = "0.12.0-rc.5" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-actix-native-tls", diff --git a/examples/actix_example/service/Cargo.toml b/examples/actix_example/service/Cargo.toml index d785922627..038396653a 100644 --- a/examples/actix_example/service/Cargo.toml +++ b/examples/actix_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.4" # sea-orm version +version = "0.12.0-rc.5" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/axum_example/entity/Cargo.toml b/examples/axum_example/entity/Cargo.toml index 5171c39b8b..acd3010b8e 100644 --- a/examples/axum_example/entity/Cargo.toml +++ b/examples/axum_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.4" # sea-orm version +version = "0.12.0-rc.5" # sea-orm version diff --git a/examples/axum_example/migration/Cargo.toml b/examples/axum_example/migration/Cargo.toml index b7da1873b6..51906be1f5 100644 --- a/examples/axum_example/migration/Cargo.toml +++ b/examples/axum_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0-rc.4" # sea-orm-migration version +version = "0.12.0-rc.5" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/axum_example/service/Cargo.toml b/examples/axum_example/service/Cargo.toml index 7fe9c2f179..3075342567 100644 --- a/examples/axum_example/service/Cargo.toml +++ b/examples/axum_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.4" # sea-orm version +version = "0.12.0-rc.5" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/graphql_example/entity/Cargo.toml b/examples/graphql_example/entity/Cargo.toml index 74812d24c6..47cee2630e 100644 --- a/examples/graphql_example/entity/Cargo.toml +++ b/examples/graphql_example/entity/Cargo.toml @@ -16,4 +16,4 @@ version = "5.0.10" [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.4" # sea-orm version +version = "0.12.0-rc.5" # sea-orm version diff --git a/examples/graphql_example/migration/Cargo.toml b/examples/graphql_example/migration/Cargo.toml index 5347fce8e3..2788f87e5e 100644 --- a/examples/graphql_example/migration/Cargo.toml +++ b/examples/graphql_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0-rc.4" # sea-orm-migration version +version = "0.12.0-rc.5" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/graphql_example/service/Cargo.toml b/examples/graphql_example/service/Cargo.toml index 9451909f4e..715305cea0 100644 --- a/examples/graphql_example/service/Cargo.toml +++ b/examples/graphql_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.4" # sea-orm version +version = "0.12.0-rc.5" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/jsonrpsee_example/entity/Cargo.toml b/examples/jsonrpsee_example/entity/Cargo.toml index 5171c39b8b..acd3010b8e 100644 --- a/examples/jsonrpsee_example/entity/Cargo.toml +++ b/examples/jsonrpsee_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.4" # sea-orm version +version = "0.12.0-rc.5" # sea-orm version diff --git a/examples/jsonrpsee_example/migration/Cargo.toml b/examples/jsonrpsee_example/migration/Cargo.toml index 5fa11fc400..10a147c3f6 100644 --- a/examples/jsonrpsee_example/migration/Cargo.toml +++ b/examples/jsonrpsee_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0-rc.4" # sea-orm-migration version +version = "0.12.0-rc.5" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/jsonrpsee_example/service/Cargo.toml b/examples/jsonrpsee_example/service/Cargo.toml index d0d5e6306e..0b54fe5d96 100644 --- a/examples/jsonrpsee_example/service/Cargo.toml +++ b/examples/jsonrpsee_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.4" # sea-orm version +version = "0.12.0-rc.5" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/poem_example/entity/Cargo.toml b/examples/poem_example/entity/Cargo.toml index 5171c39b8b..acd3010b8e 100644 --- a/examples/poem_example/entity/Cargo.toml +++ b/examples/poem_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.4" # sea-orm version +version = "0.12.0-rc.5" # sea-orm version diff --git a/examples/poem_example/migration/Cargo.toml b/examples/poem_example/migration/Cargo.toml index 5fa11fc400..10a147c3f6 100644 --- a/examples/poem_example/migration/Cargo.toml +++ b/examples/poem_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0-rc.4" # sea-orm-migration version +version = "0.12.0-rc.5" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/poem_example/service/Cargo.toml b/examples/poem_example/service/Cargo.toml index b0287a3373..089e37a675 100644 --- a/examples/poem_example/service/Cargo.toml +++ b/examples/poem_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.4" # sea-orm version +version = "0.12.0-rc.5" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/rocket_example/entity/Cargo.toml b/examples/rocket_example/entity/Cargo.toml index e56b23b7af..7f03766a8a 100644 --- a/examples/rocket_example/entity/Cargo.toml +++ b/examples/rocket_example/entity/Cargo.toml @@ -15,4 +15,4 @@ rocket = { version = "0.5.0-rc.1", features = [ [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.4" # sea-orm version +version = "0.12.0-rc.5" # sea-orm version diff --git a/examples/rocket_example/migration/Cargo.toml b/examples/rocket_example/migration/Cargo.toml index eab9a6b52a..af06c42e7a 100644 --- a/examples/rocket_example/migration/Cargo.toml +++ b/examples/rocket_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0-rc.4" # sea-orm-migration version +version = "0.12.0-rc.5" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/rocket_example/service/Cargo.toml b/examples/rocket_example/service/Cargo.toml index 7dfb3cc8ea..017628cd92 100644 --- a/examples/rocket_example/service/Cargo.toml +++ b/examples/rocket_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.4" # sea-orm version +version = "0.12.0-rc.5" # sea-orm version features = [ "runtime-tokio-native-tls", "sqlx-postgres", diff --git a/examples/rocket_okapi_example/entity/Cargo.toml b/examples/rocket_okapi_example/entity/Cargo.toml index 58dcc8336a..a76cdf69f3 100644 --- a/examples/rocket_okapi_example/entity/Cargo.toml +++ b/examples/rocket_okapi_example/entity/Cargo.toml @@ -15,7 +15,7 @@ rocket = { version = "0.5.0-rc.1", features = [ [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.4" # sea-orm version +version = "0.12.0-rc.5" # sea-orm version [dependencies.rocket_okapi] version = "0.8.0-rc.2" diff --git a/examples/rocket_okapi_example/migration/Cargo.toml b/examples/rocket_okapi_example/migration/Cargo.toml index eab9a6b52a..af06c42e7a 100644 --- a/examples/rocket_okapi_example/migration/Cargo.toml +++ b/examples/rocket_okapi_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0-rc.4" # sea-orm-migration version +version = "0.12.0-rc.5" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/rocket_okapi_example/service/Cargo.toml b/examples/rocket_okapi_example/service/Cargo.toml index 7dfb3cc8ea..017628cd92 100644 --- a/examples/rocket_okapi_example/service/Cargo.toml +++ b/examples/rocket_okapi_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.4" # sea-orm version +version = "0.12.0-rc.5" # sea-orm version features = [ "runtime-tokio-native-tls", "sqlx-postgres", diff --git a/examples/salvo_example/entity/Cargo.toml b/examples/salvo_example/entity/Cargo.toml index dfc8470e80..0ae12cb704 100644 --- a/examples/salvo_example/entity/Cargo.toml +++ b/examples/salvo_example/entity/Cargo.toml @@ -14,4 +14,4 @@ salvo = { version = "0.49" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.4" # sea-orm version +version = "0.12.0-rc.5" # sea-orm version diff --git a/examples/salvo_example/migration/Cargo.toml b/examples/salvo_example/migration/Cargo.toml index 5fa11fc400..10a147c3f6 100644 --- a/examples/salvo_example/migration/Cargo.toml +++ b/examples/salvo_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0-rc.4" # sea-orm-migration version +version = "0.12.0-rc.5" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/salvo_example/service/Cargo.toml b/examples/salvo_example/service/Cargo.toml index e5a1fe9c45..d99bf0aa14 100644 --- a/examples/salvo_example/service/Cargo.toml +++ b/examples/salvo_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.4" # sea-orm version +version = "0.12.0-rc.5" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/tonic_example/entity/Cargo.toml b/examples/tonic_example/entity/Cargo.toml index 5171c39b8b..acd3010b8e 100644 --- a/examples/tonic_example/entity/Cargo.toml +++ b/examples/tonic_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.4" # sea-orm version +version = "0.12.0-rc.5" # sea-orm version diff --git a/examples/tonic_example/migration/Cargo.toml b/examples/tonic_example/migration/Cargo.toml index 252f0aaa59..0260289c0d 100644 --- a/examples/tonic_example/migration/Cargo.toml +++ b/examples/tonic_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0-rc.4" # sea-orm-migration version +version = "0.12.0-rc.5" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-rustls", diff --git a/examples/tonic_example/service/Cargo.toml b/examples/tonic_example/service/Cargo.toml index 5319688b2d..9334536a13 100644 --- a/examples/tonic_example/service/Cargo.toml +++ b/examples/tonic_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.4" # sea-orm version +version = "0.12.0-rc.5" # sea-orm version features = [ "debug-print", "runtime-tokio-rustls", diff --git a/sea-orm-cli/Cargo.toml b/sea-orm-cli/Cargo.toml index 2ceafaee55..187c6d64d4 100644 --- a/sea-orm-cli/Cargo.toml +++ b/sea-orm-cli/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "sea-orm-cli" -version = "0.12.0-rc.4" +version = "0.12.0-rc.5" authors = [ "Chris Tsang ", "Billy Chan ", @@ -37,7 +37,7 @@ required-features = ["cli", "codegen"] clap = { version = "4.3", features = ["env", "derive"], optional = true } dotenvy = { version = "0.15", default-features = false, optional = true } async-std = { version = "1.9", default-features = false, features = ["attributes", "tokio1"], optional = true } -sea-orm-codegen = { version = "=0.12.0-rc.4", path = "../sea-orm-codegen", default-features = false, optional = true } +sea-orm-codegen = { version = "=0.12.0-rc.5", path = "../sea-orm-codegen", default-features = false, optional = true } sea-schema = { version = "0.14.0" } sqlx = { version = "0.7", default-features = false, features = ["mysql", "postgres"], optional = true } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["env-filter", "fmt"] } diff --git a/sea-orm-codegen/Cargo.toml b/sea-orm-codegen/Cargo.toml index 7a0ca52f1e..085f4585d7 100644 --- a/sea-orm-codegen/Cargo.toml +++ b/sea-orm-codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sea-orm-codegen" -version = "0.12.0-rc.4" +version = "0.12.0-rc.5" authors = ["Billy Chan "] edition = "2021" description = "Code Generator for SeaORM" diff --git a/sea-orm-macros/Cargo.toml b/sea-orm-macros/Cargo.toml index 49b71a38b1..b5866fc786 100644 --- a/sea-orm-macros/Cargo.toml +++ b/sea-orm-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sea-orm-macros" -version = "0.12.0-rc.4" +version = "0.12.0-rc.5" authors = [ "Billy Chan " ] edition = "2021" description = "Derive macros for SeaORM" diff --git a/sea-orm-migration/Cargo.toml b/sea-orm-migration/Cargo.toml index 16d83bfe5c..b790654043 100644 --- a/sea-orm-migration/Cargo.toml +++ b/sea-orm-migration/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "sea-orm-migration" -version = "0.12.0-rc.4" +version = "0.12.0-rc.5" authors = ["Billy Chan "] edition = "2021" description = "Migration utility for SeaORM" @@ -23,8 +23,8 @@ path = "src/lib.rs" async-trait = { version = "0.1", default-features = false } clap = { version = "4.3", features = ["env", "derive"], optional = true } dotenvy = { version = "0.15", default-features = false, optional = true } -sea-orm = { version = "0.12.0-rc.4", path = "../", default-features = false, features = ["macros"] } -sea-orm-cli = { version = "0.12.0-rc.4", path = "../sea-orm-cli", default-features = false, optional = true } +sea-orm = { version = "0.12.0-rc.5", path = "../", default-features = false, features = ["macros"] } +sea-orm-cli = { version = "0.12.0-rc.5", path = "../sea-orm-cli", default-features = false, optional = true } sea-schema = { version = "0.14.0" } tracing = { version = "0.1", default-features = false, features = ["log"] } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["env-filter", "fmt"] } From 90b72420630492824fdc925177974677935a2d39 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sat, 22 Jul 2023 20:02:49 +0800 Subject: [PATCH 0095/1371] Update build script --- build-tools/cargo-publish.sh | 4 ---- 1 file changed, 4 deletions(-) diff --git a/build-tools/cargo-publish.sh b/build-tools/cargo-publish.sh index f5f3d04c67..a561a55429 100644 --- a/build-tools/cargo-publish.sh +++ b/build-tools/cargo-publish.sh @@ -5,23 +5,19 @@ set -e cd sea-orm-codegen cargo publish cd .. -# sleep 60 # publish `sea-orm-cli` cd sea-orm-cli cargo publish cd .. -# sleep 60 # publish `sea-orm-macros` cd sea-orm-macros cargo publish cd .. -# sleep 60 # publish `sea-orm` cargo publish -# sleep 60 # publish `sea-orm-migration` cd sea-orm-migration From f589fb03d1c6224e576944be4eec214f85075b00 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sat, 22 Jul 2023 20:21:02 +0800 Subject: [PATCH 0096/1371] 0.12.0-rc.5 --- CHANGELOG.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4e4f4d0fc..a4de2217ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## 0.12.0 - Pending -+ Yanked : `0.12.0-rc.1` -+ 2023-05-19: `0.12.0-rc.2` -+ 2023-06-22: `0.12.0-rc.3` -+ 2023-07-08: `0.12.0-rc.4` ++ `0.12.0-rc.1`: Yanked ++ `0.12.0-rc.2`: 2023-05-19 ++ `0.12.0-rc.3`: 2023-06-22 ++ `0.12.0-rc.4`: 2023-07-08 ++ `0.12.0-rc.5`: 2023-07-22 ### New Features From 2ce41fa6f2807f2c6c34a6f16630deb750eef061 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Wed, 26 Jul 2023 18:59:16 +0800 Subject: [PATCH 0097/1371] Readme --- README.md | 1 - src/lib.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/README.md b/README.md index bfc00b0a2c..31f5af254e 100644 --- a/README.md +++ b/README.md @@ -208,7 +208,6 @@ The following products are powered by SeaORM: SeaORM is the foundation of: -+ [StarfishQL](https://github.com/SeaQL/starfish-ql): an experimental graph database + [Seaography](https://github.com/SeaQL/seaography): GraphQL framework for SeaORM For more projects, see [Built with SeaORM](https://github.com/SeaQL/sea-orm/blob/master/COMMUNITY.md#built-with-seaorm). diff --git a/src/lib.rs b/src/lib.rs index f7367f021f..fdc399d243 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -281,7 +281,6 @@ //! //! //! SeaORM is the foundation of: -//! + [StarfishQL](https://github.com/SeaQL/starfish-ql): an experimental graph database //! + [Seaography](https://github.com/SeaQL/seaography): GraphQL framework for SeaORM //! //! For more projects, see [Built with SeaORM](https://github.com/SeaQL/sea-orm/blob/master/COMMUNITY.md#built-with-seaorm). From ad33d7745d5d22607de8efad76974752f09740a2 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Wed, 26 Jul 2023 19:07:36 +0800 Subject: [PATCH 0098/1371] Readme --- README.md | 8 ++++---- src/lib.rs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 31f5af254e..a5fc2eb265 100644 --- a/README.md +++ b/README.md @@ -49,15 +49,15 @@ Integration examples 2. Dynamic - Built upon [SeaQuery](https://github.com/SeaQL/sea-query), SeaORM allows you to build complex queries without 'fighting the ORM'. + Built upon [SeaQuery](https://github.com/SeaQL/sea-query), SeaORM allows you to build complex dynamic queries. -3. Testable +3. Multi-backend - Use mock connections to write unit tests for your logic. + You can develop applications targeting multiple backends, or use Mock / SQLite as a test double in test suites. 4. Service Oriented - Quickly build services that join, filter, sort and paginate data in APIs. + Quickly build services that join, filter, sort and paginate data in REST, GraphQL and gRPC APIs. ## A quick taste of SeaORM diff --git a/src/lib.rs b/src/lib.rs index fdc399d243..a5ce1fe3df 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,15 +59,15 @@ //! //! 2. Dynamic //! -//! Built upon [SeaQuery](https://github.com/SeaQL/sea-query), SeaORM allows you to build complex queries without 'fighting the ORM'. +//! Built upon [SeaQuery](https://github.com/SeaQL/sea-query), SeaORM allows you to build complex dynamic queries. //! -//! 3. Testable +//! 3. Multi-backend //! -//! Use mock connections to write unit tests for your logic. +//! You can develop applications targeting multiple backends, or use Mock / SQLite as a test double in test suites. //! //! 4. Service Oriented //! -//! Quickly build services that join, filter, sort and paginate data in APIs. +//! Quickly build services that join, filter, sort and paginate data in REST, GraphQL and gRPC APIs. //! //! ## A quick taste of SeaORM //! From ebbfc0f5be9e23c7ac1ace8068ad9b24f72c1b4f Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Wed, 26 Jul 2023 17:18:01 +0100 Subject: [PATCH 0099/1371] Changelog --- CHANGELOG.md | 45 ++------------------------------------------- 1 file changed, 2 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4de2217ec..6abec42cce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -98,58 +98,17 @@ assert_eq!( The DeriveRelatedEntity derive macro will implement `seaography::RelationBuilder` for `RelatedEntity` enumeration when the `seaography` feature is enabled * Add `expr`, `exprs` and `expr_as` methods to `QuerySelect` trait https://github.com/SeaQL/sea-orm/pull/1702 -```rust -use sea_orm::sea_query::Expr; -use sea_orm::{entity::*, tests_cfg::cake, DbBackend, QuerySelect, QueryTrait}; - -assert_eq!( - cake::Entity::find() - .select_only() - .expr(Expr::col((cake::Entity, cake::Column::Id))) - .build(DbBackend::MySql) - .to_string(), - "SELECT `cake`.`id` FROM `cake`" -); - -assert_eq!( - cake::Entity::find() - .select_only() - .exprs([ - Expr::col((cake::Entity, cake::Column::Id)), - Expr::col((cake::Entity, cake::Column::Name)), - ]) - .build(DbBackend::MySql) - .to_string(), - "SELECT `cake`.`id`, `cake`.`name` FROM `cake`" -); - -assert_eq!( - cake::Entity::find() - .expr_as( - Func::upper(Expr::col((cake::Entity, cake::Column::Name))), - "name_upper" - ) - .build(DbBackend::MySql) - .to_string(), - "SELECT `cake`.`id`, `cake`.`name`, UPPER(`cake`.`name`) AS `name_upper` FROM `cake`" -); -``` * Add `DbErr::sql_err()` method to convert error into common database errors `SqlErr`, such as unique constraint or foreign key violation errors. https://github.com/SeaQL/sea-orm/pull/1707 ```rust assert!(matches!( - cake - .into_active_model() - .insert(db) - .await + cake.into_active_model().insert(db).await .expect_err("Insert a row with duplicated primary key") .sql_err(), Some(SqlErr::UniqueConstraintViolation(_)) )); assert!(matches!( - fk_cake - .insert(db) - .await + fk_cake.insert(db).await .expect_err("Insert a row with invalid foreign key") .sql_err(), Some(SqlErr::ForeignKeyConstraintViolation(_)) From 8064097639849a8f90c3af41b6c301271af339ee Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Wed, 26 Jul 2023 18:43:55 +0100 Subject: [PATCH 0100/1371] Comment --- CHANGELOG.md | 4 ++-- src/executor/insert.rs | 6 +++--- src/executor/update.rs | 2 +- tests/relational_tests.rs | 1 - 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6abec42cce..99572d69da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -149,10 +149,10 @@ assert_eq!(format!("{}", DisplayTea::BreakfastTea), "Breakfast Tea"); ``` * Added `UpdateMany::exec_with_returning()` https://github.com/SeaQL/sea-orm/pull/1677 ```rust -Entity::update_many() +let models: Vec = Entity::update_many() .col_expr(Column::Values, Expr::expr(..)) .exec_with_returning(db) - .await + .await?; ``` * Supporting `default_expr` in `DeriveEntityModel` https://github.com/SeaQL/sea-orm/pull/1474 ```rust diff --git a/src/executor/insert.rs b/src/executor/insert.rs index cf6f320fd4..2f6aa74ce7 100644 --- a/src/executor/insert.rs +++ b/src/executor/insert.rs @@ -82,7 +82,7 @@ where } } - /// Execute an insert operation and return the inserted model (use `RETURNING` syntax if database supported) + /// Execute an insert operation and return the inserted model (use `RETURNING` syntax if supported) pub async fn exec_with_returning<'a, C>( self, db: &'a C, @@ -141,7 +141,7 @@ where Inserter::::new(self.primary_key, self.query).exec_without_returning(db) } - /// Execute an insert operation and return the inserted model (use `RETURNING` syntax if database supported) + /// Execute an insert operation and return the inserted model (use `RETURNING` syntax if supported) pub fn exec_with_returning<'a, C>( self, db: &'a C, @@ -190,7 +190,7 @@ where exec_insert_without_returning(self.query, db) } - /// Execute an insert operation and return the inserted model (use `RETURNING` syntax if database supported) + /// Execute an insert operation and return the inserted model (use `RETURNING` syntax if supported) pub fn exec_with_returning<'a, C>( self, db: &'a C, diff --git a/src/executor/update.rs b/src/executor/update.rs index cf8938a579..e2ae3550b4 100644 --- a/src/executor/update.rs +++ b/src/executor/update.rs @@ -46,7 +46,7 @@ where Updater::new(self.query).exec(db).await } - /// Execute an update operation and return the updated model (use `RETURNING` syntax if database supported) + /// Execute an update operation and return the updated model (use `RETURNING` syntax if supported) /// /// # Panics /// diff --git a/tests/relational_tests.rs b/tests/relational_tests.rs index 6b0c6950b4..a2994ee84a 100644 --- a/tests/relational_tests.rs +++ b/tests/relational_tests.rs @@ -758,7 +758,6 @@ pub async fn linked() -> Result<(), DbErr> { .build(sea_orm::DatabaseBackend::MySql) .to_string(), [ - // FIXME: This might be faulty! "SELECT `baker`.`id` AS `A_id`,", "`baker`.`name` AS `A_name`,", "`baker`.`contact_details` AS `A_contact_details`,", From 97ee0a970368be8ef62266453bd400b24ffe4f5c Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Wed, 26 Jul 2023 20:17:35 +0100 Subject: [PATCH 0101/1371] Test derive FromJsonQueryResult --- sea-orm-macros/src/derives/value_type.rs | 2 +- tests/common/features/json_vec_derive.rs | 19 +++++++++++++++++++ tests/common/features/mod.rs | 1 + tests/json_vec_tests.rs | 21 +++++++++++++++++++++ 4 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 tests/common/features/json_vec_derive.rs diff --git a/sea-orm-macros/src/derives/value_type.rs b/sea-orm-macros/src/derives/value_type.rs index 2a1ee606ec..d7ebfe4907 100644 --- a/sea-orm-macros/src/derives/value_type.rs +++ b/sea-orm-macros/src/derives/value_type.rs @@ -100,7 +100,7 @@ impl DeriveValueType { quote!( #[automatically_derived] - impl From<#name> for Value { + impl std::convert::From<#name> for Value { fn from(source: #name) -> Self { source.0.into() } diff --git a/tests/common/features/json_vec_derive.rs b/tests/common/features/json_vec_derive.rs new file mode 100644 index 0000000000..ebe954955d --- /dev/null +++ b/tests/common/features/json_vec_derive.rs @@ -0,0 +1,19 @@ +use sea_orm::entity::prelude::*; +use sea_orm::FromJsonQueryResult; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] +#[sea_orm(table_name = "json_vec")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub str_vec: StringVec, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromJsonQueryResult)] +pub struct StringVec(pub Vec); diff --git a/tests/common/features/mod.rs b/tests/common/features/mod.rs index 407a599171..84a6f7f223 100644 --- a/tests/common/features/mod.rs +++ b/tests/common/features/mod.rs @@ -13,6 +13,7 @@ pub mod event_trigger; pub mod insert_default; pub mod json_struct; pub mod json_vec; +pub mod json_vec_derive; pub mod metadata; pub mod pi; pub mod repository; diff --git a/tests/json_vec_tests.rs b/tests/json_vec_tests.rs index cd89d121c7..0221f0cdd9 100644 --- a/tests/json_vec_tests.rs +++ b/tests/json_vec_tests.rs @@ -14,6 +14,7 @@ async fn main() -> Result<(), DbErr> { let ctx = TestContext::new("json_vec_tests").await; create_tables(&ctx.db).await?; insert_json_vec(&ctx.db).await?; + insert_json_vec_derive(&ctx.db).await?; ctx.delete().await; Ok(()) @@ -38,3 +39,23 @@ pub async fn insert_json_vec(db: &DatabaseConnection) -> Result<(), DbErr> { Ok(()) } + +pub async fn insert_json_vec_derive(db: &DatabaseConnection) -> Result<(), DbErr> { + let json_vec = json_vec_derive::Model { + id: 2, + str_vec: json_vec_derive::StringVec(vec!["4".to_string(), "5".to_string(), "6".to_string()]), + }; + + let result = json_vec.clone().into_active_model().insert(db).await?; + + assert_eq!(result, json_vec); + + let model = json_vec_derive::Entity::find() + .filter(json_vec_derive::Column::Id.eq(json_vec.id)) + .one(db) + .await?; + + assert_eq!(model, Some(json_vec)); + + Ok(()) +} From 3f81fd5e89f98b58dae54c479afe06219087c4cf Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Wed, 26 Jul 2023 20:27:26 +0100 Subject: [PATCH 0102/1371] cargo fmt --- tests/json_vec_tests.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/json_vec_tests.rs b/tests/json_vec_tests.rs index 0221f0cdd9..0fa183a9f3 100644 --- a/tests/json_vec_tests.rs +++ b/tests/json_vec_tests.rs @@ -43,7 +43,11 @@ pub async fn insert_json_vec(db: &DatabaseConnection) -> Result<(), DbErr> { pub async fn insert_json_vec_derive(db: &DatabaseConnection) -> Result<(), DbErr> { let json_vec = json_vec_derive::Model { id: 2, - str_vec: json_vec_derive::StringVec(vec!["4".to_string(), "5".to_string(), "6".to_string()]), + str_vec: json_vec_derive::StringVec(vec![ + "4".to_string(), + "5".to_string(), + "6".to_string(), + ]), }; let result = json_vec.clone().into_active_model().insert(db).await?; From 530c612bc4010bd6ba08f60c3e87a9d00046278d Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Wed, 26 Jul 2023 20:31:58 +0100 Subject: [PATCH 0103/1371] Edit --- CHANGELOG.md | 40 ++++------------------------------------ 1 file changed, 4 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99572d69da..6a376669e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -333,39 +333,8 @@ pub enum StringValueVariant { _0x300x20123, } ``` -* [sea-orm-cli] The implementation of `Related` with `via` and `to` methods will not be generated if there exists multiple paths via an intermediate table. Like in the schema defined below - Path 1. `users <-> users_votes <-> bills`, Path 2. `users <-> users_saved_bills <-> bills` https://github.com/SeaQL/sea-orm/pull/1435 -```sql -CREATE TABLE users -( - id uuid PRIMARY KEY DEFAULT uuid_generate_v1mc(), - email TEXT UNIQUE NOT NULL, - ... -); -``` -```sql -CREATE TABLE bills -( - id uuid PRIMARY KEY DEFAULT uuid_generate_v1mc(), - ... -); -``` -```sql -CREATE TABLE users_votes -( - user_id uuid REFERENCES users (id) ON UPDATE CASCADE ON DELETE CASCADE, - bill_id uuid REFERENCES bills (id) ON UPDATE CASCADE ON DELETE CASCADE, - vote boolean NOT NULL, - CONSTRAINT users_bills_pkey PRIMARY KEY (user_id, bill_id) -); -``` -```sql -CREATE TABLE users_saved_bills -( - user_id uuid REFERENCES users (id) ON UPDATE CASCADE ON DELETE CASCADE, - bill_id uuid REFERENCES bills (id) ON UPDATE CASCADE ON DELETE CASCADE, - CONSTRAINT users_saved_bills_pkey PRIMARY KEY (user_id, bill_id) -); -``` +* [sea-orm-cli] Fix Postgres enum arrays https://github.com/SeaQL/sea-orm/pull/1678 +* [sea-orm-cli] The implementation of `Related` with `via` and `to` methods will not be generated if there exists multiple paths via an intermediate table https://github.com/SeaQL/sea-orm/pull/1435 * [sea-orm-cli] fixed entity generation includes partitioned tables https://github.com/SeaQL/sea-orm/issues/1582, https://github.com/SeaQL/sea-schema/pull/105 * Fixed `ActiveEnum::db_type()` return type does not implement `ColumnTypeTrait` https://github.com/SeaQL/sea-orm/pull/1576 ```rust @@ -375,15 +344,14 @@ impl ColumnTrait for Column { match self { ... // `db_type()` returns `ColumnDef`; now it implements `ColumnTypeTrait` - Self::Thing => AnActiveEnumThing::db_type().def(), + Self::Thing => SomeThing::db_type().def(), ... } } } ``` * Resolved `insert_many` failing if the models iterator is empty https://github.com/SeaQL/sea-orm/issues/873 -* Update the template MD file of `migration/README.md`, fix a faulty sample `migrate init` shell script https://github.com/SeaQL/sea-orm/pull/1723 -* [sea-orm-cli] Fix Postgres enum arrays https://github.com/SeaQL/sea-orm/pull/1678 +* Update the template MD file of `migration/README.md`, fixed a faulty sample `migrate init` shell script https://github.com/SeaQL/sea-orm/pull/1723 ### Breaking changes From ec581abf561f165dd05f76aa6a388679bf1cab45 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Wed, 26 Jul 2023 21:09:25 +0100 Subject: [PATCH 0104/1371] Fix test --- tests/common/features/json_vec.rs | 37 ++++++++++++++---------- tests/common/features/json_vec_derive.rs | 2 +- tests/common/features/schema.rs | 2 +- tests/json_vec_tests.rs | 10 +++++-- 4 files changed, 30 insertions(+), 21 deletions(-) diff --git a/tests/common/features/json_vec.rs b/tests/common/features/json_vec.rs index 8e2fdcc600..5eb6234964 100644 --- a/tests/common/features/json_vec.rs +++ b/tests/common/features/json_vec.rs @@ -1,5 +1,5 @@ use sea_orm::entity::prelude::*; -use sea_orm::{TryGetError, TryGetable}; +use sea_orm::TryGetableFromJson; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub str_vec: StringVec, + pub str_vec: Option, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] @@ -18,26 +18,25 @@ impl ActiveModelBehavior for ActiveModel {} #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct StringVec(pub Vec); +impl TryGetableFromJson for StringVec {} + impl From for Value { fn from(source: StringVec) -> Self { - Value::String(serde_json::to_string(&source).ok().map(Box::new)) - } -} - -impl TryGetable for StringVec { - fn try_get_by(res: &QueryResult, idx: I) -> Result { - let json_str: String = res.try_get_by(idx).map_err(TryGetError::DbErr)?; - serde_json::from_str(&json_str).map_err(|e| TryGetError::DbErr(DbErr::Json(e.to_string()))) + sea_orm::Value::Json( + serde_json::to_value(&source) + .ok() + .map(|s| std::boxed::Box::new(s)), + ) } } impl sea_query::ValueType for StringVec { fn try_from(v: Value) -> Result { match v { - Value::String(Some(x)) => Ok(StringVec( - serde_json::from_str(&x).map_err(|_| sea_query::ValueTypeErr)?, - )), - _ => Err(sea_query::ValueTypeErr), + sea_orm::Value::Json(Some(json)) => { + Ok(serde_json::from_value(*json).map_err(|_| sea_orm::sea_query::ValueTypeErr)?) + } + _ => Err(sea_orm::sea_query::ValueTypeErr), } } @@ -46,10 +45,16 @@ impl sea_query::ValueType for StringVec { } fn array_type() -> sea_orm::sea_query::ArrayType { - sea_orm::sea_query::ArrayType::String + sea_orm::sea_query::ArrayType::Json } fn column_type() -> sea_query::ColumnType { - sea_query::ColumnType::String(None) + sea_query::ColumnType::Json + } +} + +impl sea_orm::sea_query::Nullable for StringVec { + fn null() -> sea_orm::Value { + sea_orm::Value::Json(None) } } diff --git a/tests/common/features/json_vec_derive.rs b/tests/common/features/json_vec_derive.rs index ebe954955d..cc6e177b7a 100644 --- a/tests/common/features/json_vec_derive.rs +++ b/tests/common/features/json_vec_derive.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub str_vec: StringVec, + pub str_vec: Option, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] diff --git a/tests/common/features/schema.rs b/tests/common/features/schema.rs index 058474e0ed..6e1f71cc01 100644 --- a/tests/common/features/schema.rs +++ b/tests/common/features/schema.rs @@ -313,7 +313,7 @@ pub async fn create_json_vec_table(db: &DbConn) -> Result { .auto_increment() .primary_key(), ) - .col(ColumnDef::new(json_vec::Column::StrVec).string().not_null()) + .col(ColumnDef::new(json_vec::Column::StrVec).json()) .to_owned(); create_table(db, &create_table_stmt, JsonVec).await diff --git a/tests/json_vec_tests.rs b/tests/json_vec_tests.rs index 0fa183a9f3..9af2a33046 100644 --- a/tests/json_vec_tests.rs +++ b/tests/json_vec_tests.rs @@ -23,7 +23,11 @@ async fn main() -> Result<(), DbErr> { pub async fn insert_json_vec(db: &DatabaseConnection) -> Result<(), DbErr> { let json_vec = json_vec::Model { id: 1, - str_vec: json_vec::StringVec(vec!["1".to_string(), "2".to_string(), "3".to_string()]), + str_vec: Some(json_vec::StringVec(vec![ + "1".to_string(), + "2".to_string(), + "3".to_string(), + ])), }; let result = json_vec.clone().into_active_model().insert(db).await?; @@ -43,11 +47,11 @@ pub async fn insert_json_vec(db: &DatabaseConnection) -> Result<(), DbErr> { pub async fn insert_json_vec_derive(db: &DatabaseConnection) -> Result<(), DbErr> { let json_vec = json_vec_derive::Model { id: 2, - str_vec: json_vec_derive::StringVec(vec![ + str_vec: Some(json_vec_derive::StringVec(vec![ "4".to_string(), "5".to_string(), "6".to_string(), - ]), + ])), }; let result = json_vec.clone().into_active_model().insert(db).await?; From ae8dcceb43822c5ef41462be79c3ccadeffe39de Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 27 Jul 2023 19:11:17 +0800 Subject: [PATCH 0105/1371] Community Survey --- README.md | 17 +++++++++-------- sea-orm-cli/src/cli.rs | 17 ++++++++++------- src/lib.rs | 17 +++++++++-------- 3 files changed, 28 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index a5fc2eb265..bbf2ca3a24 100644 --- a/README.md +++ b/README.md @@ -19,17 +19,18 @@ ## Getting Started [![GitHub stars](https://img.shields.io/github/stars/SeaQL/sea-orm.svg?style=social&label=Star&maxAge=1)](https://github.com/SeaQL/sea-orm/stargazers/) -If you like what we do, consider starring, commenting, sharing and contributing! +If you like what we do, consider starring, sharing and contributing! + ++ [Documentation](https://www.sea-ql.org/SeaORM) ++ [Tutorial](https://www.sea-ql.org/sea-orm-tutorial) ++ [Cookbook](https://www.sea-ql.org/sea-orm-cookbook) [![Discord](https://img.shields.io/discord/873880840487206962?label=Discord)](https://discord.com/invite/uCPdDXzbdv) Join our Discord server to chat with others in the SeaQL community! -+ [Getting Started](https://www.sea-ql.org/SeaORM/docs/index) -+ [Step-by-step Tutorials](https://www.sea-ql.org/sea-orm-tutorial/) -+ [Cookbook](https://www.sea-ql.org/sea-orm-cookbook/) -+ [Usage Example](https://github.com/SeaQL/sea-orm/tree/master/examples/basic) +Please help us with maintaining SeaORM by completing the [SeaQL Community Survey 2023](https://sea-ql.org/community-survey)! -Integration examples +Integration examples: + [Actix v4 Example](https://github.com/SeaQL/sea-orm/tree/master/examples/actix_example) + [Actix v3 Example](https://github.com/SeaQL/sea-orm/tree/master/examples/actix3_example) @@ -51,9 +52,9 @@ Integration examples Built upon [SeaQuery](https://github.com/SeaQL/sea-query), SeaORM allows you to build complex dynamic queries. -3. Multi-backend +3. Testable - You can develop applications targeting multiple backends, or use Mock / SQLite as a test double in test suites. + Use Mock / SQLite to write tests for your application logic. 4. Service Oriented diff --git a/sea-orm-cli/src/cli.rs b/sea-orm-cli/src/cli.rs index 1421646f34..ff88d30a4a 100644 --- a/sea-orm-cli/src/cli.rs +++ b/sea-orm-cli/src/cli.rs @@ -24,16 +24,19 @@ AUTHORS: An async & dynamic ORM for Rust \ | ,/ =============================== \|_/ - Getting Started! - - documentation: https://www.sea-ql.org/SeaORM - - step-by-step tutorials: https://www.sea-ql.org/sea-orm-tutorial - - integration examples: https://github.com/SeaQL/sea-orm/tree/master/examples - - cookbook: https://www.sea-ql.org/sea-orm-cookbook + Getting Started + - Documentation: https://www.sea-ql.org/SeaORM + - Tutorial: https://www.sea-ql.org/sea-orm-tutorial + - Examples: https://github.com/SeaQL/sea-orm/tree/master/examples + - Cookbook: https://www.sea-ql.org/sea-orm-cookbook Join our Discord server to chat with others in the SeaQL community! - - invitation link: https://discord.com/invite/uCPdDXzbdv + - Invitation: https://discord.com/invite/uCPdDXzbdv - If you like what we do, consider starring, commenting, sharing and contributing! + SeaQL Community Survey 2023 + - Link: https://sea-ql.org/community-survey + + If you like what we do, consider starring, sharing and contributing! "# )] pub struct Cli { diff --git a/src/lib.rs b/src/lib.rs index a5ce1fe3df..d77f52d850 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,17 +29,18 @@ //! ## Getting Started //! //! [![GitHub stars](https://img.shields.io/github/stars/SeaQL/sea-orm.svg?style=social&label=Star&maxAge=1)](https://github.com/SeaQL/sea-orm/stargazers/) -//! If you like what we do, consider starring, commenting, sharing and contributing! +//! If you like what we do, consider starring, sharing and contributing! +//! +//! + [Documentation](https://www.sea-ql.org/SeaORM) +//! + [Tutorial](https://www.sea-ql.org/sea-orm-tutorial) +//! + [Cookbook](https://www.sea-ql.org/sea-orm-cookbook) //! //! [![Discord](https://img.shields.io/discord/873880840487206962?label=Discord)](https://discord.com/invite/uCPdDXzbdv) //! Join our Discord server to chat with others in the SeaQL community! //! -//! + [Getting Started](https://www.sea-ql.org/SeaORM/docs/index) -//! + [Step-by-step Tutorials](https://www.sea-ql.org/sea-orm-tutorial/) -//! + [Cookbook](https://www.sea-ql.org/sea-orm-cookbook/) -//! + [Usage Example](https://github.com/SeaQL/sea-orm/tree/master/examples/basic) +//! Please help us with maintaining SeaORM by completing the [SeaQL Community Survey 2023](https://sea-ql.org/community-survey)! //! -//! Integration examples +//! Integration examples: //! //! + [Actix v4 Example](https://github.com/SeaQL/sea-orm/tree/master/examples/actix_example) //! + [Actix v3 Example](https://github.com/SeaQL/sea-orm/tree/master/examples/actix3_example) @@ -61,9 +62,9 @@ //! //! Built upon [SeaQuery](https://github.com/SeaQL/sea-query), SeaORM allows you to build complex dynamic queries. //! -//! 3. Multi-backend +//! 3. Testable //! -//! You can develop applications targeting multiple backends, or use Mock / SQLite as a test double in test suites. +//! Use Mock / SQLite to write tests for your application logic. //! //! 4. Service Oriented //! From cc677e37d54d49a8a76340a7ea39eb4ff6642237 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 27 Jul 2023 19:19:04 +0800 Subject: [PATCH 0106/1371] Print some Entity Generate messages (#1787) --- sea-orm-cli/src/commands/generate.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sea-orm-cli/src/commands/generate.rs b/sea-orm-cli/src/commands/generate.rs index 934f91a8d6..5c41a5cfad 100644 --- a/sea-orm-cli/src/commands/generate.rs +++ b/sea-orm-cli/src/commands/generate.rs @@ -110,7 +110,9 @@ pub async fn run_generate_command( use sea_schema::mysql::discovery::SchemaDiscovery; use sqlx::MySql; + println!("Connecting to MySQL ..."); let connection = connect::(max_connections, url.as_str(), None).await?; + println!("Discovering schema ..."); let schema_discovery = SchemaDiscovery::new(connection, database_name); let schema = schema_discovery.discover().await?; let table_stmts = schema @@ -127,7 +129,9 @@ pub async fn run_generate_command( use sea_schema::sqlite::discovery::SchemaDiscovery; use sqlx::Sqlite; + println!("Connecting to SQLite ..."); let connection = connect::(max_connections, url.as_str(), None).await?; + println!("Discovering schema ..."); let schema_discovery = SchemaDiscovery::new(connection); let schema = schema_discovery.discover().await?; let table_stmts = schema @@ -144,9 +148,11 @@ pub async fn run_generate_command( use sea_schema::postgres::discovery::SchemaDiscovery; use sqlx::Postgres; + println!("Connecting to Postgres ..."); let schema = &database_schema; let connection = connect::(max_connections, url.as_str(), Some(schema)).await?; + println!("Discovering schema ..."); let schema_discovery = SchemaDiscovery::new(connection, schema); let schema = schema_discovery.discover().await?; let table_stmts = schema @@ -161,6 +167,7 @@ pub async fn run_generate_command( } _ => unimplemented!("{} is not supported", url.scheme()), }; + println!("... discovered."); let writer_context = EntityWriterContext::new( expanded_format, @@ -182,6 +189,7 @@ pub async fn run_generate_command( for OutputFile { name, content } in output.files.iter() { let file_path = dir.join(name); + println!("Writing {file_path}"); let mut file = fs::File::create(file_path)?; file.write_all(content.as_bytes())?; } @@ -194,6 +202,8 @@ pub async fn run_generate_command( return Err(format!("Fail to format file `{name}`").into()); } } + + println!("... Done."); } } From e7661a742355f4fd3d3a9dea6d54e7e018066ff2 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 27 Jul 2023 12:51:03 +0100 Subject: [PATCH 0107/1371] Fix --- sea-orm-cli/src/commands/generate.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sea-orm-cli/src/commands/generate.rs b/sea-orm-cli/src/commands/generate.rs index 5c41a5cfad..7b7a895620 100644 --- a/sea-orm-cli/src/commands/generate.rs +++ b/sea-orm-cli/src/commands/generate.rs @@ -189,7 +189,7 @@ pub async fn run_generate_command( for OutputFile { name, content } in output.files.iter() { let file_path = dir.join(name); - println!("Writing {file_path}"); + println!("Writing {}", file_path.display()); let mut file = fs::File::create(file_path)?; file.write_all(content.as_bytes())?; } From c5529b33f9782e1baba50204c4b2a5cd8d3095aa Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 27 Jul 2023 14:29:38 +0100 Subject: [PATCH 0108/1371] Readme --- README.md | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bbf2ca3a24..beefbdf789 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ Integration examples: 3. Testable - Use Mock / SQLite to write tests for your application logic. + Use mock connections and/or SQLite to write tests for your application logic. 4. Service Oriented diff --git a/src/lib.rs b/src/lib.rs index d77f52d850..ce02a92d7d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -64,7 +64,7 @@ //! //! 3. Testable //! -//! Use Mock / SQLite to write tests for your application logic. +//! Use mock connections and/or SQLite to write tests for your application logic. //! //! 4. Service Oriented //! From 4e6c1c6a561ac970560fa305afc583af39ea893b Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Thu, 27 Jul 2023 21:48:26 +0800 Subject: [PATCH 0109/1371] build-tools --- .github/workflows/rust.yml | 1 + build-tools/del-rel-dep.sh | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 build-tools/del-rel-dep.sh diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 763fd32372..6e3a523bd5 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -63,6 +63,7 @@ on: - master - 0.*.x - pr/**/ci + - ci-* concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.ref || github.run_id }} diff --git a/build-tools/del-rel-dep.sh b/build-tools/del-rel-dep.sh new file mode 100644 index 0000000000..d507fc2760 --- /dev/null +++ b/build-tools/del-rel-dep.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -e + +find examples/ -depth -type f -name '*.toml' -exec sed -i '/^path = "..\/..\/..\/sea-orm-migration"/d' {} \; +find examples/ -depth -type f -name '*.toml' -exec sed -i '/^path = "..\/..\/..\/"/d' {} \; \ No newline at end of file From 3002c0cf8d3d844ef281bd784bb1fa425c05c75f Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 27 Jul 2023 14:47:57 +0100 Subject: [PATCH 0110/1371] Changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a376669e8..ed27265c1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## 0.12.0 - Pending +## 0.12.1 - 2023-07-27 + `0.12.0-rc.1`: Yanked + `0.12.0-rc.2`: 2023-05-19 From 1e6b10b532ee984b2f1380cb04c4daeee97db71a Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 27 Jul 2023 14:48:37 +0100 Subject: [PATCH 0111/1371] 0.12.1 --- Cargo.toml | 4 ++-- sea-orm-cli/Cargo.toml | 4 ++-- sea-orm-codegen/Cargo.toml | 2 +- sea-orm-macros/Cargo.toml | 2 +- sea-orm-migration/Cargo.toml | 6 +++--- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index aa626aa342..e4d5b74aa9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [".", "sea-orm-macros", "sea-orm-codegen"] [package] name = "sea-orm" -version = "0.12.0-rc.5" +version = "0.12.1" authors = ["Chris Tsang "] edition = "2021" description = "🐚 An async & dynamic ORM for Rust" @@ -33,7 +33,7 @@ log = { version = "0.4", default-features = false } tracing = { version = "0.1", default-features = false, features = ["attributes", "log"] } rust_decimal = { version = "1", default-features = false, optional = true } bigdecimal = { version = "0.3", default-features = false, optional = true } -sea-orm-macros = { version = "0.12.0-rc.5", path = "sea-orm-macros", default-features = false, features = ["strum"] } +sea-orm-macros = { version = "0.12.1", path = "sea-orm-macros", default-features = false, features = ["strum"] } sea-query = { version = "0.30.0", default-features = false, features = ["thread-safe", "hashable-value", "backend-mysql", "backend-postgres", "backend-sqlite"] } sea-query-binder = { version = "0.5.0", default-features = false, optional = true } strum = { version = "0.25", default-features = false } diff --git a/sea-orm-cli/Cargo.toml b/sea-orm-cli/Cargo.toml index 187c6d64d4..ae20072e47 100644 --- a/sea-orm-cli/Cargo.toml +++ b/sea-orm-cli/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "sea-orm-cli" -version = "0.12.0-rc.5" +version = "0.12.1" authors = [ "Chris Tsang ", "Billy Chan ", @@ -37,7 +37,7 @@ required-features = ["cli", "codegen"] clap = { version = "4.3", features = ["env", "derive"], optional = true } dotenvy = { version = "0.15", default-features = false, optional = true } async-std = { version = "1.9", default-features = false, features = ["attributes", "tokio1"], optional = true } -sea-orm-codegen = { version = "=0.12.0-rc.5", path = "../sea-orm-codegen", default-features = false, optional = true } +sea-orm-codegen = { version = "=0.12.1", path = "../sea-orm-codegen", default-features = false, optional = true } sea-schema = { version = "0.14.0" } sqlx = { version = "0.7", default-features = false, features = ["mysql", "postgres"], optional = true } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["env-filter", "fmt"] } diff --git a/sea-orm-codegen/Cargo.toml b/sea-orm-codegen/Cargo.toml index 085f4585d7..5974a1d2ee 100644 --- a/sea-orm-codegen/Cargo.toml +++ b/sea-orm-codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sea-orm-codegen" -version = "0.12.0-rc.5" +version = "0.12.1" authors = ["Billy Chan "] edition = "2021" description = "Code Generator for SeaORM" diff --git a/sea-orm-macros/Cargo.toml b/sea-orm-macros/Cargo.toml index b5866fc786..b4c2f93b6b 100644 --- a/sea-orm-macros/Cargo.toml +++ b/sea-orm-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sea-orm-macros" -version = "0.12.0-rc.5" +version = "0.12.1" authors = [ "Billy Chan " ] edition = "2021" description = "Derive macros for SeaORM" diff --git a/sea-orm-migration/Cargo.toml b/sea-orm-migration/Cargo.toml index b790654043..d8235dc104 100644 --- a/sea-orm-migration/Cargo.toml +++ b/sea-orm-migration/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "sea-orm-migration" -version = "0.12.0-rc.5" +version = "0.12.1" authors = ["Billy Chan "] edition = "2021" description = "Migration utility for SeaORM" @@ -23,8 +23,8 @@ path = "src/lib.rs" async-trait = { version = "0.1", default-features = false } clap = { version = "4.3", features = ["env", "derive"], optional = true } dotenvy = { version = "0.15", default-features = false, optional = true } -sea-orm = { version = "0.12.0-rc.5", path = "../", default-features = false, features = ["macros"] } -sea-orm-cli = { version = "0.12.0-rc.5", path = "../sea-orm-cli", default-features = false, optional = true } +sea-orm = { version = "0.12.1", path = "../", default-features = false, features = ["macros"] } +sea-orm-cli = { version = "0.12.1", path = "../sea-orm-cli", default-features = false, optional = true } sea-schema = { version = "0.14.0" } tracing = { version = "0.1", default-features = false, features = ["log"] } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["env-filter", "fmt"] } From d7d45ae66931565c90a1d1cc510a20866ee1cccc Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 27 Jul 2023 14:48:37 +0100 Subject: [PATCH 0112/1371] update examples --- examples/actix3_example/entity/Cargo.toml | 2 +- examples/actix3_example/migration/Cargo.toml | 2 +- examples/actix3_example/service/Cargo.toml | 2 +- examples/actix_example/entity/Cargo.toml | 2 +- examples/actix_example/migration/Cargo.toml | 2 +- examples/actix_example/service/Cargo.toml | 2 +- examples/axum_example/entity/Cargo.toml | 2 +- examples/axum_example/migration/Cargo.toml | 2 +- examples/axum_example/service/Cargo.toml | 2 +- examples/graphql_example/entity/Cargo.toml | 2 +- examples/graphql_example/migration/Cargo.toml | 2 +- examples/graphql_example/service/Cargo.toml | 2 +- examples/jsonrpsee_example/entity/Cargo.toml | 2 +- examples/jsonrpsee_example/migration/Cargo.toml | 2 +- examples/jsonrpsee_example/service/Cargo.toml | 2 +- examples/poem_example/entity/Cargo.toml | 2 +- examples/poem_example/migration/Cargo.toml | 2 +- examples/poem_example/service/Cargo.toml | 2 +- examples/rocket_example/entity/Cargo.toml | 2 +- examples/rocket_example/migration/Cargo.toml | 2 +- examples/rocket_example/service/Cargo.toml | 2 +- examples/rocket_okapi_example/entity/Cargo.toml | 2 +- examples/rocket_okapi_example/migration/Cargo.toml | 2 +- examples/rocket_okapi_example/service/Cargo.toml | 2 +- examples/salvo_example/entity/Cargo.toml | 2 +- examples/salvo_example/migration/Cargo.toml | 2 +- examples/salvo_example/service/Cargo.toml | 2 +- examples/tonic_example/entity/Cargo.toml | 2 +- examples/tonic_example/migration/Cargo.toml | 2 +- examples/tonic_example/service/Cargo.toml | 2 +- 30 files changed, 30 insertions(+), 30 deletions(-) diff --git a/examples/actix3_example/entity/Cargo.toml b/examples/actix3_example/entity/Cargo.toml index acd3010b8e..3a18e7bc4b 100644 --- a/examples/actix3_example/entity/Cargo.toml +++ b/examples/actix3_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.5" # sea-orm version +version = "0.12.1" # sea-orm version diff --git a/examples/actix3_example/migration/Cargo.toml b/examples/actix3_example/migration/Cargo.toml index 6c5ae11317..ddecf3bed6 100644 --- a/examples/actix3_example/migration/Cargo.toml +++ b/examples/actix3_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0-rc.5" # sea-orm-migration version +version = "0.12.1" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-async-std-native-tls", diff --git a/examples/actix3_example/service/Cargo.toml b/examples/actix3_example/service/Cargo.toml index f8e61a5045..2db4960124 100644 --- a/examples/actix3_example/service/Cargo.toml +++ b/examples/actix3_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.5" # sea-orm version +version = "0.12.1" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/actix_example/entity/Cargo.toml b/examples/actix_example/entity/Cargo.toml index acd3010b8e..3a18e7bc4b 100644 --- a/examples/actix_example/entity/Cargo.toml +++ b/examples/actix_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.5" # sea-orm version +version = "0.12.1" # sea-orm version diff --git a/examples/actix_example/migration/Cargo.toml b/examples/actix_example/migration/Cargo.toml index 0c075677a0..f3090dfc46 100644 --- a/examples/actix_example/migration/Cargo.toml +++ b/examples/actix_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0-rc.5" # sea-orm-migration version +version = "0.12.1" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-actix-native-tls", diff --git a/examples/actix_example/service/Cargo.toml b/examples/actix_example/service/Cargo.toml index 038396653a..46aace63bc 100644 --- a/examples/actix_example/service/Cargo.toml +++ b/examples/actix_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.5" # sea-orm version +version = "0.12.1" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/axum_example/entity/Cargo.toml b/examples/axum_example/entity/Cargo.toml index acd3010b8e..3a18e7bc4b 100644 --- a/examples/axum_example/entity/Cargo.toml +++ b/examples/axum_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.5" # sea-orm version +version = "0.12.1" # sea-orm version diff --git a/examples/axum_example/migration/Cargo.toml b/examples/axum_example/migration/Cargo.toml index 51906be1f5..fa43fb3ad9 100644 --- a/examples/axum_example/migration/Cargo.toml +++ b/examples/axum_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0-rc.5" # sea-orm-migration version +version = "0.12.1" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/axum_example/service/Cargo.toml b/examples/axum_example/service/Cargo.toml index 3075342567..9099cba1c6 100644 --- a/examples/axum_example/service/Cargo.toml +++ b/examples/axum_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.5" # sea-orm version +version = "0.12.1" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/graphql_example/entity/Cargo.toml b/examples/graphql_example/entity/Cargo.toml index 47cee2630e..4ebefab08d 100644 --- a/examples/graphql_example/entity/Cargo.toml +++ b/examples/graphql_example/entity/Cargo.toml @@ -16,4 +16,4 @@ version = "5.0.10" [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.5" # sea-orm version +version = "0.12.1" # sea-orm version diff --git a/examples/graphql_example/migration/Cargo.toml b/examples/graphql_example/migration/Cargo.toml index 2788f87e5e..51c59d9ad6 100644 --- a/examples/graphql_example/migration/Cargo.toml +++ b/examples/graphql_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0-rc.5" # sea-orm-migration version +version = "0.12.1" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/graphql_example/service/Cargo.toml b/examples/graphql_example/service/Cargo.toml index 715305cea0..af0eda05cb 100644 --- a/examples/graphql_example/service/Cargo.toml +++ b/examples/graphql_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.5" # sea-orm version +version = "0.12.1" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/jsonrpsee_example/entity/Cargo.toml b/examples/jsonrpsee_example/entity/Cargo.toml index acd3010b8e..3a18e7bc4b 100644 --- a/examples/jsonrpsee_example/entity/Cargo.toml +++ b/examples/jsonrpsee_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.5" # sea-orm version +version = "0.12.1" # sea-orm version diff --git a/examples/jsonrpsee_example/migration/Cargo.toml b/examples/jsonrpsee_example/migration/Cargo.toml index 10a147c3f6..7b2d5391b4 100644 --- a/examples/jsonrpsee_example/migration/Cargo.toml +++ b/examples/jsonrpsee_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0-rc.5" # sea-orm-migration version +version = "0.12.1" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/jsonrpsee_example/service/Cargo.toml b/examples/jsonrpsee_example/service/Cargo.toml index 0b54fe5d96..e78006d04c 100644 --- a/examples/jsonrpsee_example/service/Cargo.toml +++ b/examples/jsonrpsee_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.5" # sea-orm version +version = "0.12.1" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/poem_example/entity/Cargo.toml b/examples/poem_example/entity/Cargo.toml index acd3010b8e..3a18e7bc4b 100644 --- a/examples/poem_example/entity/Cargo.toml +++ b/examples/poem_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.5" # sea-orm version +version = "0.12.1" # sea-orm version diff --git a/examples/poem_example/migration/Cargo.toml b/examples/poem_example/migration/Cargo.toml index 10a147c3f6..7b2d5391b4 100644 --- a/examples/poem_example/migration/Cargo.toml +++ b/examples/poem_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0-rc.5" # sea-orm-migration version +version = "0.12.1" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/poem_example/service/Cargo.toml b/examples/poem_example/service/Cargo.toml index 089e37a675..daf227e603 100644 --- a/examples/poem_example/service/Cargo.toml +++ b/examples/poem_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.5" # sea-orm version +version = "0.12.1" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/rocket_example/entity/Cargo.toml b/examples/rocket_example/entity/Cargo.toml index 7f03766a8a..b6b407edce 100644 --- a/examples/rocket_example/entity/Cargo.toml +++ b/examples/rocket_example/entity/Cargo.toml @@ -15,4 +15,4 @@ rocket = { version = "0.5.0-rc.1", features = [ [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.5" # sea-orm version +version = "0.12.1" # sea-orm version diff --git a/examples/rocket_example/migration/Cargo.toml b/examples/rocket_example/migration/Cargo.toml index af06c42e7a..625f0a6512 100644 --- a/examples/rocket_example/migration/Cargo.toml +++ b/examples/rocket_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0-rc.5" # sea-orm-migration version +version = "0.12.1" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/rocket_example/service/Cargo.toml b/examples/rocket_example/service/Cargo.toml index 017628cd92..630d699251 100644 --- a/examples/rocket_example/service/Cargo.toml +++ b/examples/rocket_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.5" # sea-orm version +version = "0.12.1" # sea-orm version features = [ "runtime-tokio-native-tls", "sqlx-postgres", diff --git a/examples/rocket_okapi_example/entity/Cargo.toml b/examples/rocket_okapi_example/entity/Cargo.toml index a76cdf69f3..e0729f9cfc 100644 --- a/examples/rocket_okapi_example/entity/Cargo.toml +++ b/examples/rocket_okapi_example/entity/Cargo.toml @@ -15,7 +15,7 @@ rocket = { version = "0.5.0-rc.1", features = [ [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.5" # sea-orm version +version = "0.12.1" # sea-orm version [dependencies.rocket_okapi] version = "0.8.0-rc.2" diff --git a/examples/rocket_okapi_example/migration/Cargo.toml b/examples/rocket_okapi_example/migration/Cargo.toml index af06c42e7a..625f0a6512 100644 --- a/examples/rocket_okapi_example/migration/Cargo.toml +++ b/examples/rocket_okapi_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0-rc.5" # sea-orm-migration version +version = "0.12.1" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/rocket_okapi_example/service/Cargo.toml b/examples/rocket_okapi_example/service/Cargo.toml index 017628cd92..630d699251 100644 --- a/examples/rocket_okapi_example/service/Cargo.toml +++ b/examples/rocket_okapi_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.5" # sea-orm version +version = "0.12.1" # sea-orm version features = [ "runtime-tokio-native-tls", "sqlx-postgres", diff --git a/examples/salvo_example/entity/Cargo.toml b/examples/salvo_example/entity/Cargo.toml index 0ae12cb704..9f26f06f25 100644 --- a/examples/salvo_example/entity/Cargo.toml +++ b/examples/salvo_example/entity/Cargo.toml @@ -14,4 +14,4 @@ salvo = { version = "0.49" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.5" # sea-orm version +version = "0.12.1" # sea-orm version diff --git a/examples/salvo_example/migration/Cargo.toml b/examples/salvo_example/migration/Cargo.toml index 10a147c3f6..7b2d5391b4 100644 --- a/examples/salvo_example/migration/Cargo.toml +++ b/examples/salvo_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0-rc.5" # sea-orm-migration version +version = "0.12.1" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/salvo_example/service/Cargo.toml b/examples/salvo_example/service/Cargo.toml index d99bf0aa14..d90467cf5d 100644 --- a/examples/salvo_example/service/Cargo.toml +++ b/examples/salvo_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.5" # sea-orm version +version = "0.12.1" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/tonic_example/entity/Cargo.toml b/examples/tonic_example/entity/Cargo.toml index acd3010b8e..3a18e7bc4b 100644 --- a/examples/tonic_example/entity/Cargo.toml +++ b/examples/tonic_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.5" # sea-orm version +version = "0.12.1" # sea-orm version diff --git a/examples/tonic_example/migration/Cargo.toml b/examples/tonic_example/migration/Cargo.toml index 0260289c0d..225035e9b7 100644 --- a/examples/tonic_example/migration/Cargo.toml +++ b/examples/tonic_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.0-rc.5" # sea-orm-migration version +version = "0.12.1" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-rustls", diff --git a/examples/tonic_example/service/Cargo.toml b/examples/tonic_example/service/Cargo.toml index 9334536a13..776716aad4 100644 --- a/examples/tonic_example/service/Cargo.toml +++ b/examples/tonic_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.0-rc.5" # sea-orm version +version = "0.12.1" # sea-orm version features = [ "debug-print", "runtime-tokio-rustls", From 7af76fc7530b41fc01a035bd36b8032a8c2ecd5f Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sun, 30 Jul 2023 05:08:28 +0800 Subject: [PATCH 0113/1371] Seaography example WIP (#1788) * Seaography example WIP * Seaography example * Screenshot --- examples/seaography_example/README.md | 80 +++++++ .../seaography_example/Seaography example.png | Bin 0 -> 200065 bytes examples/seaography_example/graphql/.env | 3 + .../seaography_example/graphql/Cargo.toml | 27 +++ .../graphql/src/entities/baker.rs | 60 ++++++ .../graphql/src/entities/bakery.rs | 43 ++++ .../graphql/src/entities/cake.rs | 62 ++++++ .../graphql/src/entities/cake_baker.rs | 54 +++++ .../graphql/src/entities/mod.rs | 8 + .../graphql/src/entities/prelude.rs | 6 + .../seaography_example/graphql/src/lib.rs | 8 + .../seaography_example/graphql/src/main.rs | 64 ++++++ .../graphql/src/query_root.rs | 28 +++ .../seaography_example/migration/Cargo.toml | 26 +++ .../seaography_example/migration/README.md | 51 +++++ .../migration/src/entity/baker.rs | 50 +++++ .../migration/src/entity/bakery.rs | 35 ++++ .../migration/src/entity/cake.rs | 52 +++++ .../migration/src/entity/cake_baker.rs | 46 +++++ .../migration/src/entity/mod.rs | 8 + .../migration/src/entity/prelude.rs | 6 + .../seaography_example/migration/src/lib.rs | 29 +++ .../m20230101_000001_create_bakery_table.rs | 40 ++++ .../m20230101_000002_create_baker_table.rs | 56 +++++ .../src/m20230101_000003_create_cake_table.rs | 58 ++++++ ...20230101_000004_create_cake_baker_table.rs | 66 ++++++ .../m20230101_000005_create_customer_table.rs | 1 + .../m20230101_000006_create_order_table.rs | 1 + .../m20230101_000007_create_lineitem_table.rs | 1 + .../src/m20230102_000001_seed_bakery_data.rs | 195 ++++++++++++++++++ .../seaography_example/migration/src/main.rs | 6 + 31 files changed, 1170 insertions(+) create mode 100644 examples/seaography_example/README.md create mode 100644 examples/seaography_example/Seaography example.png create mode 100644 examples/seaography_example/graphql/.env create mode 100644 examples/seaography_example/graphql/Cargo.toml create mode 100644 examples/seaography_example/graphql/src/entities/baker.rs create mode 100644 examples/seaography_example/graphql/src/entities/bakery.rs create mode 100644 examples/seaography_example/graphql/src/entities/cake.rs create mode 100644 examples/seaography_example/graphql/src/entities/cake_baker.rs create mode 100644 examples/seaography_example/graphql/src/entities/mod.rs create mode 100644 examples/seaography_example/graphql/src/entities/prelude.rs create mode 100644 examples/seaography_example/graphql/src/lib.rs create mode 100644 examples/seaography_example/graphql/src/main.rs create mode 100644 examples/seaography_example/graphql/src/query_root.rs create mode 100644 examples/seaography_example/migration/Cargo.toml create mode 100644 examples/seaography_example/migration/README.md create mode 100644 examples/seaography_example/migration/src/entity/baker.rs create mode 100644 examples/seaography_example/migration/src/entity/bakery.rs create mode 100644 examples/seaography_example/migration/src/entity/cake.rs create mode 100644 examples/seaography_example/migration/src/entity/cake_baker.rs create mode 100644 examples/seaography_example/migration/src/entity/mod.rs create mode 100644 examples/seaography_example/migration/src/entity/prelude.rs create mode 100644 examples/seaography_example/migration/src/lib.rs create mode 100644 examples/seaography_example/migration/src/m20230101_000001_create_bakery_table.rs create mode 100644 examples/seaography_example/migration/src/m20230101_000002_create_baker_table.rs create mode 100644 examples/seaography_example/migration/src/m20230101_000003_create_cake_table.rs create mode 100644 examples/seaography_example/migration/src/m20230101_000004_create_cake_baker_table.rs create mode 100644 examples/seaography_example/migration/src/m20230101_000005_create_customer_table.rs create mode 100644 examples/seaography_example/migration/src/m20230101_000006_create_order_table.rs create mode 100644 examples/seaography_example/migration/src/m20230101_000007_create_lineitem_table.rs create mode 100644 examples/seaography_example/migration/src/m20230102_000001_seed_bakery_data.rs create mode 100644 examples/seaography_example/migration/src/main.rs diff --git a/examples/seaography_example/README.md b/examples/seaography_example/README.md new file mode 100644 index 0000000000..b4325b4773 --- /dev/null +++ b/examples/seaography_example/README.md @@ -0,0 +1,80 @@ +# SeaORM Seaography Example + +![Seaography screenshot with Bakery schema]() + +## Specifiy a database url + +``` +export DATABASE_URL=mysql://sea:sea@localhost/bakery +``` + +## Setup the Database first + +Cd into `migration` folder, follow instructions there, but basically: + +``` +cargo run +``` + +## Install Seaography + +``` +cargo install seaography-cli@^1.0.0-rc.2 +``` + +## Generate Seaography project + +``` +rm -rf graphql # this entire folder is generated +sea-orm-cli generate entity --output-dir graphql/src/entities --seaography +seaography-cli graphql graphql/src/entities $DATABASE_URL sea-orm-seaography-example +``` + +## Running the project + +``` +cd graphql +cargo run +``` + +## Run some queries + +### Bakery -> Cake -> Baker + +```graphql +{ + bakery(pagination: { page: { limit: 10, page: 0 } }, orderBy: { name: ASC }) { + nodes { + name + cake { + nodes { + name + price + baker { + nodes { + name + } + } + } + } + } + } +} +``` + +### List gluten-free cakes and know where to buy them + +```graphql +{ + cake(filters: { glutenFree: { eq: 1 } }) { + nodes { + name + price + glutenFree + bakery { + name + } + } + } +} +``` diff --git a/examples/seaography_example/Seaography example.png b/examples/seaography_example/Seaography example.png new file mode 100644 index 0000000000000000000000000000000000000000..a13d4a6ac39214e0dea8d3b27e77ca4c2b8a8062 GIT binary patch literal 200065 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%0DX#_R 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

f}3Qt#5 z8{by%+jyW6C~kEK(_VQ=ah@Db{Wc+y^{UG&cdlNB*{RDBS5#`&wh^fB zFWQ`C6^I6kwXtphoJhu9H#E$pFP&j8)9Xc!EjHFmdTSMJhlhT(HOE=nvI8N#kvl)n z!(b5VFWE`ikM@$B6CTJ?Qj$agXdlxdZj6WbD+Yg1)4N7ZnFB_lwLo3`n%+)H2-|Hp zSBU}OIE_mg23~di48aCe+I?-JCsz8IYtL7d-uW30I5>z5Z)~%7rvJ9U&gjorPXFvW zfRB7+)lJYFOc*{s_}Zkii)t4gHe$?mH-r)C@^v6Nar=5Hr^8-zM+@tN#4lsK+OT`E zj*Jr*myGL20Gk}FTV@IJ8h_RozX)G5^G8IAknTv>4e#u$65ptOFZH{zOAaBby#GeX z@K8g8Yzis*}x-4hgVb~mGZju&L5B06|~VidV?CQuE+ z?K{tB^y=-K%3)WdOl-Tro?y`)??!e?LSBfG|5M|>attMh_}o3uNJWj$J$=5qQ+H7P zdv4(xz%kyo8UA8C8iwb~tKYjiiOl4b zFqu`?mRo{bZ8;aId-WwBrEY9o%1d=^CXZ}muhK+K zQt}5Kj^nySf51k~s{iYpg2FiIzLx$S7?#1{fg@08%{Z!~Wst$Ga^v_N>zOsFR?|Pl z*1`GndeRKnjCs=VMa*J%X76ez!%Yto~f%RN~Ep zAK2pFQCl4+3xf)^R5j?!Zro?{APTPU&K>p4^sDuYSA&JsJ$;TMM4%k<j5UrL-(y zrqW*GuO6ZgK^)v|X5K>sO5$&x=%9I-tTXU0F)ZQ377174BL>w^g;SKGfG7wm(!+hW zc2ClKV^uFG@h2My%#x8#vDKq)W1dJ#QlE-w%u5!$v!-K-Eo1&R4y;+}-I3cpiKUji z4Cca$zc_*qI2Fq{<4QsrbV6HOMM)<2wOdz6p!#ci2y$B1b+z1_-|5e}V!}GeyQ3C- zirRRO!`MfCq@%cJKng?04nP9m{J--DLaL$chQ)@_{8%jv4miEDkMyF=7Z_!((`^nJ zU&M`d7h|XI6ej?$zs~jG{bpS3PrF&Pphky~#qLBcfGN-u(joNtY~!X=KNsTuT#zuR z683!&Icp;ohzr0A)|b+8w=1IB3PhS&%VzZ_p1X@ZgQx4)SH>Pa8l@qEMFJ_F zGs}`0m?(&fk37kGf8Ok60VKW}ndkQ^rL@7^4ydSGY;L3CLwDqyFOA@;aRZ5nzgy1< z-mOsi-iMEepdG@6Z8D@HK78PNgE8=rZw^?Q0-@IVEA{5dxf?{Kc5G8Iy%&=g&6d|w zG1%tb*GP@8ODpivO=+mBga+3V1ToCnUmN=l(lF#Z%ecK^HYUkX9mwSI^i`YX_&-1c z*WnDa7H2(mnM1ltJL;dl^@>4&w@K57E}=%BQ!`5j=Rw)NutfN``N4vPF%3TF$ITv; zkiGE_c9^nPGgl@tbkDo=4BMHl1Lp4c%vm>|etaET{FMzc*Qy>dyIQ311J!p)^^ncp z!0C2vN9dvxb**l6uY6Z=uVD=1AO82~!%CM07j`oKs)u#kYOyuwWO|}z2dj_~WFY93 z65@smbRKj~w#m0vnt$HmHrc=6svN6dW31H8W*huc&#ZAh)$o0MsO@wuM^|+&6`Yqg z`PO|k^=@mW1xwGB0mg)bqrN51_(A=P*tiwVH|Ms>Yn$WqDj?(Wp9>@%ErM8M2h)aC zDze(4?xeC$%MF`8 zJZ`fDt@rns8Z0+gl$6rx6lGPuyMqz~L`QWxX@dy+*k)=#@zMxkDgX3>_|O5S^*IVn zVfnasi^UoGw-l)7Q9)DnwxAP#hrCMkHytt3eVZm$DsR zzX8&eqB^RE9f9C#@}s3$_fD}2B_XaswPHt)z2x4T!*d?Ka;-6=c5cfyTs$XMZ~9bP zBl*E1mGVFcb2¬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 literal 0 HcmV?d00001 diff --git a/examples/seaography_example/graphql/.env b/examples/seaography_example/graphql/.env new file mode 100644 index 0000000000..3f3e265a71 --- /dev/null +++ b/examples/seaography_example/graphql/.env @@ -0,0 +1,3 @@ +DATABASE_URL="mysql://sea:sea@localhost/bakery" +# COMPLEXITY_LIMIT= +# DEPTH_LIMIT= \ No newline at end of file diff --git a/examples/seaography_example/graphql/Cargo.toml b/examples/seaography_example/graphql/Cargo.toml new file mode 100644 index 0000000000..4a9da62c3b --- /dev/null +++ b/examples/seaography_example/graphql/Cargo.toml @@ -0,0 +1,27 @@ +[package] +edition = "2021" +name = "sea-orm-seaography-example" +version = "0.3.0" +publish = false + +[dependencies] +poem = { version = "1.3.56" } +async-graphql-poem = { version = "5.0.10" } +async-graphql = { version = "5.0.10", features = ["decimal", "chrono", "dataloader", "dynamic-schema"] } +async-trait = { version = "0.1.72" } +dotenv = "0.15.0" +sea-orm = { version = "0.12.0", features = ["sqlx-mysql", "runtime-async-std-native-tls", "seaography"] } +tokio = { version = "1.29.1", features = ["macros", "rt-multi-thread"] } +tracing = { version = "0.1.37" } +tracing-subscriber = { version = "0.3.17" } +lazy_static = { version = "1.4.0" } + +[dependencies.seaography] +version = "1.0.0-rc.2" # seaography version +features = ["with-decimal", "with-chrono"] + +[dev-dependencies] +serde_json = { version = "1.0.103" } + +[workspace] +members = [] \ No newline at end of file diff --git a/examples/seaography_example/graphql/src/entities/baker.rs b/examples/seaography_example/graphql/src/entities/baker.rs new file mode 100644 index 0000000000..ba2e6ac227 --- /dev/null +++ b/examples/seaography_example/graphql/src/entities/baker.rs @@ -0,0 +1,60 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "baker")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub name: String, + pub contact: String, + pub bakery_id: Option, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::bakery::Entity", + from = "Column::BakeryId", + to = "super::bakery::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + Bakery, + #[sea_orm(has_many = "super::cake_baker::Entity")] + CakeBaker, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Bakery.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::CakeBaker.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::cake_baker::Relation::Cake.def() + } + fn via() -> Option { + Some(super::cake_baker::Relation::Baker.def().rev()) + } +} + +impl ActiveModelBehavior for ActiveModel {} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelatedEntity)] +pub enum RelatedEntity { + #[sea_orm(entity = "super::bakery::Entity")] + Bakery, + #[sea_orm(entity = "super::cake_baker::Entity")] + CakeBaker, + #[sea_orm(entity = "super::cake::Entity")] + Cake, +} diff --git a/examples/seaography_example/graphql/src/entities/bakery.rs b/examples/seaography_example/graphql/src/entities/bakery.rs new file mode 100644 index 0000000000..a168d51ec8 --- /dev/null +++ b/examples/seaography_example/graphql/src/entities/bakery.rs @@ -0,0 +1,43 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel)] +#[sea_orm(table_name = "bakery")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub name: String, + #[sea_orm(column_type = "Double")] + pub profit_margin: f64, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::baker::Entity")] + Baker, + #[sea_orm(has_many = "super::cake::Entity")] + Cake, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Baker.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Cake.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelatedEntity)] +pub enum RelatedEntity { + #[sea_orm(entity = "super::baker::Entity")] + Baker, + #[sea_orm(entity = "super::cake::Entity")] + Cake, +} diff --git a/examples/seaography_example/graphql/src/entities/cake.rs b/examples/seaography_example/graphql/src/entities/cake.rs new file mode 100644 index 0000000000..2fe78ca1d6 --- /dev/null +++ b/examples/seaography_example/graphql/src/entities/cake.rs @@ -0,0 +1,62 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "cake")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub name: String, + #[sea_orm(column_type = "Decimal(Some((19, 4)))")] + pub price: Decimal, + pub bakery_id: Option, + pub gluten_free: i8, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::bakery::Entity", + from = "Column::BakeryId", + to = "super::bakery::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + Bakery, + #[sea_orm(has_many = "super::cake_baker::Entity")] + CakeBaker, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Bakery.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::CakeBaker.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::cake_baker::Relation::Baker.def() + } + fn via() -> Option { + Some(super::cake_baker::Relation::Cake.def().rev()) + } +} + +impl ActiveModelBehavior for ActiveModel {} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelatedEntity)] +pub enum RelatedEntity { + #[sea_orm(entity = "super::bakery::Entity")] + Bakery, + #[sea_orm(entity = "super::cake_baker::Entity")] + CakeBaker, + #[sea_orm(entity = "super::baker::Entity")] + Baker, +} diff --git a/examples/seaography_example/graphql/src/entities/cake_baker.rs b/examples/seaography_example/graphql/src/entities/cake_baker.rs new file mode 100644 index 0000000000..7a6295882c --- /dev/null +++ b/examples/seaography_example/graphql/src/entities/cake_baker.rs @@ -0,0 +1,54 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "cake_baker")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub cake_id: i32, + #[sea_orm(primary_key, auto_increment = false)] + pub baker_id: i32, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::baker::Entity", + from = "Column::BakerId", + to = "super::baker::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + Baker, + #[sea_orm( + belongs_to = "super::cake::Entity", + from = "Column::CakeId", + to = "super::cake::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + Cake, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Baker.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Cake.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelatedEntity)] +pub enum RelatedEntity { + #[sea_orm(entity = "super::baker::Entity")] + Baker, + #[sea_orm(entity = "super::cake::Entity")] + Cake, +} diff --git a/examples/seaography_example/graphql/src/entities/mod.rs b/examples/seaography_example/graphql/src/entities/mod.rs new file mode 100644 index 0000000000..dc59aec47f --- /dev/null +++ b/examples/seaography_example/graphql/src/entities/mod.rs @@ -0,0 +1,8 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1 + +pub mod prelude; + +pub mod baker; +pub mod bakery; +pub mod cake; +pub mod cake_baker; diff --git a/examples/seaography_example/graphql/src/entities/prelude.rs b/examples/seaography_example/graphql/src/entities/prelude.rs new file mode 100644 index 0000000000..9433bfe6a8 --- /dev/null +++ b/examples/seaography_example/graphql/src/entities/prelude.rs @@ -0,0 +1,6 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1 + +pub use super::baker::Entity as Baker; +pub use super::bakery::Entity as Bakery; +pub use super::cake::Entity as Cake; +pub use super::cake_baker::Entity as CakeBaker; diff --git a/examples/seaography_example/graphql/src/lib.rs b/examples/seaography_example/graphql/src/lib.rs new file mode 100644 index 0000000000..770aa614e3 --- /dev/null +++ b/examples/seaography_example/graphql/src/lib.rs @@ -0,0 +1,8 @@ +use sea_orm::prelude::*; + +pub mod entities; +pub mod query_root; + +pub struct OrmDataloader { + pub db: DatabaseConnection, +} diff --git a/examples/seaography_example/graphql/src/main.rs b/examples/seaography_example/graphql/src/main.rs new file mode 100644 index 0000000000..6ddab6890e --- /dev/null +++ b/examples/seaography_example/graphql/src/main.rs @@ -0,0 +1,64 @@ +use async_graphql::{ + dataloader::DataLoader, + http::{playground_source, GraphQLPlaygroundConfig}, +}; +use async_graphql_poem::GraphQL; +use dotenv::dotenv; +use lazy_static::lazy_static; +use poem::{get, handler, listener::TcpListener, web::Html, IntoResponse, Route, Server}; +use sea_orm::Database; +use sea_orm_seaography_example::*; +use std::env; + +lazy_static! { + static ref URL: String = env::var("URL").unwrap_or("0.0.0.0:8000".into()); + static ref ENDPOINT: String = env::var("ENDPOINT").unwrap_or("/".into()); + static ref DATABASE_URL: String = + env::var("DATABASE_URL").expect("DATABASE_URL environment variable not set"); + static ref DEPTH_LIMIT: Option = env::var("DEPTH_LIMIT").map_or(None, |data| Some( + data.parse().expect("DEPTH_LIMIT is not a number") + )); + static ref COMPLEXITY_LIMIT: Option = env::var("COMPLEXITY_LIMIT") + .map_or(None, |data| { + Some(data.parse().expect("COMPLEXITY_LIMIT is not a number")) + }); +} + +#[handler] +async fn graphql_playground() -> impl IntoResponse { + Html(playground_source(GraphQLPlaygroundConfig::new(&ENDPOINT))) +} + +#[tokio::main] +async fn main() { + dotenv().ok(); + tracing_subscriber::fmt() + .with_max_level(tracing::Level::INFO) + .with_test_writer() + .init(); + let database = Database::connect(&*DATABASE_URL) + .await + .expect("Fail to initialize database connection"); + let orm_dataloader: DataLoader = DataLoader::new( + OrmDataloader { + db: database.clone(), + }, + tokio::spawn, + ); + let schema = sea_orm_seaography_example::query_root::schema( + database, + orm_dataloader, + *DEPTH_LIMIT, + *COMPLEXITY_LIMIT, + ) + .unwrap(); + let app = Route::new().at( + &*ENDPOINT, + get(graphql_playground).post(GraphQL::new(schema)), + ); + println!("Visit GraphQL Playground at http://{}", *URL); + Server::new(TcpListener::bind(&*URL)) + .run(app) + .await + .expect("Fail to start web server"); +} diff --git a/examples/seaography_example/graphql/src/query_root.rs b/examples/seaography_example/graphql/src/query_root.rs new file mode 100644 index 0000000000..5fe88b2ddd --- /dev/null +++ b/examples/seaography_example/graphql/src/query_root.rs @@ -0,0 +1,28 @@ +use crate::{entities::*, OrmDataloader}; +use async_graphql::{dataloader::DataLoader, dynamic::*}; +use sea_orm::DatabaseConnection; +use seaography::{Builder, BuilderContext}; + +lazy_static::lazy_static! { static ref CONTEXT : BuilderContext = BuilderContext :: default () ; } + +pub fn schema( + database: DatabaseConnection, + orm_dataloader: DataLoader, + depth: Option, + complexity: Option, +) -> Result { + let mut builder = Builder::new(&CONTEXT); + seaography::register_entities!(builder, [baker, bakery, cake, cake_baker,]); + let schema = builder.schema_builder(); + let schema = if let Some(depth) = depth { + schema.limit_depth(depth) + } else { + schema + }; + let schema = if let Some(complexity) = complexity { + schema.limit_complexity(complexity) + } else { + schema + }; + schema.data(database).data(orm_dataloader).finish() +} diff --git a/examples/seaography_example/migration/Cargo.toml b/examples/seaography_example/migration/Cargo.toml new file mode 100644 index 0000000000..e6f156a093 --- /dev/null +++ b/examples/seaography_example/migration/Cargo.toml @@ -0,0 +1,26 @@ +[workspace] + +[package] +name = "migration" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +name = "migration" +path = "src/lib.rs" + +[dependencies] +async-std = { version = "1", features = ["attributes", "tokio1"] } + +[dependencies.sea-orm] +path = "../../.." # remove this line in your own project +version = "0.12.1" # sea-orm version + +[dependencies.sea-orm-migration] +path = "../../../sea-orm-migration" # remove this line in your own project +version = "0.12.1" # sea-orm-migration version +features = [ + "runtime-async-std-native-tls", + "sqlx-mysql", +] diff --git a/examples/seaography_example/migration/README.md b/examples/seaography_example/migration/README.md new file mode 100644 index 0000000000..cf79bde037 --- /dev/null +++ b/examples/seaography_example/migration/README.md @@ -0,0 +1,51 @@ +# Bakery Schema + +Assume the database is named `bakery`: + +```sh +export DATABASE_URL=mysql://sea:sea@localhost/bakery +``` + +# Re-generate entities + +```sh +sea-orm-cli generate entity --output-dir src/entity +``` + +# Running Migrator CLI + +- Apply all pending migrations + ```sh + cargo run + ``` + ```sh + cargo run -- up + ``` +- Apply first 10 pending migrations + ```sh + cargo run -- up -n 10 + ``` +- Rollback last applied migrations + ```sh + cargo run -- down + ``` +- Rollback last 10 applied migrations + ```sh + cargo run -- down -n 10 + ``` +- Drop all tables from the database, then reapply all migrations + ```sh + cargo run -- fresh + ``` +- Rollback all applied migrations, then reapply all migrations + ```sh + cargo run -- refresh + ``` +- Rollback all applied migrations + ```sh + cargo run -- reset + ``` +- Check the status of all migrations + ```sh + cargo run -- status + ``` diff --git a/examples/seaography_example/migration/src/entity/baker.rs b/examples/seaography_example/migration/src/entity/baker.rs new file mode 100644 index 0000000000..a83d1ed20d --- /dev/null +++ b/examples/seaography_example/migration/src/entity/baker.rs @@ -0,0 +1,50 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "baker")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub name: String, + pub contact: String, + pub bakery_id: Option, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::bakery::Entity", + from = "Column::BakeryId", + to = "super::bakery::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + Bakery, + #[sea_orm(has_many = "super::cake_baker::Entity")] + CakeBaker, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Bakery.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::CakeBaker.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::cake_baker::Relation::Cake.def() + } + fn via() -> Option { + Some(super::cake_baker::Relation::Baker.def().rev()) + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/examples/seaography_example/migration/src/entity/bakery.rs b/examples/seaography_example/migration/src/entity/bakery.rs new file mode 100644 index 0000000000..82186837aa --- /dev/null +++ b/examples/seaography_example/migration/src/entity/bakery.rs @@ -0,0 +1,35 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel)] +#[sea_orm(table_name = "bakery")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub name: String, + #[sea_orm(column_type = "Double")] + pub profit_margin: f64, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::baker::Entity")] + Baker, + #[sea_orm(has_many = "super::cake::Entity")] + Cake, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Baker.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Cake.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/examples/seaography_example/migration/src/entity/cake.rs b/examples/seaography_example/migration/src/entity/cake.rs new file mode 100644 index 0000000000..58bc8edc0c --- /dev/null +++ b/examples/seaography_example/migration/src/entity/cake.rs @@ -0,0 +1,52 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "cake")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub name: String, + #[sea_orm(column_type = "Decimal(Some((19, 4)))")] + pub price: Decimal, + pub bakery_id: Option, + pub gluten_free: i8, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::bakery::Entity", + from = "Column::BakeryId", + to = "super::bakery::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + Bakery, + #[sea_orm(has_many = "super::cake_baker::Entity")] + CakeBaker, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Bakery.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::CakeBaker.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::cake_baker::Relation::Baker.def() + } + fn via() -> Option { + Some(super::cake_baker::Relation::Cake.def().rev()) + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/examples/seaography_example/migration/src/entity/cake_baker.rs b/examples/seaography_example/migration/src/entity/cake_baker.rs new file mode 100644 index 0000000000..ace84e2eba --- /dev/null +++ b/examples/seaography_example/migration/src/entity/cake_baker.rs @@ -0,0 +1,46 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "cake_baker")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub cake_id: i32, + #[sea_orm(primary_key, auto_increment = false)] + pub baker_id: i32, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::baker::Entity", + from = "Column::BakerId", + to = "super::baker::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + Baker, + #[sea_orm( + belongs_to = "super::cake::Entity", + from = "Column::CakeId", + to = "super::cake::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + Cake, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Baker.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Cake.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/examples/seaography_example/migration/src/entity/mod.rs b/examples/seaography_example/migration/src/entity/mod.rs new file mode 100644 index 0000000000..dc59aec47f --- /dev/null +++ b/examples/seaography_example/migration/src/entity/mod.rs @@ -0,0 +1,8 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1 + +pub mod prelude; + +pub mod baker; +pub mod bakery; +pub mod cake; +pub mod cake_baker; diff --git a/examples/seaography_example/migration/src/entity/prelude.rs b/examples/seaography_example/migration/src/entity/prelude.rs new file mode 100644 index 0000000000..9433bfe6a8 --- /dev/null +++ b/examples/seaography_example/migration/src/entity/prelude.rs @@ -0,0 +1,6 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1 + +pub use super::baker::Entity as Baker; +pub use super::bakery::Entity as Bakery; +pub use super::cake::Entity as Cake; +pub use super::cake_baker::Entity as CakeBaker; diff --git a/examples/seaography_example/migration/src/lib.rs b/examples/seaography_example/migration/src/lib.rs new file mode 100644 index 0000000000..cdc6fe14b0 --- /dev/null +++ b/examples/seaography_example/migration/src/lib.rs @@ -0,0 +1,29 @@ +pub use sea_orm_migration::prelude::*; + +mod entity; +mod m20230101_000001_create_bakery_table; +mod m20230101_000002_create_baker_table; +mod m20230101_000003_create_cake_table; +mod m20230101_000004_create_cake_baker_table; +mod m20230101_000005_create_customer_table; +mod m20230101_000006_create_order_table; +mod m20230101_000007_create_lineitem_table; +mod m20230102_000001_seed_bakery_data; + +pub struct Migrator; + +#[async_trait::async_trait] +impl MigratorTrait for Migrator { + fn migrations() -> Vec> { + vec![ + Box::new(m20230101_000001_create_bakery_table::Migration), + Box::new(m20230101_000002_create_baker_table::Migration), + Box::new(m20230101_000003_create_cake_table::Migration), + Box::new(m20230101_000004_create_cake_baker_table::Migration), + // Box::new(m20230101_000005_create_customer_table::Migration), + // Box::new(m20230101_000006_create_order_table::Migration), + // Box::new(m20230101_000007_create_lineitem_table::Migration), + Box::new(m20230102_000001_seed_bakery_data::Migration), + ] + } +} diff --git a/examples/seaography_example/migration/src/m20230101_000001_create_bakery_table.rs b/examples/seaography_example/migration/src/m20230101_000001_create_bakery_table.rs new file mode 100644 index 0000000000..efcb681aa9 --- /dev/null +++ b/examples/seaography_example/migration/src/m20230101_000001_create_bakery_table.rs @@ -0,0 +1,40 @@ +use sea_orm_migration::prelude::*; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(Bakery::Table) + .col( + ColumnDef::new(Bakery::Id) + .integer() + .not_null() + .auto_increment() + .primary_key(), + ) + .col(ColumnDef::new(Bakery::Name).string().not_null()) + .col(ColumnDef::new(Bakery::ProfitMargin).double().not_null()) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(Bakery::Table).to_owned()) + .await + } +} + +#[derive(DeriveIden)] +enum Bakery { + Table, + Id, + Name, + ProfitMargin, +} diff --git a/examples/seaography_example/migration/src/m20230101_000002_create_baker_table.rs b/examples/seaography_example/migration/src/m20230101_000002_create_baker_table.rs new file mode 100644 index 0000000000..44f0a88d30 --- /dev/null +++ b/examples/seaography_example/migration/src/m20230101_000002_create_baker_table.rs @@ -0,0 +1,56 @@ +use sea_orm_migration::prelude::*; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(Baker::Table) + .col( + ColumnDef::new(Baker::Id) + .integer() + .not_null() + .auto_increment() + .primary_key(), + ) + .col(ColumnDef::new(Baker::Name).string().not_null()) + .col(ColumnDef::new(Baker::Contact).string().not_null()) + .col(ColumnDef::new(Baker::BakeryId).integer()) + .foreign_key( + ForeignKey::create() + .name("fk-baker-bakery_id") + .from(Baker::Table, Baker::BakeryId) + .to(Bakery::Table, Bakery::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(Baker::Table).to_owned()) + .await + } +} + +#[derive(DeriveIden)] +enum Baker { + Table, + Id, + Name, + Contact, + BakeryId, +} + +#[derive(DeriveIden)] +enum Bakery { + Table, + Id, +} diff --git a/examples/seaography_example/migration/src/m20230101_000003_create_cake_table.rs b/examples/seaography_example/migration/src/m20230101_000003_create_cake_table.rs new file mode 100644 index 0000000000..a73b53f59d --- /dev/null +++ b/examples/seaography_example/migration/src/m20230101_000003_create_cake_table.rs @@ -0,0 +1,58 @@ +use sea_orm_migration::prelude::*; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(Cake::Table) + .col( + ColumnDef::new(Cake::Id) + .integer() + .not_null() + .auto_increment() + .primary_key(), + ) + .col(ColumnDef::new(Cake::Name).string().not_null()) + .col(ColumnDef::new(Cake::Price).decimal_len(19, 4).not_null()) + .col(ColumnDef::new(Cake::BakeryId).integer()) + .foreign_key( + ForeignKey::create() + .name("fk-cake-bakery_id") + .from(Cake::Table, Cake::BakeryId) + .to(Bakery::Table, Bakery::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .col(ColumnDef::new(Cake::GlutenFree).boolean().not_null()) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(Cake::Table).to_owned()) + .await + } +} + +#[derive(DeriveIden)] +enum Cake { + Table, + Id, + Name, + Price, + GlutenFree, + BakeryId, +} + +#[derive(DeriveIden)] +enum Bakery { + Table, + Id, +} diff --git a/examples/seaography_example/migration/src/m20230101_000004_create_cake_baker_table.rs b/examples/seaography_example/migration/src/m20230101_000004_create_cake_baker_table.rs new file mode 100644 index 0000000000..b20926f06b --- /dev/null +++ b/examples/seaography_example/migration/src/m20230101_000004_create_cake_baker_table.rs @@ -0,0 +1,66 @@ +use sea_orm_migration::prelude::*; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(CakeBaker::Table) + .col(ColumnDef::new(CakeBaker::CakeId).integer().not_null()) + .col(ColumnDef::new(CakeBaker::BakerId).integer().not_null()) + .primary_key( + Index::create() + .name("pk-cake_baker") + .col(CakeBaker::CakeId) + .col(CakeBaker::BakerId), + ) + .foreign_key( + ForeignKey::create() + .name("fk-cake_baker-cake_id") + .from(CakeBaker::Table, CakeBaker::CakeId) + .to(Cake::Table, Cake::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .foreign_key( + ForeignKey::create() + .name("fk-cake_baker-baker_id") + .from(CakeBaker::Table, CakeBaker::BakerId) + .to(Baker::Table, Baker::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(CakeBaker::Table).to_owned()) + .await + } +} + +#[derive(DeriveIden)] +enum CakeBaker { + Table, + CakeId, + BakerId, +} + +#[derive(DeriveIden)] +enum Baker { + Table, + Id, +} + +#[derive(DeriveIden)] +enum Cake { + Table, + Id, +} diff --git a/examples/seaography_example/migration/src/m20230101_000005_create_customer_table.rs b/examples/seaography_example/migration/src/m20230101_000005_create_customer_table.rs new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/examples/seaography_example/migration/src/m20230101_000005_create_customer_table.rs @@ -0,0 +1 @@ + diff --git a/examples/seaography_example/migration/src/m20230101_000006_create_order_table.rs b/examples/seaography_example/migration/src/m20230101_000006_create_order_table.rs new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/examples/seaography_example/migration/src/m20230101_000006_create_order_table.rs @@ -0,0 +1 @@ + diff --git a/examples/seaography_example/migration/src/m20230101_000007_create_lineitem_table.rs b/examples/seaography_example/migration/src/m20230101_000007_create_lineitem_table.rs new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/examples/seaography_example/migration/src/m20230101_000007_create_lineitem_table.rs @@ -0,0 +1 @@ + diff --git a/examples/seaography_example/migration/src/m20230102_000001_seed_bakery_data.rs b/examples/seaography_example/migration/src/m20230102_000001_seed_bakery_data.rs new file mode 100644 index 0000000000..3303bfc061 --- /dev/null +++ b/examples/seaography_example/migration/src/m20230102_000001_seed_bakery_data.rs @@ -0,0 +1,195 @@ +use crate::entity::{prelude::*, *}; +use sea_orm::entity::*; +use sea_orm_migration::prelude::*; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + let db = manager.get_connection(); + + let bakery = bakery::ActiveModel { + name: Set("SeaSide Bakery".to_owned()), + profit_margin: Set(10.4), + ..Default::default() + }; + let sea = Bakery::insert(bakery).exec(db).await?.last_insert_id; + + let bakery = bakery::ActiveModel { + name: Set("LakeSide Bakery".to_owned()), + profit_margin: Set(5.8), + ..Default::default() + }; + let lake = Bakery::insert(bakery).exec(db).await?.last_insert_id; + + let alice = baker::ActiveModel { + name: Set("Alice".to_owned()), + contact: Set("+44 15273388".to_owned()), + bakery_id: Set(Some(sea)), + ..Default::default() + }; + let alice = Baker::insert(alice).exec(db).await?.last_insert_id; + + let bob = baker::ActiveModel { + name: Set("Bob".to_owned()), + contact: Set("+852 12345678".to_owned()), + bakery_id: Set(Some(lake)), + ..Default::default() + }; + let bob = Baker::insert(bob).exec(db).await?.last_insert_id; + + let cake = cake::ActiveModel { + name: Set("Chocolate Cake".to_owned()), + price: Set("10.25".parse().unwrap()), + gluten_free: Set(0), + bakery_id: Set(Some(sea)), + ..Default::default() + }; + let choco = Cake::insert(cake).exec(db).await?.last_insert_id; + + let mut cake = cake::ActiveModel { + name: Set("Double Chocolate".to_owned()), + price: Set("12.5".parse().unwrap()), + gluten_free: Set(0), + bakery_id: Set(Some(sea)), + ..Default::default() + }; + let double_1 = Cake::insert(cake.clone()).exec(db).await?.last_insert_id; + cake.bakery_id = Set(Some(lake)); + let double_2 = Cake::insert(cake).exec(db).await?.last_insert_id; + + let mut cake = cake::ActiveModel { + name: Set("Lemon Cake".to_owned()), + price: Set("8.8".parse().unwrap()), + gluten_free: Set(0), + bakery_id: Set(Some(sea)), + ..Default::default() + }; + let lemon_1 = Cake::insert(cake.clone()).exec(db).await?.last_insert_id; + cake.bakery_id = Set(Some(lake)); + let lemon_2 = Cake::insert(cake).exec(db).await?.last_insert_id; + + let mut cake = cake::ActiveModel { + name: Set("Strawberry Cake".to_owned()), + price: Set("9.9".parse().unwrap()), + gluten_free: Set(0), + bakery_id: Set(Some(sea)), + ..Default::default() + }; + let straw_1 = Cake::insert(cake.clone()).exec(db).await?.last_insert_id; + cake.bakery_id = Set(Some(lake)); + let straw_2 = Cake::insert(cake).exec(db).await?.last_insert_id; + + let cake = cake::ActiveModel { + name: Set("Orange Cake".to_owned()), + price: Set("6.5".parse().unwrap()), + gluten_free: Set(1), + bakery_id: Set(Some(lake)), + ..Default::default() + }; + let orange = Cake::insert(cake).exec(db).await?.last_insert_id; + + let mut cake = cake::ActiveModel { + name: Set("New York Cheese".to_owned()), + price: Set("12.5".parse().unwrap()), + gluten_free: Set(0), + bakery_id: Set(Some(sea)), + ..Default::default() + }; + let cheese_1 = Cake::insert(cake.clone()).exec(db).await?.last_insert_id; + cake.bakery_id = Set(Some(lake)); + let cheese_2 = Cake::insert(cake).exec(db).await?.last_insert_id; + + let mut cake = cake::ActiveModel { + name: Set("Blueburry Cheese".to_owned()), + price: Set("11.5".parse().unwrap()), + gluten_free: Set(1), + bakery_id: Set(Some(sea)), + ..Default::default() + }; + let blue_1 = Cake::insert(cake.clone()).exec(db).await?.last_insert_id; + cake.bakery_id = Set(Some(lake)); + let blue_2 = Cake::insert(cake).exec(db).await?.last_insert_id; + + let rel = cake_baker::ActiveModel { + cake_id: Set(choco), + baker_id: Set(alice), + }; + CakeBaker::insert(rel).exec(db).await?; + + let rel = cake_baker::ActiveModel { + cake_id: Set(double_1), + baker_id: Set(alice), + }; + CakeBaker::insert(rel).exec(db).await?; + let rel = cake_baker::ActiveModel { + cake_id: Set(double_2), + baker_id: Set(bob), + }; + CakeBaker::insert(rel).exec(db).await?; + + let rel = cake_baker::ActiveModel { + cake_id: Set(lemon_1), + baker_id: Set(alice), + }; + CakeBaker::insert(rel).exec(db).await?; + let rel = cake_baker::ActiveModel { + cake_id: Set(lemon_2), + baker_id: Set(bob), + }; + CakeBaker::insert(rel).exec(db).await?; + + let rel = cake_baker::ActiveModel { + cake_id: Set(straw_1), + baker_id: Set(alice), + }; + CakeBaker::insert(rel).exec(db).await?; + let rel = cake_baker::ActiveModel { + cake_id: Set(straw_2), + baker_id: Set(bob), + }; + CakeBaker::insert(rel).exec(db).await?; + + let rel = cake_baker::ActiveModel { + cake_id: Set(orange), + baker_id: Set(bob), + }; + CakeBaker::insert(rel).exec(db).await?; + + let rel = cake_baker::ActiveModel { + cake_id: Set(cheese_1), + baker_id: Set(alice), + }; + CakeBaker::insert(rel).exec(db).await?; + let rel = cake_baker::ActiveModel { + cake_id: Set(cheese_2), + baker_id: Set(bob), + }; + CakeBaker::insert(rel).exec(db).await?; + + let rel = cake_baker::ActiveModel { + cake_id: Set(blue_1), + baker_id: Set(alice), + }; + CakeBaker::insert(rel).exec(db).await?; + let rel = cake_baker::ActiveModel { + cake_id: Set(blue_2), + baker_id: Set(bob), + }; + CakeBaker::insert(rel).exec(db).await?; + + Ok(()) + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + let db = manager.get_connection(); + + Cake::delete_many().exec(db).await?; + Baker::delete_many().exec(db).await?; + Bakery::delete_many().exec(db).await?; + + Ok(()) + } +} diff --git a/examples/seaography_example/migration/src/main.rs b/examples/seaography_example/migration/src/main.rs new file mode 100644 index 0000000000..c6b6e48dbc --- /dev/null +++ b/examples/seaography_example/migration/src/main.rs @@ -0,0 +1,6 @@ +use sea_orm_migration::prelude::*; + +#[async_std::main] +async fn main() { + cli::run_cli(migration::Migrator).await; +} From 33fc350e76e9b61d508a1068a3ae8f031b858d9b Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sun, 30 Jul 2023 05:58:31 +0800 Subject: [PATCH 0114/1371] Readme --- README.md | 24 +++++++++++++++++++----- examples/seaography_example/README.md | 14 +++++++------- src/lib.rs | 24 +++++++++++++++++++----- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index beefbdf789..fb823419b3 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ Integration examples: + [Rocket Example](https://github.com/SeaQL/sea-orm/tree/master/examples/rocket_example) + [Salvo Example](https://github.com/SeaQL/sea-orm/tree/master/examples/salvo_example) + [Tonic Example](https://github.com/SeaQL/sea-orm/tree/master/examples/tonic_example) ++ [Seaography Example](https://github.com/SeaQL/sea-orm/tree/master/examples/seaography_example) ## Features @@ -187,11 +188,21 @@ fruit::Entity::delete_many() ``` +## Seaography: first-class GraphQL integration (preview) + +[Seaography](https://github.com/SeaQL/seaography) is a GraphQL framework built on top of SeaORM. It embodies our vision of 'full-stack Rust': with just a few commands, you can launch a GraphQL server on SeaORM entities! + +Starting `0.12`, `seaography` is integrated into `sea-orm`. While Seaography development is still in an early stage, it especially useful in prototyping and building internal-use admin panels. + + + +Look at the [Seaography Example](https://github.com/SeaQL/sea-orm/tree/master/examples/seaography_example) to learn more. + ## Learn More 1. [Design](https://github.com/SeaQL/sea-orm/tree/master/DESIGN.md) 1. [Architecture](https://www.sea-ql.org/SeaORM/docs/internal-design/architecture/) -1. [Release Model](https://www.sea-ql.org/SeaORM/blog/2021-08-30-release-model) +1. [Engineering](https://www.sea-ql.org/blog/2022-07-30-engineering/) 1. [Change Log](https://github.com/SeaQL/sea-orm/tree/master/CHANGELOG.md) ## Who's using SeaORM? @@ -208,9 +219,6 @@ The following products are powered by SeaORM: -SeaORM is the foundation of: -+ [Seaography](https://github.com/SeaQL/seaography): GraphQL framework for SeaORM - For more projects, see [Built with SeaORM](https://github.com/SeaQL/sea-orm/blob/master/COMMUNITY.md#built-with-seaorm). ## License @@ -232,10 +240,16 @@ dual licensed as above, without any additional terms or conditions. SeaORM is a community driven project. We welcome you to participate, contribute and together help build Rust's future. -A big shout out to our contributors: +A big shout out to our contributors! [![Contributors](https://opencollective.com/sea-orm/contributors.svg?width=1000&button=false)](https://github.com/SeaQL/sea-orm/graphs/contributors) +## Sponsorship + +[SeaQL.org](https://www.sea-ql.org/) is an independent open-source organization run by passionate developers. If you enjoy using our libraries, please star and share our repositories. If you feel generous, a small donation via [GitHub Sponsor](https://github.com/sponsors/SeaQL) will be greatly appreciated, and goes a long way towards sustaining the organization. + +We invite you to participate, contribute and together help build Rust's future. + ## Mascot A friend of Ferris, Terres the hermit crab is the official mascot of SeaORM. His hobby is collecting shells. diff --git a/examples/seaography_example/README.md b/examples/seaography_example/README.md index b4325b4773..ac9602d3bb 100644 --- a/examples/seaography_example/README.md +++ b/examples/seaography_example/README.md @@ -8,23 +8,23 @@ export DATABASE_URL=mysql://sea:sea@localhost/bakery ``` -## Setup the Database first +## Setup the Database -Cd into `migration` folder, follow instructions there, but basically: +Cd into `migration` folder and follow instructions there, but basically: -``` +```sh cargo run ``` ## Install Seaography -``` +```sh cargo install seaography-cli@^1.0.0-rc.2 ``` -## Generate Seaography project +## Generate GraphQL project -``` +```sh rm -rf graphql # this entire folder is generated sea-orm-cli generate entity --output-dir graphql/src/entities --seaography seaography-cli graphql graphql/src/entities $DATABASE_URL sea-orm-seaography-example @@ -32,7 +32,7 @@ seaography-cli graphql graphql/src/entities $DATABASE_URL sea-orm-seaography-exa ## Running the project -``` +```sh cd graphql cargo run ``` diff --git a/src/lib.rs b/src/lib.rs index ce02a92d7d..826c925393 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,6 +51,7 @@ //! + [Rocket Example](https://github.com/SeaQL/sea-orm/tree/master/examples/rocket_example) //! + [Salvo Example](https://github.com/SeaQL/sea-orm/tree/master/examples/salvo_example) //! + [Tonic Example](https://github.com/SeaQL/sea-orm/tree/master/examples/tonic_example) +//! + [Seaography Example](https://github.com/SeaQL/sea-orm/tree/master/examples/seaography_example) //! //! ## Features //! @@ -260,11 +261,21 @@ //! # } //! ``` //! +//! ## Seaography: first-class GraphQL integration (preview) +//! +//! [Seaography](https://github.com/SeaQL/seaography) is a GraphQL framework built on top of SeaORM. It embodies our vision of 'full-stack Rust': with just a few commands, you can launch a GraphQL server on SeaORM entities! +//! +//! Starting `0.12`, `seaography` is integrated into `sea-orm`. While Seaography development is still in an early stage, it especially useful in prototyping and building internal-use admin panels. +//! +//! +//! +//! Look at the [Seaography Example](https://github.com/SeaQL/sea-orm/tree/master/examples/seaography_example) to learn more. +//! //! ## Learn More //! //! 1. [Design](https://github.com/SeaQL/sea-orm/tree/master/DESIGN.md) //! 1. [Architecture](https://www.sea-ql.org/SeaORM/docs/internal-design/architecture/) -//! 1. [Release Model](https://www.sea-ql.org/SeaORM/blog/2021-08-30-release-model) +//! 1. [Engineering](https://www.sea-ql.org/blog/2022-07-30-engineering/) //! 1. [Change Log](https://github.com/SeaQL/sea-orm/tree/master/CHANGELOG.md) //! //! ## Who's using SeaORM? @@ -281,9 +292,6 @@ //! //! //! -//! SeaORM is the foundation of: -//! + [Seaography](https://github.com/SeaQL/seaography): GraphQL framework for SeaORM -//! //! For more projects, see [Built with SeaORM](https://github.com/SeaQL/sea-orm/blob/master/COMMUNITY.md#built-with-seaorm). //! //! ## License @@ -305,10 +313,16 @@ //! //! SeaORM is a community driven project. We welcome you to participate, contribute and together help build Rust's future. //! -//! A big shout out to our contributors: +//! A big shout out to our contributors! //! //! [![Contributors](https://opencollective.com/sea-orm/contributors.svg?width=1000&button=false)](https://github.com/SeaQL/sea-orm/graphs/contributors) //! +//! ## Sponsorship +//! +//! [SeaQL.org](https://www.sea-ql.org/) is an independent open-source organization run by passionate developers. If you enjoy using our libraries, please star and share our repositories. If you feel generous, a small donation via [GitHub Sponsor](https://github.com/sponsors/SeaQL) will be greatly appreciated, and goes a long way towards sustaining the organization. +//! +//! We invite you to participate, contribute and together help build Rust's future. +//! //! ## Mascot //! //! A friend of Ferris, Terres the hermit crab is the official mascot of SeaORM. His hobby is collecting shells. From dcd37dea272d9e4d35a9609679d473111bf4c564 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sun, 30 Jul 2023 06:05:38 +0800 Subject: [PATCH 0115/1371] Readme --- README.md | 6 +++--- src/lib.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index fb823419b3..215b39e6b1 100644 --- a/README.md +++ b/README.md @@ -190,9 +190,9 @@ fruit::Entity::delete_many() ## Seaography: first-class GraphQL integration (preview) -[Seaography](https://github.com/SeaQL/seaography) is a GraphQL framework built on top of SeaORM. It embodies our vision of 'full-stack Rust': with just a few commands, you can launch a GraphQL server on SeaORM entities! +[Seaography](https://github.com/SeaQL/seaography) is a GraphQL framework built on top of SeaORM. It embodies our vision of 'full-stack Rust': with just a few commands, you can launch a GraphQL server from SeaORM entities! -Starting `0.12`, `seaography` is integrated into `sea-orm`. While Seaography development is still in an early stage, it especially useful in prototyping and building internal-use admin panels. +Starting `0.12`, `seaography` is integrated into `sea-orm`. While Seaography development is still in an early stage, it is especially useful in prototyping and building internal-use admin panels. @@ -219,7 +219,7 @@ The following products are powered by SeaORM: -For more projects, see [Built with SeaORM](https://github.com/SeaQL/sea-orm/blob/master/COMMUNITY.md#built-with-seaorm). +For more projects, see [Built with SeaORM](https://github.com/SeaQL/sea-orm/blob/master/COMMUNITY.md#built-with-seaorm). Feel free to [submit yours](https://github.com/SeaQL/sea-orm/issues/403)! ## License diff --git a/src/lib.rs b/src/lib.rs index 826c925393..15734f9b7b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -263,9 +263,9 @@ //! //! ## Seaography: first-class GraphQL integration (preview) //! -//! [Seaography](https://github.com/SeaQL/seaography) is a GraphQL framework built on top of SeaORM. It embodies our vision of 'full-stack Rust': with just a few commands, you can launch a GraphQL server on SeaORM entities! +//! [Seaography](https://github.com/SeaQL/seaography) is a GraphQL framework built on top of SeaORM. It embodies our vision of 'full-stack Rust': with just a few commands, you can launch a GraphQL server from SeaORM entities! //! -//! Starting `0.12`, `seaography` is integrated into `sea-orm`. While Seaography development is still in an early stage, it especially useful in prototyping and building internal-use admin panels. +//! Starting `0.12`, `seaography` is integrated into `sea-orm`. While Seaography development is still in an early stage, it is especially useful in prototyping and building internal-use admin panels. //! //! //! @@ -292,7 +292,7 @@ //! //! //! -//! For more projects, see [Built with SeaORM](https://github.com/SeaQL/sea-orm/blob/master/COMMUNITY.md#built-with-seaorm). +//! For more projects, see [Built with SeaORM](https://github.com/SeaQL/sea-orm/blob/master/COMMUNITY.md#built-with-seaorm). Feel free to [submit yours](https://github.com/SeaQL/sea-orm/issues/403)! //! //! ## License //! From 4418227d679b63ab6545e48c471c40f2f48bc9ef Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sun, 30 Jul 2023 06:06:35 +0800 Subject: [PATCH 0116/1371] Readme --- README.md | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 215b39e6b1..94ad052f5b 100644 --- a/README.md +++ b/README.md @@ -192,7 +192,7 @@ fruit::Entity::delete_many() [Seaography](https://github.com/SeaQL/seaography) is a GraphQL framework built on top of SeaORM. It embodies our vision of 'full-stack Rust': with just a few commands, you can launch a GraphQL server from SeaORM entities! -Starting `0.12`, `seaography` is integrated into `sea-orm`. While Seaography development is still in an early stage, it is especially useful in prototyping and building internal-use admin panels. +Starting `0.12`, `seaography` integration is built into `sea-orm`. While Seaography development is still in an early stage, it is especially useful in prototyping and building internal-use admin panels. diff --git a/src/lib.rs b/src/lib.rs index 15734f9b7b..8c7c8cce88 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -265,7 +265,7 @@ //! //! [Seaography](https://github.com/SeaQL/seaography) is a GraphQL framework built on top of SeaORM. It embodies our vision of 'full-stack Rust': with just a few commands, you can launch a GraphQL server from SeaORM entities! //! -//! Starting `0.12`, `seaography` is integrated into `sea-orm`. While Seaography development is still in an early stage, it is especially useful in prototyping and building internal-use admin panels. +//! Starting `0.12`, `seaography` integration is built into `sea-orm`. While Seaography development is still in an early stage, it is especially useful in prototyping and building internal-use admin panels. //! //! //! From 3a72c61bcc3718717e371b9d8a1daf6a59896822 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sun, 30 Jul 2023 06:07:35 +0800 Subject: [PATCH 0117/1371] Readme --- examples/seaography_example/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/seaography_example/README.md b/examples/seaography_example/README.md index ac9602d3bb..9481685c25 100644 --- a/examples/seaography_example/README.md +++ b/examples/seaography_example/README.md @@ -1,4 +1,4 @@ -# SeaORM Seaography Example +# SeaORM + Seaography Example ![Seaography screenshot with Bakery schema]() From 81158da9c6ee16946f063315548220ae5097142f Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sun, 30 Jul 2023 06:16:34 +0800 Subject: [PATCH 0118/1371] Readme --- examples/seaography_example/README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/examples/seaography_example/README.md b/examples/seaography_example/README.md index 9481685c25..5e91921bf7 100644 --- a/examples/seaography_example/README.md +++ b/examples/seaography_example/README.md @@ -1,6 +1,12 @@ # SeaORM + Seaography Example -![Seaography screenshot with Bakery schema]() +| ![]() | +|:--:| +| Seaography screenshot with Bakery schema | + +| ![](https://raw.githubusercontent.com/SeaQL/sea-orm/master/tests/common/bakery_chain/bakery_chain_erd.png) | +|:--:| +| The Bakery schema | ## Specifiy a database url From dd1def8bf3e92dcdb5fdf916c249c651e83051f6 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Mon, 31 Jul 2023 08:19:36 +0100 Subject: [PATCH 0119/1371] Readme --- examples/seaography_example/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/seaography_example/README.md b/examples/seaography_example/README.md index 5e91921bf7..a77a00a747 100644 --- a/examples/seaography_example/README.md +++ b/examples/seaography_example/README.md @@ -1,6 +1,6 @@ # SeaORM + Seaography Example -| ![]() | +| ![](https://raw.githubusercontent.com/SeaQL/sea-orm/master/examples/seaography_example/Seaography%20example.png) | |:--:| | Seaography screenshot with Bakery schema | From 63d8cdf7e50499ccae70f0555a7976328b893f07 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Mon, 31 Jul 2023 08:35:29 +0100 Subject: [PATCH 0120/1371] Readme --- README.md | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 94ad052f5b..716117d3ee 100644 --- a/README.md +++ b/README.md @@ -188,7 +188,7 @@ fruit::Entity::delete_many() ``` -## Seaography: first-class GraphQL integration (preview) +## 🧭 Seaography: first-class GraphQL integration (preview) [Seaography](https://github.com/SeaQL/seaography) is a GraphQL framework built on top of SeaORM. It embodies our vision of 'full-stack Rust': with just a few commands, you can launch a GraphQL server from SeaORM entities! diff --git a/src/lib.rs b/src/lib.rs index 8c7c8cce88..0a713416bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -261,7 +261,7 @@ //! # } //! ``` //! -//! ## Seaography: first-class GraphQL integration (preview) +//! ## 🧭 Seaography: first-class GraphQL integration (preview) //! //! [Seaography](https://github.com/SeaQL/seaography) is a GraphQL framework built on top of SeaORM. It embodies our vision of 'full-stack Rust': with just a few commands, you can launch a GraphQL server from SeaORM entities! //! From 5fa2c1c28ced52cfdd2574c30aae3b37b5ed3026 Mon Sep 17 00:00:00 2001 From: Anshul Sanghi Date: Wed, 2 Aug 2023 00:12:16 +0530 Subject: [PATCH 0121/1371] Add Support For PostgreSQL Arrays In FromQueryResult Implementation Of JsonValue (#1598) * Add Support For PostgreSQL Arrays In FromQueryResult Implementation Of JsonValue * Add support for root arrays in JSON in SeaORM #1517 * Refactoring * Only when `postgres-array` is enabled * Add test cases --------- Co-authored-by: Billy Chan --- src/query/json.rs | 23 ++++++++++++++++-- tests/collection_tests.rs | 49 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/src/query/json.rs b/src/query/json.rs index 066f69c24e..66b4dc9785 100644 --- a/src/query/json.rs +++ b/src/query/json.rs @@ -77,6 +77,7 @@ impl FromQueryResult for JsonValue { crate::QueryResultRow::SqlxPostgres(row) => { use serde_json::json; use sqlx::{postgres::types::Oid, Column, Postgres, Row, Type}; + for column in row.columns() { let col = if !column.name().starts_with(pre) { continue; @@ -84,13 +85,25 @@ impl FromQueryResult for JsonValue { column.name().replacen(pre, "", 1) }; let col_type = column.type_info(); + macro_rules! match_postgres_type { ( $type: ty ) => { - if <$type as Type>::type_info().eq(col_type) { - try_get_type!($type, col) + match col_type.kind() { + #[cfg(feature = "postgres-array")] + sqlx::postgres::PgTypeKind::Array(_) => { + if as Type>::type_info().eq(col_type) { + try_get_type!(Vec<$type>, col); + } + } + _ => { + if <$type as Type>::type_info().eq(col_type) { + try_get_type!($type, col); + } + } } }; } + match_postgres_type!(bool); match_postgres_type!(i8); match_postgres_type!(i16); @@ -126,9 +139,15 @@ impl FromQueryResult for JsonValue { match_postgres_type!(rust_decimal::Decimal); #[cfg(feature = "with-json")] try_get_type!(serde_json::Value, col); + #[cfg(all(feature = "with-json", feature = "postgres-array"))] + try_get_type!(Vec, col); try_get_type!(String, col); + #[cfg(feature = "postgres-array")] + try_get_type!(Vec, col); #[cfg(feature = "with-uuid")] try_get_type!(uuid::Uuid, col); + #[cfg(all(feature = "with-uuid", feature = "postgres-array"))] + try_get_type!(Vec, col); try_get_type!(Vec, col); } Ok(JsonValue::Object(map)) diff --git a/tests/collection_tests.rs b/tests/collection_tests.rs index 2447bee9fb..8bd0eafb6b 100644 --- a/tests/collection_tests.rs +++ b/tests/collection_tests.rs @@ -5,6 +5,7 @@ use pretty_assertions::assert_eq; use sea_orm::{ entity::prelude::*, entity::*, DatabaseConnection, DerivePartialModel, FromQueryResult, }; +use serde_json::json; #[sea_orm_macros::test] #[cfg(all(feature = "sqlx-postgres", feature = "postgres-array"))] @@ -114,6 +115,54 @@ pub async fn insert_collection(db: &DatabaseConnection) -> Result<(), DbErr> { } ); + assert_eq!( + Entity::find_by_id(1).into_json().one(db).await?, + Some(json!({ + "id": 1, + "name": "Collection 1", + "integers": [1, 2, 3], + "integers_opt": [1, 2, 3], + "teas": ["BreakfastTea"], + "teas_opt": ["BreakfastTea"], + "colors": [0], + "colors_opt": [0], + "uuid": [uuid], + "uuid_hyphenated": [uuid.hyphenated()], + })) + ); + + assert_eq!( + Entity::find_by_id(2).into_json().one(db).await?, + Some(json!({ + "id": 2, + "name": "Collection 2", + "integers": [10, 9], + "integers_opt": null, + "teas": ["BreakfastTea"], + "teas_opt": null, + "colors": [0], + "colors_opt": null, + "uuid": [uuid], + "uuid_hyphenated": [uuid.hyphenated()], + })) + ); + + assert_eq!( + Entity::find_by_id(3).into_json().one(db).await?, + Some(json!({ + "id": 3, + "name": "Collection 3", + "integers": [], + "integers_opt": [], + "teas": [], + "teas_opt": [], + "colors": [], + "colors_opt": [], + "uuid": [uuid], + "uuid_hyphenated": [uuid.hyphenated()], + })) + ); + Ok(()) } From 178f5907f1696f90dce9279a24b67cfb9291eda8 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Tue, 1 Aug 2023 19:44:51 +0100 Subject: [PATCH 0122/1371] Changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed27265c1d..9f2bfb98c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## 0.12.2 - Pending + +### Enhancements + +* Added support for Postgres arrays in `FromQueryResult` impl of `JsonValue` https://github.com/SeaQL/sea-orm/pull/1598 + ## 0.12.1 - 2023-07-27 + `0.12.0-rc.1`: Yanked From fb8627dab653fd89dd4d3869ab15e8932758b7c7 Mon Sep 17 00:00:00 2001 From: Himanshu Neema Date: Wed, 2 Aug 2023 08:37:26 -0700 Subject: [PATCH 0123/1371] Add UpVPN to COMMUNITY startups (#1791) --- COMMUNITY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/COMMUNITY.md b/COMMUNITY.md index c40958cefb..198aa8ba96 100644 --- a/COMMUNITY.md +++ b/COMMUNITY.md @@ -13,6 +13,7 @@ If you have built an app using SeaORM and want to showcase it, feel free to open - [Sensei](https://l2.technology/sensei) ([repository](https://github.com/L2-Technology/sensei)) | A Bitcoin lightning node implementation | DB: MySQL, Postgres, SQLite - [Spyglass](https://www.spyglass.fyi/) ([repository](https://github.com/a5huynh/spyglass)) ![GitHub stars](https://img.shields.io/github/stars/a5huynh/spyglass.svg?style=social) | 🔭 A personal search engine that indexes what you want w/ a simple set of rules. | DB: SQLite - [Svix](https://www.svix.com/) ([repository](https://github.com/svix/svix-webhooks)) ![GitHub stars](https://img.shields.io/github/stars/svix/svix-webhooks.svg?style=social) | The enterprise ready webhooks service | DB: Postgres +- [UpVPN](https://upvpn.app) ([repository](https://github.com/upvpn/upvpn-app)) | Serverless VPN on WireGuard® | DB: SQLite, Postgres ### Open Source Projects From a9f5a7933a858989b4d1d157795e15ed36fd4bb3 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Wed, 2 Aug 2023 16:48:30 +0100 Subject: [PATCH 0124/1371] Changelog --- CHANGELOG.md | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f2bfb98c6..11d025e071 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -190,27 +190,20 @@ enum ConnAcquireErr { #### Seaography * Add generation of `seaography` related information to `sea-orm-codegen` https://github.com/SeaQL/sea-orm/pull/1599 - - The following information is added in entities files by `sea-orm-cli` when flag `seaography` is `true` +The `RelatedEntity` enum is added in entities files by `sea-orm-cli` when flag `seaography` is set: ```rust /// ... Entity File ... - #[derive(Copy, Clone, Debug, EnumIter, DeriveRelatedEntity)] pub enum RelatedEntity { - #[sea_orm(entity = "super::address::Entity")] - Address, - #[sea_orm(entity = "super::payment::Entity")] - Payment, - #[sea_orm(entity = "super::rental::Entity")] - Rental, - #[sea_orm(entity = "Entity", def = "Relation::SelfRef.def()")] - SelfRef, - #[sea_orm(entity = "super::store::Entity")] - Store, - #[sea_orm(entity = "Entity", def = "Relation::SelfRef.def().rev()")] - SelfRefRev, + #[sea_orm(entity = "super::bakery::Entity")] + Bakery, + #[sea_orm(entity = "super::cake_baker::Entity")] + CakeBaker, + #[sea_orm(entity = "super::cake::Entity")] + Cake, } ``` +* Added `seaography_example` ### Enhancements From 2c2a83936c252dfa0ef4dd217b6f9112429788b1 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Wed, 2 Aug 2023 17:33:20 +0100 Subject: [PATCH 0125/1371] Changelog --- CHANGELOG.md | 117 ++++++++++++--------------------------------------- 1 file changed, 28 insertions(+), 89 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11d025e071..b109857646 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,13 +61,8 @@ pub enum Relation { OrTropicalFruit, } ``` -* Supports entity with composite primary key of length 12 https://github.com/SeaQL/sea-orm/pull/1508 - * Implemented `IntoIdentity` for `Identity` https://github.com/SeaQL/sea-orm/pull/1508 - * `Identity` supports up to identity tuple of `DynIden` with length up to 12 https://github.com/SeaQL/sea-orm/pull/1508 - * Implemented `IntoIdentity` for tuple of `IdenStatic` with length up to 12 https://github.com/SeaQL/sea-orm/pull/1508 - * Implemented `IdentityOf` for tuple of `ColumnTrait` with length up to 12 https://github.com/SeaQL/sea-orm/pull/1508 - * Implemented `TryGetableMany` for tuple of `TryGetable` with length up to 12 https://github.com/SeaQL/sea-orm/pull/1508 - * Implemented `TryFromU64` for tuple of `TryFromU64` with length up to 12 https://github.com/SeaQL/sea-orm/pull/1508 +* Supports entity with composite primary key of arity 12 https://github.com/SeaQL/sea-orm/pull/1508 + * `Identity` supports tuple of `DynIden` with arity up to 12 ```rust #[derive(Clone, Debug, PartialEq, DeriveEntityModel)] #[sea_orm(table_name = "primary_key_of_12")] @@ -99,11 +94,6 @@ assert_eq!( r#"SELECT "cake"."name", UPPER("cake"."name") AS "name_upper" FROM "cake""# ); ``` -* Add `DeriveEntityRelated` macro https://github.com/SeaQL/sea-orm/pull/1599 - - The DeriveRelatedEntity derive macro will implement `seaography::RelationBuilder` for `RelatedEntity` enumeration when the `seaography` feature is enabled - -* Add `expr`, `exprs` and `expr_as` methods to `QuerySelect` trait https://github.com/SeaQL/sea-orm/pull/1702 * Add `DbErr::sql_err()` method to convert error into common database errors `SqlErr`, such as unique constraint or foreign key violation errors. https://github.com/SeaQL/sea-orm/pull/1707 ```rust assert!(matches!( @@ -142,7 +132,7 @@ pub struct Boolbean(pub String); #[derive(DeriveValueType)] pub struct StringVec(pub Vec); ``` -* Add `DeriveDisplay` derive macro to implements `std::fmt::Display` for active enum https://github.com/SeaQL/sea-orm/pull/1726 +* Add `DeriveDisplay` derive macro to implements `std::fmt::Display` for enum https://github.com/SeaQL/sea-orm/pull/1726 ```rust #[derive(DeriveDisplay)] enum DisplayTea { @@ -171,7 +161,8 @@ pub struct Model { assert_eq!( Column::Timestamp.def(), - ColumnType::TimestampWithTimeZone.def().default(Expr::current_timestamp()) + ColumnType::TimestampWithTimeZone.def() + .default(Expr::current_timestamp()) ); ``` * Introduced new `ConnAcquireErr` https://github.com/SeaQL/sea-orm/pull/1737 @@ -189,10 +180,14 @@ enum ConnAcquireErr { #### Seaography -* Add generation of `seaography` related information to `sea-orm-codegen` https://github.com/SeaQL/sea-orm/pull/1599 -The `RelatedEntity` enum is added in entities files by `sea-orm-cli` when flag `seaography` is set: +Added Seaography integration https://github.com/SeaQL/sea-orm/pull/1599 + +* Add `DeriveEntityRelated` macro which will implement `seaography::RelationBuilder` for `RelatedEntity` enumeration when the `seaography` feature is enabled +* Added generation of `seaography` related information to `sea-orm-codegen`. + + The `RelatedEntity` enum is added in entities files by `sea-orm-cli` when flag `seaography` is set: ```rust -/// ... Entity File ... +/// SeaORM Entity #[derive(Copy, Clone, Debug, EnumIter, DeriveRelatedEntity)] pub enum RelatedEntity { #[sea_orm(entity = "super::bakery::Entity")] @@ -203,33 +198,11 @@ pub enum RelatedEntity { Cake, } ``` -* Added `seaography_example` +* Added [`seaography_example`](https://github.com/SeaQL/sea-orm/tree/master/examples/seaography_example) ### Enhancements * Supports for partial select of `Option` model field. A `None` value will be filled when the select result does not contain the `Option` field without throwing an error. https://github.com/SeaQL/sea-orm/pull/1513 -```rust -customer::ActiveModel { - name: Set("Alice".to_owned()), - notes: Set(Some("Want to communicate with Bob".to_owned())), - ..Default::default() -} -.save(db) -.await?; - -// The `notes` field was intentionally leaved out -let customer = Customer::find() - .select_only() - .column(customer::Column::Id) - .column(customer::Column::Name) - .one(db) - .await - .unwrap(); - -// The select result does not contain `notes` field. -// Since it's of type `Option`, it'll be `None` and no error will be thrown. -assert_eq!(customers.notes, None); -``` * [sea-orm-cli] the `migrate init` command will create a `.gitignore` file when the migration folder reside in a Git repository https://github.com/SeaQL/sea-orm/pull/1334 * [sea-orm-cli] Added support for generating migration of space separated name, for example executing `sea-orm-cli migrate generate "create accounts table"` command will create `m20230503_000000_create_accounts_table.rs` for you https://github.com/SeaQL/sea-orm/pull/1570 * Added `Migration::name()` and `Migration::status()` getters for the name and status of `sea_orm_migration::Migration` https://github.com/SeaQL/sea-orm/pull/1519 @@ -252,6 +225,7 @@ assert_eq!(migration.status(), MigrationStatus::Pending); * Changed the parameter of method `ConnectOptions::set_schema_search_path(T) where T: Into` to takes any string * Changed the parameter of method `ColumnTrait::like()`, `ColumnTrait::not_like()`, `ColumnTrait::starts_with()`, `ColumnTrait::ends_with()` and `ColumnTrait::contains()` to takes any string * Re-export `sea_query::{DynIden, RcOrArc, SeaRc}` in `sea_orm::entity::prelude` module https://github.com/SeaQL/sea-orm/pull/1661 +* Add `expr`, `exprs` and `expr_as` methods to `QuerySelect` trait https://github.com/SeaQL/sea-orm/pull/1702 * Added `DatabaseConnection::ping` https://github.com/SeaQL/sea-orm/pull/1627 ```rust |db: DatabaseConnection| { @@ -279,7 +253,8 @@ let res = Entity::insert_many([..]).on_conflict(on).exec(db).await; assert!(matches!(res, Err(DbErr::RecordNotInserted))); // New API; now you can: -let res = Entity::insert_many([..]).on_conflict(on).do_nothing().exec(db).await; +let res = +Entity::insert_many([..]).on_conflict(on).do_nothing().exec(db).await; assert!(matches!(res, Ok(TryInsertResult::Conflicted))); ``` @@ -296,61 +271,27 @@ assert!(matches!(res, Ok(TryInsertResult::Conflicted))); ### Bug Fixes -* `DeriveActiveEnum` no longer impl `Display` https://github.com/SeaQL/sea-orm/pull/1726 * Fixed `DeriveActiveEnum` throwing errors because `string_value` consists non-UAX#31 compliant characters https://github.com/SeaQL/sea-orm/pull/1374 - -For example, ```rust -#[derive(Clone, Debug, PartialEq, EnumIter, DeriveActiveEnum)] +#[derive(DeriveActiveEnum)] #[sea_orm(rs_type = "String", db_type = "String(None)")] pub enum StringValue { #[sea_orm(string_value = "")] Member1, - #[sea_orm(string_value = "$")] - Member2, #[sea_orm(string_value = "$$")] - Member3, - #[sea_orm(string_value = "AB")] - Member4, - #[sea_orm(string_value = "A_B")] - Member5, - #[sea_orm(string_value = "A$B")] - Member6, - #[sea_orm(string_value = "0 123")] - Member7, + Member2, } -``` -will now produce the following Variant Enum: -```rust +// will now produce the following enum: pub enum StringValueVariant { __Empty, - _0x24, _0x240x24, - Ab, - A0x5Fb, - A0x24B, - _0x300x20123, } ``` * [sea-orm-cli] Fix Postgres enum arrays https://github.com/SeaQL/sea-orm/pull/1678 * [sea-orm-cli] The implementation of `Related` with `via` and `to` methods will not be generated if there exists multiple paths via an intermediate table https://github.com/SeaQL/sea-orm/pull/1435 * [sea-orm-cli] fixed entity generation includes partitioned tables https://github.com/SeaQL/sea-orm/issues/1582, https://github.com/SeaQL/sea-schema/pull/105 * Fixed `ActiveEnum::db_type()` return type does not implement `ColumnTypeTrait` https://github.com/SeaQL/sea-orm/pull/1576 -```rust -impl ColumnTrait for Column { - type EntityName = Entity; - fn def(&self) -> ColumnDef { - match self { -... - // `db_type()` returns `ColumnDef`; now it implements `ColumnTypeTrait` - Self::Thing => SomeThing::db_type().def(), -... - } - } -} -``` * Resolved `insert_many` failing if the models iterator is empty https://github.com/SeaQL/sea-orm/issues/873 -* Update the template MD file of `migration/README.md`, fixed a faulty sample `migrate init` shell script https://github.com/SeaQL/sea-orm/pull/1723 ### Breaking changes @@ -361,7 +302,7 @@ impl ColumnTrait for Column { * Added a new variant `Many` to `Identity` https://github.com/SeaQL/sea-orm/pull/1508 * Replace the use of `SeaRc` where `T` isn't `dyn Iden` with `RcOrArc` https://github.com/SeaQL/sea-orm/pull/1661 * Enabled `hashable-value` feature in SeaQuery, thus `Value::Float(NaN) == Value::Float(NaN)` would be true https://github.com/SeaQL/sea-orm/pull/1728, https://github.com/SeaQL/sea-orm/pull/1743 -* The `DeriveActiveEnum` derive macro no longer provide `std::fmt::Display` implementation for the enum. You need to derive an extra `DeriveDisplay` macro alongside with `DeriveActiveEnum` derive macro. https://github.com/SeaQL/sea-orm/pull/1726 +* The `DeriveActiveEnum` derive macro no longer implement `std::fmt::Display`. You can use the new `DeriveDisplay` macro https://github.com/SeaQL/sea-orm/pull/1726 * `sea-query/derive` is no longer enabled by `sea-orm`, as such, `Iden` no longer works as a derive macro (it's still a trait). Instead, we are shipping a new macro `DeriveIden`: ```rust // then: @@ -395,7 +336,9 @@ pub enum Tea { ### House keeping -* Replace `bae` with `sea-bae` https://github.com/SeaQL/sea-orm/pull/1739 +* Replaced `bae` with `sea-bae` https://github.com/SeaQL/sea-orm/pull/1739 + +**Full Changelog**: https://github.com/SeaQL/sea-orm/compare/0.11.1...0.12.1 ## 0.11.3 - 2023-04-24 @@ -645,7 +588,7 @@ impl ColumnTrait for Column { * Fixed a small typo https://github.com/SeaQL/sea-orm/pull/1391 * `axum` example should use tokio runtime https://github.com/SeaQL/sea-orm/pull/1428 -Full Changelog: https://github.com/SeaQL/sea-orm/compare/0.10.0...0.11.0 +**Full Changelog**: https://github.com/SeaQL/sea-orm/compare/0.10.0...0.11.0 ## 0.10.7 - 2023-01-19 @@ -1157,8 +1100,6 @@ In this minor release, we removed `time` v0.1 from the dependency graph * Temporary Fix: Handling MySQL & SQLite timestamp columns by @billy1624 in https://github.com/SeaQL/sea-orm/pull/379 * Add feature to generate table Iden by @Sytten in https://github.com/SeaQL/sea-orm/pull/360 -**Full Changelog**: https://github.com/SeaQL/sea-orm/compare/0.4.1...0.4.2 - ## 0.4.1 - 2021-12-05 ### Fixed Issues @@ -1172,8 +1113,6 @@ In this minor release, we removed `time` v0.1 from the dependency graph * Support Up to 6 Values Composite Primary Key by @billy1624 in https://github.com/SeaQL/sea-orm/pull/353 * Codegen Handle Self Referencing & Multiple Relations to the Same Related Entity by @billy1624 in https://github.com/SeaQL/sea-orm/pull/347 -**Full Changelog**: https://github.com/SeaQL/sea-orm/compare/0.4.0...0.4.1 - ## 0.4.0 - 2021-11-19 ### Fixed Issues @@ -1280,8 +1219,6 @@ In this minor release, we removed `time` v0.1 from the dependency graph * Documentation for sea-orm by @charleschege in https://github.com/SeaQL/sea-orm/pull/280 * Support `Vec` primary key by @billy1624 in https://github.com/SeaQL/sea-orm/pull/287 -**Full Changelog**: https://github.com/SeaQL/sea-orm/compare/0.3.1...0.3.2 - ## 0.3.1 - 2021-10-23 (We are changing our Changelog format from now on) @@ -1299,8 +1236,6 @@ In this minor release, we removed `time` v0.1 from the dependency graph * CI cleaning by @AngelOnFira in https://github.com/SeaQL/sea-orm/pull/263 * CI install sea-orm-cli in debug mode by @billy1624 in https://github.com/SeaQL/sea-orm/pull/265 -**Full Changelog**: https://github.com/SeaQL/sea-orm/compare/0.3.0...0.3.1 - ## 0.3.0 - 2021-10-15 https://www.sea-ql.org/SeaORM/blog/2021-10-15-whats-new-in-0.3.0 @@ -1360,6 +1295,8 @@ assert_eq!( [#237]: https://github.com/SeaQL/sea-orm/pull/237 [#246]: https://github.com/SeaQL/sea-orm/pull/246 +**Full Changelog**: https://github.com/SeaQL/sea-orm/compare/0.2.6...0.3.0 + ## 0.2.6 - 2021-10-09 - [[#224]] [sea-orm-cli] Date & Time column type mapping @@ -1439,6 +1376,8 @@ https://www.sea-ql.org/SeaORM/blog/2021-10-01-whats-new-in-0.2.4 [#89]: https://github.com/SeaQL/sea-orm/issues/89 [#59]: https://github.com/SeaQL/sea-orm/issues/59 +**Full Changelog**: https://github.com/SeaQL/sea-orm/compare/0.1.3...0.2.0 + ## 0.1.3 - 2021-08-30 - [[#108]] Remove impl TryGetable for Option From 97dbb4a2f4e2096037936ed9301f6d886472fa80 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Wed, 2 Aug 2023 17:54:36 +0100 Subject: [PATCH 0126/1371] Changelog --- CHANGELOG.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b109857646..be30cd37da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -94,7 +94,7 @@ assert_eq!( r#"SELECT "cake"."name", UPPER("cake"."name") AS "name_upper" FROM "cake""# ); ``` -* Add `DbErr::sql_err()` method to convert error into common database errors `SqlErr`, such as unique constraint or foreign key violation errors. https://github.com/SeaQL/sea-orm/pull/1707 +* Added `DbErr::sql_err()` method to convert error into common database errors `SqlErr`, such as unique constraint or foreign key violation errors. https://github.com/SeaQL/sea-orm/pull/1707 ```rust assert!(matches!( cake.into_active_model().insert(db).await @@ -110,7 +110,7 @@ assert!(matches!( Some(SqlErr::ForeignKeyConstraintViolation(_)) )); ``` -* Add `Select::find_with_linked`, similar to `find_with_related`: https://github.com/SeaQL/sea-orm/pull/1728, https://github.com/SeaQL/sea-orm/pull/1743 +* Added `Select::find_with_linked`, similar to `find_with_related`: https://github.com/SeaQL/sea-orm/pull/1728, https://github.com/SeaQL/sea-orm/pull/1743 ```rust fn find_with_related(self, r: R) -> SelectTwoMany where R: EntityTrait, E: Related; @@ -119,7 +119,7 @@ fn find_with_linked(self, l: L) -> SelectTwoMany // boths yields `Vec<(E::Model, Vec)>` ``` -* Add `DeriveValueType` derive macro for custom wrapper types, implementations of the required traits will be provided, you can customize the `column_type` and `array_type` if needed https://github.com/SeaQL/sea-orm/pull/1720 +* Added `DeriveValueType` derive macro for custom wrapper types, implementations of the required traits will be provided, you can customize the `column_type` and `array_type` if needed https://github.com/SeaQL/sea-orm/pull/1720 ```rust #[derive(DeriveValueType)] #[sea_orm(array_type = "Int")] @@ -182,7 +182,7 @@ enum ConnAcquireErr { Added Seaography integration https://github.com/SeaQL/sea-orm/pull/1599 -* Add `DeriveEntityRelated` macro which will implement `seaography::RelationBuilder` for `RelatedEntity` enumeration when the `seaography` feature is enabled +* Added `DeriveEntityRelated` macro which will implement `seaography::RelationBuilder` for `RelatedEntity` enumeration when the `seaography` feature is enabled * Added generation of `seaography` related information to `sea-orm-codegen`. The `RelatedEntity` enum is added in entities files by `sea-orm-cli` when flag `seaography` is set: @@ -244,7 +244,7 @@ let res = Bakery::insert_many(std::iter::empty()) assert!(matches!(res, Ok(TryInsertResult::Empty))); ``` -* On conflict do nothing not resulting in Err https://github.com/SeaQL/sea-orm/pull/1712 +* Insert on conflict do nothing to return Ok https://github.com/SeaQL/sea-orm/pull/1712 ```rust let on = OnConflict::column(Column::Id).do_nothing().to_owned(); From 25fa9d4bf27be855ab57241017a8931206b62b49 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Wed, 2 Aug 2023 17:55:33 +0100 Subject: [PATCH 0127/1371] Changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index be30cd37da..ccf3c3bfd9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -132,7 +132,7 @@ pub struct Boolbean(pub String); #[derive(DeriveValueType)] pub struct StringVec(pub Vec); ``` -* Add `DeriveDisplay` derive macro to implements `std::fmt::Display` for enum https://github.com/SeaQL/sea-orm/pull/1726 +* Added `DeriveDisplay` derive macro to implements `std::fmt::Display` for enum https://github.com/SeaQL/sea-orm/pull/1726 ```rust #[derive(DeriveDisplay)] enum DisplayTea { @@ -225,7 +225,7 @@ assert_eq!(migration.status(), MigrationStatus::Pending); * Changed the parameter of method `ConnectOptions::set_schema_search_path(T) where T: Into` to takes any string * Changed the parameter of method `ColumnTrait::like()`, `ColumnTrait::not_like()`, `ColumnTrait::starts_with()`, `ColumnTrait::ends_with()` and `ColumnTrait::contains()` to takes any string * Re-export `sea_query::{DynIden, RcOrArc, SeaRc}` in `sea_orm::entity::prelude` module https://github.com/SeaQL/sea-orm/pull/1661 -* Add `expr`, `exprs` and `expr_as` methods to `QuerySelect` trait https://github.com/SeaQL/sea-orm/pull/1702 +* Added `expr`, `exprs` and `expr_as` methods to `QuerySelect` trait https://github.com/SeaQL/sea-orm/pull/1702 * Added `DatabaseConnection::ping` https://github.com/SeaQL/sea-orm/pull/1627 ```rust |db: DatabaseConnection| { From e8500e661a2d2dbe2fcaae545f426bbbacbe4285 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Wed, 2 Aug 2023 17:58:27 +0100 Subject: [PATCH 0128/1371] Changelog --- CHANGELOG.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ccf3c3bfd9..adc52ee701 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -258,17 +258,6 @@ Entity::insert_many([..]).on_conflict(on).do_nothing().exec(db).await; assert!(matches!(res, Ok(TryInsertResult::Conflicted))); ``` -### Upgrades - -* Upgrade `sqlx` to `0.7` https://github.com/SeaQL/sea-orm/pull/1742 -* Upgrade `sea-query` to `0.30` https://github.com/SeaQL/sea-orm/pull/1742 -* Upgrade `sea-schema` to `0.14` https://github.com/SeaQL/sea-orm/pull/1742 -* Upgrade `syn` to `2` https://github.com/SeaQL/sea-orm/pull/1713 -* Upgrade `heck` to `0.4` https://github.com/SeaQL/sea-orm/pull/1520, https://github.com/SeaQL/sea-orm/pull/1544 -* Upgrade `strum` to `0.25` https://github.com/SeaQL/sea-orm/pull/1752 -* Upgrade `clap` to `4.3` https://github.com/SeaQL/sea-orm/pull/1468 -* Upgrade `ouroboros` to `0.17` https://github.com/SeaQL/sea-orm/pull/1724 - ### Bug Fixes * Fixed `DeriveActiveEnum` throwing errors because `string_value` consists non-UAX#31 compliant characters https://github.com/SeaQL/sea-orm/pull/1374 @@ -334,6 +323,17 @@ pub enum Tea { * Definition of `DbErr::ConnectionAcquire` changed to `ConnectionAcquire(ConnAcquireErr)` https://github.com/SeaQL/sea-orm/pull/1737 * `FromJsonQueryResult` removed from entity prelude +### Upgrades + +* Upgrade `sqlx` to `0.7` https://github.com/SeaQL/sea-orm/pull/1742 +* Upgrade `sea-query` to `0.30` https://github.com/SeaQL/sea-orm/pull/1742 +* Upgrade `sea-schema` to `0.14` https://github.com/SeaQL/sea-orm/pull/1742 +* Upgrade `syn` to `2` https://github.com/SeaQL/sea-orm/pull/1713 +* Upgrade `heck` to `0.4` https://github.com/SeaQL/sea-orm/pull/1520, https://github.com/SeaQL/sea-orm/pull/1544 +* Upgrade `strum` to `0.25` https://github.com/SeaQL/sea-orm/pull/1752 +* Upgrade `clap` to `4.3` https://github.com/SeaQL/sea-orm/pull/1468 +* Upgrade `ouroboros` to `0.17` https://github.com/SeaQL/sea-orm/pull/1724 + ### House keeping * Replaced `bae` with `sea-bae` https://github.com/SeaQL/sea-orm/pull/1739 From 8db3463f1cbd080fea136f3d854b7542d59480ac Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Wed, 2 Aug 2023 18:00:47 +0100 Subject: [PATCH 0129/1371] Changelog --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index adc52ee701..aad799f7f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -224,7 +224,6 @@ assert_eq!(migration.status(), MigrationStatus::Pending); * Changed the parameter of method `Transaction::from_sql_and_values(DbBackend, T, I) where I: IntoIterator, T: Into` to takes any string SQL * Changed the parameter of method `ConnectOptions::set_schema_search_path(T) where T: Into` to takes any string * Changed the parameter of method `ColumnTrait::like()`, `ColumnTrait::not_like()`, `ColumnTrait::starts_with()`, `ColumnTrait::ends_with()` and `ColumnTrait::contains()` to takes any string -* Re-export `sea_query::{DynIden, RcOrArc, SeaRc}` in `sea_orm::entity::prelude` module https://github.com/SeaQL/sea-orm/pull/1661 * Added `expr`, `exprs` and `expr_as` methods to `QuerySelect` trait https://github.com/SeaQL/sea-orm/pull/1702 * Added `DatabaseConnection::ping` https://github.com/SeaQL/sea-orm/pull/1627 ```rust From 1d0b1476a92087d7ad4edac253bc044b158c9148 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Wed, 2 Aug 2023 18:06:17 +0100 Subject: [PATCH 0130/1371] Changelog --- CHANGELOG.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aad799f7f1..76a5fe8369 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -224,6 +224,7 @@ assert_eq!(migration.status(), MigrationStatus::Pending); * Changed the parameter of method `Transaction::from_sql_and_values(DbBackend, T, I) where I: IntoIterator, T: Into` to takes any string SQL * Changed the parameter of method `ConnectOptions::set_schema_search_path(T) where T: Into` to takes any string * Changed the parameter of method `ColumnTrait::like()`, `ColumnTrait::not_like()`, `ColumnTrait::starts_with()`, `ColumnTrait::ends_with()` and `ColumnTrait::contains()` to takes any string +* Added `sea_query::{DynIden, RcOrArc, SeaRc}` to entity prelude https://github.com/SeaQL/sea-orm/pull/1661 * Added `expr`, `exprs` and `expr_as` methods to `QuerySelect` trait https://github.com/SeaQL/sea-orm/pull/1702 * Added `DatabaseConnection::ping` https://github.com/SeaQL/sea-orm/pull/1627 ```rust @@ -288,7 +289,6 @@ pub enum StringValueVariant { * Added `derive` and `strum` features to `sea-orm-macros` * The derive macro `EnumIter` is now shipped by `sea-orm-macros` * Added a new variant `Many` to `Identity` https://github.com/SeaQL/sea-orm/pull/1508 -* Replace the use of `SeaRc` where `T` isn't `dyn Iden` with `RcOrArc` https://github.com/SeaQL/sea-orm/pull/1661 * Enabled `hashable-value` feature in SeaQuery, thus `Value::Float(NaN) == Value::Float(NaN)` would be true https://github.com/SeaQL/sea-orm/pull/1728, https://github.com/SeaQL/sea-orm/pull/1743 * The `DeriveActiveEnum` derive macro no longer implement `std::fmt::Display`. You can use the new `DeriveDisplay` macro https://github.com/SeaQL/sea-orm/pull/1726 * `sea-query/derive` is no longer enabled by `sea-orm`, as such, `Iden` no longer works as a derive macro (it's still a trait). Instead, we are shipping a new macro `DeriveIden`: @@ -324,14 +324,14 @@ pub enum Tea { ### Upgrades -* Upgrade `sqlx` to `0.7` https://github.com/SeaQL/sea-orm/pull/1742 -* Upgrade `sea-query` to `0.30` https://github.com/SeaQL/sea-orm/pull/1742 -* Upgrade `sea-schema` to `0.14` https://github.com/SeaQL/sea-orm/pull/1742 -* Upgrade `syn` to `2` https://github.com/SeaQL/sea-orm/pull/1713 -* Upgrade `heck` to `0.4` https://github.com/SeaQL/sea-orm/pull/1520, https://github.com/SeaQL/sea-orm/pull/1544 -* Upgrade `strum` to `0.25` https://github.com/SeaQL/sea-orm/pull/1752 -* Upgrade `clap` to `4.3` https://github.com/SeaQL/sea-orm/pull/1468 -* Upgrade `ouroboros` to `0.17` https://github.com/SeaQL/sea-orm/pull/1724 +* Upgraded `sqlx` to `0.7` https://github.com/SeaQL/sea-orm/pull/1742 +* Upgraded `sea-query` to `0.30` https://github.com/SeaQL/sea-orm/pull/1742 +* Upgraded `sea-schema` to `0.14` https://github.com/SeaQL/sea-orm/pull/1742 +* Upgraded `syn` to `2` https://github.com/SeaQL/sea-orm/pull/1713 +* Upgraded `heck` to `0.4` https://github.com/SeaQL/sea-orm/pull/1520, https://github.com/SeaQL/sea-orm/pull/1544 +* Upgraded `strum` to `0.25` https://github.com/SeaQL/sea-orm/pull/1752 +* Upgraded `clap` to `4.3` https://github.com/SeaQL/sea-orm/pull/1468 +* Upgraded `ouroboros` to `0.17` https://github.com/SeaQL/sea-orm/pull/1724 ### House keeping From 66907667262295a18455a5272f89b78b02455a50 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Wed, 2 Aug 2023 18:14:41 +0100 Subject: [PATCH 0131/1371] Changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76a5fe8369..42c35d2e63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -291,7 +291,7 @@ pub enum StringValueVariant { * Added a new variant `Many` to `Identity` https://github.com/SeaQL/sea-orm/pull/1508 * Enabled `hashable-value` feature in SeaQuery, thus `Value::Float(NaN) == Value::Float(NaN)` would be true https://github.com/SeaQL/sea-orm/pull/1728, https://github.com/SeaQL/sea-orm/pull/1743 * The `DeriveActiveEnum` derive macro no longer implement `std::fmt::Display`. You can use the new `DeriveDisplay` macro https://github.com/SeaQL/sea-orm/pull/1726 -* `sea-query/derive` is no longer enabled by `sea-orm`, as such, `Iden` no longer works as a derive macro (it's still a trait). Instead, we are shipping a new macro `DeriveIden`: +* `sea-query/derive` is no longer enabled by `sea-orm`, as such, `Iden` no longer works as a derive macro (it's still a trait). Instead, we are shipping a new macro `DeriveIden` https://github.com/SeaQL/sea-orm/pull/1740 https://github.com/SeaQL/sea-orm/pull/1755 ```rust // then: From 96e7e7a3d388267e5d87f6abfbf179d1f586a8d5 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 3 Aug 2023 12:24:00 +0100 Subject: [PATCH 0132/1371] Edit --- COMMUNITY.md | 1 - 1 file changed, 1 deletion(-) diff --git a/COMMUNITY.md b/COMMUNITY.md index 198aa8ba96..cff129bb10 100644 --- a/COMMUNITY.md +++ b/COMMUNITY.md @@ -10,7 +10,6 @@ If you have built an app using SeaORM and want to showcase it, feel free to open - [CodeCTRL](https://codectrl.authentura.com) ([repository](https://github.com/Authentura/codectrl)) | A self-hostable code logging platform | DB: SQLite - [My Data My Consent](https://mydatamyconsent.com/) | Online data sharing for people and businesses simplified - [Prefix.dev](https://prefix.dev/) | Conda Package Search, Environment Management and Deployment built for [mamba](https://github.com/mamba-org/mamba) | DB: Postgres, SQLite -- [Sensei](https://l2.technology/sensei) ([repository](https://github.com/L2-Technology/sensei)) | A Bitcoin lightning node implementation | DB: MySQL, Postgres, SQLite - [Spyglass](https://www.spyglass.fyi/) ([repository](https://github.com/a5huynh/spyglass)) ![GitHub stars](https://img.shields.io/github/stars/a5huynh/spyglass.svg?style=social) | 🔭 A personal search engine that indexes what you want w/ a simple set of rules. | DB: SQLite - [Svix](https://www.svix.com/) ([repository](https://github.com/svix/svix-webhooks)) ![GitHub stars](https://img.shields.io/github/stars/svix/svix-webhooks.svg?style=social) | The enterprise ready webhooks service | DB: Postgres - [UpVPN](https://upvpn.app) ([repository](https://github.com/upvpn/upvpn-app)) | Serverless VPN on WireGuard® | DB: SQLite, Postgres From 03786fbde2b00e654fea3d9850ff7fdb4cb4f7e9 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 4 Aug 2023 17:58:59 +0100 Subject: [PATCH 0133/1371] Edit --- COMMUNITY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/COMMUNITY.md b/COMMUNITY.md index cff129bb10..c19dd24ae6 100644 --- a/COMMUNITY.md +++ b/COMMUNITY.md @@ -7,7 +7,6 @@ If you have built an app using SeaORM and want to showcase it, feel free to open ### Startups - [Caido](https://caido.io/) | A lightweight web security auditing toolkit -- [CodeCTRL](https://codectrl.authentura.com) ([repository](https://github.com/Authentura/codectrl)) | A self-hostable code logging platform | DB: SQLite - [My Data My Consent](https://mydatamyconsent.com/) | Online data sharing for people and businesses simplified - [Prefix.dev](https://prefix.dev/) | Conda Package Search, Environment Management and Deployment built for [mamba](https://github.com/mamba-org/mamba) | DB: Postgres, SQLite - [Spyglass](https://www.spyglass.fyi/) ([repository](https://github.com/a5huynh/spyglass)) ![GitHub stars](https://img.shields.io/github/stars/a5huynh/spyglass.svg?style=social) | 🔭 A personal search engine that indexes what you want w/ a simple set of rules. | DB: SQLite @@ -53,6 +52,7 @@ If you have built an app using SeaORM and want to showcase it, feel free to open - [RGB Lib](https://github.com/RGB-Tools/rgb-lib) | A library to manage wallets for RGB assets | DB: MySQL, Postgres, SQLite #### Dev Tools +- [CodeCTRL](https://codectrl.authentura.com) ([repository](https://github.com/Authentura/codectrl)) | A self-hostable code logging platform | DB: SQLite - [Crosup](https://github.com/tsirysndr/crosup) | Quickly setup your development environment on your Chromebook/ChromeOS or any Linux distribution 🐧 ❄️ 💻 🚀 ✨ | DB: SQLite - [CyberAPI](https://github.com/vicanso/cyberapi) API tool client for developer. | DB: SQLite - [nitro_repo](https://github.com/wyatt-herkamp/nitro_repo) | An OpenSource, lightweight, and fast artifact manager. | DB: MySQL, SQLite From f2dfbab3a01750fc9f9a7ef69589dc8974a8b83d Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 4 Aug 2023 20:22:07 +0100 Subject: [PATCH 0134/1371] Fix find_with_related #1800 --- src/executor/select.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/executor/select.rs b/src/executor/select.rs index 32f633f8ea..afd300d51e 100644 --- a/src/executor/select.rs +++ b/src/executor/select.rs @@ -1054,6 +1054,10 @@ where } else { acc.insert(key, vec![value]); } + } else { + if acc.get(&key).is_none() { + acc.insert(key, vec![]); + } } acc From ef861294e2987c5c2cc1fc2b909c72aaf2132ca5 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 4 Aug 2023 20:26:12 +0100 Subject: [PATCH 0135/1371] Fix clippy --- examples/basic/src/main.rs | 6 +----- examples/basic/src/select.rs | 38 ------------------------------------ src/executor/select.rs | 6 ++---- 3 files changed, 3 insertions(+), 47 deletions(-) diff --git a/examples/basic/src/main.rs b/examples/basic/src/main.rs index 58c09db007..7d0a2ca742 100644 --- a/examples/basic/src/main.rs +++ b/examples/basic/src/main.rs @@ -23,7 +23,7 @@ use select::*; #[async_std::main] async fn main() { - let db = Database::connect("sql://sea:sea@localhost/bakery") + let db = Database::connect("mysql://sea:sea@localhost/test") .await .unwrap(); @@ -32,8 +32,4 @@ async fn main() { println!("===== =====\n"); all_about_select(&db).await.unwrap(); - - println!("===== =====\n"); - - all_about_operation(&db).await.unwrap(); } diff --git a/examples/basic/src/select.rs b/examples/basic/src/select.rs index bb1354fba6..7d0511444e 100644 --- a/examples/basic/src/select.rs +++ b/examples/basic/src/select.rs @@ -2,46 +2,8 @@ use super::*; use sea_orm::{entity::*, error::*, query::*, DbConn, FromQueryResult}; pub async fn all_about_select(db: &DbConn) -> Result<(), DbErr> { - find_all(db).await?; - - println!("===== =====\n"); - - find_one(db).await?; - - println!("===== =====\n"); - - find_one_to_one(db).await?; - - println!("===== =====\n"); - find_one_to_many(db).await?; - println!("===== =====\n"); - - count_fruits_by_cake(db).await?; - - println!("===== =====\n"); - - find_many_to_many(db).await?; - - if false { - println!("===== =====\n"); - - all_about_select_json(db).await?; - } - - println!("===== =====\n"); - - find_all_stream(db).await.unwrap(); - - println!("===== =====\n"); - - find_first_page(db).await.unwrap(); - - println!("===== =====\n"); - - find_num_pages(db).await.unwrap(); - Ok(()) } diff --git a/src/executor/select.rs b/src/executor/select.rs index afd300d51e..cd7d122cb4 100644 --- a/src/executor/select.rs +++ b/src/executor/select.rs @@ -1054,10 +1054,8 @@ where } else { acc.insert(key, vec![value]); } - } else { - if acc.get(&key).is_none() { - acc.insert(key, vec![]); - } + } else if acc.get(&key).is_none() { + acc.insert(key, vec![]); } acc From 727390b8d2995de8601c0ae10ebf66369eecfba9 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 4 Aug 2023 20:39:38 +0100 Subject: [PATCH 0136/1371] Revert unintended code changes. This reverts commit ef861294e2987c5c2cc1fc2b909c72aaf2132ca5. --- examples/basic/src/main.rs | 6 +++++- examples/basic/src/select.rs | 38 ++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/examples/basic/src/main.rs b/examples/basic/src/main.rs index 7d0a2ca742..58c09db007 100644 --- a/examples/basic/src/main.rs +++ b/examples/basic/src/main.rs @@ -23,7 +23,7 @@ use select::*; #[async_std::main] async fn main() { - let db = Database::connect("mysql://sea:sea@localhost/test") + let db = Database::connect("sql://sea:sea@localhost/bakery") .await .unwrap(); @@ -32,4 +32,8 @@ async fn main() { println!("===== =====\n"); all_about_select(&db).await.unwrap(); + + println!("===== =====\n"); + + all_about_operation(&db).await.unwrap(); } diff --git a/examples/basic/src/select.rs b/examples/basic/src/select.rs index 7d0511444e..bb1354fba6 100644 --- a/examples/basic/src/select.rs +++ b/examples/basic/src/select.rs @@ -2,8 +2,46 @@ use super::*; use sea_orm::{entity::*, error::*, query::*, DbConn, FromQueryResult}; pub async fn all_about_select(db: &DbConn) -> Result<(), DbErr> { + find_all(db).await?; + + println!("===== =====\n"); + + find_one(db).await?; + + println!("===== =====\n"); + + find_one_to_one(db).await?; + + println!("===== =====\n"); + find_one_to_many(db).await?; + println!("===== =====\n"); + + count_fruits_by_cake(db).await?; + + println!("===== =====\n"); + + find_many_to_many(db).await?; + + if false { + println!("===== =====\n"); + + all_about_select_json(db).await?; + } + + println!("===== =====\n"); + + find_all_stream(db).await.unwrap(); + + println!("===== =====\n"); + + find_first_page(db).await.unwrap(); + + println!("===== =====\n"); + + find_num_pages(db).await.unwrap(); + Ok(()) } From 88431c10f6f408a2932f0991e48c986524ce261e Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 4 Aug 2023 20:37:55 +0100 Subject: [PATCH 0137/1371] 0.12.2 --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42c35d2e63..6e2703a279 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,12 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## 0.12.2 - Pending +## 0.12.2 - 2023-08-04 ### Enhancements * Added support for Postgres arrays in `FromQueryResult` impl of `JsonValue` https://github.com/SeaQL/sea-orm/pull/1598 +### Bug fixes + +* Fixed `find_with_related` consolidation logic https://github.com/SeaQL/sea-orm/issues/1800 + ## 0.12.1 - 2023-07-27 + `0.12.0-rc.1`: Yanked From 242237aa511e1711560e81c2346ac0eaa99400ec Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 4 Aug 2023 20:44:05 +0100 Subject: [PATCH 0138/1371] 0.12.2 --- Cargo.toml | 4 ++-- sea-orm-cli/Cargo.toml | 4 ++-- sea-orm-codegen/Cargo.toml | 2 +- sea-orm-macros/Cargo.toml | 2 +- sea-orm-migration/Cargo.toml | 6 +++--- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e4d5b74aa9..02be2c3992 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [".", "sea-orm-macros", "sea-orm-codegen"] [package] name = "sea-orm" -version = "0.12.1" +version = "0.12.2" authors = ["Chris Tsang "] edition = "2021" description = "🐚 An async & dynamic ORM for Rust" @@ -33,7 +33,7 @@ log = { version = "0.4", default-features = false } tracing = { version = "0.1", default-features = false, features = ["attributes", "log"] } rust_decimal = { version = "1", default-features = false, optional = true } bigdecimal = { version = "0.3", default-features = false, optional = true } -sea-orm-macros = { version = "0.12.1", path = "sea-orm-macros", default-features = false, features = ["strum"] } +sea-orm-macros = { version = "0.12.2", path = "sea-orm-macros", default-features = false, features = ["strum"] } sea-query = { version = "0.30.0", default-features = false, features = ["thread-safe", "hashable-value", "backend-mysql", "backend-postgres", "backend-sqlite"] } sea-query-binder = { version = "0.5.0", default-features = false, optional = true } strum = { version = "0.25", default-features = false } diff --git a/sea-orm-cli/Cargo.toml b/sea-orm-cli/Cargo.toml index ae20072e47..35db446ebc 100644 --- a/sea-orm-cli/Cargo.toml +++ b/sea-orm-cli/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "sea-orm-cli" -version = "0.12.1" +version = "0.12.2" authors = [ "Chris Tsang ", "Billy Chan ", @@ -37,7 +37,7 @@ required-features = ["cli", "codegen"] clap = { version = "4.3", features = ["env", "derive"], optional = true } dotenvy = { version = "0.15", default-features = false, optional = true } async-std = { version = "1.9", default-features = false, features = ["attributes", "tokio1"], optional = true } -sea-orm-codegen = { version = "=0.12.1", path = "../sea-orm-codegen", default-features = false, optional = true } +sea-orm-codegen = { version = "=0.12.2", path = "../sea-orm-codegen", default-features = false, optional = true } sea-schema = { version = "0.14.0" } sqlx = { version = "0.7", default-features = false, features = ["mysql", "postgres"], optional = true } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["env-filter", "fmt"] } diff --git a/sea-orm-codegen/Cargo.toml b/sea-orm-codegen/Cargo.toml index 5974a1d2ee..fdb2f3cb7b 100644 --- a/sea-orm-codegen/Cargo.toml +++ b/sea-orm-codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sea-orm-codegen" -version = "0.12.1" +version = "0.12.2" authors = ["Billy Chan "] edition = "2021" description = "Code Generator for SeaORM" diff --git a/sea-orm-macros/Cargo.toml b/sea-orm-macros/Cargo.toml index b4c2f93b6b..6d55d2debb 100644 --- a/sea-orm-macros/Cargo.toml +++ b/sea-orm-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sea-orm-macros" -version = "0.12.1" +version = "0.12.2" authors = [ "Billy Chan " ] edition = "2021" description = "Derive macros for SeaORM" diff --git a/sea-orm-migration/Cargo.toml b/sea-orm-migration/Cargo.toml index d8235dc104..3722b9ce4a 100644 --- a/sea-orm-migration/Cargo.toml +++ b/sea-orm-migration/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "sea-orm-migration" -version = "0.12.1" +version = "0.12.2" authors = ["Billy Chan "] edition = "2021" description = "Migration utility for SeaORM" @@ -23,8 +23,8 @@ path = "src/lib.rs" async-trait = { version = "0.1", default-features = false } clap = { version = "4.3", features = ["env", "derive"], optional = true } dotenvy = { version = "0.15", default-features = false, optional = true } -sea-orm = { version = "0.12.1", path = "../", default-features = false, features = ["macros"] } -sea-orm-cli = { version = "0.12.1", path = "../sea-orm-cli", default-features = false, optional = true } +sea-orm = { version = "0.12.2", path = "../", default-features = false, features = ["macros"] } +sea-orm-cli = { version = "0.12.2", path = "../sea-orm-cli", default-features = false, optional = true } sea-schema = { version = "0.14.0" } tracing = { version = "0.1", default-features = false, features = ["log"] } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["env-filter", "fmt"] } From 43e2b9d314a75fa4fdefe53c9009483eb90553ec Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 4 Aug 2023 20:44:06 +0100 Subject: [PATCH 0139/1371] update examples --- examples/actix3_example/entity/Cargo.toml | 2 +- examples/actix3_example/migration/Cargo.toml | 2 +- examples/actix3_example/service/Cargo.toml | 2 +- examples/actix_example/entity/Cargo.toml | 2 +- examples/actix_example/migration/Cargo.toml | 2 +- examples/actix_example/service/Cargo.toml | 2 +- examples/axum_example/entity/Cargo.toml | 2 +- examples/axum_example/migration/Cargo.toml | 2 +- examples/axum_example/service/Cargo.toml | 2 +- examples/graphql_example/entity/Cargo.toml | 2 +- examples/graphql_example/migration/Cargo.toml | 2 +- examples/graphql_example/service/Cargo.toml | 2 +- examples/jsonrpsee_example/entity/Cargo.toml | 2 +- examples/jsonrpsee_example/migration/Cargo.toml | 2 +- examples/jsonrpsee_example/service/Cargo.toml | 2 +- examples/poem_example/entity/Cargo.toml | 2 +- examples/poem_example/migration/Cargo.toml | 2 +- examples/poem_example/service/Cargo.toml | 2 +- examples/rocket_example/entity/Cargo.toml | 2 +- examples/rocket_example/migration/Cargo.toml | 2 +- examples/rocket_example/service/Cargo.toml | 2 +- examples/rocket_okapi_example/entity/Cargo.toml | 2 +- examples/rocket_okapi_example/migration/Cargo.toml | 2 +- examples/rocket_okapi_example/service/Cargo.toml | 2 +- examples/salvo_example/entity/Cargo.toml | 2 +- examples/salvo_example/migration/Cargo.toml | 2 +- examples/salvo_example/service/Cargo.toml | 2 +- examples/seaography_example/migration/Cargo.toml | 4 ++-- examples/tonic_example/entity/Cargo.toml | 2 +- examples/tonic_example/migration/Cargo.toml | 2 +- examples/tonic_example/service/Cargo.toml | 2 +- 31 files changed, 32 insertions(+), 32 deletions(-) diff --git a/examples/actix3_example/entity/Cargo.toml b/examples/actix3_example/entity/Cargo.toml index 3a18e7bc4b..e55661d417 100644 --- a/examples/actix3_example/entity/Cargo.toml +++ b/examples/actix3_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.1" # sea-orm version +version = "0.12.2" # sea-orm version diff --git a/examples/actix3_example/migration/Cargo.toml b/examples/actix3_example/migration/Cargo.toml index ddecf3bed6..3d27833d5e 100644 --- a/examples/actix3_example/migration/Cargo.toml +++ b/examples/actix3_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.1" # sea-orm-migration version +version = "0.12.2" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-async-std-native-tls", diff --git a/examples/actix3_example/service/Cargo.toml b/examples/actix3_example/service/Cargo.toml index 2db4960124..8f1345534e 100644 --- a/examples/actix3_example/service/Cargo.toml +++ b/examples/actix3_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.1" # sea-orm version +version = "0.12.2" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/actix_example/entity/Cargo.toml b/examples/actix_example/entity/Cargo.toml index 3a18e7bc4b..e55661d417 100644 --- a/examples/actix_example/entity/Cargo.toml +++ b/examples/actix_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.1" # sea-orm version +version = "0.12.2" # sea-orm version diff --git a/examples/actix_example/migration/Cargo.toml b/examples/actix_example/migration/Cargo.toml index f3090dfc46..327817c4ea 100644 --- a/examples/actix_example/migration/Cargo.toml +++ b/examples/actix_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.1" # sea-orm-migration version +version = "0.12.2" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-actix-native-tls", diff --git a/examples/actix_example/service/Cargo.toml b/examples/actix_example/service/Cargo.toml index 46aace63bc..2fa040a5c5 100644 --- a/examples/actix_example/service/Cargo.toml +++ b/examples/actix_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.1" # sea-orm version +version = "0.12.2" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/axum_example/entity/Cargo.toml b/examples/axum_example/entity/Cargo.toml index 3a18e7bc4b..e55661d417 100644 --- a/examples/axum_example/entity/Cargo.toml +++ b/examples/axum_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.1" # sea-orm version +version = "0.12.2" # sea-orm version diff --git a/examples/axum_example/migration/Cargo.toml b/examples/axum_example/migration/Cargo.toml index fa43fb3ad9..8372174a59 100644 --- a/examples/axum_example/migration/Cargo.toml +++ b/examples/axum_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.1" # sea-orm-migration version +version = "0.12.2" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/axum_example/service/Cargo.toml b/examples/axum_example/service/Cargo.toml index 9099cba1c6..6de4f140d0 100644 --- a/examples/axum_example/service/Cargo.toml +++ b/examples/axum_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.1" # sea-orm version +version = "0.12.2" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/graphql_example/entity/Cargo.toml b/examples/graphql_example/entity/Cargo.toml index 4ebefab08d..07cb479cf7 100644 --- a/examples/graphql_example/entity/Cargo.toml +++ b/examples/graphql_example/entity/Cargo.toml @@ -16,4 +16,4 @@ version = "5.0.10" [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.1" # sea-orm version +version = "0.12.2" # sea-orm version diff --git a/examples/graphql_example/migration/Cargo.toml b/examples/graphql_example/migration/Cargo.toml index 51c59d9ad6..1f32a01cd1 100644 --- a/examples/graphql_example/migration/Cargo.toml +++ b/examples/graphql_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.1" # sea-orm-migration version +version = "0.12.2" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/graphql_example/service/Cargo.toml b/examples/graphql_example/service/Cargo.toml index af0eda05cb..5e8d1f8091 100644 --- a/examples/graphql_example/service/Cargo.toml +++ b/examples/graphql_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.1" # sea-orm version +version = "0.12.2" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/jsonrpsee_example/entity/Cargo.toml b/examples/jsonrpsee_example/entity/Cargo.toml index 3a18e7bc4b..e55661d417 100644 --- a/examples/jsonrpsee_example/entity/Cargo.toml +++ b/examples/jsonrpsee_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.1" # sea-orm version +version = "0.12.2" # sea-orm version diff --git a/examples/jsonrpsee_example/migration/Cargo.toml b/examples/jsonrpsee_example/migration/Cargo.toml index 7b2d5391b4..9130321612 100644 --- a/examples/jsonrpsee_example/migration/Cargo.toml +++ b/examples/jsonrpsee_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.1" # sea-orm-migration version +version = "0.12.2" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/jsonrpsee_example/service/Cargo.toml b/examples/jsonrpsee_example/service/Cargo.toml index e78006d04c..c38b350dfc 100644 --- a/examples/jsonrpsee_example/service/Cargo.toml +++ b/examples/jsonrpsee_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.1" # sea-orm version +version = "0.12.2" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/poem_example/entity/Cargo.toml b/examples/poem_example/entity/Cargo.toml index 3a18e7bc4b..e55661d417 100644 --- a/examples/poem_example/entity/Cargo.toml +++ b/examples/poem_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.1" # sea-orm version +version = "0.12.2" # sea-orm version diff --git a/examples/poem_example/migration/Cargo.toml b/examples/poem_example/migration/Cargo.toml index 7b2d5391b4..9130321612 100644 --- a/examples/poem_example/migration/Cargo.toml +++ b/examples/poem_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.1" # sea-orm-migration version +version = "0.12.2" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/poem_example/service/Cargo.toml b/examples/poem_example/service/Cargo.toml index daf227e603..1ac5bbc688 100644 --- a/examples/poem_example/service/Cargo.toml +++ b/examples/poem_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.1" # sea-orm version +version = "0.12.2" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/rocket_example/entity/Cargo.toml b/examples/rocket_example/entity/Cargo.toml index b6b407edce..564b70e94f 100644 --- a/examples/rocket_example/entity/Cargo.toml +++ b/examples/rocket_example/entity/Cargo.toml @@ -15,4 +15,4 @@ rocket = { version = "0.5.0-rc.1", features = [ [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.1" # sea-orm version +version = "0.12.2" # sea-orm version diff --git a/examples/rocket_example/migration/Cargo.toml b/examples/rocket_example/migration/Cargo.toml index 625f0a6512..d1110bcea4 100644 --- a/examples/rocket_example/migration/Cargo.toml +++ b/examples/rocket_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.1" # sea-orm-migration version +version = "0.12.2" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/rocket_example/service/Cargo.toml b/examples/rocket_example/service/Cargo.toml index 630d699251..8829abdfbe 100644 --- a/examples/rocket_example/service/Cargo.toml +++ b/examples/rocket_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.1" # sea-orm version +version = "0.12.2" # sea-orm version features = [ "runtime-tokio-native-tls", "sqlx-postgres", diff --git a/examples/rocket_okapi_example/entity/Cargo.toml b/examples/rocket_okapi_example/entity/Cargo.toml index e0729f9cfc..d7246597d3 100644 --- a/examples/rocket_okapi_example/entity/Cargo.toml +++ b/examples/rocket_okapi_example/entity/Cargo.toml @@ -15,7 +15,7 @@ rocket = { version = "0.5.0-rc.1", features = [ [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.1" # sea-orm version +version = "0.12.2" # sea-orm version [dependencies.rocket_okapi] version = "0.8.0-rc.2" diff --git a/examples/rocket_okapi_example/migration/Cargo.toml b/examples/rocket_okapi_example/migration/Cargo.toml index 625f0a6512..d1110bcea4 100644 --- a/examples/rocket_okapi_example/migration/Cargo.toml +++ b/examples/rocket_okapi_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.1" # sea-orm-migration version +version = "0.12.2" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/rocket_okapi_example/service/Cargo.toml b/examples/rocket_okapi_example/service/Cargo.toml index 630d699251..8829abdfbe 100644 --- a/examples/rocket_okapi_example/service/Cargo.toml +++ b/examples/rocket_okapi_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.1" # sea-orm version +version = "0.12.2" # sea-orm version features = [ "runtime-tokio-native-tls", "sqlx-postgres", diff --git a/examples/salvo_example/entity/Cargo.toml b/examples/salvo_example/entity/Cargo.toml index 9f26f06f25..1c2d248a11 100644 --- a/examples/salvo_example/entity/Cargo.toml +++ b/examples/salvo_example/entity/Cargo.toml @@ -14,4 +14,4 @@ salvo = { version = "0.49" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.1" # sea-orm version +version = "0.12.2" # sea-orm version diff --git a/examples/salvo_example/migration/Cargo.toml b/examples/salvo_example/migration/Cargo.toml index 7b2d5391b4..9130321612 100644 --- a/examples/salvo_example/migration/Cargo.toml +++ b/examples/salvo_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.1" # sea-orm-migration version +version = "0.12.2" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/salvo_example/service/Cargo.toml b/examples/salvo_example/service/Cargo.toml index d90467cf5d..7774385f11 100644 --- a/examples/salvo_example/service/Cargo.toml +++ b/examples/salvo_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.1" # sea-orm version +version = "0.12.2" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/seaography_example/migration/Cargo.toml b/examples/seaography_example/migration/Cargo.toml index e6f156a093..51c968f0cf 100644 --- a/examples/seaography_example/migration/Cargo.toml +++ b/examples/seaography_example/migration/Cargo.toml @@ -15,11 +15,11 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm] path = "../../.." # remove this line in your own project -version = "0.12.1" # sea-orm version +version = "0.12.2" # sea-orm version [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.1" # sea-orm-migration version +version = "0.12.2" # sea-orm-migration version features = [ "runtime-async-std-native-tls", "sqlx-mysql", diff --git a/examples/tonic_example/entity/Cargo.toml b/examples/tonic_example/entity/Cargo.toml index 3a18e7bc4b..e55661d417 100644 --- a/examples/tonic_example/entity/Cargo.toml +++ b/examples/tonic_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.1" # sea-orm version +version = "0.12.2" # sea-orm version diff --git a/examples/tonic_example/migration/Cargo.toml b/examples/tonic_example/migration/Cargo.toml index 225035e9b7..2cb5e7e7dd 100644 --- a/examples/tonic_example/migration/Cargo.toml +++ b/examples/tonic_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.1" # sea-orm-migration version +version = "0.12.2" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-rustls", diff --git a/examples/tonic_example/service/Cargo.toml b/examples/tonic_example/service/Cargo.toml index 776716aad4..da456d6b51 100644 --- a/examples/tonic_example/service/Cargo.toml +++ b/examples/tonic_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.1" # sea-orm version +version = "0.12.2" # sea-orm version features = [ "debug-print", "runtime-tokio-rustls", From 649df749ba8414eb81c12281b80cf9d720f762c0 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 4 Aug 2023 23:47:01 +0100 Subject: [PATCH 0140/1371] Edit --- COMMUNITY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/COMMUNITY.md b/COMMUNITY.md index c19dd24ae6..42aaf6d3f2 100644 --- a/COMMUNITY.md +++ b/COMMUNITY.md @@ -47,6 +47,7 @@ If you have built an app using SeaORM and want to showcase it, feel free to open - [SophyCore](https://github.com/FarDragi/SophyCore) | Main system that centralizes all rules, to be used by both the discord bot and the future site | DB: Postgres #### Crypto +- [Sensei](https://l2.technology/sensei) ([repository](https://github.com/L2-Technology/sensei)) | A Bitcoin lightning node implementation | DB: MySQL, Postgres, SQLite - [MoonRamp](https://github.com/MoonRamp/MoonRamp) | A free and open source crypto payment gateway | DB: MySQL, Postgres, SQLite - [Oura Postgres Sink](https://github.com/dcSpark/oura-postgres-sink) | Sync a postgres database with the cardano blockchain using [Oura](https://github.com/txpipe/oura) | DB: Postgres - [RGB Lib](https://github.com/RGB-Tools/rgb-lib) | A library to manage wallets for RGB assets | DB: MySQL, Postgres, SQLite From 379c56e38d3f5350043b4605118d0f15933127ff Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 4 Aug 2023 23:49:23 +0100 Subject: [PATCH 0141/1371] Edit --- COMMUNITY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/COMMUNITY.md b/COMMUNITY.md index 42aaf6d3f2..49181187ba 100644 --- a/COMMUNITY.md +++ b/COMMUNITY.md @@ -47,7 +47,7 @@ If you have built an app using SeaORM and want to showcase it, feel free to open - [SophyCore](https://github.com/FarDragi/SophyCore) | Main system that centralizes all rules, to be used by both the discord bot and the future site | DB: Postgres #### Crypto -- [Sensei](https://l2.technology/sensei) ([repository](https://github.com/L2-Technology/sensei)) | A Bitcoin lightning node implementation | DB: MySQL, Postgres, SQLite +- [Sensei](https://github.com/L2-Technology/sensei) | A Bitcoin lightning node implementation | DB: MySQL, Postgres, SQLite - [MoonRamp](https://github.com/MoonRamp/MoonRamp) | A free and open source crypto payment gateway | DB: MySQL, Postgres, SQLite - [Oura Postgres Sink](https://github.com/dcSpark/oura-postgres-sink) | Sync a postgres database with the cardano blockchain using [Oura](https://github.com/txpipe/oura) | DB: Postgres - [RGB Lib](https://github.com/RGB-Tools/rgb-lib) | A library to manage wallets for RGB assets | DB: MySQL, Postgres, SQLite From 046fca5c96fc3801fa06b05b0d2eaa4c2b7287f0 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 4 Aug 2023 23:53:36 +0100 Subject: [PATCH 0142/1371] Edit --- COMMUNITY.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/COMMUNITY.md b/COMMUNITY.md index 49181187ba..9409ff4475 100644 --- a/COMMUNITY.md +++ b/COMMUNITY.md @@ -47,10 +47,10 @@ If you have built an app using SeaORM and want to showcase it, feel free to open - [SophyCore](https://github.com/FarDragi/SophyCore) | Main system that centralizes all rules, to be used by both the discord bot and the future site | DB: Postgres #### Crypto -- [Sensei](https://github.com/L2-Technology/sensei) | A Bitcoin lightning node implementation | DB: MySQL, Postgres, SQLite - [MoonRamp](https://github.com/MoonRamp/MoonRamp) | A free and open source crypto payment gateway | DB: MySQL, Postgres, SQLite -- [Oura Postgres Sink](https://github.com/dcSpark/oura-postgres-sink) | Sync a postgres database with the cardano blockchain using [Oura](https://github.com/txpipe/oura) | DB: Postgres +- [Oura Postgres Sink](https://github.com/dcSpark/oura-postgres-sink) | Sync a postgres database with the cardano blockchain using Oura | DB: Postgres - [RGB Lib](https://github.com/RGB-Tools/rgb-lib) | A library to manage wallets for RGB assets | DB: MySQL, Postgres, SQLite +- [Sensei](https://github.com/L2-Technology/sensei) | A Bitcoin lightning node implementation | DB: MySQL, Postgres, SQLite #### Dev Tools - [CodeCTRL](https://codectrl.authentura.com) ([repository](https://github.com/Authentura/codectrl)) | A self-hostable code logging platform | DB: SQLite From 12a42d524ef6f6f49cd40c4fb2dfb397b8616efa Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sat, 5 Aug 2023 00:03:32 +0100 Subject: [PATCH 0143/1371] Edit --- COMMUNITY.md | 1 - 1 file changed, 1 deletion(-) diff --git a/COMMUNITY.md b/COMMUNITY.md index 9409ff4475..59011c658a 100644 --- a/COMMUNITY.md +++ b/COMMUNITY.md @@ -36,7 +36,6 @@ If you have built an app using SeaORM and want to showcase it, feel free to open - [aeroFans](https://github.com/naryand/aerofans) | Full stack forum-like social media platform in Rust and WebAssembly | DB: Postgres - [Crab Fit](https://crab.fit) [![GitHub stars](https://img.shields.io/github/stars/GRA0007/crab.fit.svg?style=social)](https://github.com/GRA0007/crab.fit) | Align your schedules to find the perfect time that works for everyone. | DB: MySQL, Postgres, SQLite - [JinShu](https://github.com/gengteng/jinshu) | A cross-platform **I**nstant **M**essaging system written in 🦀 | DB: MySQL, Postgres -- [Kitsune](https://github.com/kitsune-soc/kitsune) | 🦊 ActivityPub-federated microblogging | DB: Postgres, SQLite - [Portfolio](https://github.com/admisio/Portfolio) | Encrypted high school 🏫 admissions service | DB: Postgres, SQLite - [THUBurrow](https://github.com/BobAnkh/THUBurrow) | A campus forum built by Next.js and Rocket | DB: Postgres From c86744c6861ae38223605537a6d3b7d52c9bd549 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 10 Aug 2023 01:38:32 +0100 Subject: [PATCH 0144/1371] Readme --- README.md | 4 ++-- src/lib.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 716117d3ee..d24f19b5aa 100644 --- a/README.md +++ b/README.md @@ -188,9 +188,9 @@ fruit::Entity::delete_many() ``` -## 🧭 Seaography: first-class GraphQL integration (preview) +## 🧭 Seaography: GraphQL integration (preview) -[Seaography](https://github.com/SeaQL/seaography) is a GraphQL framework built on top of SeaORM. It embodies our vision of 'full-stack Rust': with just a few commands, you can launch a GraphQL server from SeaORM entities! +[Seaography](https://github.com/SeaQL/seaography) is a GraphQL framework built on top of SeaORM. Seaography allows you to build GraphQL resolvers quickly. With just a few commands, you can launch a GraphQL server from SeaORM entities! Starting `0.12`, `seaography` integration is built into `sea-orm`. While Seaography development is still in an early stage, it is especially useful in prototyping and building internal-use admin panels. diff --git a/src/lib.rs b/src/lib.rs index 0a713416bb..d7424dc25b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -261,9 +261,9 @@ //! # } //! ``` //! -//! ## 🧭 Seaography: first-class GraphQL integration (preview) +//! ## 🧭 Seaography: GraphQL integration (preview) //! -//! [Seaography](https://github.com/SeaQL/seaography) is a GraphQL framework built on top of SeaORM. It embodies our vision of 'full-stack Rust': with just a few commands, you can launch a GraphQL server from SeaORM entities! +//! [Seaography](https://github.com/SeaQL/seaography) is a GraphQL framework built on top of SeaORM. Seaography allows you to build GraphQL resolvers quickly. With just a few commands, you can launch a GraphQL server from SeaORM entities! //! //! Starting `0.12`, `seaography` integration is built into `sea-orm`. While Seaography development is still in an early stage, it is especially useful in prototyping and building internal-use admin panels. //! From a711a883de34b4607a56be943b307bd2bf6b2a24 Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Tue, 15 Aug 2023 02:19:53 +0530 Subject: [PATCH 0145/1371] Update COMMUNITY.md (#1796) Add RYot to showcase --- COMMUNITY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/COMMUNITY.md b/COMMUNITY.md index 59011c658a..c8c3d4ae6c 100644 --- a/COMMUNITY.md +++ b/COMMUNITY.md @@ -23,6 +23,7 @@ If you have built an app using SeaORM and want to showcase it, feel free to open - [mediarepo](https://mediarepo.trivernis.dev) ([repository](https://github.com/Trivernis/mediarepo)) ![GitHub stars](https://img.shields.io/github/stars/Trivernis/mediarepo.svg?style=social) | A tag-based media management application | DB: SQLite - [mugen](https://github.com/koopa1338/mugen-dms) | DMS written in 🦀 | DB: Postgres - [OctoBase](https://github.com/toeverything/OctoBase) ![GitHub stars](https://img.shields.io/github/stars/toeverything/OctoBase.svg?style=social) | A light-weight, scalable, offline collaborative data backend written in 🦀 | DB: MySQL, Postgres, SQLite +- [Ryot](https://ignisda.github.io/ryot) ([repository](https://github.com/IgnisDa/ryot)) ![GitHub stars](https://img.shields.io/github/stars/ignisda/ryot.svg?style=social) | The only self hosted tracker you will ever need | DB: MySQL, Postgres, SQLite - [Wikijump](https://github.com/scpwiki/wikijump) ([repository](https://github.com/scpwiki/wikijump/tree/develop/deepwell)) ![GitHub stars](https://img.shields.io/github/stars/scpwiki/wikijump.svg?style=social) | API service for Wikijump, a fork of Wikidot | DB: Postgres #### Game From 2a8efed98d4d50758236faa3c6ecf98a9fe7ff98 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 18 Aug 2023 12:51:02 +0100 Subject: [PATCH 0146/1371] Update salvo to 0.50 --- examples/salvo_example/api/Cargo.toml | 2 +- examples/salvo_example/entity/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/salvo_example/api/Cargo.toml b/examples/salvo_example/api/Cargo.toml index 460425de2d..9f0463b323 100644 --- a/examples/salvo_example/api/Cargo.toml +++ b/examples/salvo_example/api/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] salvo-example-service = { path = "../service" } tokio = { version = "1.29.0", features = ["macros", "rt-multi-thread"] } -salvo = { version = "0.49", features = ["affix", "serve-static"] } +salvo = { version = "0.50", features = ["affix", "serve-static"] } tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } serde = { version = "1", features = ["derive"] } tera = "1.19.0" diff --git a/examples/salvo_example/entity/Cargo.toml b/examples/salvo_example/entity/Cargo.toml index 1c2d248a11..d2ee0d6a63 100644 --- a/examples/salvo_example/entity/Cargo.toml +++ b/examples/salvo_example/entity/Cargo.toml @@ -10,7 +10,7 @@ path = "src/lib.rs" [dependencies] serde = { version = "1", features = ["derive"] } -salvo = { version = "0.49" } +salvo = { version = "0.50" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project From d50312c081f319b96466ccf3d318a4c4fa78e7bf Mon Sep 17 00:00:00 2001 From: Ivan Yiu Date: Fri, 18 Aug 2023 20:02:17 +0800 Subject: [PATCH 0147/1371] Test cases improvement (#1811) * adds find_with_linked test * WIP(related test) * mock related test done * complete relation test * loader update * find_with/also_related missing test case for empty from other side * comments fixup * revert loader test * related select test done * find with/also linked test cases * removed due to it being functionally same as the new one * fmt, remove excess import * improved model generation * issue related test case #1790 * added loader test cases and slight improvement to find_related/linked * miscellaneous changes * added empty insert, merge load_one test case * completed loader many to many test case, fmt * removed empty_insert test case for now * commented insert_test * added Cargo.toml for issue 1790's folder * buffed salvo version for ci(0.49 yanked) * revert version for salvo example --- issues/1790/Cargo.toml | 18 ++ issues/1790/insert_test.rs | 58 ++++ src/database/mock.rs | 27 ++ src/entity/base_entity.rs | 1 - src/executor/select.rs | 564 +++++++++++++++++++++++++++++++++++- src/query/loader.rs | 275 ++++++++++++++---- tests/active_enum_tests.rs | 42 +++ tests/crud/updates.rs | 1 + tests/empty_insert_tests.rs | 7 + tests/loader_tests.rs | 35 ++- tests/relational_tests.rs | 280 ++++++++++++++++++ tests/sql_err_tests.rs | 3 + tests/value_type_tests.rs | 1 + 13 files changed, 1251 insertions(+), 61 deletions(-) create mode 100644 issues/1790/Cargo.toml create mode 100644 issues/1790/insert_test.rs diff --git a/issues/1790/Cargo.toml b/issues/1790/Cargo.toml new file mode 100644 index 0000000000..e3872e2557 --- /dev/null +++ b/issues/1790/Cargo.toml @@ -0,0 +1,18 @@ +[workspace] +# A separate workspace + +[package] +name = "sea-orm-issues-1790" +version = "0.1.0" +edition = "2023" +publish = false + +[dependencies] +anyhow = "1" +serde = "1" +tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] } + +[dependencies.sea-orm] +path = "../../" +default-features = false +features = ["macros", "runtime-tokio-native-tls", "sqlx-sqlite"] diff --git a/issues/1790/insert_test.rs b/issues/1790/insert_test.rs new file mode 100644 index 0000000000..cf30e58c10 --- /dev/null +++ b/issues/1790/insert_test.rs @@ -0,0 +1,58 @@ +mod tests { + // currently ok + #[test] + fn insert_do_nothing_postgres() { + assert_eq!( + Insert::::new() + .add(cake::Model { + id: 1, + name: "Apple Pie".to_owned(), + }) + .on_conflict(OnConflict::new() + .do_nothing() + .to_owned() + ) + .build(DbBackend::Postgres) + .to_string(), + r#"INSERT INTO "cake" ("id", "name") VALUES (1, 'Apple Pie') ON CONFLICT DO NOTHING"#, + ); + } + + //failed to run + #[test] + fn insert_do_nothing_mysql() { + assert_eq!( + Insert::::new() + .add(cake::Model { + id: 1, + name: "Apple Pie".to_owned(), + }) + .on_conflict(OnConflict::new() + .do_nothing() + .to_owned() + ) + .build(DbBackend::Mysql) + .to_string(), + r#"INSERT IGNORE INTO "cake" ("id", "name") VALUES (1, 'Apple Pie')"#, + ); + } + + // currently ok + #[test] + fn insert_do_nothing() { + assert_eq!( + Insert::::new() + .add(cake::Model { + id: 1, + name: "Apple Pie".to_owned(), + }) + .on_conflict(OnConflict::new() + .do_nothing() + .to_owned() + ) + .build(DbBackend::Sqlite) + .to_string(), + r#"INSERT IGNORE INTO "cake" ("id", "name") VALUES (1, 'Apple Pie')"#, + ); + } +} \ No newline at end of file diff --git a/src/database/mock.rs b/src/database/mock.rs index df222d8eea..e760ceaf7e 100644 --- a/src/database/mock.rs +++ b/src/database/mock.rs @@ -285,6 +285,33 @@ where } } +impl IntoMockRow for (M, Option) +where + M: ModelTrait, + N: ModelTrait, +{ + fn into_mock_row(self) -> MockRow { + let mut mapped_join = BTreeMap::new(); + + for column in <::Entity as EntityTrait>::Column::iter() { + mapped_join.insert( + format!("{}{}", SelectA.as_str(), column.as_str()), + self.0.get(column), + ); + } + if let Some(b_entity) = self.1 { + for column in <::Entity as EntityTrait>::Column::iter() { + mapped_join.insert( + format!("{}{}", SelectB.as_str(), column.as_str()), + b_entity.get(column), + ); + } + } + + mapped_join.into_mock_row() + } +} + impl IntoMockRow for BTreeMap where T: Into, diff --git a/src/entity/base_entity.rs b/src/entity/base_entity.rs index 02917749f7..5f7422e338 100644 --- a/src/entity/base_entity.rs +++ b/src/entity/base_entity.rs @@ -960,7 +960,6 @@ mod tests { ); } - delete_by_id("UUID".to_string()); delete_by_id("UUID".to_string()); delete_by_id("UUID"); delete_by_id(Cow::from("UUID")); diff --git a/src/executor/select.rs b/src/executor/select.rs index cd7d122cb4..ae2ac5eb20 100644 --- a/src/executor/select.rs +++ b/src/executor/select.rs @@ -595,7 +595,7 @@ where /// /// > `SelectTwoMany::one()` method has been dropped (#486) /// > - /// > You can get `(Entity, Vec)` by first querying a single model from Entity, + /// > You can get `(Entity, Vec)` by first querying a single model from Entity, /// > then use [`ModelTrait::find_related`] on the model. /// > /// > See https://www.sea-ql.org/SeaORM/docs/basic-crud/select#lazy-loading for details. @@ -1107,3 +1107,565 @@ where } acc } + +#[cfg(test)] +mod tests { + use pretty_assertions::assert_eq; + + fn cake_fruit_model( + cake_id: i32, + fruit_id: i32, + ) -> ( + sea_orm::tests_cfg::cake::Model, + sea_orm::tests_cfg::fruit::Model, + ) { + (cake_model(cake_id), fruit_model(fruit_id, Some(cake_id))) + } + + fn cake_model(id: i32) -> sea_orm::tests_cfg::cake::Model { + let name = match id { + 1 => "apple cake", + 2 => "orange cake", + 3 => "fruit cake", + 4 => "chocolate cake", + _ => "", + } + .to_string(); + sea_orm::tests_cfg::cake::Model { id, name } + } + + fn fruit_model(id: i32, cake_id: Option) -> sea_orm::tests_cfg::fruit::Model { + let name = match id { + 1 => "apple", + 2 => "orange", + 3 => "grape", + 4 => "strawberry", + _ => "", + } + .to_string(); + sea_orm::tests_cfg::fruit::Model { id, name, cake_id } + } + + fn cake_vendor_link( + cake_id: i32, + vendor_id: i32, + ) -> ( + sea_orm::tests_cfg::cake::Model, + sea_orm::tests_cfg::vendor::Model, + ) { + (cake_model(cake_id), vendor_model(vendor_id)) + } + + fn vendor_model(id: i32) -> sea_orm::tests_cfg::vendor::Model { + let name = match id { + 1 => "Apollo", + 2 => "Benny", + 3 => "Christine", + 4 => "David", + _ => "", + } + .to_string(); + sea_orm::tests_cfg::vendor::Model { id, name } + } + + #[smol_potat::test] + pub async fn also_related() -> Result<(), sea_orm::DbErr> { + use sea_orm::tests_cfg::*; + use sea_orm::{DbBackend, EntityTrait, MockDatabase, Statement, Transaction}; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[cake_fruit_model(1, 1)]]) + .into_connection(); + + assert_eq!( + Cake::find().find_also_related(Fruit).all(&db).await?, + [(cake_model(1), Some(fruit_model(1, Some(1))))] + ); + + assert_eq!( + db.into_transaction_log(), + [Transaction::many([Statement::from_sql_and_values( + DbBackend::Postgres, + [ + r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#, + r#""fruit"."id" AS "B_id", "fruit"."name" AS "B_name", "fruit"."cake_id" AS "B_cake_id""#, + r#"FROM "cake""#, + r#"LEFT JOIN "fruit" ON "cake"."id" = "fruit"."cake_id""#, + ] + .join(" ") + .as_str(), + [] + ),])] + ); + + Ok(()) + } + + #[smol_potat::test] + pub async fn also_related_2() -> Result<(), sea_orm::DbErr> { + use sea_orm::tests_cfg::*; + use sea_orm::{DbBackend, EntityTrait, MockDatabase}; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[cake_fruit_model(1, 1), cake_fruit_model(1, 2)]]) + .into_connection(); + + assert_eq!( + Cake::find().find_also_related(Fruit).all(&db).await?, + [ + (cake_model(1), Some(fruit_model(1, Some(1)))), + (cake_model(1), Some(fruit_model(2, Some(1)))) + ] + ); + + Ok(()) + } + + #[smol_potat::test] + pub async fn also_related_3() -> Result<(), sea_orm::DbErr> { + use sea_orm::tests_cfg::*; + use sea_orm::{DbBackend, EntityTrait, MockDatabase}; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[ + cake_fruit_model(1, 1), + cake_fruit_model(1, 2), + cake_fruit_model(2, 2), + ]]) + .into_connection(); + + assert_eq!( + Cake::find().find_also_related(Fruit).all(&db).await?, + [ + (cake_model(1), Some(fruit_model(1, Some(1)))), + (cake_model(1), Some(fruit_model(2, Some(1)))), + (cake_model(2), Some(fruit_model(2, Some(2)))) + ] + ); + + Ok(()) + } + + #[smol_potat::test] + pub async fn also_related_4() -> Result<(), sea_orm::DbErr> { + use sea_orm::tests_cfg::*; + use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase}; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[ + cake_fruit_model(1, 1).into_mock_row(), + cake_fruit_model(1, 2).into_mock_row(), + cake_fruit_model(2, 2).into_mock_row(), + (cake_model(3), None::).into_mock_row(), + ]]) + .into_connection(); + + assert_eq!( + Cake::find().find_also_related(Fruit).all(&db).await?, + [ + (cake_model(1), Some(fruit_model(1, Some(1)))), + (cake_model(1), Some(fruit_model(2, Some(1)))), + (cake_model(2), Some(fruit_model(2, Some(2)))), + (cake_model(3), None) + ] + ); + + Ok(()) + } + + #[smol_potat::test] + pub async fn with_related() -> Result<(), sea_orm::DbErr> { + use sea_orm::tests_cfg::*; + use sea_orm::{DbBackend, EntityTrait, MockDatabase, Statement, Transaction}; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[ + cake_fruit_model(1, 1), + cake_fruit_model(2, 2), + cake_fruit_model(2, 3), + ]]) + .into_connection(); + + assert_eq!( + Cake::find().find_with_related(Fruit).all(&db).await?, + [ + (cake_model(1), vec![fruit_model(1, Some(1))]), + ( + cake_model(2), + vec![fruit_model(2, Some(2)), fruit_model(3, Some(2))] + ) + ] + ); + + assert_eq!( + db.into_transaction_log(), + [Transaction::many([Statement::from_sql_and_values( + DbBackend::Postgres, + [ + r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#, + r#""fruit"."id" AS "B_id", "fruit"."name" AS "B_name", "fruit"."cake_id" AS "B_cake_id""#, + r#"FROM "cake""#, + r#"LEFT JOIN "fruit" ON "cake"."id" = "fruit"."cake_id""#, + r#"ORDER BY "cake"."id" ASC"# + ] + .join(" ") + .as_str(), + [] + ),])] + ); + + Ok(()) + } + + #[smol_potat::test] + pub async fn with_related_2() -> Result<(), sea_orm::DbErr> { + use sea_orm::tests_cfg::*; + use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase}; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[ + cake_fruit_model(1, 1).into_mock_row(), + cake_fruit_model(2, 1).into_mock_row(), + cake_fruit_model(2, 2).into_mock_row(), + cake_fruit_model(2, 3).into_mock_row(), + ]]) + .into_connection(); + + assert_eq!( + Cake::find().find_with_related(Fruit).all(&db).await?, + [ + (cake_model(1), vec![fruit_model(1, Some(1)),]), + ( + cake_model(2), + vec![ + fruit_model(1, Some(2)), + fruit_model(2, Some(2)), + fruit_model(3, Some(2)), + ] + ), + ] + ); + + Ok(()) + } + + #[smol_potat::test] + pub async fn with_related_empty() -> Result<(), sea_orm::DbErr> { + use sea_orm::tests_cfg::*; + use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase}; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[ + cake_fruit_model(1, 1).into_mock_row(), + cake_fruit_model(2, 1).into_mock_row(), + cake_fruit_model(2, 2).into_mock_row(), + cake_fruit_model(2, 3).into_mock_row(), + (cake_model(3), None::).into_mock_row(), + ]]) + .into_connection(); + + assert_eq!( + Cake::find().find_with_related(Fruit).all(&db).await?, + [ + (cake_model(1), vec![fruit_model(1, Some(1)),]), + ( + cake_model(2), + vec![ + fruit_model(1, Some(2)), + fruit_model(2, Some(2)), + fruit_model(3, Some(2)), + ] + ), + (cake_model(3), vec![]) + ] + ); + + Ok(()) + } + + #[smol_potat::test] + pub async fn also_linked_base() -> Result<(), sea_orm::DbErr> { + use sea_orm::tests_cfg::*; + use sea_orm::{DbBackend, EntityTrait, MockDatabase, Statement, Transaction}; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[cake_vendor_link(1, 1)]]) + .into_connection(); + + assert_eq!( + Cake::find() + .find_also_linked(entity_linked::CakeToFillingVendor) + .all(&db) + .await?, + [(cake_model(1), Some(vendor_model(1)))] + ); + + assert_eq!( + db.into_transaction_log(), + [Transaction::many([Statement::from_sql_and_values( + DbBackend::Postgres, + [ + r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#, + r#""r2"."id" AS "B_id", "r2"."name" AS "B_name""#, + r#"FROM "cake""#, + r#"LEFT JOIN "cake_filling" AS "r0" ON "cake"."id" = "r0"."cake_id""#, + r#"LEFT JOIN "filling" AS "r1" ON "r0"."filling_id" = "r1"."id""#, + r#"LEFT JOIN "vendor" AS "r2" ON "r1"."vendor_id" = "r2"."id""#, + ] + .join(" ") + .as_str(), + [] + ),])] + ); + + Ok(()) + } + + #[smol_potat::test] + pub async fn also_linked_same_cake() -> Result<(), sea_orm::DbErr> { + use sea_orm::tests_cfg::*; + use sea_orm::{DbBackend, EntityTrait, MockDatabase}; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[ + cake_vendor_link(1, 1), + cake_vendor_link(1, 2), + cake_vendor_link(2, 3), + ]]) + .into_connection(); + + assert_eq!( + Cake::find() + .find_also_linked(entity_linked::CakeToFillingVendor) + .all(&db) + .await?, + [ + (cake_model(1), Some(vendor_model(1))), + (cake_model(1), Some(vendor_model(2))), + (cake_model(2), Some(vendor_model(3))) + ] + ); + + Ok(()) + } + + #[smol_potat::test] + pub async fn also_linked_same_vendor() -> Result<(), sea_orm::DbErr> { + use sea_orm::tests_cfg::*; + use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase}; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[ + cake_vendor_link(1, 1).into_mock_row(), + cake_vendor_link(2, 1).into_mock_row(), + cake_vendor_link(3, 2).into_mock_row(), + ]]) + .into_connection(); + + assert_eq!( + Cake::find() + .find_also_linked(entity_linked::CakeToFillingVendor) + .all(&db) + .await?, + [ + (cake_model(1), Some(vendor_model(1))), + (cake_model(2), Some(vendor_model(1))), + (cake_model(3), Some(vendor_model(2))), + ] + ); + + Ok(()) + } + + #[smol_potat::test] + pub async fn also_linked_many_to_many() -> Result<(), sea_orm::DbErr> { + use sea_orm::tests_cfg::*; + use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase}; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[ + cake_vendor_link(1, 1).into_mock_row(), + cake_vendor_link(1, 2).into_mock_row(), + cake_vendor_link(1, 3).into_mock_row(), + cake_vendor_link(2, 1).into_mock_row(), + cake_vendor_link(2, 2).into_mock_row(), + ]]) + .into_connection(); + + assert_eq!( + Cake::find() + .find_also_linked(entity_linked::CakeToFillingVendor) + .all(&db) + .await?, + [ + (cake_model(1), Some(vendor_model(1))), + (cake_model(1), Some(vendor_model(2))), + (cake_model(1), Some(vendor_model(3))), + (cake_model(2), Some(vendor_model(1))), + (cake_model(2), Some(vendor_model(2))), + ] + ); + + Ok(()) + } + + #[smol_potat::test] + pub async fn also_linked_empty() -> Result<(), sea_orm::DbErr> { + use sea_orm::tests_cfg::*; + use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase}; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[ + cake_vendor_link(1, 1).into_mock_row(), + cake_vendor_link(2, 2).into_mock_row(), + cake_vendor_link(3, 3).into_mock_row(), + (cake_model(4), None::).into_mock_row(), + ]]) + .into_connection(); + + assert_eq!( + Cake::find() + .find_also_linked(entity_linked::CakeToFillingVendor) + .all(&db) + .await?, + [ + (cake_model(1), Some(vendor_model(1))), + (cake_model(2), Some(vendor_model(2))), + (cake_model(3), Some(vendor_model(3))), + (cake_model(4), None) + ] + ); + + Ok(()) + } + + #[smol_potat::test] + pub async fn with_linked_base() -> Result<(), sea_orm::DbErr> { + use sea_orm::tests_cfg::*; + use sea_orm::{DbBackend, EntityTrait, MockDatabase, Statement, Transaction}; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[ + cake_vendor_link(1, 1), + cake_vendor_link(2, 2), + cake_vendor_link(2, 3), + ]]) + .into_connection(); + + assert_eq!( + Cake::find() + .find_with_linked(entity_linked::CakeToFillingVendor) + .all(&db) + .await?, + [ + (cake_model(1), vec![vendor_model(1)]), + (cake_model(2), vec![vendor_model(2), vendor_model(3)]) + ] + ); + + assert_eq!( + db.into_transaction_log(), + [Transaction::many([Statement::from_sql_and_values( + DbBackend::Postgres, + [ + r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#, + r#""r2"."id" AS "B_id", "r2"."name" AS "B_name" FROM "cake""#, + r#"LEFT JOIN "cake_filling" AS "r0" ON "cake"."id" = "r0"."cake_id""#, + r#"LEFT JOIN "filling" AS "r1" ON "r0"."filling_id" = "r1"."id""#, + r#"LEFT JOIN "vendor" AS "r2" ON "r1"."vendor_id" = "r2"."id""#, + ] + .join(" ") + .as_str(), + [] + ),])] + ); + + Ok(()) + } + + #[smol_potat::test] + pub async fn with_linked_same_vendor() -> Result<(), sea_orm::DbErr> { + use sea_orm::tests_cfg::*; + use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase}; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[ + cake_vendor_link(1, 1).into_mock_row(), + cake_vendor_link(2, 2).into_mock_row(), + cake_vendor_link(3, 2).into_mock_row(), + ]]) + .into_connection(); + + assert_eq!( + Cake::find() + .find_with_linked(entity_linked::CakeToFillingVendor) + .all(&db) + .await?, + [ + (cake_model(1), vec![vendor_model(1)]), + (cake_model(2), vec![vendor_model(2)]), + (cake_model(3), vec![vendor_model(2)]) + ] + ); + + Ok(()) + } + + #[smol_potat::test] + pub async fn with_linked_empty() -> Result<(), sea_orm::DbErr> { + use sea_orm::tests_cfg::*; + use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase}; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[ + cake_vendor_link(1, 1).into_mock_row(), + cake_vendor_link(2, 1).into_mock_row(), + cake_vendor_link(2, 2).into_mock_row(), + (cake_model(3), None::).into_mock_row(), + ]]) + .into_connection(); + + assert_eq!( + Cake::find() + .find_with_linked(entity_linked::CakeToFillingVendor) + .all(&db) + .await?, + [ + (cake_model(1), vec![vendor_model(1)]), + (cake_model(2), vec![vendor_model(1), vendor_model(2)]), + (cake_model(3), vec![]) + ] + ); + + Ok(()) + } + + // normally would not happen + #[smol_potat::test] + pub async fn with_linked_repeated() -> Result<(), sea_orm::DbErr> { + use sea_orm::tests_cfg::*; + use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase}; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[ + cake_vendor_link(1, 1).into_mock_row(), + cake_vendor_link(1, 1).into_mock_row(), + cake_vendor_link(2, 1).into_mock_row(), + cake_vendor_link(2, 2).into_mock_row(), + ]]) + .into_connection(); + + assert_eq!( + Cake::find() + .find_with_linked(entity_linked::CakeToFillingVendor) + .all(&db) + .await?, + [ + (cake_model(1), vec![vendor_model(1), vendor_model(1)]), + (cake_model(2), vec![vendor_model(1), vendor_model(2)]), + ] + ); + + Ok(()) + } +} diff --git a/src/query/loader.rs b/src/query/loader.rs index 28663898eb..2f3693a30b 100644 --- a/src/query/loader.rs +++ b/src/query/loader.rs @@ -440,72 +440,138 @@ fn table_column(tbl: &TableRef, col: &DynIden) -> ColumnRef { #[cfg(test)] mod tests { + fn cake_model(id: i32) -> sea_orm::tests_cfg::cake::Model { + let name = match id { + 1 => "apple cake", + 2 => "orange cake", + 3 => "fruit cake", + 4 => "chocolate cake", + _ => "", + } + .to_string(); + sea_orm::tests_cfg::cake::Model { id, name } + } + + fn fruit_model(id: i32, cake_id: Option) -> sea_orm::tests_cfg::fruit::Model { + let name = match id { + 1 => "apple", + 2 => "orange", + 3 => "grape", + 4 => "strawberry", + _ => "", + } + .to_string(); + sea_orm::tests_cfg::fruit::Model { id, name, cake_id } + } + + fn filling_model(id: i32) -> sea_orm::tests_cfg::filling::Model { + let name = match id { + 1 => "apple juice", + 2 => "orange jam", + 3 => "chocolate crust", + 4 => "strawberry jam", + _ => "", + } + .to_string(); + sea_orm::tests_cfg::filling::Model { + id, + name, + vendor_id: Some(1), + ignored_attr: 0, + } + } + + fn cake_filling_model( + cake_id: i32, + filling_id: i32, + ) -> sea_orm::tests_cfg::cake_filling::Model { + sea_orm::tests_cfg::cake_filling::Model { + cake_id, + filling_id, + } + } + #[tokio::test] async fn test_load_one() { - use crate::{ - entity::prelude::*, tests_cfg::*, DbBackend, IntoMockRow, LoaderTrait, MockDatabase, - }; + use sea_orm::{entity::prelude::*, tests_cfg::*, DbBackend, LoaderTrait, MockDatabase}; let db = MockDatabase::new(DbBackend::Postgres) - .append_query_results([[ - cake::Model { - id: 1, - name: "New York Cheese".to_owned(), - } - .into_mock_row(), - cake::Model { - id: 2, - name: "London Cheese".to_owned(), - } - .into_mock_row(), - ]]) + .append_query_results([[cake_model(1), cake_model(2)]]) .into_connection(); - let fruits = vec![fruit::Model { - id: 1, - name: "Apple".to_owned(), - cake_id: Some(1), - }]; + let fruits = vec![fruit_model(1, Some(1))]; let cakes = fruits .load_one(cake::Entity::find(), &db) .await .expect("Should return something"); - assert_eq!( - cakes, - [Some(cake::Model { - id: 1, - name: "New York Cheese".to_owned(), - })] - ); + assert_eq!(cakes, [Some(cake_model(1))]); + } + + #[tokio::test] + async fn test_load_one_same_cake() { + use sea_orm::{entity::prelude::*, tests_cfg::*, DbBackend, LoaderTrait, MockDatabase}; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[cake_model(1), cake_model(2)]]) + .into_connection(); + + let fruits = vec![fruit_model(1, Some(1)), fruit_model(2, Some(1))]; + + let cakes = fruits + .load_one(cake::Entity::find(), &db) + .await + .expect("Should return something"); + + assert_eq!(cakes, [Some(cake_model(1)), Some(cake_model(1))]); + } + + #[tokio::test] + async fn test_load_one_empty() { + use sea_orm::{entity::prelude::*, tests_cfg::*, DbBackend, LoaderTrait, MockDatabase}; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[cake_model(1), cake_model(2)]]) + .into_connection(); + + let fruits: Vec = vec![]; + + let cakes = fruits + .load_one(cake::Entity::find(), &db) + .await + .expect("Should return something"); + + assert_eq!(cakes, []); } #[tokio::test] async fn test_load_many() { - use crate::{ - entity::prelude::*, tests_cfg::*, DbBackend, IntoMockRow, LoaderTrait, MockDatabase, - }; + use sea_orm::{entity::prelude::*, tests_cfg::*, DbBackend, LoaderTrait, MockDatabase}; let db = MockDatabase::new(DbBackend::Postgres) - .append_query_results([[fruit::Model { - id: 1, - name: "Apple".to_owned(), - cake_id: Some(1), - } - .into_mock_row()]]) + .append_query_results([[fruit_model(1, Some(1))]]) .into_connection(); - let cakes = vec![ - cake::Model { - id: 1, - name: "New York Cheese".to_owned(), - }, - cake::Model { - id: 2, - name: "London Cheese".to_owned(), - }, - ]; + let cakes = vec![cake_model(1), cake_model(2)]; + + let fruits = cakes + .load_many(fruit::Entity::find(), &db) + .await + .expect("Should return something"); + + assert_eq!(fruits, [vec![fruit_model(1, Some(1))], vec![]]); + } + + #[tokio::test] + async fn test_load_many_same_fruit() { + use sea_orm::{entity::prelude::*, tests_cfg::*, DbBackend, LoaderTrait, MockDatabase}; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[fruit_model(1, Some(1)), fruit_model(2, Some(1))]]) + .into_connection(); + + let cakes = vec![cake_model(1), cake_model(2)]; let fruits = cakes .load_many(fruit::Entity::find(), &db) @@ -515,13 +581,120 @@ mod tests { assert_eq!( fruits, [ - vec![fruit::Model { - id: 1, - name: "Apple".to_owned(), - cake_id: Some(1), - }], + vec![fruit_model(1, Some(1)), fruit_model(2, Some(1))], vec![] ] ); } + + // FIXME: load many with empty vector will panic + // #[tokio::test] + async fn test_load_many_empty() { + use sea_orm::{entity::prelude::*, tests_cfg::*, DbBackend, MockDatabase}; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[fruit_model(1, Some(1)), fruit_model(2, Some(1))]]) + .into_connection(); + + let cakes: Vec = vec![]; + + let fruits = cakes + .load_many(fruit::Entity::find(), &db) + .await + .expect("Should return something"); + + let empty_vec: Vec> = vec![]; + + assert_eq!(fruits, empty_vec); + } + + #[tokio::test] + async fn test_load_many_to_many_base() { + use sea_orm::{ + entity::prelude::*, tests_cfg::*, DbBackend, IntoMockRow, LoaderTrait, MockDatabase, + }; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([ + [cake_filling_model(1, 1).into_mock_row()], + [filling_model(1).into_mock_row()], + ]) + .into_connection(); + + let cakes = vec![cake_model(1)]; + + let fillings = cakes + .load_many_to_many(Filling, CakeFilling, &db) + .await + .expect("Should return something"); + + assert_eq!(fillings, vec![vec![filling_model(1)]]); + } + + #[tokio::test] + async fn test_load_many_to_many_complex() { + use sea_orm::{ + entity::prelude::*, tests_cfg::*, DbBackend, IntoMockRow, LoaderTrait, MockDatabase, + }; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([ + [ + cake_filling_model(1, 1).into_mock_row(), + cake_filling_model(1, 2).into_mock_row(), + cake_filling_model(1, 3).into_mock_row(), + cake_filling_model(2, 1).into_mock_row(), + cake_filling_model(2, 2).into_mock_row(), + ], + [ + filling_model(1).into_mock_row(), + filling_model(2).into_mock_row(), + filling_model(3).into_mock_row(), + filling_model(4).into_mock_row(), + filling_model(5).into_mock_row(), + ], + ]) + .into_connection(); + + let cakes = vec![cake_model(1), cake_model(2), cake_model(3)]; + + let fillings = cakes + .load_many_to_many(Filling, CakeFilling, &db) + .await + .expect("Should return something"); + + assert_eq!( + fillings, + vec![ + vec![filling_model(1), filling_model(2), filling_model(3)], + vec![filling_model(1), filling_model(2)], + vec![], + ] + ); + } + + #[tokio::test] + async fn test_load_many_to_many_empty() { + use sea_orm::{ + entity::prelude::*, tests_cfg::*, DbBackend, IntoMockRow, LoaderTrait, MockDatabase, + }; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([ + [cake_filling_model(1, 1).into_mock_row()], + [filling_model(1).into_mock_row()], + ]) + .into_connection(); + + let cakes: Vec = vec![]; + + let fillings = cakes + .load_many_to_many(Filling, CakeFilling, &db) + .await + .expect("Should return something"); + + let empty_vec: Vec> = vec![]; + + assert_eq!(fillings, empty_vec); + } } diff --git a/tests/active_enum_tests.rs b/tests/active_enum_tests.rs index 14ebb436b8..e1aff3f983 100644 --- a/tests/active_enum_tests.rs +++ b/tests/active_enum_tests.rs @@ -371,6 +371,27 @@ pub async fn find_linked_active_enum(db: &DatabaseConnection) -> Result<(), DbEr }) )] ); + assert_eq!( + ActiveEnum::find() + .find_with_linked(active_enum::ActiveEnumChildLink) + .all(db) + .await?, + [( + active_enum::Model { + id: 2, + category: Some(Category::Small), + color: Some(Color::White), + tea: Some(Tea::BreakfastTea), + }, + vec![active_enum_child::Model { + id: 1, + parent_id: 2, + category: Some(Category::Big), + color: Some(Color::Black), + tea: Some(Tea::EverydayTea), + }] + )] + ); assert_eq!( active_enum_child::Model { @@ -411,6 +432,27 @@ pub async fn find_linked_active_enum(db: &DatabaseConnection) -> Result<(), DbEr }) )] ); + assert_eq!( + ActiveEnumChild::find() + .find_with_linked(active_enum_child::ActiveEnumLink) + .all(db) + .await?, + [( + active_enum_child::Model { + id: 1, + parent_id: 2, + category: Some(Category::Big), + color: Some(Color::Black), + tea: Some(Tea::EverydayTea), + }, + vec![active_enum::Model { + id: 2, + category: Some(Category::Small), + color: Some(Color::White), + tea: Some(Tea::BreakfastTea), + }] + )] + ); Ok(()) } diff --git a/tests/crud/updates.rs b/tests/crud/updates.rs index 5c1f39d90b..6dbd29e93c 100644 --- a/tests/crud/updates.rs +++ b/tests/crud/updates.rs @@ -52,6 +52,7 @@ pub async fn test_update_cake(db: &DbConn) { let cake_model = cake.unwrap(); assert_eq!(cake_model.name, "Extra chocolate mud cake"); assert_eq!(cake_model.price, dec!(20.00)); + assert!(!cake_model.gluten_free); } pub async fn test_update_bakery(db: &DbConn) { diff --git a/tests/empty_insert_tests.rs b/tests/empty_insert_tests.rs index e3a8bfa762..ea61a37fba 100644 --- a/tests/empty_insert_tests.rs +++ b/tests/empty_insert_tests.rs @@ -9,6 +9,7 @@ pub use sea_orm::{ pub use crud::*; // use common::bakery_chain::*; use sea_orm::{DbConn, TryInsertResult}; +use sea_query::OnConflict; #[sea_orm_macros::test] #[cfg(any( @@ -37,6 +38,12 @@ pub async fn test(db: &DbConn) { assert!(matches!(res, Ok(TryInsertResult::Inserted(_)))); + let double_seaside_bakery = bakery::ActiveModel { + name: Set("SeaSide Bakery".to_owned()), + profit_margin: Set(10.4), + id: Set(1), + }; + let empty_insert = Bakery::insert_many(std::iter::empty::()) .on_empty_do_nothing() .exec(db) diff --git a/tests/loader_tests.rs b/tests/loader_tests.rs index c0cb408b12..67341fdc63 100644 --- a/tests/loader_tests.rs +++ b/tests/loader_tests.rs @@ -1,7 +1,7 @@ pub mod common; pub use common::{bakery_chain::*, setup::*, TestContext}; -use sea_orm::{entity::*, query::*, DbConn, DbErr}; +use sea_orm::{entity::*, query::*, DbConn, DbErr, RuntimeErr}; #[sea_orm_macros::test] #[cfg(any( @@ -32,6 +32,17 @@ async fn loader_load_one() -> Result<(), DbErr> { assert_eq!(bakers, [baker_1, baker_2, baker_3]); assert_eq!(bakeries, [Some(bakery_0.clone()), Some(bakery_0), None]); + // has many find, should use load_many instead + let bakeries = bakery::Entity::find().all(&ctx.db).await?; + let bakers = bakeries.load_one(baker::Entity, &ctx.db).await; + + assert_eq!( + bakers, + Err(DbErr::Query(RuntimeErr::Internal( + "Relation is HasMany instead of HasOne".to_string() + ))) + ); + Ok(()) } @@ -47,6 +58,7 @@ async fn loader_load_many() -> Result<(), DbErr> { let bakery_1 = insert_bakery(&ctx.db, "SeaSide Bakery").await?; let bakery_2 = insert_bakery(&ctx.db, "Offshore Bakery").await?; + let bakery_3 = insert_bakery(&ctx.db, "Rocky Bakery").await?; let baker_1 = insert_baker(&ctx.db, "Baker 1", bakery_1.id).await?; let baker_2 = insert_baker(&ctx.db, "Baker 2", bakery_1.id).await?; @@ -57,12 +69,16 @@ async fn loader_load_many() -> Result<(), DbErr> { let bakeries = bakery::Entity::find().all(&ctx.db).await?; let bakers = bakeries.load_many(baker::Entity, &ctx.db).await?; - assert_eq!(bakeries, [bakery_1.clone(), bakery_2.clone()]); + assert_eq!( + bakeries, + [bakery_1.clone(), bakery_2.clone(), bakery_3.clone()] + ); assert_eq!( bakers, [ - [baker_1.clone(), baker_2.clone()], - [baker_3.clone(), baker_4.clone()] + vec![baker_1.clone(), baker_2.clone()], + vec![baker_3.clone(), baker_4.clone()], + vec![] ] ); @@ -79,7 +95,8 @@ async fn loader_load_many() -> Result<(), DbErr> { bakers, [ vec![baker_1.clone(), baker_2.clone()], - vec![baker_4.clone()] + vec![baker_4.clone()], + vec![] ] ); @@ -150,6 +167,7 @@ async fn loader_load_many_to_many() -> Result<(), DbErr> { let baker_1 = insert_baker(&ctx.db, "Jane", bakery_1.id).await?; let baker_2 = insert_baker(&ctx.db, "Peter", bakery_1.id).await?; + let baker_3 = insert_baker(&ctx.db, "Fred", bakery_1.id).await?; // does not make cake let cake_1 = insert_cake(&ctx.db, "Cheesecake", None).await?; let cake_2 = insert_cake(&ctx.db, "Coffee", None).await?; @@ -166,12 +184,13 @@ async fn loader_load_many_to_many() -> Result<(), DbErr> { .load_many_to_many(cake::Entity, cakes_bakers::Entity, &ctx.db) .await?; - assert_eq!(bakers, [baker_1.clone(), baker_2.clone()]); + assert_eq!(bakers, [baker_1.clone(), baker_2.clone(), baker_3.clone()]); assert_eq!( cakes, [ vec![cake_1.clone(), cake_2.clone()], - vec![cake_2.clone(), cake_3.clone()] + vec![cake_2.clone(), cake_3.clone()], + vec![] ] ); @@ -184,7 +203,7 @@ async fn loader_load_many_to_many() -> Result<(), DbErr> { &ctx.db, ) .await?; - assert_eq!(cakes, [vec![cake_1.clone()], vec![cake_3.clone()]]); + assert_eq!(cakes, [vec![cake_1.clone()], vec![cake_3.clone()], vec![]]); // now, start again from cakes diff --git a/tests/relational_tests.rs b/tests/relational_tests.rs index a2994ee84a..c8c41b17a4 100644 --- a/tests/relational_tests.rs +++ b/tests/relational_tests.rs @@ -494,6 +494,255 @@ pub async fn having() { ctx.delete().await; } +#[sea_orm_macros::test] +#[cfg(any( + feature = "sqlx-mysql", + feature = "sqlx-sqlite", + feature = "sqlx-postgres" +))] +pub async fn related() -> Result<(), DbErr> { + use sea_orm::{SelectA, SelectB}; + + let ctx = TestContext::new("test_related").await; + create_tables(&ctx.db).await?; + + // SeaSide Bakery + let seaside_bakery = bakery::ActiveModel { + name: Set("SeaSide Bakery".to_owned()), + profit_margin: Set(10.4), + ..Default::default() + }; + let seaside_bakery_res = Bakery::insert(seaside_bakery).exec(&ctx.db).await?; + + // Bob's Baker + let baker_bob = baker::ActiveModel { + name: Set("Baker Bob".to_owned()), + contact_details: Set(serde_json::json!({ + "mobile": "+61424000000", + "home": "0395555555", + "address": "12 Test St, Testville, Vic, Australia" + })), + bakery_id: Set(Some(seaside_bakery_res.last_insert_id)), + ..Default::default() + }; + let _baker_bob_res = Baker::insert(baker_bob).exec(&ctx.db).await?; + + // Bobby's Baker + let baker_bobby = baker::ActiveModel { + name: Set("Baker Bobby".to_owned()), + contact_details: Set(serde_json::json!({ + "mobile": "+85212345678", + })), + bakery_id: Set(Some(seaside_bakery_res.last_insert_id)), + ..Default::default() + }; + let _baker_bobby_res = Baker::insert(baker_bobby).exec(&ctx.db).await?; + + // Terres Bakery + let terres_bakery = bakery::ActiveModel { + name: Set("Terres Bakery".to_owned()), + profit_margin: Set(13.5), + ..Default::default() + }; + let terres_bakery_res = Bakery::insert(terres_bakery).exec(&ctx.db).await?; + + // Ada's Baker + let baker_ada = baker::ActiveModel { + name: Set("Baker Ada".to_owned()), + contact_details: Set(serde_json::json!({ + "mobile": "+61424000000", + "home": "0395555555", + "address": "12 Test St, Testville, Vic, Australia" + })), + bakery_id: Set(Some(terres_bakery_res.last_insert_id)), + ..Default::default() + }; + let _baker_ada_res = Baker::insert(baker_ada).exec(&ctx.db).await?; + + // Stone Bakery, with no baker + let stone_bakery = bakery::ActiveModel { + name: Set("Stone Bakery".to_owned()), + profit_margin: Set(13.5), + ..Default::default() + }; + let _stone_bakery_res = Bakery::insert(stone_bakery).exec(&ctx.db).await?; + + #[derive(Debug, FromQueryResult, PartialEq)] + struct BakerLite { + name: String, + } + + #[derive(Debug, FromQueryResult, PartialEq)] + struct BakeryLite { + name: String, + } + + // get all bakery and baker's name and put them into tuples + let bakers_in_bakery: Vec<(BakeryLite, Option)> = Bakery::find() + .find_also_related(Baker) + .select_only() + .column_as(bakery::Column::Name, (SelectA, bakery::Column::Name)) + .column_as(baker::Column::Name, (SelectB, baker::Column::Name)) + .order_by_asc(bakery::Column::Id) + .order_by_asc(baker::Column::Id) + .into_model() + .all(&ctx.db) + .await?; + + assert_eq!( + bakers_in_bakery, + [ + ( + BakeryLite { + name: "SeaSide Bakery".to_owned(), + }, + Some(BakerLite { + name: "Baker Bob".to_owned(), + }) + ), + ( + BakeryLite { + name: "SeaSide Bakery".to_owned(), + }, + Some(BakerLite { + name: "Baker Bobby".to_owned(), + }) + ), + ( + BakeryLite { + name: "Terres Bakery".to_owned(), + }, + Some(BakerLite { + name: "Baker Ada".to_owned(), + }) + ), + ( + BakeryLite { + name: "Stone Bakery".to_owned(), + }, + None, + ), + ] + ); + + let seaside_bakery = Bakery::find() + .filter(bakery::Column::Id.eq(1)) + .one(&ctx.db) + .await? + .unwrap(); + + let bakers = seaside_bakery.find_related(Baker).all(&ctx.db).await?; + + assert_eq!( + bakers, + [ + baker::Model { + id: 1, + name: "Baker Bob".to_owned(), + contact_details: serde_json::json!({ + "mobile": "+61424000000", + "home": "0395555555", + "address": "12 Test St, Testville, Vic, Australia" + }), + bakery_id: Some(1), + }, + baker::Model { + id: 2, + name: "Baker Bobby".to_owned(), + contact_details: serde_json::json!({ + "mobile": "+85212345678", + }), + bakery_id: Some(1), + } + ] + ); + + let select_bakery_with_baker = Bakery::find() + .find_with_related(Baker) + .order_by_asc(baker::Column::Id); + + assert_eq!( + select_bakery_with_baker + .build(sea_orm::DatabaseBackend::MySql) + .to_string(), + [ + "SELECT `bakery`.`id` AS `A_id`,", + "`bakery`.`name` AS `A_name`,", + "`bakery`.`profit_margin` AS `A_profit_margin`,", + "`baker`.`id` AS `B_id`,", + "`baker`.`name` AS `B_name`,", + "`baker`.`contact_details` AS `B_contact_details`,", + "`baker`.`bakery_id` AS `B_bakery_id`", + "FROM `bakery`", + "LEFT JOIN `baker` ON `bakery`.`id` = `baker`.`bakery_id`", + "ORDER BY `bakery`.`id` ASC, `baker`.`id` ASC" + ] + .join(" ") + ); + + assert_eq!( + select_bakery_with_baker.all(&ctx.db).await?, + [ + ( + bakery::Model { + id: 1, + name: "SeaSide Bakery".to_owned(), + profit_margin: 10.4, + }, + vec![ + baker::Model { + id: 1, + name: "Baker Bob".to_owned(), + contact_details: serde_json::json!({ + "mobile": "+61424000000", + "home": "0395555555", + "address": "12 Test St, Testville, Vic, Australia" + }), + bakery_id: Some(seaside_bakery_res.last_insert_id), + }, + baker::Model { + id: 2, + name: "Baker Bobby".to_owned(), + contact_details: serde_json::json!({ + "mobile": "+85212345678", + }), + bakery_id: Some(seaside_bakery_res.last_insert_id), + } + ] + ), + ( + bakery::Model { + id: 2, + name: "Terres Bakery".to_owned(), + profit_margin: 13.5, + }, + vec![baker::Model { + id: 3, + name: "Baker Ada".to_owned(), + contact_details: serde_json::json!({ + "mobile": "+61424000000", + "home": "0395555555", + "address": "12 Test St, Testville, Vic, Australia" + }), + bakery_id: Some(terres_bakery_res.last_insert_id), + }] + ), + ( + bakery::Model { + id: 3, + name: "Stone Bakery".to_owned(), + profit_margin: 13.5, + }, + vec![] + ), + ] + ); + + ctx.delete().await; + + Ok(()) +} + #[sea_orm_macros::test] #[cfg(any( feature = "sqlx-mysql", @@ -586,6 +835,17 @@ pub async fn linked() -> Result<(), DbErr> { .exec(&ctx.db) .await?; + // Freerider's Baker, no cake baked + let baker_freerider = baker::ActiveModel { + name: Set("Freerider".to_owned()), + contact_details: Set(serde_json::json!({ + "mobile": "+85298765432", + })), + bakery_id: Set(Some(seaside_bakery_res.last_insert_id)), + ..Default::default() + }; + let _baker_freerider_res = Baker::insert(baker_freerider).exec(&ctx.db).await?; + // Kate's Customer, Order & Line Item let customer_kate = customer::ActiveModel { name: Set("Kate".to_owned()), @@ -680,6 +940,7 @@ pub async fn linked() -> Result<(), DbErr> { name: String, } + // filtered find let baked_for_customers: Vec<(BakerLite, Option)> = Baker::find() .find_also_linked(baker::BakedForCustomer) .select_only() @@ -725,9 +986,16 @@ pub async fn linked() -> Result<(), DbErr> { name: "Kara".to_owned(), }) ), + ( + BakerLite { + name: "Freerider".to_owned(), + }, + None, + ), ] ); + // try to use find_linked instead let baker_bob = Baker::find() .filter(baker::Column::Id.eq(1)) .one(&ctx.db) @@ -748,6 +1016,7 @@ pub async fn linked() -> Result<(), DbErr> { }] ); + // find full model using with_linked let select_baker_with_customer = Baker::find() .find_with_linked(baker::BakedForCustomer) .order_by_asc(baker::Column::Id) @@ -823,6 +1092,17 @@ pub async fn linked() -> Result<(), DbErr> { }, ] ), + ( + baker::Model { + id: 3, + name: "Freerider".into(), + contact_details: serde_json::json!({ + "mobile": "+85298765432", + }), + bakery_id: Some(1), + }, + vec![] + ), ] ); diff --git a/tests/sql_err_tests.rs b/tests/sql_err_tests.rs index e73b3afe5d..e086d730c2 100644 --- a/tests/sql_err_tests.rs +++ b/tests/sql_err_tests.rs @@ -64,4 +64,7 @@ pub async fn test_error(db: &DatabaseConnection) { fk_error.sql_err(), Some(SqlErr::ForeignKeyConstraintViolation(_)) )); + + let invalid_error = DbErr::Custom("random error".to_string()); + assert_eq!(invalid_error.sql_err(), None) } diff --git a/tests/value_type_tests.rs b/tests/value_type_tests.rs index 2271866063..1898f22a31 100644 --- a/tests/value_type_tests.rs +++ b/tests/value_type_tests.rs @@ -71,6 +71,7 @@ pub fn type_test() { assert_eq!(Integer::array_type(), ArrayType::Int); assert_eq!(Boolbean::column_type(), ColumnType::Boolean); assert_eq!(Boolbean::array_type(), ArrayType::Bool); + // self implied assert_eq!( StringVec::column_type(), From 08c3441a9cbce3744d3a07fc19dc31741d2e1e90 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 18 Aug 2023 13:10:31 +0100 Subject: [PATCH 0148/1371] Add more find_also_related test cases From darkmmon/sea-orm/commit/ce5f6991391fdbb0b8deb72ddd404944bb05cf98 --- src/executor/select.rs | 139 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 134 insertions(+), 5 deletions(-) diff --git a/src/executor/select.rs b/src/executor/select.rs index ae2ac5eb20..1ffaf79650 100644 --- a/src/executor/select.rs +++ b/src/executor/select.rs @@ -1134,6 +1134,33 @@ mod tests { sea_orm::tests_cfg::cake::Model { id, name } } + fn filling_model(id: i32) -> sea_orm::tests_cfg::filling::Model { + let name = match id { + 1 => "apple juice", + 2 => "orange jam", + 3 => "fruit", + 4 => "chocolate crust", + _ => "", + } + .to_string(); + sea_orm::tests_cfg::filling::Model { + id, + name, + vendor_id: Some(1), + ignored_attr: 0, + } + } + + fn cake_filling_models( + cake_id: i32, + filling_id: i32, + ) -> ( + sea_orm::tests_cfg::cake::Model, + sea_orm::tests_cfg::filling::Model, + ) { + (cake_model(cake_id), filling_model(filling_id)) + } + fn fruit_model(id: i32, cake_id: Option) -> sea_orm::tests_cfg::fruit::Model { let name = match id { 1 => "apple", @@ -1230,7 +1257,7 @@ mod tests { .append_query_results([[ cake_fruit_model(1, 1), cake_fruit_model(1, 2), - cake_fruit_model(2, 2), + cake_fruit_model(2, 3), ]]) .into_connection(); @@ -1239,7 +1266,7 @@ mod tests { [ (cake_model(1), Some(fruit_model(1, Some(1)))), (cake_model(1), Some(fruit_model(2, Some(1)))), - (cake_model(2), Some(fruit_model(2, Some(2)))) + (cake_model(2), Some(fruit_model(3, Some(2)))) ] ); @@ -1255,7 +1282,7 @@ mod tests { .append_query_results([[ cake_fruit_model(1, 1).into_mock_row(), cake_fruit_model(1, 2).into_mock_row(), - cake_fruit_model(2, 2).into_mock_row(), + cake_fruit_model(2, 3).into_mock_row(), (cake_model(3), None::).into_mock_row(), ]]) .into_connection(); @@ -1265,7 +1292,59 @@ mod tests { [ (cake_model(1), Some(fruit_model(1, Some(1)))), (cake_model(1), Some(fruit_model(2, Some(1)))), - (cake_model(2), Some(fruit_model(2, Some(2)))), + (cake_model(2), Some(fruit_model(3, Some(2)))), + (cake_model(3), None) + ] + ); + + Ok(()) + } + + #[smol_potat::test] + pub async fn also_related_many_to_many() -> Result<(), sea_orm::DbErr> { + use sea_orm::tests_cfg::*; + use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase}; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[ + cake_filling_models(1, 1).into_mock_row(), + cake_filling_models(1, 2).into_mock_row(), + cake_filling_models(2, 2).into_mock_row(), + ]]) + .into_connection(); + + assert_eq!( + Cake::find().find_also_related(Filling).all(&db).await?, + [ + (cake_model(1), Some(filling_model(1))), + (cake_model(1), Some(filling_model(2))), + (cake_model(2), Some(filling_model(2))), + ] + ); + + Ok(()) + } + + #[smol_potat::test] + pub async fn also_related_many_to_many_2() -> Result<(), sea_orm::DbErr> { + use sea_orm::tests_cfg::*; + use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase}; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[ + cake_filling_models(1, 1).into_mock_row(), + cake_filling_models(1, 2).into_mock_row(), + cake_filling_models(2, 2).into_mock_row(), + (cake_model(3), None::).into_mock_row(), + ]]) + .into_connection(); + + assert_eq!( + Cake::find().find_also_related(Filling).all(&db).await?, + [ + (cake_model(1), Some(filling_model(1))), + (cake_model(1), Some(filling_model(2))), + (cake_model(2), Some(filling_model(2))), (cake_model(3), None) ] ); @@ -1383,6 +1462,56 @@ mod tests { Ok(()) } + #[smol_potat::test] + pub async fn with_related_many_to_many() -> Result<(), sea_orm::DbErr> { + use sea_orm::tests_cfg::*; + use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase}; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[ + cake_filling_models(1, 1).into_mock_row(), + cake_filling_models(1, 2).into_mock_row(), + cake_filling_models(2, 2).into_mock_row(), + ]]) + .into_connection(); + + assert_eq!( + Cake::find().find_with_related(Filling).all(&db).await?, + [ + (cake_model(1), vec![filling_model(1), filling_model(2)]), + (cake_model(2), vec![filling_model(2)]), + ] + ); + + Ok(()) + } + + #[smol_potat::test] + pub async fn with_related_many_to_many_2() -> Result<(), sea_orm::DbErr> { + use sea_orm::tests_cfg::*; + use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase}; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[ + cake_filling_models(1, 1).into_mock_row(), + cake_filling_models(1, 2).into_mock_row(), + cake_filling_models(2, 2).into_mock_row(), + (cake_model(3), None::).into_mock_row(), + ]]) + .into_connection(); + + assert_eq!( + Cake::find().find_with_related(Filling).all(&db).await?, + [ + (cake_model(1), vec![filling_model(1), filling_model(2)]), + (cake_model(2), vec![filling_model(2)]), + (cake_model(3), vec![]) + ] + ); + + Ok(()) + } + #[smol_potat::test] pub async fn also_linked_base() -> Result<(), sea_orm::DbErr> { use sea_orm::tests_cfg::*; @@ -1668,4 +1797,4 @@ mod tests { Ok(()) } -} +} \ No newline at end of file From cca65b7875b62b6692096e03ac3419bcdc96205d Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 18 Aug 2023 13:14:52 +0100 Subject: [PATCH 0149/1371] Slight tweaks --- src/executor/select.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/executor/select.rs b/src/executor/select.rs index 1ffaf79650..7c07580831 100644 --- a/src/executor/select.rs +++ b/src/executor/select.rs @@ -1404,9 +1404,9 @@ mod tests { let db = MockDatabase::new(DbBackend::Postgres) .append_query_results([[ cake_fruit_model(1, 1).into_mock_row(), - cake_fruit_model(2, 1).into_mock_row(), cake_fruit_model(2, 2).into_mock_row(), cake_fruit_model(2, 3).into_mock_row(), + cake_fruit_model(2, 4).into_mock_row(), ]]) .into_connection(); @@ -1417,9 +1417,9 @@ mod tests { ( cake_model(2), vec![ - fruit_model(1, Some(2)), fruit_model(2, Some(2)), fruit_model(3, Some(2)), + fruit_model(4, Some(2)), ] ), ] @@ -1436,9 +1436,9 @@ mod tests { let db = MockDatabase::new(DbBackend::Postgres) .append_query_results([[ cake_fruit_model(1, 1).into_mock_row(), - cake_fruit_model(2, 1).into_mock_row(), cake_fruit_model(2, 2).into_mock_row(), cake_fruit_model(2, 3).into_mock_row(), + cake_fruit_model(2, 4).into_mock_row(), (cake_model(3), None::).into_mock_row(), ]]) .into_connection(); @@ -1450,9 +1450,9 @@ mod tests { ( cake_model(2), vec![ - fruit_model(1, Some(2)), fruit_model(2, Some(2)), fruit_model(3, Some(2)), + fruit_model(4, Some(2)), ] ), (cake_model(3), vec![]) From af52e86a9ced16156899f682011713363e071fcd Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 18 Aug 2023 13:47:27 +0100 Subject: [PATCH 0150/1371] Newline --- src/executor/select.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/executor/select.rs b/src/executor/select.rs index 7c07580831..493f861fe3 100644 --- a/src/executor/select.rs +++ b/src/executor/select.rs @@ -1797,4 +1797,4 @@ mod tests { Ok(()) } -} \ No newline at end of file +} From 71afa56569db69ff0e5f1d0a0ea6e3ce99cd459c Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 25 Aug 2023 21:46:20 +0100 Subject: [PATCH 0151/1371] Bump sea-query --- Cargo.toml | 2 +- src/query/update.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 02be2c3992..fad2909686 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ tracing = { version = "0.1", default-features = false, features = ["attributes", rust_decimal = { version = "1", default-features = false, optional = true } bigdecimal = { version = "0.3", default-features = false, optional = true } sea-orm-macros = { version = "0.12.2", path = "sea-orm-macros", default-features = false, features = ["strum"] } -sea-query = { version = "0.30.0", default-features = false, features = ["thread-safe", "hashable-value", "backend-mysql", "backend-postgres", "backend-sqlite"] } +sea-query = { version = "0.30.1", default-features = false, features = ["thread-safe", "hashable-value", "backend-mysql", "backend-postgres", "backend-sqlite"] } sea-query-binder = { version = "0.5.0", default-features = false, optional = true } strum = { version = "0.25", default-features = false } serde = { version = "1.0", default-features = false } diff --git a/src/query/update.rs b/src/query/update.rs index 9a1c60c907..9863af6fcf 100644 --- a/src/query/update.rs +++ b/src/query/update.rs @@ -311,7 +311,7 @@ mod tests { .filter(lunch_set::Column::Tea.eq(Tea::BreakfastTea)) .build(DbBackend::Postgres) .to_string(), - r#"UPDATE "lunch_set" SET "tea" = CAST('EverydayTea' AS tea) WHERE "lunch_set"."tea" = CAST('BreakfastTea' AS tea)"#, + r#"UPDATE "lunch_set" SET "tea" = CAST('EverydayTea' AS tea) WHERE "lunch_set"."tea" = (CAST('BreakfastTea' AS tea))"#, ); } From edd6c9b3622ed22d77835e4b0de854fddf5b2f22 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 25 Aug 2023 22:02:33 +0100 Subject: [PATCH 0152/1371] cargo clippy --- sea-orm-codegen/src/entity/base_entity.rs | 2 +- src/database/db_connection.rs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/sea-orm-codegen/src/entity/base_entity.rs b/sea-orm-codegen/src/entity/base_entity.rs index c7a89b96f3..f261f22e49 100644 --- a/sea-orm-codegen/src/entity/base_entity.rs +++ b/sea-orm-codegen/src/entity/base_entity.rs @@ -108,7 +108,7 @@ impl Entity { self.get_relation_enum_name() .into_iter() .chain(self_relations_reverse) - .chain(conjunct_related_names.into_iter()) + .chain(conjunct_related_names) .collect() } diff --git a/src/database/db_connection.rs b/src/database/db_connection.rs index 158cf224b4..ad2b56900e 100644 --- a/src/database/db_connection.rs +++ b/src/database/db_connection.rs @@ -470,6 +470,10 @@ impl DatabaseConnection { impl DbBackend { /// Check if the URI is the same as the specified database backend. /// Returns true if they match. + /// + /// # Panics + /// + /// Panics if `base_url` cannot be parsed as `Url`. pub fn is_prefix_of(self, base_url: &str) -> bool { let base_url_parsed = Url::parse(base_url).expect("Fail to parse database URL"); match self { From 1470dd332b7ab60f9ea6642974f65e2fafe83d98 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 7 Sep 2023 15:00:49 +0100 Subject: [PATCH 0153/1371] Copyright --- LICENSE-MIT | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE-MIT b/LICENSE-MIT index 438ca971b1..719424ff8e 100644 --- a/LICENSE-MIT +++ b/LICENSE-MIT @@ -1,4 +1,4 @@ -Copyright (c) 2021 Tsang Hao Fung +Copyright (c) 2023 Seafire Software Limited Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated From 4804170208d5ef178b73320f7a6b7748b736eb72 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Tue, 12 Sep 2023 19:40:55 +0100 Subject: [PATCH 0154/1371] Fix DeriveValueType by qualifying QueryResult #1855 --- sea-orm-macros/src/derives/value_type.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sea-orm-macros/src/derives/value_type.rs b/sea-orm-macros/src/derives/value_type.rs index d7ebfe4907..e6cee9e6b9 100644 --- a/sea-orm-macros/src/derives/value_type.rs +++ b/sea-orm-macros/src/derives/value_type.rs @@ -108,7 +108,7 @@ impl DeriveValueType { #[automatically_derived] impl sea_orm::TryGetable for #name { - fn try_get_by(res: &QueryResult, idx: I) -> Result { + fn try_get_by(res: &sea_orm::QueryResult, idx: I) -> Result { <#field_type as sea_orm::TryGetable>::try_get_by(res, idx).map(|v| #name(v)) } } From f6b94c3a7a3b9d794e1afb2c7a19a2eea2850e45 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Wed, 13 Sep 2023 23:03:57 +0800 Subject: [PATCH 0155/1371] Bump `chrono` to 0.4.30 (#1858) * Bump `chrono` to 0.4.30 * Fix test cases --- Cargo.toml | 2 +- tests/time_crate_tests.rs | 3 ++- tests/timestamp_tests.rs | 8 ++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fad2909686..036ea2bd72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ path = "src/lib.rs" [dependencies] async-stream = { version = "0.3", default-features = false } async-trait = { version = "0.1", default-features = false } -chrono = { version = "0.4.20", default-features = false, optional = true } +chrono = { version = "0.4.30", default-features = false, optional = true } time = { version = "0.3", default-features = false, optional = true } futures = { version = "0.3", default-features = false, features = ["std"] } log = { version = "0.4", default-features = false } diff --git a/tests/time_crate_tests.rs b/tests/time_crate_tests.rs index 39d8256e45..6a940330fb 100644 --- a/tests/time_crate_tests.rs +++ b/tests/time_crate_tests.rs @@ -1,5 +1,6 @@ pub mod common; pub use common::{features::*, setup::*, TestContext}; +use pretty_assertions::assert_eq; use sea_orm::{entity::prelude::*, DatabaseConnection, IntoActiveModel}; use serde_json::json; use time::macros::{date, time}; @@ -49,7 +50,7 @@ pub async fn create_transaction_log(db: &DatabaseConnection) -> Result<(), DbErr "date": "2022-03-13", "time": "16:24:00", "date_time": "2022-03-13T16:24:00", - "date_time_tz": "2022-03-13T16:24:00+00:00", + "date_time_tz": "2022-03-13T16:24:00Z", }) ); diff --git a/tests/timestamp_tests.rs b/tests/timestamp_tests.rs index c151ee331d..90e2da638b 100644 --- a/tests/timestamp_tests.rs +++ b/tests/timestamp_tests.rs @@ -62,7 +62,7 @@ pub async fn create_applog(db: &DatabaseConnection) -> Result<(), DbErr> { "id": 1, "action": "Testing", "json": "HI", - "created_at": "2021-09-17T09:50:20+00:00", + "created_at": "2021-09-17T09:50:20Z", })) ); @@ -91,7 +91,7 @@ pub async fn create_satellites_log(db: &DatabaseConnection) -> Result<(), DbErr> "id": 1, "satellite_name": "Sea-00001-2022", "launch_date": "2022-01-07T12:11:23+00:00", - "deployment_date": "2022-01-07T12:11:23Z".parse::().unwrap(), + "deployment_date": "2022-01-07T12:11:23Z".parse::().unwrap().to_rfc3339(), })) ); #[cfg(feature = "sqlx-mysql")] @@ -110,8 +110,8 @@ pub async fn create_satellites_log(db: &DatabaseConnection) -> Result<(), DbErr> Some(serde_json::json!({ "id": 1, "satellite_name": "Sea-00001-2022", - "launch_date": "2022-01-07T12:11:23+00:00", - "deployment_date": "2022-01-07T12:11:23+00:00", + "launch_date": "2022-01-07T12:11:23Z", + "deployment_date": "2022-01-07T12:11:23Z", })) ); From b029e87fd90b109dbdeb565b49b68a60f4d6a216 Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Thu, 14 Sep 2023 13:37:33 +0530 Subject: [PATCH 0156/1371] Check if an index exists (#1828) * feat: add method to check for index * tests(sea-orm-migrations): add index to cake name * tests(sea-orm-migrations): check if `has_index` works * Update sea-orm-migration/tests/common/migration/m20220118_000001_create_cake_table.rs * Update Cargo.toml --------- Co-authored-by: Chris Tsang --- sea-orm-migration/Cargo.toml | 2 +- sea-orm-migration/src/manager.rs | 21 +++++++++++++++++++ .../m20220118_000001_create_cake_table.rs | 13 +++++++++++- sea-orm-migration/tests/main.rs | 3 +++ 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/sea-orm-migration/Cargo.toml b/sea-orm-migration/Cargo.toml index 3722b9ce4a..ae3f2c75e1 100644 --- a/sea-orm-migration/Cargo.toml +++ b/sea-orm-migration/Cargo.toml @@ -25,7 +25,7 @@ clap = { version = "4.3", features = ["env", "derive"], optional = true } dotenvy = { version = "0.15", default-features = false, optional = true } sea-orm = { version = "0.12.2", path = "../", default-features = false, features = ["macros"] } sea-orm-cli = { version = "0.12.2", path = "../sea-orm-cli", default-features = false, optional = true } -sea-schema = { version = "0.14.0" } +sea-schema = { version = "0.14.1" } tracing = { version = "0.1", default-features = false, features = ["log"] } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["env-filter", "fmt"] } futures = { version = "0.3", default-features = false, features = ["std"] } diff --git a/sea-orm-migration/src/manager.rs b/sea-orm-migration/src/manager.rs index f70c9212ac..994204e142 100644 --- a/sea-orm-migration/src/manager.rs +++ b/sea-orm-migration/src/manager.rs @@ -136,4 +136,25 @@ impl<'c> SchemaManager<'c> { res.try_get("", "has_column") } + + pub async fn has_index(&self, table: T, index: I) -> Result + where + T: AsRef, + I: AsRef, + { + let stmt = match self.conn.get_database_backend() { + DbBackend::MySql => MySql::has_index(table, index), + DbBackend::Postgres => Postgres::has_index(table, index), + DbBackend::Sqlite => Sqlite::has_index(table, index), + }; + + let builder = self.conn.get_database_backend(); + let res = self + .conn + .query_one(builder.build(&stmt)) + .await? + .ok_or_else(|| DbErr::Custom("Failed to check index exists".to_owned()))?; + + res.try_get("", "has_index") + } } diff --git a/sea-orm-migration/tests/common/migration/m20220118_000001_create_cake_table.rs b/sea-orm-migration/tests/common/migration/m20220118_000001_create_cake_table.rs index 370697fbfe..ea733f8c20 100644 --- a/sea-orm-migration/tests/common/migration/m20220118_000001_create_cake_table.rs +++ b/sea-orm-migration/tests/common/migration/m20220118_000001_create_cake_table.rs @@ -20,7 +20,18 @@ impl MigrationTrait for Migration { .col(ColumnDef::new(Cake::Name).string().not_null()) .to_owned(), ) - .await + .await?; + + manager + .create_index( + Index::create() + .name("cake_name_index") + .table(Cake::Table) + .col(Cake::Name) + .to_owned(), + ) + .await?; + Ok(()) } async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { diff --git a/sea-orm-migration/tests/main.rs b/sea-orm-migration/tests/main.rs index 3247bae25f..673c51c481 100644 --- a/sea-orm-migration/tests/main.rs +++ b/sea-orm-migration/tests/main.rs @@ -182,6 +182,9 @@ where let migrations = Migrator::get_applied_migrations(db).await?; assert_eq!(migrations.len(), 6); + assert!(!manager.has_index("cake", "non_existent_index").await?); + assert!(manager.has_index("cake", "cake_name_index").await?); + let migration = migrations.get(0).unwrap(); assert_eq!(migration.name(), "m20220118_000001_create_cake_table"); assert_eq!(migration.status(), MigrationStatus::Applied); From 59754f5ff9d1fea1b29bfd5f80614fa7af219ca8 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 14 Sep 2023 09:10:08 +0100 Subject: [PATCH 0157/1371] Changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e2703a279..2ee5dc6882 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## 0.12.3 - Pending + +### New Features + +* [sea-orm-migration] Check if an index exists https://github.com/SeaQL/sea-orm/pull/1828 + ## 0.12.2 - 2023-08-04 ### Enhancements From 9d033d01a8cfdb64968644be70cbd55b521c1182 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Fri, 22 Sep 2023 02:28:49 +0800 Subject: [PATCH 0158/1371] Fix: generate relation for related entity of composite foreign key (#1693) * Fix: generate relation for related entity of composite foreign key * clippy --- sea-orm-codegen/src/entity/relation.rs | 93 +++++++++++++++---- sea-orm-codegen/src/entity/writer.rs | 58 +++++++++++- .../tests/compact/cake_filling_price.rs | 31 +++++++ .../cake_filling_price.rs | 31 +++++++ .../tests/expanded/cake_filling_price.rs | 79 ++++++++++++++++ .../cake_filling_price.rs | 83 +++++++++++++++++ 6 files changed, 353 insertions(+), 22 deletions(-) create mode 100644 sea-orm-codegen/tests/compact/cake_filling_price.rs create mode 100644 sea-orm-codegen/tests/compact_with_schema_name/cake_filling_price.rs create mode 100644 sea-orm-codegen/tests/expanded/cake_filling_price.rs create mode 100644 sea-orm-codegen/tests/expanded_with_schema_name/cake_filling_price.rs diff --git a/sea-orm-codegen/src/entity/relation.rs b/sea-orm-codegen/src/entity/relation.rs index 67fa77ad53..8f3ef21702 100644 --- a/sea-orm-codegen/src/entity/relation.rs +++ b/sea-orm-codegen/src/entity/relation.rs @@ -3,6 +3,7 @@ use heck::{ToSnakeCase, ToUpperCamelCase}; use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; use sea_query::{ForeignKeyAction, TableForeignKey}; +use syn::{punctuated::Punctuated, token::Comma}; use crate::util::escape_rust_keyword; @@ -66,17 +67,27 @@ impl Relation { } } RelationType::BelongsTo => { - let column_camel_case = self.get_column_camel_case(); - let ref_column_camel_case = self.get_ref_column_camel_case(); - let to_col = if module_name.is_some() { - quote! { super::#module_name::Column::#ref_column_camel_case } - } else { - quote! { Column::#ref_column_camel_case } + let map_src_column = |src_column: &Ident| { + quote! { Column::#src_column } + }; + let map_ref_column = |ref_column: &Ident| { + if module_name.is_some() { + quote! { super::#module_name::Column::#ref_column } + } else { + quote! { Column::#ref_column } + } }; + let map_punctuated = + |punctuated: Punctuated| match punctuated.len() { + 0..=1 => quote! { #punctuated }, + _ => quote! { (#punctuated) }, + }; + let (from, to) = + self.get_src_ref_columns(map_src_column, map_ref_column, map_punctuated); quote! { Entity::#rel_type(#ref_entity) - .from(Column::#column_camel_case) - .to(#to_col) + .from(#from) + .to(#to) .into() } } @@ -98,10 +109,20 @@ impl Relation { } } RelationType::BelongsTo => { - let column_camel_case = self.get_column_camel_case(); - let ref_column_camel_case = self.get_ref_column_camel_case(); - let from = format!("Column::{column_camel_case}"); - let to = format!("{module_name}Column::{ref_column_camel_case}"); + let map_src_column = |src_column: &Ident| format!("Column::{src_column}"); + let map_ref_column = + |ref_column: &Ident| format!("{module_name}Column::{ref_column}"); + let map_punctuated = |punctuated: Vec| { + let len = punctuated.len(); + let punctuated = punctuated.join(", "); + match len { + 0..=1 => punctuated, + _ => format!("({})", punctuated), + } + }; + let (from, to) = + self.get_src_ref_columns(map_src_column, map_ref_column, map_punctuated); + let on_update = if let Some(action) = &self.on_update { let action = Self::get_foreign_key_action(action); quote! { @@ -139,12 +160,18 @@ impl Relation { } } - pub fn get_column_camel_case(&self) -> Ident { - format_ident!("{}", self.columns[0].to_upper_camel_case()) + pub fn get_column_camel_case(&self) -> Vec { + self.columns + .iter() + .map(|col| format_ident!("{}", col.to_upper_camel_case())) + .collect() } - pub fn get_ref_column_camel_case(&self) -> Ident { - format_ident!("{}", self.ref_columns[0].to_upper_camel_case()) + pub fn get_ref_column_camel_case(&self) -> Vec { + self.ref_columns + .iter() + .map(|col| format_ident!("{}", col.to_upper_camel_case())) + .collect() } pub fn get_foreign_key_action(action: &ForeignKeyAction) -> String { @@ -157,6 +184,36 @@ impl Relation { } .to_owned() } + + pub fn get_src_ref_columns( + &self, + map_src_column: F1, + map_ref_column: F2, + map_punctuated: F3, + ) -> (T, T) + where + F1: Fn(&Ident) -> T, + F2: Fn(&Ident) -> T, + F3: Fn(I) -> T, + I: Extend + Default, + { + let from: I = + self.get_column_camel_case() + .iter() + .fold(I::default(), |mut acc, src_column| { + acc.extend([map_src_column(src_column)]); + acc + }); + let to: I = + self.get_ref_column_camel_case() + .iter() + .fold(I::default(), |mut acc, ref_column| { + acc.extend([map_ref_column(ref_column)]); + acc + }); + + (map_punctuated(from), map_punctuated(to)) + } } impl From<&TableForeignKey> for Relation { @@ -278,7 +335,7 @@ mod tests { let relations = setup(); let cols = vec!["Id", "FillingId", "FillingId"]; for (rel, col) in relations.into_iter().zip(cols) { - assert_eq!(rel.get_column_camel_case(), col); + assert_eq!(rel.get_column_camel_case(), [col]); } } @@ -287,7 +344,7 @@ mod tests { let relations = setup(); let ref_cols = vec!["CakeId", "Id", "Id"]; for (rel, ref_col) in relations.into_iter().zip(ref_cols) { - assert_eq!(rel.get_ref_column_camel_case(), ref_col); + assert_eq!(rel.get_ref_column_camel_case(), [ref_col]); } } } diff --git a/sea-orm-codegen/src/entity/writer.rs b/sea-orm-codegen/src/entity/writer.rs index 637e6eb28f..dc60654fa1 100644 --- a/sea-orm-codegen/src/entity/writer.rs +++ b/sea-orm-codegen/src/entity/writer.rs @@ -908,6 +908,52 @@ mod tests { }, ], }, + Entity { + table_name: "cake_filling_price".to_owned(), + columns: vec![ + Column { + name: "cake_id".to_owned(), + col_type: ColumnType::Integer, + auto_increment: false, + not_null: true, + unique: false, + }, + Column { + name: "filling_id".to_owned(), + col_type: ColumnType::Integer, + auto_increment: false, + not_null: true, + unique: false, + }, + Column { + name: "price".to_owned(), + col_type: ColumnType::Decimal(None), + auto_increment: false, + not_null: true, + unique: false, + }, + ], + relations: vec![Relation { + ref_table: "cake_filling".to_owned(), + columns: vec!["cake_id".to_owned(), "filling_id".to_owned()], + ref_columns: vec!["cake_id".to_owned(), "filling_id".to_owned()], + rel_type: RelationType::BelongsTo, + on_delete: None, + on_update: None, + self_referencing: false, + num_suffix: 0, + impl_related: true, + }], + conjunct_relations: vec![], + primary_keys: vec![ + PrimaryKey { + name: "cake_id".to_owned(), + }, + PrimaryKey { + name: "filling_id".to_owned(), + }, + ], + }, Entity { table_name: "filling".to_owned(), columns: vec![ @@ -1361,9 +1407,10 @@ mod tests { #[test] fn test_gen_expanded_code_blocks() -> io::Result<()> { let entities = setup(); - const ENTITY_FILES: [&str; 10] = [ + const ENTITY_FILES: [&str; 11] = [ include_str!("../../tests/expanded/cake.rs"), include_str!("../../tests/expanded/cake_filling.rs"), + include_str!("../../tests/expanded/cake_filling_price.rs"), include_str!("../../tests/expanded/filling.rs"), include_str!("../../tests/expanded/fruit.rs"), include_str!("../../tests/expanded/vendor.rs"), @@ -1373,9 +1420,10 @@ mod tests { include_str!("../../tests/expanded/collection.rs"), include_str!("../../tests/expanded/collection_float.rs"), ]; - const ENTITY_FILES_WITH_SCHEMA_NAME: [&str; 10] = [ + const ENTITY_FILES_WITH_SCHEMA_NAME: [&str; 11] = [ include_str!("../../tests/expanded_with_schema_name/cake.rs"), include_str!("../../tests/expanded_with_schema_name/cake_filling.rs"), + include_str!("../../tests/expanded_with_schema_name/cake_filling_price.rs"), include_str!("../../tests/expanded_with_schema_name/filling.rs"), include_str!("../../tests/expanded_with_schema_name/fruit.rs"), include_str!("../../tests/expanded_with_schema_name/vendor.rs"), @@ -1460,9 +1508,10 @@ mod tests { #[test] fn test_gen_compact_code_blocks() -> io::Result<()> { let entities = setup(); - const ENTITY_FILES: [&str; 10] = [ + const ENTITY_FILES: [&str; 11] = [ include_str!("../../tests/compact/cake.rs"), include_str!("../../tests/compact/cake_filling.rs"), + include_str!("../../tests/compact/cake_filling_price.rs"), include_str!("../../tests/compact/filling.rs"), include_str!("../../tests/compact/fruit.rs"), include_str!("../../tests/compact/vendor.rs"), @@ -1472,9 +1521,10 @@ mod tests { include_str!("../../tests/compact/collection.rs"), include_str!("../../tests/compact/collection_float.rs"), ]; - const ENTITY_FILES_WITH_SCHEMA_NAME: [&str; 10] = [ + const ENTITY_FILES_WITH_SCHEMA_NAME: [&str; 11] = [ include_str!("../../tests/compact_with_schema_name/cake.rs"), include_str!("../../tests/compact_with_schema_name/cake_filling.rs"), + include_str!("../../tests/compact_with_schema_name/cake_filling_price.rs"), include_str!("../../tests/compact_with_schema_name/filling.rs"), include_str!("../../tests/compact_with_schema_name/fruit.rs"), include_str!("../../tests/compact_with_schema_name/vendor.rs"), diff --git a/sea-orm-codegen/tests/compact/cake_filling_price.rs b/sea-orm-codegen/tests/compact/cake_filling_price.rs new file mode 100644 index 0000000000..4cbef16034 --- /dev/null +++ b/sea-orm-codegen/tests/compact/cake_filling_price.rs @@ -0,0 +1,31 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "cake_filling_price")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub cake_id: i32, + #[sea_orm(primary_key, auto_increment = false)] + pub filling_id: i32, + pub price: Decimal, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::cake_filling::Entity", + from = "(Column::CakeId, Column::FillingId)", + to = "(super::cake_filling::Column::CakeId, super::cake_filling::Column::FillingId)", + )] + CakeFilling, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::CakeFilling.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} \ No newline at end of file diff --git a/sea-orm-codegen/tests/compact_with_schema_name/cake_filling_price.rs b/sea-orm-codegen/tests/compact_with_schema_name/cake_filling_price.rs new file mode 100644 index 0000000000..6a75dd3f27 --- /dev/null +++ b/sea-orm-codegen/tests/compact_with_schema_name/cake_filling_price.rs @@ -0,0 +1,31 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(schema_name = "schema_name", table_name = "cake_filling_price")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub cake_id: i32, + #[sea_orm(primary_key, auto_increment = false)] + pub filling_id: i32, + pub price: Decimal, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::cake_filling::Entity", + from = "(Column::CakeId, Column::FillingId)", + to = "(super::cake_filling::Column::CakeId, super::cake_filling::Column::FillingId)", + )] + CakeFilling, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::CakeFilling.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} \ No newline at end of file diff --git a/sea-orm-codegen/tests/expanded/cake_filling_price.rs b/sea-orm-codegen/tests/expanded/cake_filling_price.rs new file mode 100644 index 0000000000..c680c8813a --- /dev/null +++ b/sea-orm-codegen/tests/expanded/cake_filling_price.rs @@ -0,0 +1,79 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0 + +use sea_orm::entity::prelude::*; + +#[derive(Copy, Clone, Default, Debug, DeriveEntity)] +pub struct Entity; + +impl EntityName for Entity { + fn table_name(&self) -> &str { + "cake_filling_price" + } +} + +#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Eq)] +pub struct Model { + pub cake_id: i32, + pub filling_id: i32, + pub price: Decimal, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] +pub enum Column { + CakeId, + FillingId, + Price, +} + +#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] +pub enum PrimaryKey { + CakeId, + FillingId, +} + +impl PrimaryKeyTrait for PrimaryKey { + type ValueType = (i32, i32); + + fn auto_increment() -> bool { + false + } +} + +#[derive(Copy, Clone, Debug, EnumIter)] +pub enum Relation { + CakeFilling, +} + +impl ColumnTrait for Column { + type EntityName = Entity; + + fn def(&self) -> ColumnDef { + match self { + Self::CakeId => ColumnType::Integer.def(), + Self::FillingId => ColumnType::Integer.def(), + Self::Price => ColumnType::Decimal(None).def(), + } + } +} + +impl RelationTrait for Relation { + fn def(&self) -> RelationDef { + match self { + Self::CakeFilling => Entity::belongs_to(super::cake_filling::Entity) + .from((Column::CakeId, Column::FillingId)) + .to(( + super::cake_filling::Column::CakeId, + super::cake_filling::Column::FillingId + )) + .into(), + } + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::CakeFilling.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/sea-orm-codegen/tests/expanded_with_schema_name/cake_filling_price.rs b/sea-orm-codegen/tests/expanded_with_schema_name/cake_filling_price.rs new file mode 100644 index 0000000000..8f613c9cd6 --- /dev/null +++ b/sea-orm-codegen/tests/expanded_with_schema_name/cake_filling_price.rs @@ -0,0 +1,83 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0 + +use sea_orm::entity::prelude::*; + +#[derive(Copy, Clone, Default, Debug, DeriveEntity)] +pub struct Entity; + +impl EntityName for Entity { + fn schema_name(&self) -> Option< &str > { + Some("schema_name") + } + + fn table_name(&self) -> &str { + "cake_filling_price" + } +} + +#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Eq)] +pub struct Model { + pub cake_id: i32, + pub filling_id: i32, + pub price: Decimal, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] +pub enum Column { + CakeId, + FillingId, + Price, +} + +#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] +pub enum PrimaryKey { + CakeId, + FillingId, +} + +impl PrimaryKeyTrait for PrimaryKey { + type ValueType = (i32, i32); + + fn auto_increment() -> bool { + false + } +} + +#[derive(Copy, Clone, Debug, EnumIter)] +pub enum Relation { + CakeFilling, +} + +impl ColumnTrait for Column { + type EntityName = Entity; + + fn def(&self) -> ColumnDef { + match self { + Self::CakeId => ColumnType::Integer.def(), + Self::FillingId => ColumnType::Integer.def(), + Self::Price => ColumnType::Decimal(None).def(), + } + } +} + +impl RelationTrait for Relation { + fn def(&self) -> RelationDef { + match self { + Self::CakeFilling => Entity::belongs_to(super::cake_filling::Entity) + .from((Column::CakeId, Column::FillingId)) + .to(( + super::cake_filling::Column::CakeId, + super::cake_filling::Column::FillingId + )) + .into(), + } + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::CakeFilling.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} From 27ca7452080472e990fa39529ac711a7636ea5a2 Mon Sep 17 00:00:00 2001 From: Ivan Yiu Date: Sat, 23 Sep 2023 00:03:04 +0800 Subject: [PATCH 0159/1371] SelectTwo cursor by, cursorTrait revamp (#1826) * WIP * WIP * test cases for cursor with SelectTwo * fmt, fix test * WIP, missing test for also_linked cursor_by * completed many_to_many relationship * test fixup * fmt * extra order_by to fix test * WIP * fixed cursor_by to do ordering implicitly base on both cursor column and primary key from other table * change from map to for loop to eagerly do order_by * fix tests --- src/executor/cursor.rs | 584 ++++++++++++++++++++++++++++++++++++++--- tests/cursor_tests.rs | 264 ++++++++++++++++++- 2 files changed, 813 insertions(+), 35 deletions(-) diff --git a/src/executor/cursor.rs b/src/executor/cursor.rs index c9101de6c5..18031b962c 100644 --- a/src/executor/cursor.rs +++ b/src/executor/cursor.rs @@ -1,12 +1,14 @@ use crate::{ - ConnectionTrait, DbErr, EntityTrait, FromQueryResult, Identity, IntoIdentity, - PartialModelTrait, QueryOrder, QuerySelect, Select, SelectModel, SelectorTrait, + ConnectionTrait, DbErr, EntityTrait, FromQueryResult, Identity, IdentityOf, IntoIdentity, + PartialModelTrait, PrimaryKeyToColumn, QueryOrder, QuerySelect, Select, SelectModel, SelectTwo, + SelectTwoModel, SelectorTrait, }; use sea_query::{ Condition, DynIden, Expr, IntoValueTuple, Order, SeaRc, SelectStatement, SimpleExpr, Value, ValueTuple, }; use std::marker::PhantomData; +use strum::IntoEnumIterator as Iterable; #[cfg(feature = "with-json")] use crate::JsonValue; @@ -17,11 +19,12 @@ pub struct Cursor where S: SelectorTrait, { - pub(crate) query: SelectStatement, - pub(crate) table: DynIden, - pub(crate) order_columns: Identity, - pub(crate) last: bool, - pub(crate) phantom: PhantomData, + query: SelectStatement, + table: DynIden, + order_columns: Identity, + secondary_order_by: Vec<(DynIden, Identity)>, + last: bool, + phantom: PhantomData, } impl Cursor @@ -29,7 +32,12 @@ where S: SelectorTrait, { /// Initialize a cursor - pub fn new(query: SelectStatement, table: DynIden, order_columns: C) -> Self + pub fn new( + query: SelectStatement, + table: DynIden, + order_columns: C, + secondary_order_by: Vec<(DynIden, Identity)>, + ) -> Self where C: IntoIdentity, { @@ -39,6 +47,7 @@ where order_columns: order_columns.into_identity(), last: false, phantom: PhantomData, + secondary_order_by, } } @@ -158,10 +167,7 @@ where /// Limit result set to only first N rows in ascending order of the order by column pub fn first(&mut self, num_rows: u64) -> &mut Self { self.query.limit(num_rows).clear_order_by(); - let table = SeaRc::clone(&self.table); - self.apply_order_by(|query, col| { - query.order_by((SeaRc::clone(&table), SeaRc::clone(col)), Order::Asc); - }); + self.apply_order_by(self.table.clone(), Order::Asc); self.last = false; self } @@ -169,38 +175,41 @@ where /// Limit result set to only last N rows in ascending order of the order by column pub fn last(&mut self, num_rows: u64) -> &mut Self { self.query.limit(num_rows).clear_order_by(); - let table = SeaRc::clone(&self.table); - self.apply_order_by(|query, col| { - query.order_by((SeaRc::clone(&table), SeaRc::clone(col)), Order::Desc); - }); + self.apply_order_by(self.table.clone(), Order::Desc); self.last = true; self } - fn apply_order_by(&mut self, f: F) - where - F: Fn(&mut SelectStatement, &DynIden), - { + fn apply_order_by(&mut self, table: DynIden, ord: Order) { let query = &mut self.query; + let order = |query: &mut SelectStatement, col| { + query.order_by((SeaRc::clone(&table), SeaRc::clone(col)), ord.clone()); + }; match &self.order_columns { Identity::Unary(c1) => { - f(query, c1); + order(query, c1); } Identity::Binary(c1, c2) => { - f(query, c1); - f(query, c2); + order(query, c1); + order(query, c2); } Identity::Ternary(c1, c2, c3) => { - f(query, c1); - f(query, c2); - f(query, c3); + order(query, c1); + order(query, c2); + order(query, c3); } Identity::Many(vec) => { for col in vec.iter() { - f(query, col); + order(query, col); } } } + + for (tbl, col) in self.secondary_order_by.clone() { + if let Identity::Unary(c1) = col { + query.order_by((tbl, c1), ord.clone()); + }; + } } /// Fetch the paginated result @@ -231,6 +240,7 @@ where order_columns: self.order_columns, last: self.last, phantom: PhantomData, + secondary_order_by: self.secondary_order_by, } } @@ -251,8 +261,15 @@ where order_columns: self.order_columns, last: self.last, phantom: PhantomData, + secondary_order_by: self.secondary_order_by, } } + + /// Set the cursor ordering for another table when dealing with SelectTwo + pub fn set_secondary_order_by(&mut self, tbl_col: Vec<(DynIden, Identity)>) -> &mut Self { + self.secondary_order_by = tbl_col; + self + } } impl QuerySelect for Cursor @@ -281,11 +298,6 @@ where pub trait CursorTrait { /// Select operation type Selector: SelectorTrait + Send + Sync; - - /// Convert current type into a cursor - fn cursor_by(self, order_columns: C) -> Cursor - where - C: IntoIdentity; } impl CursorTrait for Select @@ -294,12 +306,79 @@ where M: FromQueryResult + Sized + Send + Sync, { type Selector = SelectModel; +} - fn cursor_by(self, order_columns: C) -> Cursor +impl Select +where + E: EntityTrait, + M: FromQueryResult + Sized + Send + Sync, +{ + /// Convert into a cursor + pub fn cursor_by(self, order_columns: C) -> Cursor> where C: IntoIdentity, { - Cursor::new(self.query, SeaRc::new(E::default()), order_columns) + Cursor::new(self.query, SeaRc::new(E::default()), order_columns, vec![]) + } +} + +impl CursorTrait for SelectTwo +where + E: EntityTrait, + F: EntityTrait, + M: FromQueryResult + Sized + Send + Sync, + N: FromQueryResult + Sized + Send + Sync, +{ + type Selector = SelectTwoModel; +} + +impl SelectTwo +where + E: EntityTrait, + F: EntityTrait, + M: FromQueryResult + Sized + Send + Sync, + N: FromQueryResult + Sized + Send + Sync, +{ + /// Convert into a cursor using column of first entity + pub fn cursor_by(self, order_columns: C) -> Cursor> + where + C: IdentityOf, + { + let primary_keys: Vec<(DynIden, Identity)> = ::iter() + .map(|pk| { + ( + SeaRc::new(F::default()), + Identity::Unary(SeaRc::new(pk.into_column())), + ) + }) + .collect(); + Cursor::new( + self.query, + SeaRc::new(E::default()), + order_columns.identity_of(), + primary_keys, + ) + } + + /// Convert into a cursor using column of second entity + pub fn cursor_by_other(self, order_columns: C) -> Cursor> + where + C: IdentityOf, + { + let primary_keys: Vec<(DynIden, Identity)> = ::iter() + .map(|pk| { + ( + SeaRc::new(E::default()), + Identity::Unary(SeaRc::new(pk.into_column())), + ) + }) + .collect(); + Cursor::new( + self.query, + SeaRc::new(F::default()), + order_columns.identity_of(), + primary_keys, + ) } } @@ -363,6 +442,231 @@ mod tests { Ok(()) } + #[smol_potat::test] + async fn first_2_before_10_also_related_select() -> Result<(), DbErr> { + let models = [ + ( + cake::Model { + id: 1, + name: "Blueberry Cheese Cake".into(), + }, + Some(fruit::Model { + id: 9, + name: "Blueberry".into(), + cake_id: Some(1), + }), + ), + ( + cake::Model { + id: 2, + name: "Rasberry Cheese Cake".into(), + }, + Some(fruit::Model { + id: 10, + name: "Rasberry".into(), + cake_id: Some(1), + }), + ), + ]; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([models.clone()]) + .into_connection(); + + assert_eq!( + cake::Entity::find() + .find_also_related(Fruit) + .cursor_by(cake::Column::Id) + .before(10) + .first(2) + .all(&db) + .await?, + models + ); + + assert_eq!( + db.into_transaction_log(), + [Transaction::many([Statement::from_sql_and_values( + DbBackend::Postgres, + [ + r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#, + r#""fruit"."id" AS "B_id", "fruit"."name" AS "B_name", "fruit"."cake_id" AS "B_cake_id""#, + r#"FROM "cake""#, + r#"LEFT JOIN "fruit" ON "cake"."id" = "fruit"."cake_id""#, + r#"WHERE "cake"."id" < $1"#, + r#"ORDER BY "cake"."id" ASC, "fruit"."id" ASC LIMIT $2"#, + ] + .join(" ") + .as_str(), + [10_i32.into(), 2_u64.into()] + ),])] + ); + + Ok(()) + } + + #[smol_potat::test] + async fn first_2_before_10_also_related_select_cursor_other() -> Result<(), DbErr> { + let models = [( + cake::Model { + id: 1, + name: "Blueberry Cheese Cake".into(), + }, + Some(fruit::Model { + id: 9, + name: "Blueberry".into(), + cake_id: Some(1), + }), + )]; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([models.clone()]) + .into_connection(); + + assert_eq!( + cake::Entity::find() + .find_also_related(Fruit) + .cursor_by_other(fruit::Column::Id) + .before(10) + .first(2) + .all(&db) + .await?, + models + ); + + assert_eq!( + db.into_transaction_log(), + [Transaction::many([Statement::from_sql_and_values( + DbBackend::Postgres, + [ + r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#, + r#""fruit"."id" AS "B_id", "fruit"."name" AS "B_name", "fruit"."cake_id" AS "B_cake_id""#, + r#"FROM "cake""#, + r#"LEFT JOIN "fruit" ON "cake"."id" = "fruit"."cake_id""#, + r#"WHERE "fruit"."id" < $1"#, + r#"ORDER BY "fruit"."id" ASC, "cake"."id" ASC LIMIT $2"#, + ] + .join(" ") + .as_str(), + [10_i32.into(), 2_u64.into()] + ),])] + ); + + Ok(()) + } + + #[smol_potat::test] + async fn first_2_before_10_also_linked_select() -> Result<(), DbErr> { + let models = [ + ( + cake::Model { + id: 1, + name: "Blueberry Cheese Cake".into(), + }, + Some(vendor::Model { + id: 9, + name: "Blueberry".into(), + }), + ), + ( + cake::Model { + id: 2, + name: "Rasberry Cheese Cake".into(), + }, + Some(vendor::Model { + id: 10, + name: "Rasberry".into(), + }), + ), + ]; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([models.clone()]) + .into_connection(); + + assert_eq!( + cake::Entity::find() + .find_also_linked(entity_linked::CakeToFillingVendor) + .cursor_by(cake::Column::Id) + .before(10) + .first(2) + .all(&db) + .await?, + models + ); + + assert_eq!( + db.into_transaction_log(), + [Transaction::many([Statement::from_sql_and_values( + DbBackend::Postgres, + [ + r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#, + r#""r2"."id" AS "B_id", "r2"."name" AS "B_name""#, + r#"FROM "cake""#, + r#"LEFT JOIN "cake_filling" AS "r0" ON "cake"."id" = "r0"."cake_id""#, + r#"LEFT JOIN "filling" AS "r1" ON "r0"."filling_id" = "r1"."id""#, + r#"LEFT JOIN "vendor" AS "r2" ON "r1"."vendor_id" = "r2"."id""#, + r#"WHERE "cake"."id" < $1 ORDER BY "cake"."id" ASC, "vendor"."id" ASC LIMIT $2"#, + ] + .join(" ") + .as_str(), + [10_i32.into(), 2_u64.into()] + ),])] + ); + + Ok(()) + } + + #[smol_potat::test] + async fn first_2_before_10_also_linked_select_cursor_other() -> Result<(), DbErr> { + let models = [( + cake::Model { + id: 1, + name: "Blueberry Cheese Cake".into(), + }, + Some(vendor::Model { + id: 9, + name: "Blueberry".into(), + }), + )]; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([models.clone()]) + .into_connection(); + + assert_eq!( + cake::Entity::find() + .find_also_linked(entity_linked::CakeToFillingVendor) + .cursor_by_other(vendor::Column::Id) + .before(10) + .first(2) + .all(&db) + .await?, + models + ); + + assert_eq!( + db.into_transaction_log(), + [Transaction::many([Statement::from_sql_and_values( + DbBackend::Postgres, + [ + r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#, + r#""r2"."id" AS "B_id", "r2"."name" AS "B_name""#, + r#"FROM "cake""#, + r#"LEFT JOIN "cake_filling" AS "r0" ON "cake"."id" = "r0"."cake_id""#, + r#"LEFT JOIN "filling" AS "r1" ON "r0"."filling_id" = "r1"."id""#, + r#"LEFT JOIN "vendor" AS "r2" ON "r1"."vendor_id" = "r2"."id""#, + r#"WHERE "vendor"."id" < $1 ORDER BY "vendor"."id" ASC, "cake"."id" ASC LIMIT $2"#, + ] + .join(" ") + .as_str(), + [10_i32.into(), 2_u64.into()] + ),])] + ); + + Ok(()) + } + #[smol_potat::test] async fn last_2_after_10() -> Result<(), DbErr> { use fruit::*; @@ -895,4 +1199,216 @@ mod tests { Ok(()) } + + mod test_base_entity { + use super::test_related_entity; + use crate as sea_orm; + use crate::entity::prelude::*; + + #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] + #[sea_orm(table_name = "base")] + pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + #[sea_orm(primary_key)] + pub name: String, + } + + #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] + pub enum Relation { + #[sea_orm(has_many = "super::test_related_entity::Entity")] + TestRelatedEntity, + } + + impl Related for Entity { + fn to() -> RelationDef { + Relation::TestRelatedEntity.def() + } + } + + impl ActiveModelBehavior for ActiveModel {} + } + + mod test_related_entity { + use super::test_base_entity; + use crate as sea_orm; + use crate::entity::prelude::*; + + #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] + #[sea_orm(table_name = "related")] + pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + #[sea_orm(primary_key)] + pub name: String, + pub test_id: i32, + } + + #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] + pub enum Relation { + #[sea_orm( + belongs_to = "test_base_entity::Entity", + from = "Column::TestId", + to = "super::test_base_entity::Column::Id" + )] + TestBaseEntity, + } + + impl Related for Entity { + fn to() -> RelationDef { + Relation::TestBaseEntity.def() + } + } + + impl ActiveModelBehavior for ActiveModel {} + } + + #[smol_potat::test] + async fn related_composite_keys_1() -> Result<(), DbErr> { + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[( + test_base_entity::Model { + id: 1, + name: "CAT".into(), + }, + test_related_entity::Model { + id: 1, + name: "CATE".into(), + test_id: 1, + }, + )]]) + .into_connection(); + + assert!(!test_base_entity::Entity::find() + .find_also_related(test_related_entity::Entity) + .cursor_by((test_base_entity::Column::Id, test_base_entity::Column::Name)) + .first(1) + .all(&db) + .await? + .is_empty()); + + assert_eq!( + db.into_transaction_log(), + [Transaction::many([Statement::from_sql_and_values( + DbBackend::Postgres, + [ + r#"SELECT "base"."id" AS "A_id", "base"."name" AS "A_name","#, + r#""related"."id" AS "B_id", "related"."name" AS "B_name", "related"."test_id" AS "B_test_id""#, + r#"FROM "base""#, + r#"LEFT JOIN "related" ON "base"."id" = "related"."test_id""#, + r#"ORDER BY "base"."id" ASC, "base"."name" ASC, "related"."id" ASC, "related"."name" ASC LIMIT $1"#, + ] + .join(" ") + .as_str(), + [1_u64.into()] + ),])] + ); + + Ok(()) + } + + #[smol_potat::test] + async fn related_composite_keys_2() -> Result<(), DbErr> { + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[( + test_base_entity::Model { + id: 1, + name: "CAT".into(), + }, + test_related_entity::Model { + id: 1, + name: "CATE".into(), + test_id: 1, + }, + )]]) + .into_connection(); + + assert!(!test_base_entity::Entity::find() + .find_also_related(test_related_entity::Entity) + .cursor_by((test_base_entity::Column::Id, test_base_entity::Column::Name)) + .after((1, "C".to_string())) + .first(2) + .all(&db) + .await? + .is_empty()); + + assert_eq!( + db.into_transaction_log(), + [Transaction::many([Statement::from_sql_and_values( + DbBackend::Postgres, + [ + r#"SELECT "base"."id" AS "A_id", "base"."name" AS "A_name","#, + r#""related"."id" AS "B_id", "related"."name" AS "B_name", "related"."test_id" AS "B_test_id""#, + r#"FROM "base""#, + r#"LEFT JOIN "related" ON "base"."id" = "related"."test_id""#, + r#"WHERE ("base"."id" = $1 AND "base"."name" > $2) OR "base"."id" > $3"#, + r#"ORDER BY "base"."id" ASC, "base"."name" ASC, "related"."id" ASC, "related"."name" ASC LIMIT $4"#, + ] + .join(" ") + .as_str(), + [ + 1_i32.into(), + "C".into(), + 1_i32.into(), + 2_u64.into(), + ] + ),])] + ); + + Ok(()) + } + + #[smol_potat::test] + async fn related_composite_keys_3() -> Result<(), DbErr> { + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[( + test_base_entity::Model { + id: 1, + name: "CAT".into(), + }, + test_related_entity::Model { + id: 1, + name: "CATE".into(), + test_id: 1, + }, + )]]) + .into_connection(); + + assert!(!test_base_entity::Entity::find() + .find_also_related(test_related_entity::Entity) + .cursor_by_other(( + test_related_entity::Column::Id, + test_related_entity::Column::Name + )) + .after((1, "CAT".to_string())) + .first(2) + .all(&db) + .await? + .is_empty()); + + assert_eq!( + db.into_transaction_log(), + [Transaction::many([Statement::from_sql_and_values( + DbBackend::Postgres, + [ + r#"SELECT "base"."id" AS "A_id", "base"."name" AS "A_name","#, + r#""related"."id" AS "B_id", "related"."name" AS "B_name", "related"."test_id" AS "B_test_id""#, + r#"FROM "base""#, + r#"LEFT JOIN "related" ON "base"."id" = "related"."test_id""#, + r#"WHERE ("related"."id" = $1 AND "related"."name" > $2) OR "related"."id" > $3"#, + r#"ORDER BY "related"."id" ASC, "related"."name" ASC, "base"."id" ASC, "base"."name" ASC LIMIT $4"#, + ] + .join(" ") + .as_str(), + [ + 1_i32.into(), + "CAT".into(), + 1_i32.into(), + 2_u64.into(), + ] + ),])] + ); + + Ok(()) + } } diff --git a/tests/cursor_tests.rs b/tests/cursor_tests.rs index 4d9ab91e8c..ef82805736 100644 --- a/tests/cursor_tests.rs +++ b/tests/cursor_tests.rs @@ -2,7 +2,9 @@ pub mod common; pub use common::{features::*, setup::*, TestContext}; use pretty_assertions::assert_eq; -use sea_orm::{entity::prelude::*, DerivePartialModel, FromQueryResult}; +use sea_orm::{ + entity::prelude::*, DerivePartialModel, FromQueryResult, QueryOrder, QuerySelect, Set, +}; use serde_json::json; #[sea_orm_macros::test] @@ -16,6 +18,9 @@ async fn main() -> Result<(), DbErr> { create_tables(&ctx.db).await?; create_insert_default(&ctx.db).await?; cursor_pagination(&ctx.db).await?; + schema::create_tables(&ctx.db).await?; + create_baker_cake(&ctx.db).await?; + cursor_related_pagination(&ctx.db).await?; ctx.delete().await; Ok(()) @@ -274,3 +279,260 @@ pub async fn cursor_pagination(db: &DatabaseConnection) -> Result<(), DbErr> { Ok(()) } + +use common::bakery_chain::{ + baker, bakery, cake, cakes_bakers, schema, Baker, Bakery, Cake, CakesBakers, +}; + +fn bakery(i: i32) -> bakery::Model { + bakery::Model { + name: i.to_string(), + profit_margin: 10.4, + id: i, + } +} +fn baker(c: char) -> baker::Model { + baker::Model { + name: c.clone().to_string(), + contact_details: serde_json::json!({ + "mobile": "+61424000000", + }), + bakery_id: Some((c as i32 - 65) % 10 + 1), + id: c as i32 - 64, + } +} + +#[derive(Debug, FromQueryResult, PartialEq)] +pub struct CakeBakerlite { + pub cake_name: String, + pub cake_id: i32, + pub baker_name: String, +} + +fn cakebaker(cake: char, baker: char) -> CakeBakerlite { + CakeBakerlite { + cake_name: cake.to_string(), + cake_id: cake as i32 - 96, + baker_name: baker.to_string(), + } +} + +pub async fn create_baker_cake(db: &DatabaseConnection) -> Result<(), DbErr> { + use sea_orm::IntoActiveModel; + + let mut bakeries: Vec = vec![]; + // bakeries named from 1 to 10 + for i in 1..=10 { + bakeries.push(bakery::ActiveModel { + name: Set(i.to_string()), + profit_margin: Set(10.4), + ..Default::default() + }); + } + let _ = Bakery::insert_many(bakeries).exec(db).await?; + + let mut bakers: Vec = vec![]; + let mut cakes: Vec = vec![]; + let mut cakes_bakers: Vec = vec![]; + // baker and cakes named from "A" to "Z" and from "a" to "z" + for c in 'A'..='Z' { + bakers.push(baker::ActiveModel { + name: Set(c.clone().to_string()), + contact_details: Set(serde_json::json!({ + "mobile": "+61424000000", + })), + bakery_id: Set(Some((c as i32 - 65) % 10 + 1)), + ..Default::default() + }); + cakes.push(cake::ActiveModel { + name: Set(c.to_ascii_lowercase().to_string()), + price: Set(rust_decimal_macros::dec!(10.25)), + gluten_free: Set(false), + serial: Set(Uuid::new_v4()), + bakery_id: Set(Some((c as i32 - 65) % 10 + 1)), + ..Default::default() + }); + cakes_bakers.push(cakes_bakers::ActiveModel { + cake_id: Set(c as i32 - 64), + baker_id: Set(c as i32 - 64), + }) + } + cakes_bakers.append( + vec![ + cakes_bakers::ActiveModel { + cake_id: Set(2), + baker_id: Set(1), + }, + cakes_bakers::ActiveModel { + cake_id: Set(1), + baker_id: Set(2), + }, + ] + .as_mut(), + ); + Baker::insert_many(bakers).exec(db).await?; + Cake::insert_many(cakes).exec(db).await?; + CakesBakers::insert_many(cakes_bakers).exec(db).await?; + + Ok(()) +} + +pub async fn cursor_related_pagination(db: &DatabaseConnection) -> Result<(), DbErr> { + use common::bakery_chain::*; + + assert_eq!( + bakery::Entity::find() + .cursor_by(bakery::Column::Id) + .before(5) + .first(4) + .all(db) + .await?, + [bakery(1), bakery(2), bakery(3), bakery(4),] + ); + + assert_eq!( + bakery::Entity::find() + .find_also_related(Baker) + .cursor_by(bakery::Column::Id) + .before(5) + .first(20) + .all(db) + .await?, + [ + (bakery(1), Some(baker('A'))), + (bakery(1), Some(baker('K'))), + (bakery(1), Some(baker('U'))), + (bakery(2), Some(baker('B'))), + (bakery(2), Some(baker('L'))), + (bakery(2), Some(baker('V'))), + (bakery(3), Some(baker('C'))), + (bakery(3), Some(baker('M'))), + (bakery(3), Some(baker('W'))), + (bakery(4), Some(baker('D'))), + (bakery(4), Some(baker('N'))), + (bakery(4), Some(baker('X'))), + ] + ); + + assert_eq!( + bakery::Entity::find() + .find_also_related(Baker) + .cursor_by(bakery::Column::Id) + .before(5) + .first(4) + .all(db) + .await?, + [ + (bakery(1), Some(baker('A'))), + (bakery(1), Some(baker('K'))), + (bakery(1), Some(baker('U'))), + (bakery(2), Some(baker('B'))), + ] + ); + + // since "10" is before "2" lexicologically, it return that first + assert_eq!( + bakery::Entity::find() + .find_also_related(Baker) + .cursor_by(bakery::Column::Name) + .before("3") + .first(4) + .all(db) + .await?, + [ + (bakery(1), Some(baker('A'))), + (bakery(1), Some(baker('K'))), + (bakery(1), Some(baker('U'))), + (bakery(10), Some(baker('J'))), + ] + ); + + #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] + enum QueryAs { + CakeId, + CakeName, + BakerName, + } + + assert_eq!( + cake::Entity::find() + .find_also_related(Baker) + .select_only() + .column_as(cake::Column::Id, QueryAs::CakeId) + .column_as(cake::Column::Name, QueryAs::CakeName) + .column_as(baker::Column::Name, QueryAs::BakerName) + .cursor_by(cake::Column::Name) + .before("e") + .first(4) + .clone() + .into_model::() + .all(db) + .await?, + vec![ + cakebaker('a', 'A'), + cakebaker('a', 'B'), + cakebaker('b', 'A'), + cakebaker('b', 'B') + ] + ); + + assert_eq!( + cake::Entity::find() + .find_also_related(Baker) + .select_only() + .column_as(cake::Column::Id, QueryAs::CakeId) + .column_as(cake::Column::Name, QueryAs::CakeName) + .column_as(baker::Column::Name, QueryAs::BakerName) + .cursor_by(cake::Column::Name) + .before("b") + .first(4) + .clone() + .into_model::() + .all(db) + .await?, + vec![cakebaker('a', 'A'), cakebaker('a', 'B'),] + ); + + assert_eq!( + cake::Entity::find() + .find_also_related(Baker) + .select_only() + .column_as(cake::Column::Id, QueryAs::CakeId) + .column_as(cake::Column::Name, QueryAs::CakeName) + .column_as(baker::Column::Name, QueryAs::BakerName) + .cursor_by_other(baker::Column::Name) + .before("B") + .first(4) + .clone() + .into_model::() + .all(db) + .await?, + vec![cakebaker('a', 'A'), cakebaker('b', 'A'),] + ); + + assert_eq!( + cake::Entity::find() + .find_also_related(Baker) + .select_only() + .column_as(cake::Column::Id, QueryAs::CakeId) + .column_as(cake::Column::Name, QueryAs::CakeName) + .column_as(baker::Column::Name, QueryAs::BakerName) + .cursor_by_other(baker::Column::Name) + .before("E") + .first(20) + .clone() + .into_model::() + .all(db) + .await?, + vec![ + cakebaker('a', 'A'), + cakebaker('b', 'A'), + cakebaker('a', 'B'), + cakebaker('b', 'B'), + cakebaker('c', 'C'), + cakebaker('d', 'D'), + ] + ); + + Ok(()) +} From 6eb9fb3a9f2f4209434ab41a411793dd6e0b77e3 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 22 Sep 2023 09:37:03 +0100 Subject: [PATCH 0160/1371] Changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ee5dc6882..50044992b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). * [sea-orm-migration] Check if an index exists https://github.com/SeaQL/sea-orm/pull/1828 +### Enhancements + +* [sea-orm-cli] Support generation of related entity with composite foreign key https://github.com/SeaQL/sea-orm/pull/1693 + ## 0.12.2 - 2023-08-04 ### Enhancements From a39c15db79bac8cc7957ada8b82f11c271ac73bb Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 22 Sep 2023 15:58:14 +0100 Subject: [PATCH 0161/1371] Changelog --- CHANGELOG.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50044992b1..b777cd80f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,19 @@ and this project adheres to [Semantic Versioning](http://semver.org/). * [sea-orm-cli] Support generation of related entity with composite foreign key https://github.com/SeaQL/sea-orm/pull/1693 +### Bug Fixes + +* [sea-orm-macro] Fix `DeriveValueType` by qualifying `QueryResult` https://github.com/SeaQL/sea-orm/pull/1855 + +### Upgrades + +* Upgraded `salvo` to `0.50` +* Upgraded `chrono` to `0.4.30` https://github.com/SeaQL/sea-orm/pull/1858 + +### House keeping + +* Added test cases for `find_xxx_related/linked` https://github.com/SeaQL/sea-orm/pull/1811 + ## 0.12.2 - 2023-08-04 ### Enhancements From e61b066b59a17133e11f110c8dc7f1b6fc6b1ae5 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 22 Sep 2023 17:17:01 +0100 Subject: [PATCH 0162/1371] Avoid breaking the Cursor API --- src/executor/cursor.rs | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/executor/cursor.rs b/src/executor/cursor.rs index 18031b962c..494957fa73 100644 --- a/src/executor/cursor.rs +++ b/src/executor/cursor.rs @@ -31,13 +31,8 @@ impl Cursor where S: SelectorTrait, { - /// Initialize a cursor - pub fn new( - query: SelectStatement, - table: DynIden, - order_columns: C, - secondary_order_by: Vec<(DynIden, Identity)>, - ) -> Self + /// Create a new cursor + pub fn new(query: SelectStatement, table: DynIden, order_columns: C) -> Self where C: IntoIdentity, { @@ -47,7 +42,7 @@ where order_columns: order_columns.into_identity(), last: false, phantom: PhantomData, - secondary_order_by, + secondary_order_by: Default::default(), } } @@ -205,7 +200,7 @@ where } } - for (tbl, col) in self.secondary_order_by.clone() { + for (tbl, col) in self.secondary_order_by.iter().cloned() { if let Identity::Unary(c1) = col { query.order_by((tbl, c1), ord.clone()); }; @@ -318,7 +313,7 @@ where where C: IntoIdentity, { - Cursor::new(self.query, SeaRc::new(E::default()), order_columns, vec![]) + Cursor::new(self.query, SeaRc::new(E::default()), order_columns) } } @@ -352,12 +347,13 @@ where ) }) .collect(); - Cursor::new( + let mut cursor = Cursor::new( self.query, SeaRc::new(E::default()), order_columns.identity_of(), - primary_keys, - ) + ); + cursor.set_secondary_order_by(primary_keys); + cursor } /// Convert into a cursor using column of second entity @@ -373,12 +369,13 @@ where ) }) .collect(); - Cursor::new( + let mut cursor = Cursor::new( self.query, SeaRc::new(F::default()), order_columns.identity_of(), - primary_keys, - ) + ); + cursor.set_secondary_order_by(primary_keys); + cursor } } @@ -1201,7 +1198,6 @@ mod tests { } mod test_base_entity { - use super::test_related_entity; use crate as sea_orm; use crate::entity::prelude::*; From 5a6acd67312601e4dba32896600044950e20f99f Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 22 Sep 2023 17:31:15 +0100 Subject: [PATCH 0163/1371] Fix loader panic on empty --- src/executor/cursor.rs | 1 - src/query/loader.rs | 31 +++++++++++++++++-------------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/executor/cursor.rs b/src/executor/cursor.rs index 494957fa73..50f282ef46 100644 --- a/src/executor/cursor.rs +++ b/src/executor/cursor.rs @@ -382,7 +382,6 @@ where #[cfg(test)] #[cfg(feature = "mock")] mod tests { - use super::*; use crate::entity::prelude::*; use crate::tests_cfg::*; use crate::{DbBackend, MockDatabase, Statement, Transaction}; diff --git a/src/query/loader.rs b/src/query/loader.rs index 2f3693a30b..c615b4e468 100644 --- a/src/query/loader.rs +++ b/src/query/loader.rs @@ -143,6 +143,10 @@ where return Err(query_err("Relation is HasMany instead of HasOne")); } + if self.is_empty() { + return Ok(Vec::new()); + } + let keys: Vec = self .iter() .map(|model: &M| extract_key(&rel_def.from_col, model)) @@ -194,6 +198,10 @@ where return Err(query_err("Relation is HasOne instead of HasMany")); } + if self.is_empty() { + return Ok(Vec::new()); + } + let keys: Vec = self .iter() .map(|model: &M| extract_key(&rel_def.from_col, model)) @@ -269,6 +277,10 @@ where ))); } + if self.is_empty() { + return Ok(Vec::new()); + } + let pkeys: Vec = self .iter() .map(|model: &M| extract_key(&via_rel.from_col, model)) @@ -587,14 +599,11 @@ mod tests { ); } - // FIXME: load many with empty vector will panic - // #[tokio::test] + #[tokio::test] async fn test_load_many_empty() { use sea_orm::{entity::prelude::*, tests_cfg::*, DbBackend, MockDatabase}; - let db = MockDatabase::new(DbBackend::Postgres) - .append_query_results([[fruit_model(1, Some(1)), fruit_model(2, Some(1))]]) - .into_connection(); + let db = MockDatabase::new(DbBackend::Postgres).into_connection(); let cakes: Vec = vec![]; @@ -610,9 +619,7 @@ mod tests { #[tokio::test] async fn test_load_many_to_many_base() { - use sea_orm::{ - entity::prelude::*, tests_cfg::*, DbBackend, IntoMockRow, LoaderTrait, MockDatabase, - }; + use sea_orm::{tests_cfg::*, DbBackend, IntoMockRow, LoaderTrait, MockDatabase}; let db = MockDatabase::new(DbBackend::Postgres) .append_query_results([ @@ -633,9 +640,7 @@ mod tests { #[tokio::test] async fn test_load_many_to_many_complex() { - use sea_orm::{ - entity::prelude::*, tests_cfg::*, DbBackend, IntoMockRow, LoaderTrait, MockDatabase, - }; + use sea_orm::{tests_cfg::*, DbBackend, IntoMockRow, LoaderTrait, MockDatabase}; let db = MockDatabase::new(DbBackend::Postgres) .append_query_results([ @@ -675,9 +680,7 @@ mod tests { #[tokio::test] async fn test_load_many_to_many_empty() { - use sea_orm::{ - entity::prelude::*, tests_cfg::*, DbBackend, IntoMockRow, LoaderTrait, MockDatabase, - }; + use sea_orm::{tests_cfg::*, DbBackend, IntoMockRow, LoaderTrait, MockDatabase}; let db = MockDatabase::new(DbBackend::Postgres) .append_query_results([ From ed9a437db6af4c11ab062ab9be946fd613433102 Mon Sep 17 00:00:00 2001 From: Esteban Borai Date: Fri, 22 Sep 2023 14:12:04 -0300 Subject: [PATCH 0164/1371] feat: update `linx` to be `playa` (#1853) --- COMMUNITY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/COMMUNITY.md b/COMMUNITY.md index c8c3d4ae6c..4e15ff71bc 100644 --- a/COMMUNITY.md +++ b/COMMUNITY.md @@ -39,6 +39,7 @@ If you have built an app using SeaORM and want to showcase it, feel free to open - [JinShu](https://github.com/gengteng/jinshu) | A cross-platform **I**nstant **M**essaging system written in 🦀 | DB: MySQL, Postgres - [Portfolio](https://github.com/admisio/Portfolio) | Encrypted high school 🏫 admissions service | DB: Postgres, SQLite - [THUBurrow](https://github.com/BobAnkh/THUBurrow) | A campus forum built by Next.js and Rocket | DB: Postgres +- [playa](https://github.com/whizzes/playa) | 🏖️ Decentralized Social Platform powered by Rust and Whizzes Contributors #### Bots - [bulbbot-gw](https://github.com/TeamBulbbot/bulbbot-gw) | The gateway code for the Discord bot Bulbbot | DB: Postgres @@ -69,7 +70,6 @@ If you have built an app using SeaORM and want to showcase it, feel free to open #### Url Shortener - [Dinoly](https://github.com/ippsav/Dinoly) | An url shortener using Axum web framework and SeaORM | DB: Postgres -- [Linx](https://github.com/whizzes/linx) | Headless URL Shortener written in Rust 🦀 - [SlashURL](https://github.com/henriquekirchheck/slashurl) | A url shortener using Rust designed to be implemented anywere | DB: PostgreSQL - [url_shortener](https://github.com/michidk/url_shortener) | A simple self-hosted URL shortener written in Rust | DB: MySQL, Postgres, SQLite From bb83e7a5fe42e6c7c239bc0d8dd4f3b24d3e0ac6 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 22 Sep 2023 16:31:30 +0100 Subject: [PATCH 0165/1371] Changelog --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b777cd80f4..401af97447 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## 0.12.3 - Pending +## 0.12.3 - 2023-09-22 ### New Features @@ -17,12 +17,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Bug Fixes -* [sea-orm-macro] Fix `DeriveValueType` by qualifying `QueryResult` https://github.com/SeaQL/sea-orm/pull/1855 +* [sea-orm-macro] Fixed `DeriveValueType` by qualifying `QueryResult` https://github.com/SeaQL/sea-orm/pull/1855 +* Fixed `Loader` panic on empty inputs ### Upgrades * Upgraded `salvo` to `0.50` * Upgraded `chrono` to `0.4.30` https://github.com/SeaQL/sea-orm/pull/1858 +* Updated `sea-query` to `0.30.1` ### House keeping From f9db14c68690e20773ba87bd3b7137235d6fa140 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 22 Sep 2023 16:42:29 +0100 Subject: [PATCH 0166/1371] 0.12.3 --- Cargo.toml | 4 ++-- sea-orm-cli/Cargo.toml | 4 ++-- sea-orm-codegen/Cargo.toml | 2 +- sea-orm-macros/Cargo.toml | 2 +- sea-orm-migration/Cargo.toml | 6 +++--- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 036ea2bd72..1c852a1ee8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [".", "sea-orm-macros", "sea-orm-codegen"] [package] name = "sea-orm" -version = "0.12.2" +version = "0.12.3" authors = ["Chris Tsang "] edition = "2021" description = "🐚 An async & dynamic ORM for Rust" @@ -33,7 +33,7 @@ log = { version = "0.4", default-features = false } tracing = { version = "0.1", default-features = false, features = ["attributes", "log"] } rust_decimal = { version = "1", default-features = false, optional = true } bigdecimal = { version = "0.3", default-features = false, optional = true } -sea-orm-macros = { version = "0.12.2", path = "sea-orm-macros", default-features = false, features = ["strum"] } +sea-orm-macros = { version = "0.12.3", path = "sea-orm-macros", default-features = false, features = ["strum"] } sea-query = { version = "0.30.1", default-features = false, features = ["thread-safe", "hashable-value", "backend-mysql", "backend-postgres", "backend-sqlite"] } sea-query-binder = { version = "0.5.0", default-features = false, optional = true } strum = { version = "0.25", default-features = false } diff --git a/sea-orm-cli/Cargo.toml b/sea-orm-cli/Cargo.toml index 35db446ebc..93f8fae8e5 100644 --- a/sea-orm-cli/Cargo.toml +++ b/sea-orm-cli/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "sea-orm-cli" -version = "0.12.2" +version = "0.12.3" authors = [ "Chris Tsang ", "Billy Chan ", @@ -37,7 +37,7 @@ required-features = ["cli", "codegen"] clap = { version = "4.3", features = ["env", "derive"], optional = true } dotenvy = { version = "0.15", default-features = false, optional = true } async-std = { version = "1.9", default-features = false, features = ["attributes", "tokio1"], optional = true } -sea-orm-codegen = { version = "=0.12.2", path = "../sea-orm-codegen", default-features = false, optional = true } +sea-orm-codegen = { version = "=0.12.3", path = "../sea-orm-codegen", default-features = false, optional = true } sea-schema = { version = "0.14.0" } sqlx = { version = "0.7", default-features = false, features = ["mysql", "postgres"], optional = true } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["env-filter", "fmt"] } diff --git a/sea-orm-codegen/Cargo.toml b/sea-orm-codegen/Cargo.toml index fdb2f3cb7b..2e9d94ddfa 100644 --- a/sea-orm-codegen/Cargo.toml +++ b/sea-orm-codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sea-orm-codegen" -version = "0.12.2" +version = "0.12.3" authors = ["Billy Chan "] edition = "2021" description = "Code Generator for SeaORM" diff --git a/sea-orm-macros/Cargo.toml b/sea-orm-macros/Cargo.toml index 6d55d2debb..86f031e76f 100644 --- a/sea-orm-macros/Cargo.toml +++ b/sea-orm-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sea-orm-macros" -version = "0.12.2" +version = "0.12.3" authors = [ "Billy Chan " ] edition = "2021" description = "Derive macros for SeaORM" diff --git a/sea-orm-migration/Cargo.toml b/sea-orm-migration/Cargo.toml index ae3f2c75e1..7aab07a044 100644 --- a/sea-orm-migration/Cargo.toml +++ b/sea-orm-migration/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "sea-orm-migration" -version = "0.12.2" +version = "0.12.3" authors = ["Billy Chan "] edition = "2021" description = "Migration utility for SeaORM" @@ -23,8 +23,8 @@ path = "src/lib.rs" async-trait = { version = "0.1", default-features = false } clap = { version = "4.3", features = ["env", "derive"], optional = true } dotenvy = { version = "0.15", default-features = false, optional = true } -sea-orm = { version = "0.12.2", path = "../", default-features = false, features = ["macros"] } -sea-orm-cli = { version = "0.12.2", path = "../sea-orm-cli", default-features = false, optional = true } +sea-orm = { version = "0.12.3", path = "../", default-features = false, features = ["macros"] } +sea-orm-cli = { version = "0.12.3", path = "../sea-orm-cli", default-features = false, optional = true } sea-schema = { version = "0.14.1" } tracing = { version = "0.1", default-features = false, features = ["log"] } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["env-filter", "fmt"] } From 13a74db1841095527dde4aae1a9519e76e00b813 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 22 Sep 2023 16:42:29 +0100 Subject: [PATCH 0167/1371] update examples --- examples/actix3_example/entity/Cargo.toml | 2 +- examples/actix3_example/migration/Cargo.toml | 2 +- examples/actix3_example/service/Cargo.toml | 2 +- examples/actix_example/entity/Cargo.toml | 2 +- examples/actix_example/migration/Cargo.toml | 2 +- examples/actix_example/service/Cargo.toml | 2 +- examples/axum_example/entity/Cargo.toml | 2 +- examples/axum_example/migration/Cargo.toml | 2 +- examples/axum_example/service/Cargo.toml | 2 +- examples/graphql_example/entity/Cargo.toml | 2 +- examples/graphql_example/migration/Cargo.toml | 2 +- examples/graphql_example/service/Cargo.toml | 2 +- examples/jsonrpsee_example/entity/Cargo.toml | 2 +- examples/jsonrpsee_example/migration/Cargo.toml | 2 +- examples/jsonrpsee_example/service/Cargo.toml | 2 +- examples/poem_example/entity/Cargo.toml | 2 +- examples/poem_example/migration/Cargo.toml | 2 +- examples/poem_example/service/Cargo.toml | 2 +- examples/rocket_example/entity/Cargo.toml | 2 +- examples/rocket_example/migration/Cargo.toml | 2 +- examples/rocket_example/service/Cargo.toml | 2 +- examples/rocket_okapi_example/entity/Cargo.toml | 2 +- examples/rocket_okapi_example/migration/Cargo.toml | 2 +- examples/rocket_okapi_example/service/Cargo.toml | 2 +- examples/salvo_example/entity/Cargo.toml | 2 +- examples/salvo_example/migration/Cargo.toml | 2 +- examples/salvo_example/service/Cargo.toml | 2 +- examples/seaography_example/migration/Cargo.toml | 4 ++-- examples/tonic_example/entity/Cargo.toml | 2 +- examples/tonic_example/migration/Cargo.toml | 2 +- examples/tonic_example/service/Cargo.toml | 2 +- 31 files changed, 32 insertions(+), 32 deletions(-) diff --git a/examples/actix3_example/entity/Cargo.toml b/examples/actix3_example/entity/Cargo.toml index e55661d417..135a427034 100644 --- a/examples/actix3_example/entity/Cargo.toml +++ b/examples/actix3_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.2" # sea-orm version +version = "0.12.3" # sea-orm version diff --git a/examples/actix3_example/migration/Cargo.toml b/examples/actix3_example/migration/Cargo.toml index 3d27833d5e..22ec3b73eb 100644 --- a/examples/actix3_example/migration/Cargo.toml +++ b/examples/actix3_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.2" # sea-orm-migration version +version = "0.12.3" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-async-std-native-tls", diff --git a/examples/actix3_example/service/Cargo.toml b/examples/actix3_example/service/Cargo.toml index 8f1345534e..78030c4495 100644 --- a/examples/actix3_example/service/Cargo.toml +++ b/examples/actix3_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.2" # sea-orm version +version = "0.12.3" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/actix_example/entity/Cargo.toml b/examples/actix_example/entity/Cargo.toml index e55661d417..135a427034 100644 --- a/examples/actix_example/entity/Cargo.toml +++ b/examples/actix_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.2" # sea-orm version +version = "0.12.3" # sea-orm version diff --git a/examples/actix_example/migration/Cargo.toml b/examples/actix_example/migration/Cargo.toml index 327817c4ea..50d49ff9af 100644 --- a/examples/actix_example/migration/Cargo.toml +++ b/examples/actix_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.2" # sea-orm-migration version +version = "0.12.3" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-actix-native-tls", diff --git a/examples/actix_example/service/Cargo.toml b/examples/actix_example/service/Cargo.toml index 2fa040a5c5..425a28ea17 100644 --- a/examples/actix_example/service/Cargo.toml +++ b/examples/actix_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.2" # sea-orm version +version = "0.12.3" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/axum_example/entity/Cargo.toml b/examples/axum_example/entity/Cargo.toml index e55661d417..135a427034 100644 --- a/examples/axum_example/entity/Cargo.toml +++ b/examples/axum_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.2" # sea-orm version +version = "0.12.3" # sea-orm version diff --git a/examples/axum_example/migration/Cargo.toml b/examples/axum_example/migration/Cargo.toml index 8372174a59..50e5ff7765 100644 --- a/examples/axum_example/migration/Cargo.toml +++ b/examples/axum_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.2" # sea-orm-migration version +version = "0.12.3" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/axum_example/service/Cargo.toml b/examples/axum_example/service/Cargo.toml index 6de4f140d0..e21eec5ddf 100644 --- a/examples/axum_example/service/Cargo.toml +++ b/examples/axum_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.2" # sea-orm version +version = "0.12.3" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/graphql_example/entity/Cargo.toml b/examples/graphql_example/entity/Cargo.toml index 07cb479cf7..046900b534 100644 --- a/examples/graphql_example/entity/Cargo.toml +++ b/examples/graphql_example/entity/Cargo.toml @@ -16,4 +16,4 @@ version = "5.0.10" [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.2" # sea-orm version +version = "0.12.3" # sea-orm version diff --git a/examples/graphql_example/migration/Cargo.toml b/examples/graphql_example/migration/Cargo.toml index 1f32a01cd1..61a0cc4096 100644 --- a/examples/graphql_example/migration/Cargo.toml +++ b/examples/graphql_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.2" # sea-orm-migration version +version = "0.12.3" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/graphql_example/service/Cargo.toml b/examples/graphql_example/service/Cargo.toml index 5e8d1f8091..27f688234f 100644 --- a/examples/graphql_example/service/Cargo.toml +++ b/examples/graphql_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.2" # sea-orm version +version = "0.12.3" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/jsonrpsee_example/entity/Cargo.toml b/examples/jsonrpsee_example/entity/Cargo.toml index e55661d417..135a427034 100644 --- a/examples/jsonrpsee_example/entity/Cargo.toml +++ b/examples/jsonrpsee_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.2" # sea-orm version +version = "0.12.3" # sea-orm version diff --git a/examples/jsonrpsee_example/migration/Cargo.toml b/examples/jsonrpsee_example/migration/Cargo.toml index 9130321612..d9175c65a5 100644 --- a/examples/jsonrpsee_example/migration/Cargo.toml +++ b/examples/jsonrpsee_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.2" # sea-orm-migration version +version = "0.12.3" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/jsonrpsee_example/service/Cargo.toml b/examples/jsonrpsee_example/service/Cargo.toml index c38b350dfc..898b645706 100644 --- a/examples/jsonrpsee_example/service/Cargo.toml +++ b/examples/jsonrpsee_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.2" # sea-orm version +version = "0.12.3" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/poem_example/entity/Cargo.toml b/examples/poem_example/entity/Cargo.toml index e55661d417..135a427034 100644 --- a/examples/poem_example/entity/Cargo.toml +++ b/examples/poem_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.2" # sea-orm version +version = "0.12.3" # sea-orm version diff --git a/examples/poem_example/migration/Cargo.toml b/examples/poem_example/migration/Cargo.toml index 9130321612..d9175c65a5 100644 --- a/examples/poem_example/migration/Cargo.toml +++ b/examples/poem_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.2" # sea-orm-migration version +version = "0.12.3" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/poem_example/service/Cargo.toml b/examples/poem_example/service/Cargo.toml index 1ac5bbc688..2404bae56a 100644 --- a/examples/poem_example/service/Cargo.toml +++ b/examples/poem_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.2" # sea-orm version +version = "0.12.3" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/rocket_example/entity/Cargo.toml b/examples/rocket_example/entity/Cargo.toml index 564b70e94f..72195509dd 100644 --- a/examples/rocket_example/entity/Cargo.toml +++ b/examples/rocket_example/entity/Cargo.toml @@ -15,4 +15,4 @@ rocket = { version = "0.5.0-rc.1", features = [ [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.2" # sea-orm version +version = "0.12.3" # sea-orm version diff --git a/examples/rocket_example/migration/Cargo.toml b/examples/rocket_example/migration/Cargo.toml index d1110bcea4..7ae7c3ecae 100644 --- a/examples/rocket_example/migration/Cargo.toml +++ b/examples/rocket_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.2" # sea-orm-migration version +version = "0.12.3" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/rocket_example/service/Cargo.toml b/examples/rocket_example/service/Cargo.toml index 8829abdfbe..078f2aa636 100644 --- a/examples/rocket_example/service/Cargo.toml +++ b/examples/rocket_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.2" # sea-orm version +version = "0.12.3" # sea-orm version features = [ "runtime-tokio-native-tls", "sqlx-postgres", diff --git a/examples/rocket_okapi_example/entity/Cargo.toml b/examples/rocket_okapi_example/entity/Cargo.toml index d7246597d3..bc9075f952 100644 --- a/examples/rocket_okapi_example/entity/Cargo.toml +++ b/examples/rocket_okapi_example/entity/Cargo.toml @@ -15,7 +15,7 @@ rocket = { version = "0.5.0-rc.1", features = [ [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.2" # sea-orm version +version = "0.12.3" # sea-orm version [dependencies.rocket_okapi] version = "0.8.0-rc.2" diff --git a/examples/rocket_okapi_example/migration/Cargo.toml b/examples/rocket_okapi_example/migration/Cargo.toml index d1110bcea4..7ae7c3ecae 100644 --- a/examples/rocket_okapi_example/migration/Cargo.toml +++ b/examples/rocket_okapi_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.2" # sea-orm-migration version +version = "0.12.3" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/rocket_okapi_example/service/Cargo.toml b/examples/rocket_okapi_example/service/Cargo.toml index 8829abdfbe..078f2aa636 100644 --- a/examples/rocket_okapi_example/service/Cargo.toml +++ b/examples/rocket_okapi_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.2" # sea-orm version +version = "0.12.3" # sea-orm version features = [ "runtime-tokio-native-tls", "sqlx-postgres", diff --git a/examples/salvo_example/entity/Cargo.toml b/examples/salvo_example/entity/Cargo.toml index d2ee0d6a63..4d512f8d57 100644 --- a/examples/salvo_example/entity/Cargo.toml +++ b/examples/salvo_example/entity/Cargo.toml @@ -14,4 +14,4 @@ salvo = { version = "0.50" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.2" # sea-orm version +version = "0.12.3" # sea-orm version diff --git a/examples/salvo_example/migration/Cargo.toml b/examples/salvo_example/migration/Cargo.toml index 9130321612..d9175c65a5 100644 --- a/examples/salvo_example/migration/Cargo.toml +++ b/examples/salvo_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.2" # sea-orm-migration version +version = "0.12.3" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/salvo_example/service/Cargo.toml b/examples/salvo_example/service/Cargo.toml index 7774385f11..adba027880 100644 --- a/examples/salvo_example/service/Cargo.toml +++ b/examples/salvo_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.2" # sea-orm version +version = "0.12.3" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/seaography_example/migration/Cargo.toml b/examples/seaography_example/migration/Cargo.toml index 51c968f0cf..53f36d874d 100644 --- a/examples/seaography_example/migration/Cargo.toml +++ b/examples/seaography_example/migration/Cargo.toml @@ -15,11 +15,11 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm] path = "../../.." # remove this line in your own project -version = "0.12.2" # sea-orm version +version = "0.12.3" # sea-orm version [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.2" # sea-orm-migration version +version = "0.12.3" # sea-orm-migration version features = [ "runtime-async-std-native-tls", "sqlx-mysql", diff --git a/examples/tonic_example/entity/Cargo.toml b/examples/tonic_example/entity/Cargo.toml index e55661d417..135a427034 100644 --- a/examples/tonic_example/entity/Cargo.toml +++ b/examples/tonic_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.2" # sea-orm version +version = "0.12.3" # sea-orm version diff --git a/examples/tonic_example/migration/Cargo.toml b/examples/tonic_example/migration/Cargo.toml index 2cb5e7e7dd..d309c89ae2 100644 --- a/examples/tonic_example/migration/Cargo.toml +++ b/examples/tonic_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.2" # sea-orm-migration version +version = "0.12.3" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-rustls", diff --git a/examples/tonic_example/service/Cargo.toml b/examples/tonic_example/service/Cargo.toml index da456d6b51..eff70782e4 100644 --- a/examples/tonic_example/service/Cargo.toml +++ b/examples/tonic_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.2" # sea-orm version +version = "0.12.3" # sea-orm version features = [ "debug-print", "runtime-tokio-rustls", From ae77f943d24effa5409a0d1b179db9cd47400db3 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 22 Sep 2023 18:03:05 +0100 Subject: [PATCH 0168/1371] Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 401af97447..edbe451544 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). * Upgraded `salvo` to `0.50` * Upgraded `chrono` to `0.4.30` https://github.com/SeaQL/sea-orm/pull/1858 * Updated `sea-query` to `0.30.1` +* Updated `sea-schema` to `0.14.1` ### House keeping From 36e2f14d7952559ae66b6fedad1e17e5bb4ba93c Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sat, 23 Sep 2023 10:13:20 +0100 Subject: [PATCH 0169/1371] Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index edbe451544..b175735c1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### New Features * [sea-orm-migration] Check if an index exists https://github.com/SeaQL/sea-orm/pull/1828 +* Added `cursor_by` to `SelectTwo` https://github.com/SeaQL/sea-orm/pull/1826 ### Enhancements From 17c943d84e85137fa8c2d7d96a94868cc3a3ce0a Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sun, 24 Sep 2023 23:03:14 +0800 Subject: [PATCH 0170/1371] Loader: use ValueTuple as hash key (#1868) --- Cargo.toml | 2 +- src/query/loader.rs | 46 +++++++++++++++++---------------------------- 2 files changed, 18 insertions(+), 30 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1c852a1ee8..ed33aea241 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ tracing = { version = "0.1", default-features = false, features = ["attributes", rust_decimal = { version = "1", default-features = false, optional = true } bigdecimal = { version = "0.3", default-features = false, optional = true } sea-orm-macros = { version = "0.12.3", path = "sea-orm-macros", default-features = false, features = ["strum"] } -sea-query = { version = "0.30.1", default-features = false, features = ["thread-safe", "hashable-value", "backend-mysql", "backend-postgres", "backend-sqlite"] } +sea-query = { version = "0.30.2", default-features = false, features = ["thread-safe", "hashable-value", "backend-mysql", "backend-postgres", "backend-sqlite"] } sea-query-binder = { version = "0.5.0", default-features = false, optional = true } strum = { version = "0.25", default-features = false } serde = { version = "1.0", default-features = false } diff --git a/src/query/loader.rs b/src/query/loader.rs index c615b4e468..e4e27aa2df 100644 --- a/src/query/loader.rs +++ b/src/query/loader.rs @@ -158,24 +158,20 @@ where let data = stmt.all(db).await?; - let hashmap: HashMap::Model> = data.into_iter().fold( - HashMap::::Model>::new(), - |mut acc: HashMap::Model>, - value: ::Model| { + let hashmap: HashMap::Model> = data.into_iter().fold( + HashMap::new(), + |mut acc, value: ::Model| { { let key = extract_key(&rel_def.to_col, &value); - - acc.insert(format!("{key:?}"), value); + acc.insert(key, value); } acc }, ); - let result: Vec::Model>> = keys - .iter() - .map(|key| hashmap.get(&format!("{key:?}")).cloned()) - .collect(); + let result: Vec::Model>> = + keys.iter().map(|key| hashmap.get(key).cloned()).collect(); Ok(result) } @@ -213,11 +209,10 @@ where let data = stmt.all(db).await?; - let mut hashmap: HashMap::Model>> = + let mut hashmap: HashMap::Model>> = keys.iter() .fold(HashMap::new(), |mut acc, key: &ValueTuple| { - acc.insert(format!("{key:?}"), Vec::new()); - + acc.insert(key.clone(), Vec::new()); acc }); @@ -226,7 +221,7 @@ where let key = extract_key(&rel_def.to_col, &value); let vec = hashmap - .get_mut(&format!("{key:?}")) + .get_mut(&key) .expect("Failed at finding key on hashmap"); vec.push(value); @@ -234,12 +229,7 @@ where let result: Vec> = keys .iter() - .map(|key: &ValueTuple| { - hashmap - .get(&format!("{key:?}")) - .cloned() - .unwrap_or_default() - }) + .map(|key: &ValueTuple| hashmap.get(key).cloned().unwrap_or_default()) .collect(); Ok(result) @@ -287,14 +277,14 @@ where .collect(); // Map of M::PK -> Vec - let mut keymap: HashMap> = Default::default(); + let mut keymap: HashMap> = Default::default(); let keys: Vec = { let condition = prepare_condition(&via_rel.to_tbl, &via_rel.to_col, &pkeys); let stmt = V::find().filter(condition); let data = stmt.all(db).await?; data.into_iter().for_each(|model| { - let pk = format!("{:?}", extract_key(&via_rel.to_col, &model)); + let pk = extract_key(&via_rel.to_col, &model); let entry = keymap.entry(pk).or_default(); let fk = extract_key(&rel_def.from_col, &model); @@ -309,11 +299,12 @@ where let stmt = as QueryFilter>::filter(stmt.select(), condition); let data = stmt.all(db).await?; + // Map of R::PK -> R::Model - let data: HashMap::Model> = data + let data: HashMap::Model> = data .into_iter() .map(|model| { - let key = format!("{:?}", extract_key(&rel_def.to_col, &model)); + let key = extract_key(&rel_def.to_col, &model); (key, model) }) .collect(); @@ -321,14 +312,11 @@ where let result: Vec> = pkeys .into_iter() .map(|pkey| { - let fkeys = keymap - .get(&format!("{pkey:?}")) - .cloned() - .unwrap_or_default(); + let fkeys = keymap.get(&pkey).cloned().unwrap_or_default(); let models: Vec<_> = fkeys .into_iter() - .filter_map(|fkey| data.get(&format!("{fkey:?}")).cloned()) + .filter_map(|fkey| data.get(&fkey).cloned()) .collect(); models From 2a961c6a525f13bbbdc00d2df374f289981fe12e Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sun, 24 Sep 2023 16:04:38 +0100 Subject: [PATCH 0171/1371] Changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b175735c1a..1d9d39c230 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## 0.12.4 - 2023-09-24 + +### Enhancements + +* Loader: use `ValueTuple` as hash key https://github.com/SeaQL/sea-orm/pull/1868 + ## 0.12.3 - 2023-09-22 ### New Features From bb5c21fc3982b9bae5075673e9cf3e87fc95411e Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sun, 24 Sep 2023 16:06:21 +0100 Subject: [PATCH 0172/1371] Changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d9d39c230..fd85fa654c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## 0.12.4 - 2023-09-24 +## 0.12.4 - Pending ### Enhancements From 900b4106bb4854f3105c3df342348c57498b9560 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sun, 24 Sep 2023 21:20:22 +0100 Subject: [PATCH 0173/1371] Changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd85fa654c..64d17da826 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). * Loader: use `ValueTuple` as hash key https://github.com/SeaQL/sea-orm/pull/1868 +### Upgrades + +* Updated `sea-query` to `0.30.2` + ## 0.12.3 - 2023-09-22 ### New Features From 0f6a48baa4d8b02ebc8ead1ba80baed1d9b7d55b Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Wed, 4 Oct 2023 21:30:50 +0100 Subject: [PATCH 0174/1371] Rework ActiveEnum: remove the reliance on ValueVec --- src/entity/active_enum.rs | 62 ++++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/src/entity/active_enum.rs b/src/entity/active_enum.rs index 6ac81f1128..5f48f774b0 100644 --- a/src/entity/active_enum.rs +++ b/src/entity/active_enum.rs @@ -1,4 +1,4 @@ -use crate::{ColumnDef, DbErr, Iterable, QueryResult, TryFromU64, TryGetError, TryGetable}; +use crate::{ColIdx, ColumnDef, DbErr, Iterable, QueryResult, TryFromU64, TryGetError, TryGetable}; use sea_query::{DynIden, Expr, Nullable, SimpleExpr, Value, ValueType}; /// A Rust representation of enum defined in database. @@ -110,11 +110,11 @@ use sea_query::{DynIden, Expr, Nullable, SimpleExpr, Value, ValueType}; /// impl ActiveModelBehavior for ActiveModel {} /// ``` pub trait ActiveEnum: Sized + Iterable { - /// Define the Rust type that each enum variant represents. - type Value: Into + ValueType + Nullable + TryGetable; + /// Define the Rust type that each enum variant corresponds. + type Value: ActiveEnumValue; - /// Define the enum value in Vector type. - type ValueVec: IntoIterator; + /// This has no purpose. It will be removed in the next major version. + type ValueVec; /// Get the name of enum fn name() -> DynIden; @@ -144,13 +144,59 @@ pub trait ActiveEnum: Sized + Iterable { } } +/// The Rust Value backing ActiveEnums +pub trait ActiveEnumValue: Into + ValueType + Nullable + TryGetable { + /// For getting an array of enum. Postgres only + fn try_get_vec_by(res: &QueryResult, index: I) -> Result, TryGetError>; +} + +macro_rules! impl_active_enum_value { + ($type:ident) => { + impl ActiveEnumValue for $type { + fn try_get_vec_by( + _res: &QueryResult, + _index: I, + ) -> Result, TryGetError> { + panic!("Not supported by `postgres-array`") + } + } + }; +} + +macro_rules! impl_active_enum_value_with_pg_array { + ($type:ident) => { + impl ActiveEnumValue for $type { + fn try_get_vec_by( + _res: &QueryResult, + _index: I, + ) -> Result, TryGetError> { + #[cfg(feature = "postgres-array")] + { + >::try_get_by(_res, _index) + } + #[cfg(not(feature = "postgres-array"))] + panic!("`postgres-array` is not enabled") + } + } + }; +} + +impl_active_enum_value!(u8); +impl_active_enum_value!(u16); +impl_active_enum_value!(u32); +impl_active_enum_value!(u64); +impl_active_enum_value_with_pg_array!(String); +impl_active_enum_value_with_pg_array!(i8); +impl_active_enum_value_with_pg_array!(i16); +impl_active_enum_value_with_pg_array!(i32); +impl_active_enum_value_with_pg_array!(i64); + impl TryGetable for Vec where T: ActiveEnum, - T::ValueVec: TryGetable, { - fn try_get_by(res: &QueryResult, index: I) -> Result { - ::try_get_by(res, index)? + fn try_get_by(res: &QueryResult, index: I) -> Result { + ::try_get_vec_by(res, index)? .into_iter() .map(|value| T::try_from_value(&value).map_err(Into::into)) .collect() From b2cb51380ae9a3c8fe4d57613a04c2428395c617 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Wed, 4 Oct 2023 03:50:01 +0100 Subject: [PATCH 0175/1371] Introduce TryGetableArray --- sea-orm-macros/src/derives/active_enum.rs | 10 +++++ src/entity/active_enum.rs | 12 ------ src/executor/query.rs | 45 +++++++++++++++++++++++ 3 files changed, 55 insertions(+), 12 deletions(-) diff --git a/sea-orm-macros/src/derives/active_enum.rs b/sea-orm-macros/src/derives/active_enum.rs index 591da6f331..db4d5f6e41 100644 --- a/sea-orm-macros/src/derives/active_enum.rs +++ b/sea-orm-macros/src/derives/active_enum.rs @@ -337,6 +337,16 @@ impl ActiveEnum { } } + #[automatically_derived] + impl sea_orm::TryGetableArray for #ident { + fn try_get_by(res: &sea_orm::QueryResult, index: I) -> std::result::Result, sea_orm::TryGetError> { + <::Value as sea_orm::ActiveEnumValue>::try_get_vec_by(res, index)? + .into_iter() + .map(|value| Self::try_from_value(&value).map_err(Into::into)) + .collect() + } + } + #[automatically_derived] #[allow(clippy::from_over_into)] impl Into for #ident { diff --git a/src/entity/active_enum.rs b/src/entity/active_enum.rs index 5f48f774b0..bb8df73584 100644 --- a/src/entity/active_enum.rs +++ b/src/entity/active_enum.rs @@ -191,18 +191,6 @@ impl_active_enum_value_with_pg_array!(i16); impl_active_enum_value_with_pg_array!(i32); impl_active_enum_value_with_pg_array!(i64); -impl TryGetable for Vec -where - T: ActiveEnum, -{ - fn try_get_by(res: &QueryResult, index: I) -> Result { - ::try_get_vec_by(res, index)? - .into_iter() - .map(|value| T::try_from_value(&value).map_err(Into::into)) - .collect() - } -} - impl TryFromU64 for T where T: ActiveEnum, diff --git a/src/executor/query.rs b/src/executor/query.rs index 28093822b9..95ec29c42c 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -960,6 +960,25 @@ fn try_get_many_with_slice_len_of(len: usize, cols: &[String]) -> Result<(), Try } } +/// An interface to get an array of values from the query result. +/// A type can only implement `ActiveEnum` or `TryGetableFromJson`, but not both. +/// A blanket impl is provided for `TryGetableFromJson`, while the impl for `ActiveEnum` +/// is provided by the `DeriveActiveEnum` macro. So as an end user you won't normally +/// touch this trait. +pub trait TryGetableArray: Sized { + /// Just a delegate + fn try_get_by(res: &QueryResult, index: I) -> Result, TryGetError>; +} + +impl TryGetable for Vec +where + T: TryGetableArray, +{ + fn try_get_by(res: &QueryResult, index: I) -> Result { + T::try_get_by(res, index) + } +} + // TryGetableFromJson // /// An interface to get a JSON from the query result @@ -999,6 +1018,22 @@ where _ => unreachable!(), } } + + /// Get a Vec from an Array of Json + fn from_json_vec(value: serde_json::Value) -> Result, TryGetError> { + match value { + serde_json::Value::Array(values) => { + let mut res = Vec::new(); + for item in values { + res.push(serde_json::from_value(item).map_err(json_err)?); + } + Ok(res) + } + _ => Err(TryGetError::DbErr(DbErr::Json( + "Value is not an Array".to_owned(), + ))), + } + } } #[cfg(feature = "with-json")] @@ -1011,6 +1046,16 @@ where } } +#[cfg(feature = "with-json")] +impl TryGetableArray for T +where + T: TryGetableFromJson, +{ + fn try_get_by(res: &QueryResult, index: I) -> Result, TryGetError> { + T::from_json_vec(serde_json::Value::try_get_by(res, index)?) + } +} + // TryFromU64 // /// Try to convert a type to a u64 pub trait TryFromU64: Sized { From 201d6fe9f98a8da550d64378e2c16d5366bb4193 Mon Sep 17 00:00:00 2001 From: Anshul Sanghi Date: Thu, 12 Oct 2023 13:53:29 +0530 Subject: [PATCH 0176/1371] Add Tests And Fix Build Issue For "Rework active enum" PR (#1900) * Add Tests For Root JSON Arrays And Active Enum Vectors * Fix Build Issue When Using `DeriveActiveEnum` --- sea-orm-macros/src/derives/active_enum.rs | 2 +- tests/active_enum_tests.rs | 71 +++++++++++++++++++++++ tests/common/features/active_enum_vec.rs | 16 +++++ tests/common/features/categories.rs | 15 +++++ tests/common/features/json_vec_derive.rs | 60 ++++++++++++++----- tests/common/features/mod.rs | 5 ++ tests/common/features/schema.rs | 43 +++++++++++++- tests/json_vec_tests.rs | 49 ++++++++++++---- 8 files changed, 232 insertions(+), 29 deletions(-) create mode 100644 tests/common/features/active_enum_vec.rs create mode 100644 tests/common/features/categories.rs diff --git a/sea-orm-macros/src/derives/active_enum.rs b/sea-orm-macros/src/derives/active_enum.rs index db4d5f6e41..f163275f29 100644 --- a/sea-orm-macros/src/derives/active_enum.rs +++ b/sea-orm-macros/src/derives/active_enum.rs @@ -342,7 +342,7 @@ impl ActiveEnum { fn try_get_by(res: &sea_orm::QueryResult, index: I) -> std::result::Result, sea_orm::TryGetError> { <::Value as sea_orm::ActiveEnumValue>::try_get_vec_by(res, index)? .into_iter() - .map(|value| Self::try_from_value(&value).map_err(Into::into)) + .map(|value| ::try_from_value(&value).map_err(Into::into)) .collect() } } diff --git a/tests/active_enum_tests.rs b/tests/active_enum_tests.rs index e1aff3f983..843d51058e 100644 --- a/tests/active_enum_tests.rs +++ b/tests/active_enum_tests.rs @@ -22,6 +22,11 @@ async fn main() -> Result<(), DbErr> { create_tables(&ctx.db).await?; insert_active_enum(&ctx.db).await?; insert_active_enum_child(&ctx.db).await?; + + if cfg!(feature = "sqlx-postgres") { + insert_active_enum_vec(&ctx.db).await?; + } + find_related_active_enum(&ctx.db).await?; find_linked_active_enum(&ctx.db).await?; ctx.delete().await; @@ -205,6 +210,72 @@ pub async fn insert_active_enum_child(db: &DatabaseConnection) -> Result<(), DbE Ok(()) } +pub async fn insert_active_enum_vec(db: &DatabaseConnection) -> Result<(), DbErr> { + use categories::*; + + let model = Model { + id: 1, + categories: None, + }; + + assert_eq!( + model, + ActiveModel { + id: Set(1), + categories: Set(None), + ..Default::default() + } + .insert(db) + .await? + ); + assert_eq!(model, Entity::find().one(db).await?.unwrap()); + assert_eq!( + model, + Entity::find() + .filter(Column::Id.is_not_null()) + .filter(Column::Categories.is_null()) + .one(db) + .await? + .unwrap() + ); + + let _ = ActiveModel { + id: Set(1), + categories: Set(Some(vec![Category::Big, Category::Small])), + ..model.into_active_model() + } + .save(db) + .await?; + + let model = Entity::find().one(db).await?.unwrap(); + assert_eq!( + model, + Model { + id: 1, + categories: Some(vec![Category::Big, Category::Small]), + } + ); + assert_eq!( + model, + Entity::find() + .filter(Column::Id.eq(1)) + .filter(Expr::cust_with_values( + r#"$1 = ANY("categories")"#, + vec![Category::Big] + )) + .one(db) + .await? + .unwrap() + ); + + let res = model.delete(db).await?; + + assert_eq!(res.rows_affected, 1); + assert_eq!(Entity::find().one(db).await?, None); + + Ok(()) +} + pub async fn find_related_active_enum(db: &DatabaseConnection) -> Result<(), DbErr> { assert_eq!( active_enum::Model { diff --git a/tests/common/features/active_enum_vec.rs b/tests/common/features/active_enum_vec.rs new file mode 100644 index 0000000000..66bda0de39 --- /dev/null +++ b/tests/common/features/active_enum_vec.rs @@ -0,0 +1,16 @@ +use super::sea_orm_active_enums::*; +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] +#[cfg_attr(feature = "sqlx-postgres", sea_orm(schema_name = "public"))] +#[sea_orm(table_name = "active_enum")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub categories: Option>, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/tests/common/features/categories.rs b/tests/common/features/categories.rs new file mode 100644 index 0000000000..00f25674d9 --- /dev/null +++ b/tests/common/features/categories.rs @@ -0,0 +1,15 @@ +use super::sea_orm_active_enums::*; +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] +#[sea_orm(table_name = "categories")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: i32, + pub categories: Option>, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/tests/common/features/json_vec_derive.rs b/tests/common/features/json_vec_derive.rs index cc6e177b7a..dcf16a739e 100644 --- a/tests/common/features/json_vec_derive.rs +++ b/tests/common/features/json_vec_derive.rs @@ -1,19 +1,49 @@ -use sea_orm::entity::prelude::*; -use sea_orm::FromJsonQueryResult; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] -#[sea_orm(table_name = "json_vec")] -pub struct Model { - #[sea_orm(primary_key)] - pub id: i32, - pub str_vec: Option, +pub mod json_string_vec { + use sea_orm::entity::prelude::*; + use sea_orm::FromJsonQueryResult; + use serde::{Deserialize, Serialize}; + + #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] + #[sea_orm(table_name = "json_vec")] + pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub str_vec: Option, + } + + #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] + pub enum Relation {} + + impl ActiveModelBehavior for ActiveModel {} + + #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromJsonQueryResult)] + pub struct StringVec(pub Vec); } -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation {} +pub mod json_struct_vec { + use sea_orm::entity::prelude::*; + use sea_orm_macros::FromJsonQueryResult; + use sea_query::with_array::NotU8; + use serde::{Deserialize, Serialize}; -impl ActiveModelBehavior for ActiveModel {} + #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromJsonQueryResult)] + pub struct JsonColumn { + pub value: String, + } -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromJsonQueryResult)] -pub struct StringVec(pub Vec); + impl NotU8 for JsonColumn {} + + #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] + #[sea_orm(table_name = "json_struct_vec")] + pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + #[sea_orm(column_type = "JsonBinary")] + pub struct_vec: Vec, + } + + #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] + pub enum Relation {} + + impl ActiveModelBehavior for ActiveModel {} +} diff --git a/tests/common/features/mod.rs b/tests/common/features/mod.rs index 84a6f7f223..f60950f869 100644 --- a/tests/common/features/mod.rs +++ b/tests/common/features/mod.rs @@ -1,9 +1,11 @@ pub mod active_enum; pub mod active_enum_child; +pub mod active_enum_vec; pub mod applog; pub mod binary; pub mod bits; pub mod byte_primary_key; +pub mod categories; pub mod collection; pub mod collection_expanded; pub mod custom_active_model; @@ -28,10 +30,12 @@ pub mod value_type; pub use active_enum::Entity as ActiveEnum; pub use active_enum_child::Entity as ActiveEnumChild; +pub use active_enum_vec::Entity as ActiveEnumVec; pub use applog::Entity as Applog; pub use binary::Entity as Binary; pub use bits::Entity as Bits; pub use byte_primary_key::Entity as BytePrimaryKey; +pub use categories::Entity as Categories; pub use collection::Entity as Collection; pub use collection_expanded::Entity as CollectionExpanded; pub use dyn_table_name_lazy_static::Entity as DynTableNameLazyStatic; @@ -40,6 +44,7 @@ pub use event_trigger::Entity as EventTrigger; pub use insert_default::Entity as InsertDefault; pub use json_struct::Entity as JsonStruct; pub use json_vec::Entity as JsonVec; +pub use json_vec_derive::json_struct_vec::Entity as JsonStructVec; pub use metadata::Entity as Metadata; pub use pi::Entity as Pi; pub use repository::Entity as Repository; diff --git a/tests/common/features/schema.rs b/tests/common/features/schema.rs index 6e1f71cc01..dbb5551df1 100644 --- a/tests/common/features/schema.rs +++ b/tests/common/features/schema.rs @@ -1,11 +1,13 @@ use super::*; +use crate::common::features::json_vec_derive::json_struct_vec; use crate::common::setup::{create_enum, create_table, create_table_without_asserts}; use sea_orm::{ error::*, sea_query, ConnectionTrait, DatabaseConnection, DbBackend, DbConn, EntityName, ExecResult, Schema, }; use sea_query::{ - extension::postgres::Type, Alias, BlobSize, ColumnDef, ForeignKeyCreateStatement, IntoIden, + extension::postgres::Type, Alias, BlobSize, ColumnDef, ColumnType, ForeignKeyCreateStatement, + IntoIden, }; pub async fn create_tables(db: &DatabaseConnection) -> Result<(), DbErr> { @@ -18,7 +20,6 @@ pub async fn create_tables(db: &DatabaseConnection) -> Result<(), DbErr> { create_byte_primary_key_table(db).await?; create_satellites_table(db).await?; create_transaction_log_table(db).await?; - create_json_vec_table(db).await?; create_json_struct_table(db).await?; let create_enum_stmts = match db_backend { @@ -54,6 +55,9 @@ pub async fn create_tables(db: &DatabaseConnection) -> Result<(), DbErr> { create_value_type_postgres_table(db).await?; create_collection_table(db).await?; create_event_trigger_table(db).await?; + create_json_vec_table(db).await?; + create_json_struct_vec_table(db).await?; + create_categories_table(db).await?; } Ok(()) @@ -319,6 +323,26 @@ pub async fn create_json_vec_table(db: &DbConn) -> Result { create_table(db, &create_table_stmt, JsonVec).await } +pub async fn create_json_struct_vec_table(db: &DbConn) -> Result { + let create_table_stmt = sea_query::Table::create() + .table(json_struct_vec::Entity.table_ref()) + .col( + ColumnDef::new(json_struct_vec::Column::Id) + .integer() + .not_null() + .auto_increment() + .primary_key(), + ) + .col( + ColumnDef::new(json_struct_vec::Column::StructVec) + .json_binary() + .not_null(), + ) + .to_owned(); + + create_table(db, &create_table_stmt, JsonStructVec).await +} + pub async fn create_json_struct_table(db: &DbConn) -> Result { let stmt = sea_query::Table::create() .table(json_struct::Entity) @@ -521,6 +545,21 @@ pub async fn create_teas_table(db: &DbConn) -> Result { create_table(db, &create_table_stmt, Teas).await } +pub async fn create_categories_table(db: &DbConn) -> Result { + let create_table_stmt = sea_query::Table::create() + .table(categories::Entity.table_ref()) + .col( + ColumnDef::new(categories::Column::Id) + .integer() + .not_null() + .primary_key(), + ) + .col(ColumnDef::new(categories::Column::Categories).array(ColumnType::String(Some(1)))) + .to_owned(); + + create_table(db, &create_table_stmt, Categories).await +} + pub async fn create_binary_table(db: &DbConn) -> Result { let create_table_stmt = sea_query::Table::create() .table(binary::Entity.table_ref()) diff --git a/tests/json_vec_tests.rs b/tests/json_vec_tests.rs index 9af2a33046..ab5bab2ee4 100644 --- a/tests/json_vec_tests.rs +++ b/tests/json_vec_tests.rs @@ -5,16 +5,14 @@ use pretty_assertions::assert_eq; use sea_orm::{entity::prelude::*, entity::*, DatabaseConnection}; #[sea_orm_macros::test] -#[cfg(any( - feature = "sqlx-mysql", - feature = "sqlx-sqlite", - feature = "sqlx-postgres" -))] +#[cfg(feature = "sqlx-postgres")] async fn main() -> Result<(), DbErr> { let ctx = TestContext::new("json_vec_tests").await; create_tables(&ctx.db).await?; insert_json_vec(&ctx.db).await?; - insert_json_vec_derive(&ctx.db).await?; + + insert_json_string_vec_derive(&ctx.db).await?; + ctx.delete().await; Ok(()) @@ -44,10 +42,10 @@ pub async fn insert_json_vec(db: &DatabaseConnection) -> Result<(), DbErr> { Ok(()) } -pub async fn insert_json_vec_derive(db: &DatabaseConnection) -> Result<(), DbErr> { - let json_vec = json_vec_derive::Model { +pub async fn insert_json_string_vec_derive(db: &DatabaseConnection) -> Result<(), DbErr> { + let json_vec = json_vec_derive::json_string_vec::Model { id: 2, - str_vec: Some(json_vec_derive::StringVec(vec![ + str_vec: Some(json_vec_derive::json_string_vec::StringVec(vec![ "4".to_string(), "5".to_string(), "6".to_string(), @@ -58,8 +56,37 @@ pub async fn insert_json_vec_derive(db: &DatabaseConnection) -> Result<(), DbErr assert_eq!(result, json_vec); - let model = json_vec_derive::Entity::find() - .filter(json_vec_derive::Column::Id.eq(json_vec.id)) + let model = json_vec_derive::json_string_vec::Entity::find() + .filter(json_vec_derive::json_string_vec::Column::Id.eq(json_vec.id)) + .one(db) + .await?; + + assert_eq!(model, Some(json_vec)); + + Ok(()) +} + +pub async fn insert_json_struct_vec_derive(db: &DatabaseConnection) -> Result<(), DbErr> { + let json_vec = json_vec_derive::json_struct_vec::Model { + id: 2, + struct_vec: vec![ + json_vec_derive::json_struct_vec::JsonColumn { + value: "4".to_string(), + }, + json_vec_derive::json_struct_vec::JsonColumn { + value: "5".to_string(), + }, + json_vec_derive::json_struct_vec::JsonColumn { + value: "6".to_string(), + }, + ], + }; + + let result = json_vec.clone().into_active_model().insert(db).await?; + assert_eq!(result, json_vec); + + let model = json_vec_derive::json_struct_vec::Entity::find() + .filter(json_vec_derive::json_struct_vec::Column::Id.eq(json_vec.id)) .one(db) .await?; From d29ed5b9bba9d8faada227776b4abe15286a1427 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 12 Oct 2023 13:52:05 +0100 Subject: [PATCH 0177/1371] Avoid repeating this 100 times --- tests/common/features/self_join.rs | 47 ------------------------------ tests/self_join_tests.rs | 46 ++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 48 deletions(-) diff --git a/tests/common/features/self_join.rs b/tests/common/features/self_join.rs index 933eff1584..a3c8091585 100644 --- a/tests/common/features/self_join.rs +++ b/tests/common/features/self_join.rs @@ -28,50 +28,3 @@ impl Linked for SelfReferencingLink { } impl ActiveModelBehavior for ActiveModel {} - -#[cfg(test)] -mod tests { - use super::*; - use pretty_assertions::assert_eq; - use sea_orm::*; - - #[test] - fn find_linked_001() { - let self_join_model = Model { - uuid: Uuid::default(), - uuid_ref: None, - time: None, - }; - - assert_eq!( - self_join_model - .find_linked(SelfReferencingLink) - .build(DbBackend::MySql) - .to_string(), - [ - r#"SELECT `self_join`.`uuid`, `self_join`.`uuid_ref`, `self_join`.`time`"#, - r#"FROM `self_join`"#, - r#"INNER JOIN `self_join` AS `r0` ON `r0`.`uuid_ref` = `self_join`.`uuid`"#, - r#"WHERE `r0`.`uuid` = '00000000-0000-0000-0000-000000000000'"#, - ] - .join(" ") - ); - } - - #[test] - fn find_also_linked_001() { - assert_eq!( - Entity::find() - .find_also_linked(SelfReferencingLink) - .build(DbBackend::MySql) - .to_string(), - [ - r#"SELECT `self_join`.`uuid` AS `A_uuid`, `self_join`.`uuid_ref` AS `A_uuid_ref`, `self_join`.`time` AS `A_time`,"#, - r#"`r0`.`uuid` AS `B_uuid`, `r0`.`uuid_ref` AS `B_uuid_ref`, `r0`.`time` AS `B_time`"#, - r#"FROM `self_join`"#, - r#"LEFT JOIN `self_join` AS `r0` ON `self_join`.`uuid_ref` = `r0`.`uuid`"#, - ] - .join(" ") - ); - } -} diff --git a/tests/self_join_tests.rs b/tests/self_join_tests.rs index afe4443651..f35dd5050a 100644 --- a/tests/self_join_tests.rs +++ b/tests/self_join_tests.rs @@ -2,7 +2,7 @@ pub mod common; pub use common::{features::*, setup::*, TestContext}; use pretty_assertions::assert_eq; -use sea_orm::{entity::prelude::*, IntoActiveModel, QueryOrder}; +use sea_orm::{entity::prelude::*, query::*, DbBackend, IntoActiveModel, QueryOrder}; #[sea_orm_macros::test] #[cfg(any( @@ -15,6 +15,8 @@ async fn main() -> Result<(), DbErr> { create_tables(&ctx.db).await?; create_metadata(&ctx.db).await?; ctx.delete().await; + find_linked_001(); + find_also_linked_001(); Ok(()) } @@ -87,3 +89,45 @@ pub async fn create_metadata(db: &DatabaseConnection) -> Result<(), DbErr> { Ok(()) } + +fn find_linked_001() { + use self_join::*; + + let self_join_model = Model { + uuid: Uuid::default(), + uuid_ref: None, + time: None, + }; + + assert_eq!( + self_join_model + .find_linked(SelfReferencingLink) + .build(DbBackend::MySql) + .to_string(), + [ + r#"SELECT `self_join`.`uuid`, `self_join`.`uuid_ref`, `self_join`.`time`"#, + r#"FROM `self_join`"#, + r#"INNER JOIN `self_join` AS `r0` ON `r0`.`uuid_ref` = `self_join`.`uuid`"#, + r#"WHERE `r0`.`uuid` = '00000000-0000-0000-0000-000000000000'"#, + ] + .join(" ") + ); +} + +fn find_also_linked_001() { + use self_join::*; + + assert_eq!( + Entity::find() + .find_also_linked(SelfReferencingLink) + .build(DbBackend::MySql) + .to_string(), + [ + r#"SELECT `self_join`.`uuid` AS `A_uuid`, `self_join`.`uuid_ref` AS `A_uuid_ref`, `self_join`.`time` AS `A_time`,"#, + r#"`r0`.`uuid` AS `B_uuid`, `r0`.`uuid_ref` AS `B_uuid_ref`, `r0`.`time` AS `B_time`"#, + r#"FROM `self_join`"#, + r#"LEFT JOIN `self_join` AS `r0` ON `self_join`.`uuid_ref` = `r0`.`uuid`"#, + ] + .join(" ") + ); +} From 93623f0d0280c43318256c4314c9cd6dee016f9b Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 12 Oct 2023 09:37:08 +0100 Subject: [PATCH 0178/1371] Support MySQL / SQLite --- .../src/derives/try_getable_from_json.rs | 11 ++++ src/entity/column.rs | 39 ++++++++++++- tests/active_enum_tests.rs | 6 +- tests/common/features/json_vec_derive.rs | 21 +++---- tests/common/features/mod.rs | 1 + tests/common/features/schema.rs | 57 ++++++++++++------- tests/common/setup/mod.rs | 9 ++- tests/json_vec_tests.rs | 8 ++- 8 files changed, 109 insertions(+), 43 deletions(-) diff --git a/sea-orm-macros/src/derives/try_getable_from_json.rs b/sea-orm-macros/src/derives/try_getable_from_json.rs index 8742d3c9ef..b111b24974 100644 --- a/sea-orm-macros/src/derives/try_getable_from_json.rs +++ b/sea-orm-macros/src/derives/try_getable_from_json.rs @@ -2,6 +2,15 @@ use proc_macro2::{Ident, TokenStream}; use quote::quote; pub fn expand_derive_from_json_query_result(ident: Ident) -> syn::Result { + let impl_not_u8 = if cfg!(feature = "postgres-array") { + quote!( + #[automatically_derived] + impl sea_orm::sea_query::value::with_array::NotU8 for #ident {} + ) + } else { + quote!() + }; + Ok(quote!( #[automatically_derived] impl sea_orm::TryGetableFromJson for #ident {} @@ -43,5 +52,7 @@ pub fn expand_derive_from_json_query_result(ident: Ident) -> syn::Result)` into `Json(Vec)` if the column type is `Json`. fn save_enum_as(&self, val: Expr) -> SimpleExpr { cast_enum_as(val, self, |col, enum_name, col_type| { let type_name = match col_type { @@ -412,9 +413,41 @@ where { let col_def = col.def(); let col_type = col_def.get_column_type(); - match col_type.get_enum_name() { - Some(enum_name) => f(expr, SeaRc::clone(enum_name), col_type), - None => expr.into(), + + match col_type { + #[cfg(all(feature = "with-json", feature = "postgres-array"))] + ColumnType::Json | ColumnType::JsonBinary => { + use sea_query::ArrayType; + use serde_json::Value as Json; + + #[allow(clippy::boxed_local)] + fn unbox(boxed: Box) -> T { + *boxed + } + + let expr = expr.into(); + match expr { + SimpleExpr::Value(Value::Array(ArrayType::Json, Some(json_vec))) => { + // flatten Array(Vec) into Json + let json_vec: Vec = json_vec + .into_iter() + .filter_map(|val| match val { + Value::Json(Some(json)) => Some(unbox(json)), + _ => None, + }) + .collect(); + SimpleExpr::Value(Value::Json(Some(Box::new(json_vec.into())))) + } + SimpleExpr::Value(Value::Array(ArrayType::Json, None)) => { + SimpleExpr::Value(Value::Json(None)) + } + _ => expr, + } + } + _ => match col_type.get_enum_name() { + Some(enum_name) => f(expr, SeaRc::clone(enum_name), col_type), + None => expr.into(), + }, } } diff --git a/tests/active_enum_tests.rs b/tests/active_enum_tests.rs index 843d51058e..547532052f 100644 --- a/tests/active_enum_tests.rs +++ b/tests/active_enum_tests.rs @@ -23,12 +23,12 @@ async fn main() -> Result<(), DbErr> { insert_active_enum(&ctx.db).await?; insert_active_enum_child(&ctx.db).await?; - if cfg!(feature = "sqlx-postgres") { - insert_active_enum_vec(&ctx.db).await?; - } + #[cfg(feature = "sqlx-postgres")] + insert_active_enum_vec(&ctx.db).await?; find_related_active_enum(&ctx.db).await?; find_linked_active_enum(&ctx.db).await?; + ctx.delete().await; Ok(()) diff --git a/tests/common/features/json_vec_derive.rs b/tests/common/features/json_vec_derive.rs index dcf16a739e..58eb3ed4f9 100644 --- a/tests/common/features/json_vec_derive.rs +++ b/tests/common/features/json_vec_derive.rs @@ -4,35 +4,27 @@ pub mod json_string_vec { use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] - #[sea_orm(table_name = "json_vec")] + #[sea_orm(table_name = "json_string_vec")] pub struct Model { #[sea_orm(primary_key)] pub id: i32, pub str_vec: Option, } + #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromJsonQueryResult)] + pub struct StringVec(pub Vec); + #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation {} impl ActiveModelBehavior for ActiveModel {} - - #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromJsonQueryResult)] - pub struct StringVec(pub Vec); } pub mod json_struct_vec { use sea_orm::entity::prelude::*; use sea_orm_macros::FromJsonQueryResult; - use sea_query::with_array::NotU8; use serde::{Deserialize, Serialize}; - #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromJsonQueryResult)] - pub struct JsonColumn { - pub value: String, - } - - impl NotU8 for JsonColumn {} - #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] #[sea_orm(table_name = "json_struct_vec")] pub struct Model { @@ -42,6 +34,11 @@ pub mod json_struct_vec { pub struct_vec: Vec, } + #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromJsonQueryResult)] + pub struct JsonColumn { + pub value: String, + } + #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation {} diff --git a/tests/common/features/mod.rs b/tests/common/features/mod.rs index f60950f869..3d4dee9a5b 100644 --- a/tests/common/features/mod.rs +++ b/tests/common/features/mod.rs @@ -44,6 +44,7 @@ pub use event_trigger::Entity as EventTrigger; pub use insert_default::Entity as InsertDefault; pub use json_struct::Entity as JsonStruct; pub use json_vec::Entity as JsonVec; +pub use json_vec_derive::json_string_vec::Entity as JsonStringVec; pub use json_vec_derive::json_struct_vec::Entity as JsonStructVec; pub use metadata::Entity as Metadata; pub use pi::Entity as Pi; diff --git a/tests/common/features/schema.rs b/tests/common/features/schema.rs index dbb5551df1..0fb15cf02d 100644 --- a/tests/common/features/schema.rs +++ b/tests/common/features/schema.rs @@ -1,5 +1,4 @@ use super::*; -use crate::common::features::json_vec_derive::json_struct_vec; use crate::common::setup::{create_enum, create_table, create_table_without_asserts}; use sea_orm::{ error::*, sea_query, ConnectionTrait, DatabaseConnection, DbBackend, DbConn, EntityName, @@ -20,7 +19,6 @@ pub async fn create_tables(db: &DatabaseConnection) -> Result<(), DbErr> { create_byte_primary_key_table(db).await?; create_satellites_table(db).await?; create_transaction_log_table(db).await?; - create_json_struct_table(db).await?; let create_enum_stmts = match db_backend { DbBackend::MySql | DbBackend::Sqlite => Vec::new(), @@ -51,12 +49,15 @@ pub async fn create_tables(db: &DatabaseConnection) -> Result<(), DbErr> { create_dyn_table_name_lazy_static_table(db).await?; create_value_type_table(db).await?; + create_json_vec_table(db).await?; + create_json_struct_table(db).await?; + create_json_string_vec_table(db).await?; + create_json_struct_vec_table(db).await?; + if DbBackend::Postgres == db_backend { create_value_type_postgres_table(db).await?; create_collection_table(db).await?; create_event_trigger_table(db).await?; - create_json_vec_table(db).await?; - create_json_struct_vec_table(db).await?; create_categories_table(db).await?; } @@ -323,46 +324,62 @@ pub async fn create_json_vec_table(db: &DbConn) -> Result { create_table(db, &create_table_stmt, JsonVec).await } -pub async fn create_json_struct_vec_table(db: &DbConn) -> Result { - let create_table_stmt = sea_query::Table::create() - .table(json_struct_vec::Entity.table_ref()) +pub async fn create_json_struct_table(db: &DbConn) -> Result { + let stmt = sea_query::Table::create() + .table(json_struct::Entity) .col( - ColumnDef::new(json_struct_vec::Column::Id) + ColumnDef::new(json_struct::Column::Id) .integer() .not_null() .auto_increment() .primary_key(), ) + .col(ColumnDef::new(json_struct::Column::Json).json().not_null()) .col( - ColumnDef::new(json_struct_vec::Column::StructVec) - .json_binary() + ColumnDef::new(json_struct::Column::JsonValue) + .json() .not_null(), ) + .col(ColumnDef::new(json_struct::Column::JsonValueOpt).json()) .to_owned(); - create_table(db, &create_table_stmt, JsonStructVec).await + create_table(db, &stmt, JsonStruct).await } -pub async fn create_json_struct_table(db: &DbConn) -> Result { - let stmt = sea_query::Table::create() - .table(json_struct::Entity) +pub async fn create_json_string_vec_table(db: &DbConn) -> Result { + let create_table_stmt = sea_query::Table::create() + .table(JsonStringVec.table_ref()) .col( - ColumnDef::new(json_struct::Column::Id) + ColumnDef::new(json_vec_derive::json_string_vec::Column::Id) .integer() .not_null() .auto_increment() .primary_key(), ) - .col(ColumnDef::new(json_struct::Column::Json).json().not_null()) + .col(ColumnDef::new(json_vec_derive::json_string_vec::Column::StrVec).json()) + .to_owned(); + + create_table(db, &create_table_stmt, JsonStringVec).await +} + +pub async fn create_json_struct_vec_table(db: &DbConn) -> Result { + let create_table_stmt = sea_query::Table::create() + .table(JsonStructVec.table_ref()) .col( - ColumnDef::new(json_struct::Column::JsonValue) - .json() + ColumnDef::new(json_vec_derive::json_struct_vec::Column::Id) + .integer() + .not_null() + .auto_increment() + .primary_key(), + ) + .col( + ColumnDef::new(json_vec_derive::json_struct_vec::Column::StructVec) + .json_binary() .not_null(), ) - .col(ColumnDef::new(json_struct::Column::JsonValueOpt).json()) .to_owned(); - create_table(db, &stmt, JsonStruct).await + create_table(db, &create_table_stmt, JsonStructVec).await } pub async fn create_collection_table(db: &DbConn) -> Result { diff --git a/tests/common/setup/mod.rs b/tests/common/setup/mod.rs index bd4d38bf52..2cdc00d210 100644 --- a/tests/common/setup/mod.rs +++ b/tests/common/setup/mod.rs @@ -1,7 +1,8 @@ use pretty_assertions::assert_eq; use sea_orm::{ - ColumnTrait, ColumnType, ConnectionTrait, Database, DatabaseBackend, DatabaseConnection, - DbBackend, DbConn, DbErr, EntityTrait, ExecResult, Iterable, Schema, Statement, + ColumnTrait, ColumnType, ConnectOptions, ConnectionTrait, Database, DatabaseBackend, + DatabaseConnection, DbBackend, DbConn, DbErr, EntityTrait, ExecResult, Iterable, Schema, + Statement, }; use sea_query::{ extension::postgres::{Type, TypeCreateStatement}, @@ -48,7 +49,9 @@ pub async fn setup(base_url: &str, db_name: &str) -> DatabaseConnection { let url = format!("{base_url}/{db_name}"); Database::connect(&url).await.unwrap() } else { - Database::connect(base_url).await.unwrap() + let mut options: ConnectOptions = base_url.into(); + options.sqlx_logging(false); + Database::connect(options).await.unwrap() } } diff --git a/tests/json_vec_tests.rs b/tests/json_vec_tests.rs index ab5bab2ee4..f92cb67323 100644 --- a/tests/json_vec_tests.rs +++ b/tests/json_vec_tests.rs @@ -5,13 +5,17 @@ use pretty_assertions::assert_eq; use sea_orm::{entity::prelude::*, entity::*, DatabaseConnection}; #[sea_orm_macros::test] -#[cfg(feature = "sqlx-postgres")] +#[cfg(any( + feature = "sqlx-mysql", + feature = "sqlx-sqlite", + feature = "sqlx-postgres" +))] async fn main() -> Result<(), DbErr> { let ctx = TestContext::new("json_vec_tests").await; create_tables(&ctx.db).await?; insert_json_vec(&ctx.db).await?; - insert_json_string_vec_derive(&ctx.db).await?; + insert_json_struct_vec_derive(&ctx.db).await?; ctx.delete().await; From 01cbf1cc36fa427bc0cb19e86755906bc4c2ae94 Mon Sep 17 00:00:00 2001 From: chris Date: Mon, 16 Oct 2023 10:24:48 +0100 Subject: [PATCH 0179/1371] Changelog --- CHANGELOG.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 64d17da826..89b78dda40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## 0.12.4 - Pending +### New Features + +* Add support for root JSON arrays https://github.com/SeaQL/sea-orm/pull/1898 + Now the following works! +```rust +#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] +#[sea_orm(table_name = "json_struct_vec")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + #[sea_orm(column_type = "Json")] + pub struct_vec: Vec, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromJsonQueryResult)] +pub struct JsonColumn { + pub value: String, +} +``` + ### Enhancements * Loader: use `ValueTuple` as hash key https://github.com/SeaQL/sea-orm/pull/1868 From bd9a0e9b7e8f55d4ed91511f4b088849b94d3087 Mon Sep 17 00:00:00 2001 From: chris Date: Mon, 16 Oct 2023 11:26:13 +0100 Subject: [PATCH 0180/1371] New `json-array` feature flag --- CHANGELOG.md | 2 +- Cargo.toml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89b78dda40..b4b2c15485 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### New Features * Add support for root JSON arrays https://github.com/SeaQL/sea-orm/pull/1898 - Now the following works! + Now the following works (requires the `json-array` / `postgres-array` feature)! ```rust #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] #[sea_orm(table_name = "json_struct_vec")] diff --git a/Cargo.toml b/Cargo.toml index ed33aea241..877fef5b28 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -83,6 +83,7 @@ with-bigdecimal = ["bigdecimal", "sea-query/with-bigdecimal", "sea-query-binder? with-uuid = ["uuid", "sea-query/with-uuid", "sea-query-binder?/with-uuid", "sqlx?/uuid"] with-time = ["time", "sea-query/with-time", "sea-query-binder?/with-time", "sqlx?/time"] postgres-array = ["sea-query/postgres-array", "sea-query-binder?/postgres-array", "sea-orm-macros/postgres-array"] +json-array = ["postgres-array"] # this does not actually enable sqlx-postgres, but only a few traits to support array in sea-query sea-orm-internal = [] sqlx-dep = [] sqlx-all = ["sqlx-mysql", "sqlx-postgres", "sqlx-sqlite"] From fef3a6785687c767646f7643e2599e4e8c4df51a Mon Sep 17 00:00:00 2001 From: Erik Wegner Date: Thu, 19 Oct 2023 20:02:26 +0200 Subject: [PATCH 0181/1371] Typo (#1923) --- src/entity/active_model.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/entity/active_model.rs b/src/entity/active_model.rs index 656fc1853c..c0e3448401 100644 --- a/src/entity/active_model.rs +++ b/src/entity/active_model.rs @@ -824,7 +824,7 @@ where } } - /// Check is a [Value] exists or not + /// Check if a [Value] exists or not pub fn into_value(self) -> Option { match self { ActiveValue::Set(value) | ActiveValue::Unchanged(value) => Some(value.into()), From 6e94335a69dbe5f381b1d3fa4538c65c471af449 Mon Sep 17 00:00:00 2001 From: Vladlen Date: Fri, 20 Oct 2023 05:05:18 +1100 Subject: [PATCH 0182/1371] fix(examples): rename duplicate service example (#1919) --- examples/rocket_okapi_example/service/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/rocket_okapi_example/service/Cargo.toml b/examples/rocket_okapi_example/service/Cargo.toml index 078f2aa636..0cbfa9a583 100644 --- a/examples/rocket_okapi_example/service/Cargo.toml +++ b/examples/rocket_okapi_example/service/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "rocket-example-service" +name = "rocket-okapi-example-service" version = "0.1.0" edition = "2021" From 72e9a140e3c52ad11eec587eacb262dc42ce7a4c Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 19 Oct 2023 19:24:51 +0100 Subject: [PATCH 0183/1371] Update Readme --- README.md | 18 +++++++++++------- src/lib.rs | 18 +++++++++++------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index d24f19b5aa..65c05728b9 100644 --- a/README.md +++ b/README.md @@ -16,20 +16,17 @@ #### SeaORM is a relational ORM to help you build web services in Rust with the familiarity of dynamic languages. -## Getting Started - [![GitHub stars](https://img.shields.io/github/stars/SeaQL/sea-orm.svg?style=social&label=Star&maxAge=1)](https://github.com/SeaQL/sea-orm/stargazers/) If you like what we do, consider starring, sharing and contributing! +Please help us with maintaining SeaORM by completing the [SeaQL Community Survey 2023](https://sea-ql.org/community-survey)! + +## Getting Started + + [Documentation](https://www.sea-ql.org/SeaORM) + [Tutorial](https://www.sea-ql.org/sea-orm-tutorial) + [Cookbook](https://www.sea-ql.org/sea-orm-cookbook) -[![Discord](https://img.shields.io/discord/873880840487206962?label=Discord)](https://discord.com/invite/uCPdDXzbdv) -Join our Discord server to chat with others in the SeaQL community! - -Please help us with maintaining SeaORM by completing the [SeaQL Community Survey 2023](https://sea-ql.org/community-survey)! - Integration examples: + [Actix v4 Example](https://github.com/SeaQL/sea-orm/tree/master/examples/actix_example) @@ -43,6 +40,13 @@ Integration examples: + [Tonic Example](https://github.com/SeaQL/sea-orm/tree/master/examples/tonic_example) + [Seaography Example](https://github.com/SeaQL/sea-orm/tree/master/examples/seaography_example) +## Support + +[![Discord](https://img.shields.io/discord/873880840487206962?label=Discord)](https://discord.com/invite/uCPdDXzbdv) +Join our Discord server to chat with others in the SeaQL community! + +Professional support on Rust programming and best practices is available. You can email us for a quote! + ## Features 1. Async diff --git a/src/lib.rs b/src/lib.rs index d7424dc25b..4aa0fd6994 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,20 +26,17 @@ //! //! #### SeaORM is a relational ORM to help you build web services in Rust with the familiarity of dynamic languages. //! -//! ## Getting Started -//! //! [![GitHub stars](https://img.shields.io/github/stars/SeaQL/sea-orm.svg?style=social&label=Star&maxAge=1)](https://github.com/SeaQL/sea-orm/stargazers/) //! If you like what we do, consider starring, sharing and contributing! //! +//! Please help us with maintaining SeaORM by completing the [SeaQL Community Survey 2023](https://sea-ql.org/community-survey)! +//! +//! ## Getting Started +//! //! + [Documentation](https://www.sea-ql.org/SeaORM) //! + [Tutorial](https://www.sea-ql.org/sea-orm-tutorial) //! + [Cookbook](https://www.sea-ql.org/sea-orm-cookbook) //! -//! [![Discord](https://img.shields.io/discord/873880840487206962?label=Discord)](https://discord.com/invite/uCPdDXzbdv) -//! Join our Discord server to chat with others in the SeaQL community! -//! -//! Please help us with maintaining SeaORM by completing the [SeaQL Community Survey 2023](https://sea-ql.org/community-survey)! -//! //! Integration examples: //! //! + [Actix v4 Example](https://github.com/SeaQL/sea-orm/tree/master/examples/actix_example) @@ -53,6 +50,13 @@ //! + [Tonic Example](https://github.com/SeaQL/sea-orm/tree/master/examples/tonic_example) //! + [Seaography Example](https://github.com/SeaQL/sea-orm/tree/master/examples/seaography_example) //! +//! ## Support +//! +//! [![Discord](https://img.shields.io/discord/873880840487206962?label=Discord)](https://discord.com/invite/uCPdDXzbdv) +//! Join our Discord server to chat with others in the SeaQL community! +//! +//! Professional support on Rust programming and best practices is available. You can email us for a quote! +//! //! ## Features //! //! 1. Async From 619b773ea2fdc7ba19e4826efd7dcfc791bc33ad Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 19 Oct 2023 19:27:39 +0100 Subject: [PATCH 0184/1371] Fixup --- examples/rocket_okapi_example/api/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/rocket_okapi_example/api/Cargo.toml b/examples/rocket_okapi_example/api/Cargo.toml index 6bd5abfdb9..1698ca4366 100644 --- a/examples/rocket_okapi_example/api/Cargo.toml +++ b/examples/rocket_okapi_example/api/Cargo.toml @@ -8,7 +8,7 @@ publish = false [dependencies] async-stream = { version = "0.3" } async-trait = { version = "0.1" } -rocket-example-service = { path = "../service" } +rocket-okapi-example-service = { path = "../service" } futures = { version = "0.3" } futures-util = { version = "0.3" } rocket = { version = "0.5.0-rc.1", features = [ From 240620176c5aee87b9316a67556a00452acc6082 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 19 Oct 2023 19:28:46 +0100 Subject: [PATCH 0185/1371] 0.12.4 --- Cargo.toml | 4 ++-- sea-orm-cli/Cargo.toml | 4 ++-- sea-orm-codegen/Cargo.toml | 2 +- sea-orm-macros/Cargo.toml | 2 +- sea-orm-migration/Cargo.toml | 6 +++--- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 877fef5b28..a8d9fb662a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [".", "sea-orm-macros", "sea-orm-codegen"] [package] name = "sea-orm" -version = "0.12.3" +version = "0.12.4" authors = ["Chris Tsang "] edition = "2021" description = "🐚 An async & dynamic ORM for Rust" @@ -33,7 +33,7 @@ log = { version = "0.4", default-features = false } tracing = { version = "0.1", default-features = false, features = ["attributes", "log"] } rust_decimal = { version = "1", default-features = false, optional = true } bigdecimal = { version = "0.3", default-features = false, optional = true } -sea-orm-macros = { version = "0.12.3", path = "sea-orm-macros", default-features = false, features = ["strum"] } +sea-orm-macros = { version = "0.12.4", path = "sea-orm-macros", default-features = false, features = ["strum"] } sea-query = { version = "0.30.2", default-features = false, features = ["thread-safe", "hashable-value", "backend-mysql", "backend-postgres", "backend-sqlite"] } sea-query-binder = { version = "0.5.0", default-features = false, optional = true } strum = { version = "0.25", default-features = false } diff --git a/sea-orm-cli/Cargo.toml b/sea-orm-cli/Cargo.toml index 93f8fae8e5..42a18105fa 100644 --- a/sea-orm-cli/Cargo.toml +++ b/sea-orm-cli/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "sea-orm-cli" -version = "0.12.3" +version = "0.12.4" authors = [ "Chris Tsang ", "Billy Chan ", @@ -37,7 +37,7 @@ required-features = ["cli", "codegen"] clap = { version = "4.3", features = ["env", "derive"], optional = true } dotenvy = { version = "0.15", default-features = false, optional = true } async-std = { version = "1.9", default-features = false, features = ["attributes", "tokio1"], optional = true } -sea-orm-codegen = { version = "=0.12.3", path = "../sea-orm-codegen", default-features = false, optional = true } +sea-orm-codegen = { version = "=0.12.4", path = "../sea-orm-codegen", default-features = false, optional = true } sea-schema = { version = "0.14.0" } sqlx = { version = "0.7", default-features = false, features = ["mysql", "postgres"], optional = true } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["env-filter", "fmt"] } diff --git a/sea-orm-codegen/Cargo.toml b/sea-orm-codegen/Cargo.toml index 2e9d94ddfa..61cd07127c 100644 --- a/sea-orm-codegen/Cargo.toml +++ b/sea-orm-codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sea-orm-codegen" -version = "0.12.3" +version = "0.12.4" authors = ["Billy Chan "] edition = "2021" description = "Code Generator for SeaORM" diff --git a/sea-orm-macros/Cargo.toml b/sea-orm-macros/Cargo.toml index 86f031e76f..9c0397a917 100644 --- a/sea-orm-macros/Cargo.toml +++ b/sea-orm-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sea-orm-macros" -version = "0.12.3" +version = "0.12.4" authors = [ "Billy Chan " ] edition = "2021" description = "Derive macros for SeaORM" diff --git a/sea-orm-migration/Cargo.toml b/sea-orm-migration/Cargo.toml index 7aab07a044..6d1f91d61f 100644 --- a/sea-orm-migration/Cargo.toml +++ b/sea-orm-migration/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "sea-orm-migration" -version = "0.12.3" +version = "0.12.4" authors = ["Billy Chan "] edition = "2021" description = "Migration utility for SeaORM" @@ -23,8 +23,8 @@ path = "src/lib.rs" async-trait = { version = "0.1", default-features = false } clap = { version = "4.3", features = ["env", "derive"], optional = true } dotenvy = { version = "0.15", default-features = false, optional = true } -sea-orm = { version = "0.12.3", path = "../", default-features = false, features = ["macros"] } -sea-orm-cli = { version = "0.12.3", path = "../sea-orm-cli", default-features = false, optional = true } +sea-orm = { version = "0.12.4", path = "../", default-features = false, features = ["macros"] } +sea-orm-cli = { version = "0.12.4", path = "../sea-orm-cli", default-features = false, optional = true } sea-schema = { version = "0.14.1" } tracing = { version = "0.1", default-features = false, features = ["log"] } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["env-filter", "fmt"] } From 3796ce40ad2225c5541e4212075a9bde3acc184c Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 19 Oct 2023 19:28:46 +0100 Subject: [PATCH 0186/1371] update examples --- examples/actix3_example/entity/Cargo.toml | 2 +- examples/actix3_example/migration/Cargo.toml | 2 +- examples/actix3_example/service/Cargo.toml | 2 +- examples/actix_example/entity/Cargo.toml | 2 +- examples/actix_example/migration/Cargo.toml | 2 +- examples/actix_example/service/Cargo.toml | 2 +- examples/axum_example/entity/Cargo.toml | 2 +- examples/axum_example/migration/Cargo.toml | 2 +- examples/axum_example/service/Cargo.toml | 2 +- examples/graphql_example/entity/Cargo.toml | 2 +- examples/graphql_example/migration/Cargo.toml | 2 +- examples/graphql_example/service/Cargo.toml | 2 +- examples/jsonrpsee_example/entity/Cargo.toml | 2 +- examples/jsonrpsee_example/migration/Cargo.toml | 2 +- examples/jsonrpsee_example/service/Cargo.toml | 2 +- examples/poem_example/entity/Cargo.toml | 2 +- examples/poem_example/migration/Cargo.toml | 2 +- examples/poem_example/service/Cargo.toml | 2 +- examples/rocket_example/entity/Cargo.toml | 2 +- examples/rocket_example/migration/Cargo.toml | 2 +- examples/rocket_example/service/Cargo.toml | 2 +- examples/rocket_okapi_example/entity/Cargo.toml | 2 +- examples/rocket_okapi_example/migration/Cargo.toml | 2 +- examples/rocket_okapi_example/service/Cargo.toml | 2 +- examples/salvo_example/entity/Cargo.toml | 2 +- examples/salvo_example/migration/Cargo.toml | 2 +- examples/salvo_example/service/Cargo.toml | 2 +- examples/seaography_example/migration/Cargo.toml | 4 ++-- examples/tonic_example/entity/Cargo.toml | 2 +- examples/tonic_example/migration/Cargo.toml | 2 +- examples/tonic_example/service/Cargo.toml | 2 +- 31 files changed, 32 insertions(+), 32 deletions(-) diff --git a/examples/actix3_example/entity/Cargo.toml b/examples/actix3_example/entity/Cargo.toml index 135a427034..423712d5ea 100644 --- a/examples/actix3_example/entity/Cargo.toml +++ b/examples/actix3_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.3" # sea-orm version +version = "0.12.4" # sea-orm version diff --git a/examples/actix3_example/migration/Cargo.toml b/examples/actix3_example/migration/Cargo.toml index 22ec3b73eb..cab780f350 100644 --- a/examples/actix3_example/migration/Cargo.toml +++ b/examples/actix3_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.3" # sea-orm-migration version +version = "0.12.4" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-async-std-native-tls", diff --git a/examples/actix3_example/service/Cargo.toml b/examples/actix3_example/service/Cargo.toml index 78030c4495..9c7944fabe 100644 --- a/examples/actix3_example/service/Cargo.toml +++ b/examples/actix3_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.3" # sea-orm version +version = "0.12.4" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/actix_example/entity/Cargo.toml b/examples/actix_example/entity/Cargo.toml index 135a427034..423712d5ea 100644 --- a/examples/actix_example/entity/Cargo.toml +++ b/examples/actix_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.3" # sea-orm version +version = "0.12.4" # sea-orm version diff --git a/examples/actix_example/migration/Cargo.toml b/examples/actix_example/migration/Cargo.toml index 50d49ff9af..415f600ac5 100644 --- a/examples/actix_example/migration/Cargo.toml +++ b/examples/actix_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.3" # sea-orm-migration version +version = "0.12.4" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-actix-native-tls", diff --git a/examples/actix_example/service/Cargo.toml b/examples/actix_example/service/Cargo.toml index 425a28ea17..2a2a6bbe3f 100644 --- a/examples/actix_example/service/Cargo.toml +++ b/examples/actix_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.3" # sea-orm version +version = "0.12.4" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/axum_example/entity/Cargo.toml b/examples/axum_example/entity/Cargo.toml index 135a427034..423712d5ea 100644 --- a/examples/axum_example/entity/Cargo.toml +++ b/examples/axum_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.3" # sea-orm version +version = "0.12.4" # sea-orm version diff --git a/examples/axum_example/migration/Cargo.toml b/examples/axum_example/migration/Cargo.toml index 50e5ff7765..2d06edb279 100644 --- a/examples/axum_example/migration/Cargo.toml +++ b/examples/axum_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.3" # sea-orm-migration version +version = "0.12.4" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/axum_example/service/Cargo.toml b/examples/axum_example/service/Cargo.toml index e21eec5ddf..f2b37512d9 100644 --- a/examples/axum_example/service/Cargo.toml +++ b/examples/axum_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.3" # sea-orm version +version = "0.12.4" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/graphql_example/entity/Cargo.toml b/examples/graphql_example/entity/Cargo.toml index 046900b534..4dca0e412f 100644 --- a/examples/graphql_example/entity/Cargo.toml +++ b/examples/graphql_example/entity/Cargo.toml @@ -16,4 +16,4 @@ version = "5.0.10" [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.3" # sea-orm version +version = "0.12.4" # sea-orm version diff --git a/examples/graphql_example/migration/Cargo.toml b/examples/graphql_example/migration/Cargo.toml index 61a0cc4096..85cfe44764 100644 --- a/examples/graphql_example/migration/Cargo.toml +++ b/examples/graphql_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.3" # sea-orm-migration version +version = "0.12.4" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/graphql_example/service/Cargo.toml b/examples/graphql_example/service/Cargo.toml index 27f688234f..37f761ac0b 100644 --- a/examples/graphql_example/service/Cargo.toml +++ b/examples/graphql_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.3" # sea-orm version +version = "0.12.4" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/jsonrpsee_example/entity/Cargo.toml b/examples/jsonrpsee_example/entity/Cargo.toml index 135a427034..423712d5ea 100644 --- a/examples/jsonrpsee_example/entity/Cargo.toml +++ b/examples/jsonrpsee_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.3" # sea-orm version +version = "0.12.4" # sea-orm version diff --git a/examples/jsonrpsee_example/migration/Cargo.toml b/examples/jsonrpsee_example/migration/Cargo.toml index d9175c65a5..d9727521a5 100644 --- a/examples/jsonrpsee_example/migration/Cargo.toml +++ b/examples/jsonrpsee_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.3" # sea-orm-migration version +version = "0.12.4" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/jsonrpsee_example/service/Cargo.toml b/examples/jsonrpsee_example/service/Cargo.toml index 898b645706..6cf8531d4e 100644 --- a/examples/jsonrpsee_example/service/Cargo.toml +++ b/examples/jsonrpsee_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.3" # sea-orm version +version = "0.12.4" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/poem_example/entity/Cargo.toml b/examples/poem_example/entity/Cargo.toml index 135a427034..423712d5ea 100644 --- a/examples/poem_example/entity/Cargo.toml +++ b/examples/poem_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.3" # sea-orm version +version = "0.12.4" # sea-orm version diff --git a/examples/poem_example/migration/Cargo.toml b/examples/poem_example/migration/Cargo.toml index d9175c65a5..d9727521a5 100644 --- a/examples/poem_example/migration/Cargo.toml +++ b/examples/poem_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.3" # sea-orm-migration version +version = "0.12.4" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/poem_example/service/Cargo.toml b/examples/poem_example/service/Cargo.toml index 2404bae56a..77e4f056db 100644 --- a/examples/poem_example/service/Cargo.toml +++ b/examples/poem_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.3" # sea-orm version +version = "0.12.4" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/rocket_example/entity/Cargo.toml b/examples/rocket_example/entity/Cargo.toml index 72195509dd..28bd59aefe 100644 --- a/examples/rocket_example/entity/Cargo.toml +++ b/examples/rocket_example/entity/Cargo.toml @@ -15,4 +15,4 @@ rocket = { version = "0.5.0-rc.1", features = [ [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.3" # sea-orm version +version = "0.12.4" # sea-orm version diff --git a/examples/rocket_example/migration/Cargo.toml b/examples/rocket_example/migration/Cargo.toml index 7ae7c3ecae..e2774d5651 100644 --- a/examples/rocket_example/migration/Cargo.toml +++ b/examples/rocket_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.3" # sea-orm-migration version +version = "0.12.4" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/rocket_example/service/Cargo.toml b/examples/rocket_example/service/Cargo.toml index 078f2aa636..668512eb32 100644 --- a/examples/rocket_example/service/Cargo.toml +++ b/examples/rocket_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.3" # sea-orm version +version = "0.12.4" # sea-orm version features = [ "runtime-tokio-native-tls", "sqlx-postgres", diff --git a/examples/rocket_okapi_example/entity/Cargo.toml b/examples/rocket_okapi_example/entity/Cargo.toml index bc9075f952..10cbf52a62 100644 --- a/examples/rocket_okapi_example/entity/Cargo.toml +++ b/examples/rocket_okapi_example/entity/Cargo.toml @@ -15,7 +15,7 @@ rocket = { version = "0.5.0-rc.1", features = [ [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.3" # sea-orm version +version = "0.12.4" # sea-orm version [dependencies.rocket_okapi] version = "0.8.0-rc.2" diff --git a/examples/rocket_okapi_example/migration/Cargo.toml b/examples/rocket_okapi_example/migration/Cargo.toml index 7ae7c3ecae..e2774d5651 100644 --- a/examples/rocket_okapi_example/migration/Cargo.toml +++ b/examples/rocket_okapi_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.3" # sea-orm-migration version +version = "0.12.4" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/rocket_okapi_example/service/Cargo.toml b/examples/rocket_okapi_example/service/Cargo.toml index 0cbfa9a583..caab7631ef 100644 --- a/examples/rocket_okapi_example/service/Cargo.toml +++ b/examples/rocket_okapi_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.3" # sea-orm version +version = "0.12.4" # sea-orm version features = [ "runtime-tokio-native-tls", "sqlx-postgres", diff --git a/examples/salvo_example/entity/Cargo.toml b/examples/salvo_example/entity/Cargo.toml index 4d512f8d57..a9faf0dbef 100644 --- a/examples/salvo_example/entity/Cargo.toml +++ b/examples/salvo_example/entity/Cargo.toml @@ -14,4 +14,4 @@ salvo = { version = "0.50" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.3" # sea-orm version +version = "0.12.4" # sea-orm version diff --git a/examples/salvo_example/migration/Cargo.toml b/examples/salvo_example/migration/Cargo.toml index d9175c65a5..d9727521a5 100644 --- a/examples/salvo_example/migration/Cargo.toml +++ b/examples/salvo_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.3" # sea-orm-migration version +version = "0.12.4" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/salvo_example/service/Cargo.toml b/examples/salvo_example/service/Cargo.toml index adba027880..890b871103 100644 --- a/examples/salvo_example/service/Cargo.toml +++ b/examples/salvo_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.3" # sea-orm version +version = "0.12.4" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/seaography_example/migration/Cargo.toml b/examples/seaography_example/migration/Cargo.toml index 53f36d874d..9abea87733 100644 --- a/examples/seaography_example/migration/Cargo.toml +++ b/examples/seaography_example/migration/Cargo.toml @@ -15,11 +15,11 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm] path = "../../.." # remove this line in your own project -version = "0.12.3" # sea-orm version +version = "0.12.4" # sea-orm version [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.3" # sea-orm-migration version +version = "0.12.4" # sea-orm-migration version features = [ "runtime-async-std-native-tls", "sqlx-mysql", diff --git a/examples/tonic_example/entity/Cargo.toml b/examples/tonic_example/entity/Cargo.toml index 135a427034..423712d5ea 100644 --- a/examples/tonic_example/entity/Cargo.toml +++ b/examples/tonic_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.3" # sea-orm version +version = "0.12.4" # sea-orm version diff --git a/examples/tonic_example/migration/Cargo.toml b/examples/tonic_example/migration/Cargo.toml index d309c89ae2..55bc37adf4 100644 --- a/examples/tonic_example/migration/Cargo.toml +++ b/examples/tonic_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.3" # sea-orm-migration version +version = "0.12.4" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-rustls", diff --git a/examples/tonic_example/service/Cargo.toml b/examples/tonic_example/service/Cargo.toml index eff70782e4..2182262b2e 100644 --- a/examples/tonic_example/service/Cargo.toml +++ b/examples/tonic_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.3" # sea-orm version +version = "0.12.4" # sea-orm version features = [ "debug-print", "runtime-tokio-rustls", From 091eb4d85d9a54be00df20e21768017dcf258622 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 19 Oct 2023 19:30:35 +0100 Subject: [PATCH 0187/1371] Readme --- README.md | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 65c05728b9..1f357e2782 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Integration examples: ## Support [![Discord](https://img.shields.io/discord/873880840487206962?label=Discord)](https://discord.com/invite/uCPdDXzbdv) -Join our Discord server to chat with others in the SeaQL community! +Join our Discord server to chat with other members of the SeaQL community! Professional support on Rust programming and best practices is available. You can email us for a quote! diff --git a/src/lib.rs b/src/lib.rs index 4aa0fd6994..75fb1c133a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,7 +53,7 @@ //! ## Support //! //! [![Discord](https://img.shields.io/discord/873880840487206962?label=Discord)](https://discord.com/invite/uCPdDXzbdv) -//! Join our Discord server to chat with others in the SeaQL community! +//! Join our Discord server to chat with other members of the SeaQL community! //! //! Professional support on Rust programming and best practices is available. You can email us for a quote! //! From 68c7470ac94fdb3bb840bbc4302b68f8a1901d3c Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 19 Oct 2023 19:49:53 +0100 Subject: [PATCH 0188/1371] Changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4b2c15485..46d9bec3fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## 0.12.4 - Pending +## 0.12.4 - 2023-10-19 ### New Features @@ -33,7 +33,7 @@ pub struct JsonColumn { ### Upgrades -* Updated `sea-query` to `0.30.2` +* Updated `sea-query` to `0.30.2` https://github.com/SeaQL/sea-query/releases/tag/0.30.2 ## 0.12.3 - 2023-09-22 From e9acabd847d34f5fe257dba1b0b95647853c8af0 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 19 Oct 2023 19:51:22 +0100 Subject: [PATCH 0189/1371] Fixup --- examples/rocket_okapi_example/api/src/okapi_example.rs | 2 +- examples/rocket_okapi_example/api/src/pool.rs | 2 +- examples/rocket_okapi_example/service/tests/mock.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/rocket_okapi_example/api/src/okapi_example.rs b/examples/rocket_okapi_example/api/src/okapi_example.rs index e801ffeda7..3e0f967c7f 100644 --- a/examples/rocket_okapi_example/api/src/okapi_example.rs +++ b/examples/rocket_okapi_example/api/src/okapi_example.rs @@ -1,6 +1,6 @@ use dto::dto; use rocket::serde::json::Json; -use rocket_example_service::{Mutation, Query}; +use rocket_okapi_example_service::{Mutation, Query}; use sea_orm_rocket::Connection; diff --git a/examples/rocket_okapi_example/api/src/pool.rs b/examples/rocket_okapi_example/api/src/pool.rs index 465ad53b8a..c7c54d2429 100644 --- a/examples/rocket_okapi_example/api/src/pool.rs +++ b/examples/rocket_okapi_example/api/src/pool.rs @@ -1,4 +1,4 @@ -use rocket_example_service::sea_orm; +use rocket_okapi_example_service::sea_orm; use async_trait::async_trait; use sea_orm::ConnectOptions; diff --git a/examples/rocket_okapi_example/service/tests/mock.rs b/examples/rocket_okapi_example/service/tests/mock.rs index 1f752615ed..a2518d401b 100644 --- a/examples/rocket_okapi_example/service/tests/mock.rs +++ b/examples/rocket_okapi_example/service/tests/mock.rs @@ -2,7 +2,7 @@ mod prepare; use entity::post; use prepare::prepare_mock_db; -use rocket_example_service::{Mutation, Query}; +use rocket_okapi_example_service::{Mutation, Query}; #[tokio::test] async fn main() { From 42babea318ffed731aa916522208d136174025af Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Mon, 23 Oct 2023 10:08:32 +0100 Subject: [PATCH 0190/1371] Hotfix: conditionally impl impl_try_getable_array --- sea-orm-macros/src/derives/active_enum.rs | 26 +++++++++++++++-------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/sea-orm-macros/src/derives/active_enum.rs b/sea-orm-macros/src/derives/active_enum.rs index f163275f29..41813499ad 100644 --- a/sea-orm-macros/src/derives/active_enum.rs +++ b/sea-orm-macros/src/derives/active_enum.rs @@ -290,6 +290,22 @@ impl ActiveEnum { quote!() }; + let impl_try_getable_array = if cfg!(feature = "postgres-array") { + quote!( + #[automatically_derived] + impl sea_orm::TryGetableArray for #ident { + fn try_get_by(res: &sea_orm::QueryResult, index: I) -> std::result::Result, sea_orm::TryGetError> { + <::Value as sea_orm::ActiveEnumValue>::try_get_vec_by(res, index)? + .into_iter() + .map(|value| ::try_from_value(&value).map_err(Into::into)) + .collect() + } + } + ) + } else { + quote!() + }; + quote!( #[doc = " Generated by sea-orm-macros"] #[derive(Debug, Clone, PartialEq, Eq)] @@ -337,15 +353,7 @@ impl ActiveEnum { } } - #[automatically_derived] - impl sea_orm::TryGetableArray for #ident { - fn try_get_by(res: &sea_orm::QueryResult, index: I) -> std::result::Result, sea_orm::TryGetError> { - <::Value as sea_orm::ActiveEnumValue>::try_get_vec_by(res, index)? - .into_iter() - .map(|value| ::try_from_value(&value).map_err(Into::into)) - .collect() - } - } + #impl_try_getable_array #[automatically_derived] #[allow(clippy::from_over_into)] From 06c632712f3d167df0cda742dd228661b953ab7f Mon Sep 17 00:00:00 2001 From: Dmitry Miasnenko Date: Fri, 27 Oct 2023 16:39:54 +0300 Subject: [PATCH 0191/1371] add sero to COMMUNITY.md (#1935) --- COMMUNITY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/COMMUNITY.md b/COMMUNITY.md index 4e15ff71bc..37e8635155 100644 --- a/COMMUNITY.md +++ b/COMMUNITY.md @@ -25,6 +25,7 @@ If you have built an app using SeaORM and want to showcase it, feel free to open - [OctoBase](https://github.com/toeverything/OctoBase) ![GitHub stars](https://img.shields.io/github/stars/toeverything/OctoBase.svg?style=social) | A light-weight, scalable, offline collaborative data backend written in 🦀 | DB: MySQL, Postgres, SQLite - [Ryot](https://ignisda.github.io/ryot) ([repository](https://github.com/IgnisDa/ryot)) ![GitHub stars](https://img.shields.io/github/stars/ignisda/ryot.svg?style=social) | The only self hosted tracker you will ever need | DB: MySQL, Postgres, SQLite - [Wikijump](https://github.com/scpwiki/wikijump) ([repository](https://github.com/scpwiki/wikijump/tree/develop/deepwell)) ![GitHub stars](https://img.shields.io/github/stars/scpwiki/wikijump.svg?style=social) | API service for Wikijump, a fork of Wikidot | DB: Postgres +- [sero](https://github.com/clowzed/sero): Host static sites with custom subdomains as surge.sh does. But with full control and cool new features. DB: Postgres #### Game - [Ceobe Canteen Serve](https://github.com/Enraged-Dun-Cookie-Development-Team/Ceobe-Canteen-Serve) | A tool based on Arknights mobile game, using axum as web framework | DB: MySQL From 0cffeb62a01b3030c06fc56b9b5792036e7abfc2 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Mon, 23 Oct 2023 04:08:11 +0100 Subject: [PATCH 0192/1371] sea-orm-macros 0.12.5 --- sea-orm-macros/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sea-orm-macros/Cargo.toml b/sea-orm-macros/Cargo.toml index 9c0397a917..db52991096 100644 --- a/sea-orm-macros/Cargo.toml +++ b/sea-orm-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sea-orm-macros" -version = "0.12.4" +version = "0.12.5" authors = [ "Billy Chan " ] edition = "2021" description = "Derive macros for SeaORM" From 8b8b2ae391b954609aca051e3fbdc7122cfc528e Mon Sep 17 00:00:00 2001 From: Wyatt Herkamp Date: Mon, 6 Nov 2023 06:04:18 -0500 Subject: [PATCH 0193/1371] Update Rocket (#1959) --- examples/rocket_example/api/Cargo.toml | 8 ++------ examples/rocket_example/entity/Cargo.toml | 4 +--- examples/rocket_example/migration/Cargo.toml | 2 +- examples/rocket_okapi_example/api/Cargo.toml | 16 +++++++--------- examples/rocket_okapi_example/dto/Cargo.toml | 6 ++---- examples/rocket_okapi_example/entity/Cargo.toml | 4 +--- .../rocket_okapi_example/migration/Cargo.toml | 2 +- sea-orm-rocket/codegen/Cargo.toml | 2 +- sea-orm-rocket/codegen/src/database.rs | 2 +- sea-orm-rocket/lib/Cargo.toml | 4 ++-- sea-orm-rocket/lib/src/database.rs | 6 +++--- 11 files changed, 22 insertions(+), 34 deletions(-) diff --git a/examples/rocket_example/api/Cargo.toml b/examples/rocket_example/api/Cargo.toml index 26269bcf23..f80c7d4ba0 100644 --- a/examples/rocket_example/api/Cargo.toml +++ b/examples/rocket_example/api/Cargo.toml @@ -11,12 +11,8 @@ async-trait = { version = "0.1" } rocket-example-service = { path = "../service" } futures = { version = "0.3" } futures-util = { version = "0.3" } -rocket = { version = "0.5.0-rc.1", features = [ - "json", -] } -rocket_dyn_templates = { version = "0.1.0-rc.1", features = [ - "tera", -] } +rocket = { version = "0.5.0-rc.4", features = ["json"] } +rocket_dyn_templates = { version = "0.1.0-rc.1", features = ["tera"] } serde_json = { version = "1" } entity = { path = "../entity" } migration = { path = "../migration" } diff --git a/examples/rocket_example/entity/Cargo.toml b/examples/rocket_example/entity/Cargo.toml index 28bd59aefe..f5f931816f 100644 --- a/examples/rocket_example/entity/Cargo.toml +++ b/examples/rocket_example/entity/Cargo.toml @@ -9,9 +9,7 @@ name = "entity" path = "src/lib.rs" [dependencies] -rocket = { version = "0.5.0-rc.1", features = [ - "json", -] } +rocket = { version = "0.5.0-rc.4", features = ["json"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project diff --git a/examples/rocket_example/migration/Cargo.toml b/examples/rocket_example/migration/Cargo.toml index e2774d5651..3bd0ff4f62 100644 --- a/examples/rocket_example/migration/Cargo.toml +++ b/examples/rocket_example/migration/Cargo.toml @@ -9,7 +9,7 @@ name = "migration" path = "src/lib.rs" [dependencies] -rocket = { version = "0.5.0-rc.1" } +rocket = { version = "0.5.0-rc.4" } async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] diff --git a/examples/rocket_okapi_example/api/Cargo.toml b/examples/rocket_okapi_example/api/Cargo.toml index 1698ca4366..91fe0e6a19 100644 --- a/examples/rocket_okapi_example/api/Cargo.toml +++ b/examples/rocket_okapi_example/api/Cargo.toml @@ -11,12 +11,8 @@ async-trait = { version = "0.1" } rocket-okapi-example-service = { path = "../service" } futures = { version = "0.3" } futures-util = { version = "0.3" } -rocket = { version = "0.5.0-rc.1", features = [ - "json", -] } -rocket_dyn_templates = { version = "0.1.0-rc.1", features = [ - "tera", -] } +rocket = { version = "0.5.0-rc.4", features = ["json"] } +rocket_dyn_templates = { version = "0.1.0-rc.1", features = ["tera"] } serde_json = { version = "1" } entity = { path = "../entity" } migration = { path = "../migration" } @@ -26,14 +22,16 @@ dto = { path = "../dto" } [dependencies.sea-orm-rocket] path = "../../../sea-orm-rocket/lib" # remove this line in your own project and use the version line -features = ["rocket_okapi"] # enables rocket_okapi so to have open api features enabled +features = [ + "rocket_okapi", +] # enables rocket_okapi so to have open api features enabled # version = "0.5.1" [dependencies.rocket_okapi] version = "0.8.0-rc.2" -features = ["swagger", "rapidoc","rocket_db_pools"] +features = ["swagger", "rapidoc", "rocket_db_pools"] [dependencies.rocket_cors] git = "https://github.com/lawliet89/rocket_cors.git" rev = "54fae070" -default-features = false \ No newline at end of file +default-features = false diff --git a/examples/rocket_okapi_example/dto/Cargo.toml b/examples/rocket_okapi_example/dto/Cargo.toml index a0f208dbaf..233a3dc8cf 100644 --- a/examples/rocket_okapi_example/dto/Cargo.toml +++ b/examples/rocket_okapi_example/dto/Cargo.toml @@ -9,12 +9,10 @@ name = "dto" path = "src/lib.rs" [dependencies] -rocket = { version = "0.5.0-rc.1", features = [ - "json", -] } +rocket = { version = "0.5.0-rc.4", features = ["json"] } [dependencies.entity] path = "../entity" [dependencies.rocket_okapi] -version = "0.8.0-rc.2" \ No newline at end of file +version = "0.8.0-rc.2" diff --git a/examples/rocket_okapi_example/entity/Cargo.toml b/examples/rocket_okapi_example/entity/Cargo.toml index 10cbf52a62..5cc820a064 100644 --- a/examples/rocket_okapi_example/entity/Cargo.toml +++ b/examples/rocket_okapi_example/entity/Cargo.toml @@ -9,9 +9,7 @@ name = "entity" path = "src/lib.rs" [dependencies] -rocket = { version = "0.5.0-rc.1", features = [ - "json", -] } +rocket = { version = "0.5.0-rc.4", features = ["json"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project diff --git a/examples/rocket_okapi_example/migration/Cargo.toml b/examples/rocket_okapi_example/migration/Cargo.toml index e2774d5651..3bd0ff4f62 100644 --- a/examples/rocket_okapi_example/migration/Cargo.toml +++ b/examples/rocket_okapi_example/migration/Cargo.toml @@ -9,7 +9,7 @@ name = "migration" path = "src/lib.rs" [dependencies] -rocket = { version = "0.5.0-rc.1" } +rocket = { version = "0.5.0-rc.4" } async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] diff --git a/sea-orm-rocket/codegen/Cargo.toml b/sea-orm-rocket/codegen/Cargo.toml index 03e34ab0e6..c83b273f1f 100644 --- a/sea-orm-rocket/codegen/Cargo.toml +++ b/sea-orm-rocket/codegen/Cargo.toml @@ -17,6 +17,6 @@ devise = "0.3" quote = "1" [dev-dependencies] -rocket = { version = "0.5.0-rc.1", default-features = false } +rocket = { version = "0.5.0-rc.4", default-features = false } trybuild = "1.0" version_check = "0.9" diff --git a/sea-orm-rocket/codegen/src/database.rs b/sea-orm-rocket/codegen/src/database.rs index 360c847427..c1aba5f3f7 100644 --- a/sea-orm-rocket/codegen/src/database.rs +++ b/sea-orm-rocket/codegen/src/database.rs @@ -64,7 +64,7 @@ pub fn derive_database(input: TokenStream) -> TokenStream { ) -> rocket::request::Outcome { match #db_ty::fetch(req.rocket()) { Some(db) => rocket::outcome::Outcome::Success(db), - None => rocket::outcome::Outcome::Failure(( + None => rocket::outcome::Outcome::Error(( rocket::http::Status::InternalServerError, ())) } } diff --git a/sea-orm-rocket/lib/Cargo.toml b/sea-orm-rocket/lib/Cargo.toml index 57ad8b6ace..0c882a8505 100644 --- a/sea-orm-rocket/lib/Cargo.toml +++ b/sea-orm-rocket/lib/Cargo.toml @@ -13,7 +13,7 @@ edition = "2021" all-features = true [dependencies.rocket] -version = "0.5.0-rc.1" +version = "0.5.0-rc.4" default-features = false [dependencies.sea-orm-rocket-codegen] @@ -26,6 +26,6 @@ default-features = false optional = true [dev-dependencies.rocket] -version = "0.5.0-rc.1" +version = "0.5.0-rc.4" default-features = false features = ["json"] \ No newline at end of file diff --git a/sea-orm-rocket/lib/src/database.rs b/sea-orm-rocket/lib/src/database.rs index 54f6da34d8..fe275e1854 100644 --- a/sea-orm-rocket/lib/src/database.rs +++ b/sea-orm-rocket/lib/src/database.rs @@ -131,10 +131,10 @@ pub trait Database: } let dbtype = std::any::type_name::(); - let fairing = Paint::default(format!("{dbtype}::init()")).bold(); + let fairing = Paint::new(format!("{dbtype}::init()")).bold(); error!( "Attempted to fetch unattached database `{}`.", - Paint::default(dbtype).bold() + Paint::new(dbtype).bold() ); info_!( "`{}` fairing must be attached prior to using this database.", @@ -261,7 +261,7 @@ impl<'r, D: Database> FromRequest<'r> for Connection<'r, D> { async fn from_request(req: &'r Request<'_>) -> Outcome { match D::fetch(req.rocket()) { Some(pool) => Outcome::Success(Connection(pool.borrow())), - None => Outcome::Failure((Status::InternalServerError, None)), + None => Outcome::Error((Status::InternalServerError, None)), } } } From 5bb580936d4d5cc4167b37f2b38c018c6fdb8b8b Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Mon, 6 Nov 2023 12:13:40 +0000 Subject: [PATCH 0194/1371] Bump sea-orm-rocket --- sea-orm-rocket/codegen/Cargo.toml | 2 +- sea-orm-rocket/lib/Cargo.toml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sea-orm-rocket/codegen/Cargo.toml b/sea-orm-rocket/codegen/Cargo.toml index c83b273f1f..52600884ba 100644 --- a/sea-orm-rocket/codegen/Cargo.toml +++ b/sea-orm-rocket/codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sea-orm-rocket-codegen" -version = "0.5.0" +version = "0.5.1" authors = ["Sergio Benitez ", "Jeb Rosen "] description = "Procedural macros for sea_orm_rocket." repository = "https://github.com/SergioBenitez/Rocket/contrib/db_pools" diff --git a/sea-orm-rocket/lib/Cargo.toml b/sea-orm-rocket/lib/Cargo.toml index 0c882a8505..198c57626b 100644 --- a/sea-orm-rocket/lib/Cargo.toml +++ b/sea-orm-rocket/lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sea-orm-rocket" -version = "0.5.2" +version = "0.5.3" authors = ["Sergio Benitez ", "Jeb Rosen "] description = "SeaORM Rocket support crate" repository = "https://github.com/SeaQL/sea-orm" @@ -18,10 +18,10 @@ default-features = false [dependencies.sea-orm-rocket-codegen] path = "../codegen" -version = "0.5.0" +version = "0.5.1" [dependencies.rocket_okapi] -version = "0.8.0-rc.2" +version = "0.8.0-rc.3" default-features = false optional = true From c3b725662291b17142a7b5ab4b8ab022f1d0d78e Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Mon, 6 Nov 2023 12:29:15 +0000 Subject: [PATCH 0195/1371] Remove okapi support --- sea-orm-rocket/lib/Cargo.toml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/sea-orm-rocket/lib/Cargo.toml b/sea-orm-rocket/lib/Cargo.toml index 198c57626b..c9dcaffd62 100644 --- a/sea-orm-rocket/lib/Cargo.toml +++ b/sea-orm-rocket/lib/Cargo.toml @@ -20,12 +20,15 @@ default-features = false path = "../codegen" version = "0.5.1" -[dependencies.rocket_okapi] -version = "0.8.0-rc.3" -default-features = false -optional = true +# [dependencies.rocket_okapi] +# version = "0.8.0-rc.4" +# default-features = false +# optional = true [dev-dependencies.rocket] version = "0.5.0-rc.4" default-features = false -features = ["json"] \ No newline at end of file +features = ["json"] + +[features] +rocket_okapi = [] \ No newline at end of file From 418c18ac0a483ee65923c1697eba7277afa2a85b Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Mon, 6 Nov 2023 12:38:01 +0000 Subject: [PATCH 0196/1371] Update sea-orm-rocket-example --- examples/rocket_example/api/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/rocket_example/api/Cargo.toml b/examples/rocket_example/api/Cargo.toml index f80c7d4ba0..cbcc304788 100644 --- a/examples/rocket_example/api/Cargo.toml +++ b/examples/rocket_example/api/Cargo.toml @@ -20,4 +20,4 @@ tokio = "1.29.0" [dependencies.sea-orm-rocket] path = "../../../sea-orm-rocket/lib" # remove this line in your own project and uncomment the following line -# version = "0.5.2" +# version = "0.5.3" From c81f4d37054a632eb9735270bd3bd5fcf8fd64d1 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Mon, 6 Nov 2023 21:08:33 +0800 Subject: [PATCH 0197/1371] CI: ignore `examples/rocket_okapi_example` --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 6e3a523bd5..7be8cd0f85 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -232,7 +232,7 @@ jobs: steps: - uses: actions/checkout@v3 - id: set-matrix - run: echo "path_matrix=$(find examples -mindepth 1 -maxdepth 1 -type d -printf '%P\0' | jq -Rc '[ split("\u0000") | .[] | "examples/\(.)" ]')" >> $GITHUB_OUTPUT + run: echo "path_matrix=$(find examples -mindepth 1 -maxdepth 1 -type d -not -path 'examples/rocket_okapi_example' -printf '%P\0' | jq -Rc '[ split("\u0000") | .[] | "examples/\(.)" ]')" >> $GITHUB_OUTPUT outputs: path_matrix: ${{ steps.set-matrix.outputs.path_matrix }} From b4010ecb6416b5e3e5e054800d1559945859205c Mon Sep 17 00:00:00 2001 From: Wyatt Herkamp Date: Wed, 8 Nov 2023 10:45:38 -0500 Subject: [PATCH 0198/1371] Add #[sea_orm(skip)] for FromQueryResult macro (#1954) * Add #[sea_orm(skip)] for FromQueryResult macro * Update sea-orm-rocket * Revert "Update sea-orm-rocket" This reverts commit b7226bb44dbdbab9782524feb0303c4c166d3050. --- .../src/derives/from_query_result.rs | 74 +++++++++++++++---- sea-orm-macros/src/lib.rs | 7 +- tests/derive_tests.rs | 7 ++ 3 files changed, 71 insertions(+), 17 deletions(-) diff --git a/sea-orm-macros/src/derives/from_query_result.rs b/sea-orm-macros/src/derives/from_query_result.rs index 999db62feb..a072827b22 100644 --- a/sea-orm-macros/src/derives/from_query_result.rs +++ b/sea-orm-macros/src/derives/from_query_result.rs @@ -1,6 +1,29 @@ +use self::util::GetMeta; use proc_macro2::{Ident, TokenStream}; -use quote::{format_ident, quote, quote_spanned}; -use syn::{ext::IdentExt, Data, DataStruct, Field, Fields, Generics}; +use quote::{format_ident, quote, quote_spanned, ToTokens}; +use syn::{ + ext::IdentExt, punctuated::Punctuated, token::Comma, Data, DataStruct, Fields, Generics, Meta, +}; + +pub struct FromQueryResultItem { + pub skip: bool, + pub ident: Ident, +} +impl ToTokens for FromQueryResultItem { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { ident, skip } = self; + if *skip { + tokens.extend(quote! { + #ident: std::default::Default::default(), + }); + } else { + let name = ident.unraw().to_string(); + tokens.extend(quote! { + #ident: row.try_get(pre, #name)?, + }); + } + } +} /// Method to derive a [QueryResult](sea_orm::QueryResult) pub fn expand_derive_from_query_result( @@ -19,20 +42,23 @@ pub fn expand_derive_from_query_result( }) } }; + let mut field = Vec::with_capacity(fields.len()); - let field: Vec = fields - .into_iter() - .map(|Field { ident, .. }| format_ident!("{}", ident.unwrap().to_string())) - .collect(); - - let name: Vec = field - .iter() - .map(|f| { - let s = f.unraw().to_string(); - quote! { #s } - }) - .collect(); - + for parsed_field in fields.into_iter() { + let mut skip = false; + for attr in parsed_field.attrs.iter() { + if !attr.path().is_ident("sea_orm") { + continue; + } + if let Ok(list) = attr.parse_args_with(Punctuated::::parse_terminated) { + for meta in list.iter() { + skip = meta.exists("skip"); + } + } + } + let ident = format_ident!("{}", parsed_field.ident.unwrap().to_string()); + field.push(FromQueryResultItem { skip, ident }); + } let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); Ok(quote!( @@ -40,9 +66,25 @@ pub fn expand_derive_from_query_result( impl #impl_generics sea_orm::FromQueryResult for #ident #ty_generics #where_clause { fn from_query_result(row: &sea_orm::QueryResult, pre: &str) -> std::result::Result { Ok(Self { - #(#field: row.try_get(pre, #name)?),* + #(#field)* }) } } )) } +mod util { + use syn::Meta; + + pub(super) trait GetMeta { + fn exists(&self, k: &str) -> bool; + } + + impl GetMeta for Meta { + fn exists(&self, k: &str) -> bool { + let Meta::Path(path) = self else { + return false; + }; + path.is_ident(k) + } + } +} diff --git a/sea-orm-macros/src/lib.rs b/sea-orm-macros/src/lib.rs index 85a9fee622..52f2e734ab 100644 --- a/sea-orm-macros/src/lib.rs +++ b/sea-orm-macros/src/lib.rs @@ -579,6 +579,9 @@ pub fn derive_active_enum(input: TokenStream) -> TokenStream { /// Convert a query result into the corresponding Model. /// +/// ### Attributes +/// - `skip`: Will not try to pull this field from the query result. And set it to the default value of the type. +/// /// ### Usage /// /// ``` @@ -588,10 +591,12 @@ pub fn derive_active_enum(input: TokenStream) -> TokenStream { /// struct SelectResult { /// name: String, /// num_of_fruits: i32, +/// #[sea_orm(skip)] +/// skip_me: i32, /// } /// ``` #[cfg(feature = "derive")] -#[proc_macro_derive(FromQueryResult)] +#[proc_macro_derive(FromQueryResult, attributes(sea_orm))] pub fn derive_from_query_result(input: TokenStream) -> TokenStream { let DeriveInput { ident, diff --git a/tests/derive_tests.rs b/tests/derive_tests.rs index 20e49d06bc..bd99a3b46b 100644 --- a/tests/derive_tests.rs +++ b/tests/derive_tests.rs @@ -56,3 +56,10 @@ where { _foo: T::Item, } + +#[derive(FromQueryResult)] +struct FromQueryAttributeTests { + #[sea_orm(skip)] + _foo: i32, + _bar: String, +} From d8bf5abd9aadac71dd5f162de76153784c39444e Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Wed, 8 Nov 2023 17:04:55 +0000 Subject: [PATCH 0199/1371] Changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46d9bec3fb..d554f11576 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## 0.12.6 - pending + +### New Features + +* Added `#[sea_orm(skip)]` for `FromQueryResult` derive macro + +## 0.12.5 - skipped + ## 0.12.4 - 2023-10-19 ### New Features From 5fe81c58678e214d1d5981b4ec08527b2e559acd Mon Sep 17 00:00:00 2001 From: Zander Milroy Date: Wed, 8 Nov 2023 12:06:33 -0500 Subject: [PATCH 0200/1371] Fix duplicated active enum `use` statements on generated entities (#1953) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: Only generate `use` statements once per enum * Adds gen_import_active_enum tests --------- Co-authored-by: Schlüter Lukas --- sea-orm-codegen/src/entity/writer.rs | 154 +++++++++++++++++++++++++-- 1 file changed, 144 insertions(+), 10 deletions(-) diff --git a/sea-orm-codegen/src/entity/writer.rs b/sea-orm-codegen/src/entity/writer.rs index dc60654fa1..6600f46242 100644 --- a/sea-orm-codegen/src/entity/writer.rs +++ b/sea-orm-codegen/src/entity/writer.rs @@ -460,15 +460,23 @@ impl EntityWriter { entity .columns .iter() - .fold(TokenStream::new(), |mut ts, col| { - if let sea_query::ColumnType::Enum { name, .. } = col.get_inner_col_type() { - let enum_name = format_ident!("{}", name.to_string().to_upper_camel_case()); - ts.extend([quote! { - use super::sea_orm_active_enums::#enum_name; - }]); - } - ts - }) + .fold( + (TokenStream::new(), Vec::new()), + |(mut ts, mut enums), col| { + if let sea_query::ColumnType::Enum { name, .. } = col.get_inner_col_type() { + if !enums.contains(&name) { + enums.push(name); + let enum_name = + format_ident!("{}", name.to_string().to_upper_camel_case()); + ts.extend([quote! { + use super::sea_orm_active_enums::#enum_name; + }]); + } + } + (ts, enums) + }, + ) + .0 } pub fn gen_model_struct( @@ -814,7 +822,8 @@ mod tests { }; use pretty_assertions::assert_eq; use proc_macro2::TokenStream; - use sea_query::{ColumnType, ForeignKeyAction, RcOrArc}; + use quote::quote; + use sea_query::{Alias, ColumnType, ForeignKeyAction, RcOrArc, SeaRc}; use std::io::{self, BufRead, BufReader, Read}; fn setup() -> Vec { @@ -2275,4 +2284,129 @@ mod tests { Ok(()) } + + #[test] + fn test_gen_import_active_enum() -> io::Result<()> { + let entities = vec![ + Entity { + table_name: "tea_pairing".to_owned(), + columns: vec![ + Column { + name: "id".to_owned(), + col_type: ColumnType::Integer, + auto_increment: true, + not_null: true, + unique: false, + }, + Column { + name: "first_tea".to_owned(), + col_type: ColumnType::Enum { + name: SeaRc::new(Alias::new("tea_enum")), + variants: vec![ + SeaRc::new(Alias::new("everyday_tea")), + SeaRc::new(Alias::new("breakfast_tea")), + ], + }, + auto_increment: false, + not_null: true, + unique: false, + }, + Column { + name: "second_tea".to_owned(), + col_type: ColumnType::Enum { + name: SeaRc::new(Alias::new("tea_enum")), + variants: vec![ + SeaRc::new(Alias::new("everyday_tea")), + SeaRc::new(Alias::new("breakfast_tea")), + ], + }, + auto_increment: false, + not_null: true, + unique: false, + }, + ], + relations: vec![], + conjunct_relations: vec![], + primary_keys: vec![PrimaryKey { + name: "id".to_owned(), + }], + }, + Entity { + table_name: "tea_pairing_with_size".to_owned(), + columns: vec![ + Column { + name: "id".to_owned(), + col_type: ColumnType::Integer, + auto_increment: true, + not_null: true, + unique: false, + }, + Column { + name: "first_tea".to_owned(), + col_type: ColumnType::Enum { + name: SeaRc::new(Alias::new("tea_enum")), + variants: vec![ + SeaRc::new(Alias::new("everyday_tea")), + SeaRc::new(Alias::new("breakfast_tea")), + ], + }, + auto_increment: false, + not_null: true, + unique: false, + }, + Column { + name: "second_tea".to_owned(), + col_type: ColumnType::Enum { + name: SeaRc::new(Alias::new("tea_enum")), + variants: vec![ + SeaRc::new(Alias::new("everyday_tea")), + SeaRc::new(Alias::new("breakfast_tea")), + ], + }, + auto_increment: false, + not_null: true, + unique: false, + }, + Column { + name: "size".to_owned(), + col_type: ColumnType::Enum { + name: SeaRc::new(Alias::new("tea_size")), + variants: vec![ + SeaRc::new(Alias::new("small")), + SeaRc::new(Alias::new("medium")), + SeaRc::new(Alias::new("huge")), + ], + }, + auto_increment: false, + not_null: true, + unique: false, + }, + ], + relations: vec![], + conjunct_relations: vec![], + primary_keys: vec![PrimaryKey { + name: "id".to_owned(), + }], + }, + ]; + + assert_eq!( + quote!( + use super::sea_orm_active_enums::TeaEnum; + ) + .to_string(), + EntityWriter::gen_import_active_enum(&entities[0]).to_string() + ); + + assert_eq!( + quote!( + use super::sea_orm_active_enums::TeaEnum; + use super::sea_orm_active_enums::TeaSize; + ) + .to_string(), + EntityWriter::gen_import_active_enum(&entities[1]).to_string() + ); + + Ok(()) + } } From d6835b125e42da59ae242882c899c3130e264f50 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Wed, 8 Nov 2023 17:09:15 +0000 Subject: [PATCH 0201/1371] [cli] Changelog --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d554f11576..20f6ee46f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### New Features -* Added `#[sea_orm(skip)]` for `FromQueryResult` derive macro +* Added `#[sea_orm(skip)]` for `FromQueryResult` derive macro https://github.com/SeaQL/sea-orm/pull/1954 + +### Bug Fixes + +* [sea-orm-cli] Fix duplicated active enum use statements on generated entities https://github.com/SeaQL/sea-orm/pull/1953 ## 0.12.5 - skipped From 0dbfb42bb7f0132657607fc5cbd4854d8279ba99 Mon Sep 17 00:00:00 2001 From: Lev Khoroshansky Date: Wed, 8 Nov 2023 20:04:24 +0100 Subject: [PATCH 0202/1371] Support `--enum-extra-derives` (#1934) * feat: Support `--enum-extra-derives` * test: Enum extra derives * style: Don't format derives * fix: Put `quote!` inside a function with `#[rustfmt::skip]` --- sea-orm-cli/src/cli.rs | 7 ++++ sea-orm-cli/src/commands/generate.rs | 2 + sea-orm-codegen/src/entity/active_enum.rs | 50 +++++++++++++++++++++-- sea-orm-codegen/src/entity/writer.rs | 30 +++++++++----- 4 files changed, 74 insertions(+), 15 deletions(-) diff --git a/sea-orm-cli/src/cli.rs b/sea-orm-cli/src/cli.rs index ff88d30a4a..cc92208c62 100644 --- a/sea-orm-cli/src/cli.rs +++ b/sea-orm-cli/src/cli.rs @@ -280,6 +280,13 @@ pub enum GenerateSubcommands { )] model_extra_attributes: Vec, + #[arg( + long, + value_delimiter = ',', + help = "Add extra derive macros to generated enums (comma separated), e.g. `--enum-extra-derives 'ts_rs::Ts','CustomDerive'`" + )] + enum_extra_derives: Vec, + #[arg( long, default_value = "false", diff --git a/sea-orm-cli/src/commands/generate.rs b/sea-orm-cli/src/commands/generate.rs index 7b7a895620..8e8994fc09 100644 --- a/sea-orm-cli/src/commands/generate.rs +++ b/sea-orm-cli/src/commands/generate.rs @@ -31,6 +31,7 @@ pub async fn run_generate_command( lib, model_extra_derives, model_extra_attributes, + enum_extra_derives, seaography, } => { if verbose { @@ -180,6 +181,7 @@ pub async fn run_generate_command( serde_skip_hidden_column, model_extra_derives, model_extra_attributes, + enum_extra_derives, seaography, ); let output = EntityTransformer::transform(table_stmts)?.generate(&writer_context); diff --git a/sea-orm-codegen/src/entity/active_enum.rs b/sea-orm-codegen/src/entity/active_enum.rs index 30c9c5df66..95fd0a03b7 100644 --- a/sea-orm-codegen/src/entity/active_enum.rs +++ b/sea-orm-codegen/src/entity/active_enum.rs @@ -12,7 +12,12 @@ pub struct ActiveEnum { } impl ActiveEnum { - pub fn impl_active_enum(&self, with_serde: &WithSerde, with_copy_enums: bool) -> TokenStream { + pub fn impl_active_enum( + &self, + with_serde: &WithSerde, + with_copy_enums: bool, + extra_derives: &TokenStream, + ) -> TokenStream { let enum_name = &self.enum_name.to_string(); let enum_iden = format_ident!("{}", enum_name.to_upper_camel_case()); let values: Vec = self.values.iter().map(|v| v.to_string()).collect(); @@ -24,7 +29,7 @@ impl ActiveEnum { } }); - let extra_derive = with_serde.extra_derive(); + let serde_derive = with_serde.extra_derive(); let copy_derive = if with_copy_enums { quote! { , Copy } } else { @@ -32,7 +37,7 @@ impl ActiveEnum { }; quote! { - #[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum #copy_derive #extra_derive)] + #[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum #copy_derive #serde_derive #extra_derives)] #[sea_orm(rs_type = "String", db_type = "Enum", enum_name = #enum_name)] pub enum #enum_iden { #( @@ -46,6 +51,8 @@ impl ActiveEnum { #[cfg(test)] mod tests { + use crate::entity::writer::bonus_derive; + use super::*; use pretty_assertions::assert_eq; use sea_query::{Alias, IntoIden}; @@ -72,7 +79,7 @@ mod tests { .map(|variant| Alias::new(variant).into_iden()) .collect(), } - .impl_active_enum(&WithSerde::None, true) + .impl_active_enum(&WithSerde::None, true, "e! {}) .to_string(), quote!( #[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Copy)] @@ -105,4 +112,39 @@ mod tests { .to_string() ) } + + #[test] + fn test_enum_extra_derives() { + assert_eq!( + ActiveEnum { + enum_name: Alias::new("media_type").into_iden(), + values: vec!["UNKNOWN", "BITMAP",] + .into_iter() + .map(|variant| Alias::new(variant).into_iden()) + .collect(), + } + .impl_active_enum( + &WithSerde::None, + true, + &bonus_derive(["specta::Type", "ts_rs::TS"]) + ) + .to_string(), + build_generated_enum(), + ); + + #[rustfmt::skip] + fn build_generated_enum() -> String { + quote!( + #[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Copy, specta :: Type, ts_rs :: TS)] + #[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "media_type")] + pub enum MediaType { + #[sea_orm(string_value = "UNKNOWN")] + Unknown, + #[sea_orm(string_value = "BITMAP")] + Bitmap, + } + ) + .to_string() + } + } } diff --git a/sea-orm-codegen/src/entity/writer.rs b/sea-orm-codegen/src/entity/writer.rs index 6600f46242..695a3de08b 100644 --- a/sea-orm-codegen/src/entity/writer.rs +++ b/sea-orm-codegen/src/entity/writer.rs @@ -47,6 +47,7 @@ pub struct EntityWriterContext { pub(crate) serde_skip_deserializing_primary_key: bool, pub(crate) model_extra_derives: TokenStream, pub(crate) model_extra_attributes: TokenStream, + pub(crate) enum_extra_derives: TokenStream, pub(crate) seaography: bool, } @@ -79,19 +80,19 @@ impl WithSerde { } } -/// Converts model_extra_derives argument to token stream -fn bonus_derive(model_extra_derives: I) -> TokenStream +/// Converts *_extra_derives argument to token stream +pub(crate) fn bonus_derive(extra_derives: I) -> TokenStream where T: Into, I: IntoIterator, { - model_extra_derives - .into_iter() - .map(Into::::into) - .fold(TokenStream::default(), |acc, derive| { + extra_derives.into_iter().map(Into::::into).fold( + TokenStream::default(), + |acc, derive| { let tokens: TokenStream = derive.parse().unwrap(); quote! { #acc, #tokens } - }) + }, + ) } /// convert attributes argument to token stream @@ -143,6 +144,7 @@ impl EntityWriterContext { serde_skip_hidden_column: bool, model_extra_derives: Vec, model_extra_attributes: Vec, + enum_extra_derives: Vec, seaography: bool, ) -> Self { Self { @@ -156,6 +158,7 @@ impl EntityWriterContext { serde_skip_hidden_column, model_extra_derives: bonus_derive(model_extra_derives), model_extra_attributes: bonus_attributes(model_extra_attributes), + enum_extra_derives: bonus_derive(enum_extra_derives), seaography, } } @@ -168,9 +171,11 @@ impl EntityWriter { files.push(self.write_index_file(context.lib)); files.push(self.write_prelude()); if !self.enums.is_empty() { - files.push( - self.write_sea_orm_active_enums(&context.with_serde, context.with_copy_enums), - ); + files.push(self.write_sea_orm_active_enums( + &context.with_serde, + context.with_copy_enums, + &context.enum_extra_derives, + )); } WriterOutput { files } } @@ -283,6 +288,7 @@ impl EntityWriter { &self, with_serde: &WithSerde, with_copy_enums: bool, + extra_derives: &TokenStream, ) -> OutputFile { let mut lines = Vec::new(); Self::write_doc_comment(&mut lines); @@ -291,7 +297,9 @@ impl EntityWriter { let code_blocks = self .enums .values() - .map(|active_enum| active_enum.impl_active_enum(with_serde, with_copy_enums)) + .map(|active_enum| { + active_enum.impl_active_enum(with_serde, with_copy_enums, extra_derives) + }) .collect(); Self::write(&mut lines, code_blocks); OutputFile { From 8ab24cf884ba2ea61c8be0b917e79e077b441dff Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Wed, 8 Nov 2023 19:41:57 +0000 Subject: [PATCH 0203/1371] Enum extra attributes (#1966) * Feature: Adds sea-orm-cli generate `--enum-extra-attributes` option (#1952) * Adds `--enum-extra-derives` * Adds test_enum_extra_attributes --------- Co-authored-by: Chris Tsang * Fixup --------- Co-authored-by: Zander Milroy --- sea-orm-cli/src/cli.rs | 7 ++ sea-orm-cli/src/commands/generate.rs | 2 + sea-orm-codegen/src/entity/active_enum.rs | 84 +++++++++++++++++++++-- sea-orm-codegen/src/entity/writer.rs | 16 ++++- 4 files changed, 102 insertions(+), 7 deletions(-) diff --git a/sea-orm-cli/src/cli.rs b/sea-orm-cli/src/cli.rs index cc92208c62..e8611373e2 100644 --- a/sea-orm-cli/src/cli.rs +++ b/sea-orm-cli/src/cli.rs @@ -287,6 +287,13 @@ pub enum GenerateSubcommands { )] enum_extra_derives: Vec, + #[arg( + long, + value_delimiter = ',', + help = r#"Add extra attributes to generated enums, no need for `#[]` (comma separated), e.g. `--enum-extra-attributes 'serde(rename_all = "camelCase")','ts(export)'`"# + )] + enum_extra_attributes: Vec, + #[arg( long, default_value = "false", diff --git a/sea-orm-cli/src/commands/generate.rs b/sea-orm-cli/src/commands/generate.rs index 8e8994fc09..613a541fbb 100644 --- a/sea-orm-cli/src/commands/generate.rs +++ b/sea-orm-cli/src/commands/generate.rs @@ -32,6 +32,7 @@ pub async fn run_generate_command( model_extra_derives, model_extra_attributes, enum_extra_derives, + enum_extra_attributes, seaography, } => { if verbose { @@ -182,6 +183,7 @@ pub async fn run_generate_command( model_extra_derives, model_extra_attributes, enum_extra_derives, + enum_extra_attributes, seaography, ); let output = EntityTransformer::transform(table_stmts)?.generate(&writer_context); diff --git a/sea-orm-codegen/src/entity/active_enum.rs b/sea-orm-codegen/src/entity/active_enum.rs index 95fd0a03b7..e7e0480c7b 100644 --- a/sea-orm-codegen/src/entity/active_enum.rs +++ b/sea-orm-codegen/src/entity/active_enum.rs @@ -17,6 +17,7 @@ impl ActiveEnum { with_serde: &WithSerde, with_copy_enums: bool, extra_derives: &TokenStream, + extra_attributes: &TokenStream, ) -> TokenStream { let enum_name = &self.enum_name.to_string(); let enum_iden = format_ident!("{}", enum_name.to_upper_camel_case()); @@ -39,6 +40,7 @@ impl ActiveEnum { quote! { #[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum #copy_derive #serde_derive #extra_derives)] #[sea_orm(rs_type = "String", db_type = "Enum", enum_name = #enum_name)] + #extra_attributes pub enum #enum_iden { #( #[sea_orm(string_value = #values)] @@ -51,9 +53,8 @@ impl ActiveEnum { #[cfg(test)] mod tests { - use crate::entity::writer::bonus_derive; - use super::*; + use crate::entity::writer::{bonus_attributes, bonus_derive}; use pretty_assertions::assert_eq; use sea_query::{Alias, IntoIden}; @@ -79,7 +80,12 @@ mod tests { .map(|variant| Alias::new(variant).into_iden()) .collect(), } - .impl_active_enum(&WithSerde::None, true, "e! {}) + .impl_active_enum( + &WithSerde::None, + true, + &TokenStream::new(), + &TokenStream::new(), + ) .to_string(), quote!( #[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Copy)] @@ -126,7 +132,8 @@ mod tests { .impl_active_enum( &WithSerde::None, true, - &bonus_derive(["specta::Type", "ts_rs::TS"]) + &bonus_derive(["specta::Type", "ts_rs::TS"]), + &TokenStream::new(), ) .to_string(), build_generated_enum(), @@ -147,4 +154,73 @@ mod tests { .to_string() } } + + #[test] + fn test_enum_extra_attributes() { + assert_eq!( + ActiveEnum { + enum_name: Alias::new("coinflip_result_type").into_iden(), + values: vec!["HEADS", "TAILS"] + .into_iter() + .map(|variant| Alias::new(variant).into_iden()) + .collect(), + } + .impl_active_enum( + &WithSerde::None, + true, + &TokenStream::new(), + &bonus_attributes([r#"serde(rename_all = "camelCase")"#]) + ) + .to_string(), + quote!( + #[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Copy)] + #[sea_orm( + rs_type = "String", + db_type = "Enum", + enum_name = "coinflip_result_type" + )] + #[serde(rename_all = "camelCase")] + pub enum CoinflipResultType { + #[sea_orm(string_value = "HEADS")] + Heads, + #[sea_orm(string_value = "TAILS")] + Tails, + } + ) + .to_string() + ); + assert_eq!( + ActiveEnum { + enum_name: Alias::new("coinflip_result_type").into_iden(), + values: vec!["HEADS", "TAILS"] + .into_iter() + .map(|variant| Alias::new(variant).into_iden()) + .collect(), + } + .impl_active_enum( + &WithSerde::None, + true, + &TokenStream::new(), + &bonus_attributes([r#"serde(rename_all = "camelCase")"#, "ts(export)"]) + ) + .to_string(), + quote!( + #[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Copy)] + #[sea_orm( + rs_type = "String", + db_type = "Enum", + enum_name = "coinflip_result_type" + )] + #[serde(rename_all = "camelCase")] + #[ts(export)] + pub enum CoinflipResultType { + #[sea_orm(string_value = "HEADS")] + Heads, + #[sea_orm(string_value = "TAILS")] + Tails, + } + ) + .to_string() + ) + } } diff --git a/sea-orm-codegen/src/entity/writer.rs b/sea-orm-codegen/src/entity/writer.rs index 695a3de08b..099a96797d 100644 --- a/sea-orm-codegen/src/entity/writer.rs +++ b/sea-orm-codegen/src/entity/writer.rs @@ -48,6 +48,7 @@ pub struct EntityWriterContext { pub(crate) model_extra_derives: TokenStream, pub(crate) model_extra_attributes: TokenStream, pub(crate) enum_extra_derives: TokenStream, + pub(crate) enum_extra_attributes: TokenStream, pub(crate) seaography: bool, } @@ -95,8 +96,8 @@ where ) } -/// convert attributes argument to token stream -fn bonus_attributes(attributes: I) -> TokenStream +/// convert *_extra_attributes argument to token stream +pub(crate) fn bonus_attributes(attributes: I) -> TokenStream where T: Into, I: IntoIterator, @@ -145,6 +146,7 @@ impl EntityWriterContext { model_extra_derives: Vec, model_extra_attributes: Vec, enum_extra_derives: Vec, + enum_extra_attributes: Vec, seaography: bool, ) -> Self { Self { @@ -159,6 +161,7 @@ impl EntityWriterContext { model_extra_derives: bonus_derive(model_extra_derives), model_extra_attributes: bonus_attributes(model_extra_attributes), enum_extra_derives: bonus_derive(enum_extra_derives), + enum_extra_attributes: bonus_attributes(enum_extra_attributes), seaography, } } @@ -175,6 +178,7 @@ impl EntityWriter { &context.with_serde, context.with_copy_enums, &context.enum_extra_derives, + &context.enum_extra_attributes, )); } WriterOutput { files } @@ -289,6 +293,7 @@ impl EntityWriter { with_serde: &WithSerde, with_copy_enums: bool, extra_derives: &TokenStream, + extra_attributes: &TokenStream, ) -> OutputFile { let mut lines = Vec::new(); Self::write_doc_comment(&mut lines); @@ -298,7 +303,12 @@ impl EntityWriter { .enums .values() .map(|active_enum| { - active_enum.impl_active_enum(with_serde, with_copy_enums, extra_derives) + active_enum.impl_active_enum( + with_serde, + with_copy_enums, + extra_derives, + extra_attributes, + ) }) .collect(); Self::write(&mut lines, code_blocks); From efa508432d0e1c2e98d9f97f8c5bd8befa8bdf6e Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Wed, 8 Nov 2023 20:09:21 +0000 Subject: [PATCH 0204/1371] Changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20f6ee46f5..50a6d7f34f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Bug Fixes * [sea-orm-cli] Fix duplicated active enum use statements on generated entities https://github.com/SeaQL/sea-orm/pull/1953 +* [sea-orm-cli] Added `--enum-extra-attributes` https://github.com/SeaQL/sea-orm/pull/1952 +* [sea-orm-cli] Added `--enum-extra-derives` https://github.com/SeaQL/sea-orm/pull/1934 ## 0.12.5 - skipped From 37b99228b203fc5e4a19979b4a0d5d22248fc3cd Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Wed, 8 Nov 2023 20:09:51 +0000 Subject: [PATCH 0205/1371] Changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50a6d7f34f..6f6b6c4ac0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,8 +14,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Bug Fixes * [sea-orm-cli] Fix duplicated active enum use statements on generated entities https://github.com/SeaQL/sea-orm/pull/1953 -* [sea-orm-cli] Added `--enum-extra-attributes` https://github.com/SeaQL/sea-orm/pull/1952 * [sea-orm-cli] Added `--enum-extra-derives` https://github.com/SeaQL/sea-orm/pull/1934 +* [sea-orm-cli] Added `--enum-extra-attributes` https://github.com/SeaQL/sea-orm/pull/1952 ## 0.12.5 - skipped From 070a30aab17b1adaf1ab247229f51abcb8d5ec84 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sun, 12 Nov 2023 20:14:29 +0000 Subject: [PATCH 0206/1371] 0.12.5 --- CHANGELOG.md | 4 +--- Cargo.toml | 4 ++-- sea-orm-cli/Cargo.toml | 4 ++-- sea-orm-codegen/Cargo.toml | 2 +- sea-orm-migration/Cargo.toml | 6 +++--- 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f6b6c4ac0..377c2c5e59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## 0.12.6 - pending +## 0.12.5 - 2023-11-12 ### New Features @@ -17,8 +17,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/). * [sea-orm-cli] Added `--enum-extra-derives` https://github.com/SeaQL/sea-orm/pull/1934 * [sea-orm-cli] Added `--enum-extra-attributes` https://github.com/SeaQL/sea-orm/pull/1952 -## 0.12.5 - skipped - ## 0.12.4 - 2023-10-19 ### New Features diff --git a/Cargo.toml b/Cargo.toml index a8d9fb662a..ddaa271a06 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [".", "sea-orm-macros", "sea-orm-codegen"] [package] name = "sea-orm" -version = "0.12.4" +version = "0.12.5" authors = ["Chris Tsang "] edition = "2021" description = "🐚 An async & dynamic ORM for Rust" @@ -33,7 +33,7 @@ log = { version = "0.4", default-features = false } tracing = { version = "0.1", default-features = false, features = ["attributes", "log"] } rust_decimal = { version = "1", default-features = false, optional = true } bigdecimal = { version = "0.3", default-features = false, optional = true } -sea-orm-macros = { version = "0.12.4", path = "sea-orm-macros", default-features = false, features = ["strum"] } +sea-orm-macros = { version = "0.12.5", path = "sea-orm-macros", default-features = false, features = ["strum"] } sea-query = { version = "0.30.2", default-features = false, features = ["thread-safe", "hashable-value", "backend-mysql", "backend-postgres", "backend-sqlite"] } sea-query-binder = { version = "0.5.0", default-features = false, optional = true } strum = { version = "0.25", default-features = false } diff --git a/sea-orm-cli/Cargo.toml b/sea-orm-cli/Cargo.toml index 42a18105fa..6945155b97 100644 --- a/sea-orm-cli/Cargo.toml +++ b/sea-orm-cli/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "sea-orm-cli" -version = "0.12.4" +version = "0.12.5" authors = [ "Chris Tsang ", "Billy Chan ", @@ -37,7 +37,7 @@ required-features = ["cli", "codegen"] clap = { version = "4.3", features = ["env", "derive"], optional = true } dotenvy = { version = "0.15", default-features = false, optional = true } async-std = { version = "1.9", default-features = false, features = ["attributes", "tokio1"], optional = true } -sea-orm-codegen = { version = "=0.12.4", path = "../sea-orm-codegen", default-features = false, optional = true } +sea-orm-codegen = { version = "=0.12.5", path = "../sea-orm-codegen", default-features = false, optional = true } sea-schema = { version = "0.14.0" } sqlx = { version = "0.7", default-features = false, features = ["mysql", "postgres"], optional = true } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["env-filter", "fmt"] } diff --git a/sea-orm-codegen/Cargo.toml b/sea-orm-codegen/Cargo.toml index 61cd07127c..2a1b29b447 100644 --- a/sea-orm-codegen/Cargo.toml +++ b/sea-orm-codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sea-orm-codegen" -version = "0.12.4" +version = "0.12.5" authors = ["Billy Chan "] edition = "2021" description = "Code Generator for SeaORM" diff --git a/sea-orm-migration/Cargo.toml b/sea-orm-migration/Cargo.toml index 6d1f91d61f..7785dbf645 100644 --- a/sea-orm-migration/Cargo.toml +++ b/sea-orm-migration/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "sea-orm-migration" -version = "0.12.4" +version = "0.12.5" authors = ["Billy Chan "] edition = "2021" description = "Migration utility for SeaORM" @@ -23,8 +23,8 @@ path = "src/lib.rs" async-trait = { version = "0.1", default-features = false } clap = { version = "4.3", features = ["env", "derive"], optional = true } dotenvy = { version = "0.15", default-features = false, optional = true } -sea-orm = { version = "0.12.4", path = "../", default-features = false, features = ["macros"] } -sea-orm-cli = { version = "0.12.4", path = "../sea-orm-cli", default-features = false, optional = true } +sea-orm = { version = "0.12.5", path = "../", default-features = false, features = ["macros"] } +sea-orm-cli = { version = "0.12.5", path = "../sea-orm-cli", default-features = false, optional = true } sea-schema = { version = "0.14.1" } tracing = { version = "0.1", default-features = false, features = ["log"] } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["env-filter", "fmt"] } From 5d677a401b734cd163394b1ab649d81cf170d698 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sun, 12 Nov 2023 20:14:30 +0000 Subject: [PATCH 0207/1371] update examples --- examples/actix3_example/entity/Cargo.toml | 2 +- examples/actix3_example/migration/Cargo.toml | 2 +- examples/actix3_example/service/Cargo.toml | 2 +- examples/actix_example/entity/Cargo.toml | 2 +- examples/actix_example/migration/Cargo.toml | 2 +- examples/actix_example/service/Cargo.toml | 2 +- examples/axum_example/entity/Cargo.toml | 2 +- examples/axum_example/migration/Cargo.toml | 2 +- examples/axum_example/service/Cargo.toml | 2 +- examples/graphql_example/entity/Cargo.toml | 2 +- examples/graphql_example/migration/Cargo.toml | 2 +- examples/graphql_example/service/Cargo.toml | 2 +- examples/jsonrpsee_example/entity/Cargo.toml | 2 +- examples/jsonrpsee_example/migration/Cargo.toml | 2 +- examples/jsonrpsee_example/service/Cargo.toml | 2 +- examples/poem_example/entity/Cargo.toml | 2 +- examples/poem_example/migration/Cargo.toml | 2 +- examples/poem_example/service/Cargo.toml | 2 +- examples/rocket_example/entity/Cargo.toml | 2 +- examples/rocket_example/migration/Cargo.toml | 2 +- examples/rocket_example/service/Cargo.toml | 2 +- examples/rocket_okapi_example/entity/Cargo.toml | 2 +- examples/rocket_okapi_example/migration/Cargo.toml | 2 +- examples/rocket_okapi_example/service/Cargo.toml | 2 +- examples/salvo_example/entity/Cargo.toml | 2 +- examples/salvo_example/migration/Cargo.toml | 2 +- examples/salvo_example/service/Cargo.toml | 2 +- examples/seaography_example/migration/Cargo.toml | 4 ++-- examples/tonic_example/entity/Cargo.toml | 2 +- examples/tonic_example/migration/Cargo.toml | 2 +- examples/tonic_example/service/Cargo.toml | 2 +- 31 files changed, 32 insertions(+), 32 deletions(-) diff --git a/examples/actix3_example/entity/Cargo.toml b/examples/actix3_example/entity/Cargo.toml index 423712d5ea..0bdda2b18e 100644 --- a/examples/actix3_example/entity/Cargo.toml +++ b/examples/actix3_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.4" # sea-orm version +version = "0.12.5" # sea-orm version diff --git a/examples/actix3_example/migration/Cargo.toml b/examples/actix3_example/migration/Cargo.toml index cab780f350..fde1eb6818 100644 --- a/examples/actix3_example/migration/Cargo.toml +++ b/examples/actix3_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.4" # sea-orm-migration version +version = "0.12.5" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-async-std-native-tls", diff --git a/examples/actix3_example/service/Cargo.toml b/examples/actix3_example/service/Cargo.toml index 9c7944fabe..64dd92bf75 100644 --- a/examples/actix3_example/service/Cargo.toml +++ b/examples/actix3_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.4" # sea-orm version +version = "0.12.5" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/actix_example/entity/Cargo.toml b/examples/actix_example/entity/Cargo.toml index 423712d5ea..0bdda2b18e 100644 --- a/examples/actix_example/entity/Cargo.toml +++ b/examples/actix_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.4" # sea-orm version +version = "0.12.5" # sea-orm version diff --git a/examples/actix_example/migration/Cargo.toml b/examples/actix_example/migration/Cargo.toml index 415f600ac5..c3c678c862 100644 --- a/examples/actix_example/migration/Cargo.toml +++ b/examples/actix_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.4" # sea-orm-migration version +version = "0.12.5" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-actix-native-tls", diff --git a/examples/actix_example/service/Cargo.toml b/examples/actix_example/service/Cargo.toml index 2a2a6bbe3f..cf03805895 100644 --- a/examples/actix_example/service/Cargo.toml +++ b/examples/actix_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.4" # sea-orm version +version = "0.12.5" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/axum_example/entity/Cargo.toml b/examples/axum_example/entity/Cargo.toml index 423712d5ea..0bdda2b18e 100644 --- a/examples/axum_example/entity/Cargo.toml +++ b/examples/axum_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.4" # sea-orm version +version = "0.12.5" # sea-orm version diff --git a/examples/axum_example/migration/Cargo.toml b/examples/axum_example/migration/Cargo.toml index 2d06edb279..6d2a9e23cd 100644 --- a/examples/axum_example/migration/Cargo.toml +++ b/examples/axum_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.4" # sea-orm-migration version +version = "0.12.5" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/axum_example/service/Cargo.toml b/examples/axum_example/service/Cargo.toml index f2b37512d9..087e65e07f 100644 --- a/examples/axum_example/service/Cargo.toml +++ b/examples/axum_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.4" # sea-orm version +version = "0.12.5" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/graphql_example/entity/Cargo.toml b/examples/graphql_example/entity/Cargo.toml index 4dca0e412f..cf43f673e3 100644 --- a/examples/graphql_example/entity/Cargo.toml +++ b/examples/graphql_example/entity/Cargo.toml @@ -16,4 +16,4 @@ version = "5.0.10" [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.4" # sea-orm version +version = "0.12.5" # sea-orm version diff --git a/examples/graphql_example/migration/Cargo.toml b/examples/graphql_example/migration/Cargo.toml index 85cfe44764..df9b5b8b68 100644 --- a/examples/graphql_example/migration/Cargo.toml +++ b/examples/graphql_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.4" # sea-orm-migration version +version = "0.12.5" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/graphql_example/service/Cargo.toml b/examples/graphql_example/service/Cargo.toml index 37f761ac0b..5b9d346818 100644 --- a/examples/graphql_example/service/Cargo.toml +++ b/examples/graphql_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.4" # sea-orm version +version = "0.12.5" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/jsonrpsee_example/entity/Cargo.toml b/examples/jsonrpsee_example/entity/Cargo.toml index 423712d5ea..0bdda2b18e 100644 --- a/examples/jsonrpsee_example/entity/Cargo.toml +++ b/examples/jsonrpsee_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.4" # sea-orm version +version = "0.12.5" # sea-orm version diff --git a/examples/jsonrpsee_example/migration/Cargo.toml b/examples/jsonrpsee_example/migration/Cargo.toml index d9727521a5..aa7a728279 100644 --- a/examples/jsonrpsee_example/migration/Cargo.toml +++ b/examples/jsonrpsee_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.4" # sea-orm-migration version +version = "0.12.5" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/jsonrpsee_example/service/Cargo.toml b/examples/jsonrpsee_example/service/Cargo.toml index 6cf8531d4e..4ceff63bae 100644 --- a/examples/jsonrpsee_example/service/Cargo.toml +++ b/examples/jsonrpsee_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.4" # sea-orm version +version = "0.12.5" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/poem_example/entity/Cargo.toml b/examples/poem_example/entity/Cargo.toml index 423712d5ea..0bdda2b18e 100644 --- a/examples/poem_example/entity/Cargo.toml +++ b/examples/poem_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.4" # sea-orm version +version = "0.12.5" # sea-orm version diff --git a/examples/poem_example/migration/Cargo.toml b/examples/poem_example/migration/Cargo.toml index d9727521a5..aa7a728279 100644 --- a/examples/poem_example/migration/Cargo.toml +++ b/examples/poem_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.4" # sea-orm-migration version +version = "0.12.5" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/poem_example/service/Cargo.toml b/examples/poem_example/service/Cargo.toml index 77e4f056db..f57bf12192 100644 --- a/examples/poem_example/service/Cargo.toml +++ b/examples/poem_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.4" # sea-orm version +version = "0.12.5" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/rocket_example/entity/Cargo.toml b/examples/rocket_example/entity/Cargo.toml index f5f931816f..c17888958b 100644 --- a/examples/rocket_example/entity/Cargo.toml +++ b/examples/rocket_example/entity/Cargo.toml @@ -13,4 +13,4 @@ rocket = { version = "0.5.0-rc.4", features = ["json"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.4" # sea-orm version +version = "0.12.5" # sea-orm version diff --git a/examples/rocket_example/migration/Cargo.toml b/examples/rocket_example/migration/Cargo.toml index 3bd0ff4f62..75ec098fce 100644 --- a/examples/rocket_example/migration/Cargo.toml +++ b/examples/rocket_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.4" # sea-orm-migration version +version = "0.12.5" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/rocket_example/service/Cargo.toml b/examples/rocket_example/service/Cargo.toml index 668512eb32..0a86e2141b 100644 --- a/examples/rocket_example/service/Cargo.toml +++ b/examples/rocket_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.4" # sea-orm version +version = "0.12.5" # sea-orm version features = [ "runtime-tokio-native-tls", "sqlx-postgres", diff --git a/examples/rocket_okapi_example/entity/Cargo.toml b/examples/rocket_okapi_example/entity/Cargo.toml index 5cc820a064..96196ddbad 100644 --- a/examples/rocket_okapi_example/entity/Cargo.toml +++ b/examples/rocket_okapi_example/entity/Cargo.toml @@ -13,7 +13,7 @@ rocket = { version = "0.5.0-rc.4", features = ["json"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.4" # sea-orm version +version = "0.12.5" # sea-orm version [dependencies.rocket_okapi] version = "0.8.0-rc.2" diff --git a/examples/rocket_okapi_example/migration/Cargo.toml b/examples/rocket_okapi_example/migration/Cargo.toml index 3bd0ff4f62..75ec098fce 100644 --- a/examples/rocket_okapi_example/migration/Cargo.toml +++ b/examples/rocket_okapi_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.4" # sea-orm-migration version +version = "0.12.5" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/rocket_okapi_example/service/Cargo.toml b/examples/rocket_okapi_example/service/Cargo.toml index caab7631ef..0525cbcffa 100644 --- a/examples/rocket_okapi_example/service/Cargo.toml +++ b/examples/rocket_okapi_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.4" # sea-orm version +version = "0.12.5" # sea-orm version features = [ "runtime-tokio-native-tls", "sqlx-postgres", diff --git a/examples/salvo_example/entity/Cargo.toml b/examples/salvo_example/entity/Cargo.toml index a9faf0dbef..bcd2a2074f 100644 --- a/examples/salvo_example/entity/Cargo.toml +++ b/examples/salvo_example/entity/Cargo.toml @@ -14,4 +14,4 @@ salvo = { version = "0.50" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.4" # sea-orm version +version = "0.12.5" # sea-orm version diff --git a/examples/salvo_example/migration/Cargo.toml b/examples/salvo_example/migration/Cargo.toml index d9727521a5..aa7a728279 100644 --- a/examples/salvo_example/migration/Cargo.toml +++ b/examples/salvo_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.4" # sea-orm-migration version +version = "0.12.5" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/salvo_example/service/Cargo.toml b/examples/salvo_example/service/Cargo.toml index 890b871103..ee8da382d3 100644 --- a/examples/salvo_example/service/Cargo.toml +++ b/examples/salvo_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.4" # sea-orm version +version = "0.12.5" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/seaography_example/migration/Cargo.toml b/examples/seaography_example/migration/Cargo.toml index 9abea87733..790dc31bed 100644 --- a/examples/seaography_example/migration/Cargo.toml +++ b/examples/seaography_example/migration/Cargo.toml @@ -15,11 +15,11 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm] path = "../../.." # remove this line in your own project -version = "0.12.4" # sea-orm version +version = "0.12.5" # sea-orm version [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.4" # sea-orm-migration version +version = "0.12.5" # sea-orm-migration version features = [ "runtime-async-std-native-tls", "sqlx-mysql", diff --git a/examples/tonic_example/entity/Cargo.toml b/examples/tonic_example/entity/Cargo.toml index 423712d5ea..0bdda2b18e 100644 --- a/examples/tonic_example/entity/Cargo.toml +++ b/examples/tonic_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.4" # sea-orm version +version = "0.12.5" # sea-orm version diff --git a/examples/tonic_example/migration/Cargo.toml b/examples/tonic_example/migration/Cargo.toml index 55bc37adf4..a5cb04f1f9 100644 --- a/examples/tonic_example/migration/Cargo.toml +++ b/examples/tonic_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.4" # sea-orm-migration version +version = "0.12.5" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-rustls", diff --git a/examples/tonic_example/service/Cargo.toml b/examples/tonic_example/service/Cargo.toml index 2182262b2e..beee6ad092 100644 --- a/examples/tonic_example/service/Cargo.toml +++ b/examples/tonic_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.4" # sea-orm version +version = "0.12.5" # sea-orm version features = [ "debug-print", "runtime-tokio-rustls", From 1ce3778c619d8ee4717fe1a33504f1aeb8bf2310 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Mon, 13 Nov 2023 09:03:26 +0000 Subject: [PATCH 0208/1371] 0.12.6 --- CHANGELOG.md | 4 +++- Cargo.toml | 4 ++-- sea-orm-cli/Cargo.toml | 4 ++-- sea-orm-codegen/Cargo.toml | 2 +- sea-orm-macros/Cargo.toml | 2 +- sea-orm-migration/Cargo.toml | 6 +++--- 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 377c2c5e59..9bd6166cc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,12 +5,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## 0.12.5 - 2023-11-12 +## 0.12.6 - 2023-11-13 ### New Features * Added `#[sea_orm(skip)]` for `FromQueryResult` derive macro https://github.com/SeaQL/sea-orm/pull/1954 +## 0.12.5 - 2023-11-12 + ### Bug Fixes * [sea-orm-cli] Fix duplicated active enum use statements on generated entities https://github.com/SeaQL/sea-orm/pull/1953 diff --git a/Cargo.toml b/Cargo.toml index ddaa271a06..c6b1ad9a9b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [".", "sea-orm-macros", "sea-orm-codegen"] [package] name = "sea-orm" -version = "0.12.5" +version = "0.12.6" authors = ["Chris Tsang "] edition = "2021" description = "🐚 An async & dynamic ORM for Rust" @@ -33,7 +33,7 @@ log = { version = "0.4", default-features = false } tracing = { version = "0.1", default-features = false, features = ["attributes", "log"] } rust_decimal = { version = "1", default-features = false, optional = true } bigdecimal = { version = "0.3", default-features = false, optional = true } -sea-orm-macros = { version = "0.12.5", path = "sea-orm-macros", default-features = false, features = ["strum"] } +sea-orm-macros = { version = "0.12.6", path = "sea-orm-macros", default-features = false, features = ["strum"] } sea-query = { version = "0.30.2", default-features = false, features = ["thread-safe", "hashable-value", "backend-mysql", "backend-postgres", "backend-sqlite"] } sea-query-binder = { version = "0.5.0", default-features = false, optional = true } strum = { version = "0.25", default-features = false } diff --git a/sea-orm-cli/Cargo.toml b/sea-orm-cli/Cargo.toml index 6945155b97..5db16904ae 100644 --- a/sea-orm-cli/Cargo.toml +++ b/sea-orm-cli/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "sea-orm-cli" -version = "0.12.5" +version = "0.12.6" authors = [ "Chris Tsang ", "Billy Chan ", @@ -37,7 +37,7 @@ required-features = ["cli", "codegen"] clap = { version = "4.3", features = ["env", "derive"], optional = true } dotenvy = { version = "0.15", default-features = false, optional = true } async-std = { version = "1.9", default-features = false, features = ["attributes", "tokio1"], optional = true } -sea-orm-codegen = { version = "=0.12.5", path = "../sea-orm-codegen", default-features = false, optional = true } +sea-orm-codegen = { version = "=0.12.6", path = "../sea-orm-codegen", default-features = false, optional = true } sea-schema = { version = "0.14.0" } sqlx = { version = "0.7", default-features = false, features = ["mysql", "postgres"], optional = true } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["env-filter", "fmt"] } diff --git a/sea-orm-codegen/Cargo.toml b/sea-orm-codegen/Cargo.toml index 2a1b29b447..e7f6ffd1a5 100644 --- a/sea-orm-codegen/Cargo.toml +++ b/sea-orm-codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sea-orm-codegen" -version = "0.12.5" +version = "0.12.6" authors = ["Billy Chan "] edition = "2021" description = "Code Generator for SeaORM" diff --git a/sea-orm-macros/Cargo.toml b/sea-orm-macros/Cargo.toml index db52991096..49b1653aac 100644 --- a/sea-orm-macros/Cargo.toml +++ b/sea-orm-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sea-orm-macros" -version = "0.12.5" +version = "0.12.6" authors = [ "Billy Chan " ] edition = "2021" description = "Derive macros for SeaORM" diff --git a/sea-orm-migration/Cargo.toml b/sea-orm-migration/Cargo.toml index 7785dbf645..dcb5f83a6a 100644 --- a/sea-orm-migration/Cargo.toml +++ b/sea-orm-migration/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "sea-orm-migration" -version = "0.12.5" +version = "0.12.6" authors = ["Billy Chan "] edition = "2021" description = "Migration utility for SeaORM" @@ -23,8 +23,8 @@ path = "src/lib.rs" async-trait = { version = "0.1", default-features = false } clap = { version = "4.3", features = ["env", "derive"], optional = true } dotenvy = { version = "0.15", default-features = false, optional = true } -sea-orm = { version = "0.12.5", path = "../", default-features = false, features = ["macros"] } -sea-orm-cli = { version = "0.12.5", path = "../sea-orm-cli", default-features = false, optional = true } +sea-orm = { version = "0.12.6", path = "../", default-features = false, features = ["macros"] } +sea-orm-cli = { version = "0.12.6", path = "../sea-orm-cli", default-features = false, optional = true } sea-schema = { version = "0.14.1" } tracing = { version = "0.1", default-features = false, features = ["log"] } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["env-filter", "fmt"] } From 8aa49beb6d7a2670cc955a89c7b3efbd018b9752 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Mon, 13 Nov 2023 09:03:26 +0000 Subject: [PATCH 0209/1371] update examples --- examples/actix3_example/entity/Cargo.toml | 2 +- examples/actix3_example/migration/Cargo.toml | 2 +- examples/actix3_example/service/Cargo.toml | 2 +- examples/actix_example/entity/Cargo.toml | 2 +- examples/actix_example/migration/Cargo.toml | 2 +- examples/actix_example/service/Cargo.toml | 2 +- examples/axum_example/entity/Cargo.toml | 2 +- examples/axum_example/migration/Cargo.toml | 2 +- examples/axum_example/service/Cargo.toml | 2 +- examples/graphql_example/entity/Cargo.toml | 2 +- examples/graphql_example/migration/Cargo.toml | 2 +- examples/graphql_example/service/Cargo.toml | 2 +- examples/jsonrpsee_example/entity/Cargo.toml | 2 +- examples/jsonrpsee_example/migration/Cargo.toml | 2 +- examples/jsonrpsee_example/service/Cargo.toml | 2 +- examples/poem_example/entity/Cargo.toml | 2 +- examples/poem_example/migration/Cargo.toml | 2 +- examples/poem_example/service/Cargo.toml | 2 +- examples/rocket_example/entity/Cargo.toml | 2 +- examples/rocket_example/migration/Cargo.toml | 2 +- examples/rocket_example/service/Cargo.toml | 2 +- examples/rocket_okapi_example/entity/Cargo.toml | 2 +- examples/rocket_okapi_example/migration/Cargo.toml | 2 +- examples/rocket_okapi_example/service/Cargo.toml | 2 +- examples/salvo_example/entity/Cargo.toml | 2 +- examples/salvo_example/migration/Cargo.toml | 2 +- examples/salvo_example/service/Cargo.toml | 2 +- examples/seaography_example/migration/Cargo.toml | 4 ++-- examples/tonic_example/entity/Cargo.toml | 2 +- examples/tonic_example/migration/Cargo.toml | 2 +- examples/tonic_example/service/Cargo.toml | 2 +- 31 files changed, 32 insertions(+), 32 deletions(-) diff --git a/examples/actix3_example/entity/Cargo.toml b/examples/actix3_example/entity/Cargo.toml index 0bdda2b18e..41f742f6db 100644 --- a/examples/actix3_example/entity/Cargo.toml +++ b/examples/actix3_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.5" # sea-orm version +version = "0.12.6" # sea-orm version diff --git a/examples/actix3_example/migration/Cargo.toml b/examples/actix3_example/migration/Cargo.toml index fde1eb6818..f115e64c15 100644 --- a/examples/actix3_example/migration/Cargo.toml +++ b/examples/actix3_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.5" # sea-orm-migration version +version = "0.12.6" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-async-std-native-tls", diff --git a/examples/actix3_example/service/Cargo.toml b/examples/actix3_example/service/Cargo.toml index 64dd92bf75..ccb4100700 100644 --- a/examples/actix3_example/service/Cargo.toml +++ b/examples/actix3_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.5" # sea-orm version +version = "0.12.6" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/actix_example/entity/Cargo.toml b/examples/actix_example/entity/Cargo.toml index 0bdda2b18e..41f742f6db 100644 --- a/examples/actix_example/entity/Cargo.toml +++ b/examples/actix_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.5" # sea-orm version +version = "0.12.6" # sea-orm version diff --git a/examples/actix_example/migration/Cargo.toml b/examples/actix_example/migration/Cargo.toml index c3c678c862..ae056a10ff 100644 --- a/examples/actix_example/migration/Cargo.toml +++ b/examples/actix_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.5" # sea-orm-migration version +version = "0.12.6" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-actix-native-tls", diff --git a/examples/actix_example/service/Cargo.toml b/examples/actix_example/service/Cargo.toml index cf03805895..c8092b3620 100644 --- a/examples/actix_example/service/Cargo.toml +++ b/examples/actix_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.5" # sea-orm version +version = "0.12.6" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/axum_example/entity/Cargo.toml b/examples/axum_example/entity/Cargo.toml index 0bdda2b18e..41f742f6db 100644 --- a/examples/axum_example/entity/Cargo.toml +++ b/examples/axum_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.5" # sea-orm version +version = "0.12.6" # sea-orm version diff --git a/examples/axum_example/migration/Cargo.toml b/examples/axum_example/migration/Cargo.toml index 6d2a9e23cd..518046de3d 100644 --- a/examples/axum_example/migration/Cargo.toml +++ b/examples/axum_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.5" # sea-orm-migration version +version = "0.12.6" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/axum_example/service/Cargo.toml b/examples/axum_example/service/Cargo.toml index 087e65e07f..0069d8d760 100644 --- a/examples/axum_example/service/Cargo.toml +++ b/examples/axum_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.5" # sea-orm version +version = "0.12.6" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/graphql_example/entity/Cargo.toml b/examples/graphql_example/entity/Cargo.toml index cf43f673e3..66bd454be4 100644 --- a/examples/graphql_example/entity/Cargo.toml +++ b/examples/graphql_example/entity/Cargo.toml @@ -16,4 +16,4 @@ version = "5.0.10" [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.5" # sea-orm version +version = "0.12.6" # sea-orm version diff --git a/examples/graphql_example/migration/Cargo.toml b/examples/graphql_example/migration/Cargo.toml index df9b5b8b68..e03da112a4 100644 --- a/examples/graphql_example/migration/Cargo.toml +++ b/examples/graphql_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.5" # sea-orm-migration version +version = "0.12.6" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/graphql_example/service/Cargo.toml b/examples/graphql_example/service/Cargo.toml index 5b9d346818..1158114a59 100644 --- a/examples/graphql_example/service/Cargo.toml +++ b/examples/graphql_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.5" # sea-orm version +version = "0.12.6" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/jsonrpsee_example/entity/Cargo.toml b/examples/jsonrpsee_example/entity/Cargo.toml index 0bdda2b18e..41f742f6db 100644 --- a/examples/jsonrpsee_example/entity/Cargo.toml +++ b/examples/jsonrpsee_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.5" # sea-orm version +version = "0.12.6" # sea-orm version diff --git a/examples/jsonrpsee_example/migration/Cargo.toml b/examples/jsonrpsee_example/migration/Cargo.toml index aa7a728279..f1efa6ea4b 100644 --- a/examples/jsonrpsee_example/migration/Cargo.toml +++ b/examples/jsonrpsee_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.5" # sea-orm-migration version +version = "0.12.6" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/jsonrpsee_example/service/Cargo.toml b/examples/jsonrpsee_example/service/Cargo.toml index 4ceff63bae..f056221a34 100644 --- a/examples/jsonrpsee_example/service/Cargo.toml +++ b/examples/jsonrpsee_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.5" # sea-orm version +version = "0.12.6" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/poem_example/entity/Cargo.toml b/examples/poem_example/entity/Cargo.toml index 0bdda2b18e..41f742f6db 100644 --- a/examples/poem_example/entity/Cargo.toml +++ b/examples/poem_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.5" # sea-orm version +version = "0.12.6" # sea-orm version diff --git a/examples/poem_example/migration/Cargo.toml b/examples/poem_example/migration/Cargo.toml index aa7a728279..f1efa6ea4b 100644 --- a/examples/poem_example/migration/Cargo.toml +++ b/examples/poem_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.5" # sea-orm-migration version +version = "0.12.6" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/poem_example/service/Cargo.toml b/examples/poem_example/service/Cargo.toml index f57bf12192..8dbb7bf1d6 100644 --- a/examples/poem_example/service/Cargo.toml +++ b/examples/poem_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.5" # sea-orm version +version = "0.12.6" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/rocket_example/entity/Cargo.toml b/examples/rocket_example/entity/Cargo.toml index c17888958b..60cf1e8a48 100644 --- a/examples/rocket_example/entity/Cargo.toml +++ b/examples/rocket_example/entity/Cargo.toml @@ -13,4 +13,4 @@ rocket = { version = "0.5.0-rc.4", features = ["json"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.5" # sea-orm version +version = "0.12.6" # sea-orm version diff --git a/examples/rocket_example/migration/Cargo.toml b/examples/rocket_example/migration/Cargo.toml index 75ec098fce..b5bda5b0a9 100644 --- a/examples/rocket_example/migration/Cargo.toml +++ b/examples/rocket_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.5" # sea-orm-migration version +version = "0.12.6" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/rocket_example/service/Cargo.toml b/examples/rocket_example/service/Cargo.toml index 0a86e2141b..8b72ed7971 100644 --- a/examples/rocket_example/service/Cargo.toml +++ b/examples/rocket_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.5" # sea-orm version +version = "0.12.6" # sea-orm version features = [ "runtime-tokio-native-tls", "sqlx-postgres", diff --git a/examples/rocket_okapi_example/entity/Cargo.toml b/examples/rocket_okapi_example/entity/Cargo.toml index 96196ddbad..91dd207951 100644 --- a/examples/rocket_okapi_example/entity/Cargo.toml +++ b/examples/rocket_okapi_example/entity/Cargo.toml @@ -13,7 +13,7 @@ rocket = { version = "0.5.0-rc.4", features = ["json"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.5" # sea-orm version +version = "0.12.6" # sea-orm version [dependencies.rocket_okapi] version = "0.8.0-rc.2" diff --git a/examples/rocket_okapi_example/migration/Cargo.toml b/examples/rocket_okapi_example/migration/Cargo.toml index 75ec098fce..b5bda5b0a9 100644 --- a/examples/rocket_okapi_example/migration/Cargo.toml +++ b/examples/rocket_okapi_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.5" # sea-orm-migration version +version = "0.12.6" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/rocket_okapi_example/service/Cargo.toml b/examples/rocket_okapi_example/service/Cargo.toml index 0525cbcffa..cde5208b21 100644 --- a/examples/rocket_okapi_example/service/Cargo.toml +++ b/examples/rocket_okapi_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.5" # sea-orm version +version = "0.12.6" # sea-orm version features = [ "runtime-tokio-native-tls", "sqlx-postgres", diff --git a/examples/salvo_example/entity/Cargo.toml b/examples/salvo_example/entity/Cargo.toml index bcd2a2074f..058b355f4b 100644 --- a/examples/salvo_example/entity/Cargo.toml +++ b/examples/salvo_example/entity/Cargo.toml @@ -14,4 +14,4 @@ salvo = { version = "0.50" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.5" # sea-orm version +version = "0.12.6" # sea-orm version diff --git a/examples/salvo_example/migration/Cargo.toml b/examples/salvo_example/migration/Cargo.toml index aa7a728279..f1efa6ea4b 100644 --- a/examples/salvo_example/migration/Cargo.toml +++ b/examples/salvo_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.5" # sea-orm-migration version +version = "0.12.6" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/salvo_example/service/Cargo.toml b/examples/salvo_example/service/Cargo.toml index ee8da382d3..d778d8b4b8 100644 --- a/examples/salvo_example/service/Cargo.toml +++ b/examples/salvo_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.5" # sea-orm version +version = "0.12.6" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/seaography_example/migration/Cargo.toml b/examples/seaography_example/migration/Cargo.toml index 790dc31bed..7424628660 100644 --- a/examples/seaography_example/migration/Cargo.toml +++ b/examples/seaography_example/migration/Cargo.toml @@ -15,11 +15,11 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm] path = "../../.." # remove this line in your own project -version = "0.12.5" # sea-orm version +version = "0.12.6" # sea-orm version [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.5" # sea-orm-migration version +version = "0.12.6" # sea-orm-migration version features = [ "runtime-async-std-native-tls", "sqlx-mysql", diff --git a/examples/tonic_example/entity/Cargo.toml b/examples/tonic_example/entity/Cargo.toml index 0bdda2b18e..41f742f6db 100644 --- a/examples/tonic_example/entity/Cargo.toml +++ b/examples/tonic_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.5" # sea-orm version +version = "0.12.6" # sea-orm version diff --git a/examples/tonic_example/migration/Cargo.toml b/examples/tonic_example/migration/Cargo.toml index a5cb04f1f9..3cb2baf5ca 100644 --- a/examples/tonic_example/migration/Cargo.toml +++ b/examples/tonic_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.5" # sea-orm-migration version +version = "0.12.6" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-rustls", diff --git a/examples/tonic_example/service/Cargo.toml b/examples/tonic_example/service/Cargo.toml index beee6ad092..e8fb93ec2f 100644 --- a/examples/tonic_example/service/Cargo.toml +++ b/examples/tonic_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.5" # sea-orm version +version = "0.12.6" # sea-orm version features = [ "debug-print", "runtime-tokio-rustls", From d471c4e022ae6550f2b5a062811bee2d4055b2b2 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Wed, 22 Nov 2023 22:31:57 +0000 Subject: [PATCH 0210/1371] Change method signature of `expr_as` to accept `self` #1979 --- src/query/helper.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/query/helper.rs b/src/query/helper.rs index 43bc9da9ff..976220c1f9 100644 --- a/src/query/helper.rs +++ b/src/query/helper.rs @@ -511,6 +511,8 @@ pub trait QuerySelect: Sized { /// "SELECT `cake`.`id`, `cake`.`name`, UPPER(`cake`.`name`) AS `name_upper` FROM `cake`" /// ); /// ``` + /// + /// FIXME: change signature to `mut self` fn expr_as(&mut self, expr: T, alias: A) -> &mut Self where T: Into, @@ -519,6 +521,34 @@ pub trait QuerySelect: Sized { self.query().expr_as(expr, alias.into_identity()); self } + + /// Owned version of `expr_as`. + /// + /// Select column. + /// + /// ``` + /// use sea_orm::sea_query::{Alias, Expr, Func}; + /// use sea_orm::{entity::*, tests_cfg::cake, DbBackend, QuerySelect, QueryTrait}; + /// + /// assert_eq!( + /// cake::Entity::find() + /// .expr_as( + /// Func::upper(Expr::col((cake::Entity, cake::Column::Name))), + /// "name_upper" + /// ) + /// .build(DbBackend::MySql) + /// .to_string(), + /// "SELECT `cake`.`id`, `cake`.`name`, UPPER(`cake`.`name`) AS `name_upper` FROM `cake`" + /// ); + /// ``` + fn expr_as_(mut self, expr: T, alias: A) -> Self + where + T: Into, + A: IntoIdentity, + { + self.query().expr_as(expr, alias.into_identity()); + self + } } // LINT: when the column does not appear in tables selected from From f87c6329d2ae2795d06863ab953b2bb0393a2d8f Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Wed, 22 Nov 2023 22:39:49 +0000 Subject: [PATCH 0211/1371] 0.12.7 --- .gitignore | 1 + CHANGELOG.md | 6 ++++++ Cargo.toml | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 74509beb96..413d53bfc2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ target +firedbg Cargo.lock *.sublime* .vscode diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bd6166cc7..df52373444 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## 0.12.7 - 2023-11-22 + +### Enhancements + +* Added method `expr_as_` that accepts `self` https://github.com/SeaQL/sea-orm/pull/1979 + ## 0.12.6 - 2023-11-13 ### New Features diff --git a/Cargo.toml b/Cargo.toml index c6b1ad9a9b..84235b2efa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [".", "sea-orm-macros", "sea-orm-codegen"] [package] name = "sea-orm" -version = "0.12.6" +version = "0.12.7" authors = ["Chris Tsang "] edition = "2021" description = "🐚 An async & dynamic ORM for Rust" From 09a1983a0a1cef3f4b42da811f719c97b50cdaf0 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Wed, 22 Nov 2023 22:39:49 +0000 Subject: [PATCH 0212/1371] 0.12.7 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index df52373444..5f835e0458 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). * Added method `expr_as_` that accepts `self` https://github.com/SeaQL/sea-orm/pull/1979 +### Upgrades + +* Updated `sea-query` to `0.30.3` https://github.com/SeaQL/sea-query/releases/tag/0.30.3 + ## 0.12.6 - 2023-11-13 ### New Features From 3ff4f11cb7cc63c38a415c38bfc8975fbf4ccc21 Mon Sep 17 00:00:00 2001 From: ttys3 <41882455+ttys3@users.noreply.github.com> Date: Tue, 28 Nov 2023 02:28:49 +0800 Subject: [PATCH 0213/1371] upgrade to Axum 0.7 (#1984) * refactor: upgrade to axum 0.7 * refactor: upgrade to axum 0.7 --- examples/axum_example/api/Cargo.toml | 18 +++++++++--------- examples/axum_example/api/src/lib.rs | 9 ++++----- examples/axum_example/service/Cargo.toml | 2 +- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/examples/axum_example/api/Cargo.toml b/examples/axum_example/api/Cargo.toml index de7462e485..bc82d122be 100644 --- a/examples/axum_example/api/Cargo.toml +++ b/examples/axum_example/api/Cargo.toml @@ -7,16 +7,16 @@ publish = false [dependencies] axum-example-service = { path = "../service" } -tokio = { version = "1.29.0", features = ["full"] } -axum = "0.6.19" +tokio = { version = "1.34.0", features = ["full"] } +axum = "0.7.1" tower = "0.4.13" -tower-http = { version = "0.4.3", features = ["fs"] } -tower-cookies = "0.9.0" -anyhow = "1.0.71" +tower-http = { version = "0.5.0", features = ["fs"] } +tower-cookies = "0.10.0" +anyhow = "1.0.75" dotenvy = "0.15.7" -serde = "1.0.175" -serde_json = "1.0.102" -tera = "1.17.1" -tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } +serde = "1.0.193" +serde_json = "1.0.108" +tera = "1.19.1" +tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } entity = { path = "../entity" } migration = { path = "../migration" } diff --git a/examples/axum_example/api/src/lib.rs b/examples/axum_example/api/src/lib.rs index 28a7572b94..4cf8d80e58 100644 --- a/examples/axum_example/api/src/lib.rs +++ b/examples/axum_example/api/src/lib.rs @@ -5,7 +5,7 @@ use axum::{ http::StatusCode, response::Html, routing::{get, get_service, post}, - Router, Server, + Router, }; use axum_example_service::{ sea_orm::{Database, DatabaseConnection}, @@ -15,8 +15,7 @@ use entity::post; use flash::{get_flash_cookie, post_response, PostResponse}; use migration::{Migrator, MigratorTrait}; use serde::{Deserialize, Serialize}; -use std::str::FromStr; -use std::{env, net::SocketAddr}; +use std::env; use tera::Tera; use tower_cookies::{CookieManagerLayer, Cookies}; use tower_http::services::ServeDir; @@ -63,8 +62,8 @@ async fn start() -> anyhow::Result<()> { .layer(CookieManagerLayer::new()) .with_state(state); - let addr = SocketAddr::from_str(&server_url).unwrap(); - Server::bind(&addr).serve(app.into_make_service()).await?; + let listener = tokio::net::TcpListener::bind(&server_url).await.unwrap(); + axum::serve(listener, app).await?; Ok(()) } diff --git a/examples/axum_example/service/Cargo.toml b/examples/axum_example/service/Cargo.toml index 0069d8d760..261b451b8b 100644 --- a/examples/axum_example/service/Cargo.toml +++ b/examples/axum_example/service/Cargo.toml @@ -20,7 +20,7 @@ features = [ ] [dev-dependencies] -tokio = { version = "1.20.0", features = ["macros", "rt"] } +tokio = { version = "1.34.0", features = ["macros", "rt"] } [features] mock = ["sea-orm/mock"] From 5b182509a3fa47d0d64e58968f74fecff4c2126e Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Tue, 28 Nov 2023 02:38:07 +0800 Subject: [PATCH 0214/1371] Update CHANGELOG.md --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f835e0458..d21b1bffaf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## Main Branch + +### Upgrades + +* Upgrade `axum` example to `0.7` + ## 0.12.7 - 2023-11-22 ### Enhancements From 1f8707af645fabc3eaf2c87011d212d671e8ae04 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 1 Dec 2023 09:59:10 +0000 Subject: [PATCH 0215/1371] Update config.yml --- .github/ISSUE_TEMPLATE/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 8ad0ea0e1c..d53fc90c42 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,4 +1,4 @@ -blank_issues_enabled: true +blank_issues_enabled: false contact_links: - name: Q & A url: https://github.com/SeaQL/sea-orm/discussions/new?category=q-a From c7510df0096a617dcee896fc05418f3fc6a088c1 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 1 Dec 2023 10:03:29 +0000 Subject: [PATCH 0216/1371] Update config.yml --- .github/ISSUE_TEMPLATE/config.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index d53fc90c42..a165d6ffee 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,8 @@ blank_issues_enabled: false contact_links: + - name: Feature Request + url: https://github.com/SeaQL/sea-orm/discussions/new?category=ideas + about: Suggest a new feature for this project - name: Q & A url: https://github.com/SeaQL/sea-orm/discussions/new?category=q-a about: Ask a question or look for help. Try to provide sufficient context, snippets to reproduce and error messages. From d0a162acfeba91501a4d6d73bbfe1f6c113e9eb0 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 1 Dec 2023 10:04:51 +0000 Subject: [PATCH 0217/1371] Delete .github/ISSUE_TEMPLATE/feature-request.md --- .github/ISSUE_TEMPLATE/feature-request.md | 37 ----------------------- 1 file changed, 37 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/feature-request.md diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md deleted file mode 100644 index c38dfd143c..0000000000 --- a/.github/ISSUE_TEMPLATE/feature-request.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -name: Feature Request -about: Suggest a new feature for this project -title: '' -labels: '' -assignees: '' - ---- - - - -## Motivation - - - -## Proposed Solutions - - - -## Current Workarounds - - From 5db05c82513dbe414f1e7dd4088f682d69fa85a6 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 1 Dec 2023 10:07:16 +0000 Subject: [PATCH 0218/1371] Update bug-report.md --- .github/ISSUE_TEMPLATE/bug-report.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index 07556991a3..1619c4b148 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -1,6 +1,6 @@ --- name: Bug Report -about: Report a bug or feature flaw +about: Report a bug or design flaw title: '' labels: '' assignees: '' @@ -19,7 +19,7 @@ Want to ask a question? You can reach us via: - GitHub Discussions: https://github.com/SeaQL/sea-orm/discussions/new Please make sure that you are not asking for a missing feature; a bug is incorrect behavior - -either in the feature specification or implementation. +either in the feature specification or implementation. Feature requests should be first raised in discussions. Please also make sure your description is clear and precise - maintainers don't have access to your code and can't see what you have seen. Please avoid vague descriptions like "they are different" @@ -66,4 +66,4 @@ threads if you have new information to add, but please don't ask for ETA or "+1" ## Versions - \ No newline at end of file + From 8210428674441c378582b0721b37f6609aaf005c Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 1 Dec 2023 10:16:22 +0000 Subject: [PATCH 0219/1371] Implement StatementBuilder for sea_query::WithQuery #1960 --- src/database/statement.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/database/statement.rs b/src/database/statement.rs index fa54e1de90..d001ab242d 100644 --- a/src/database/statement.rs +++ b/src/database/statement.rs @@ -108,6 +108,7 @@ build_query_stmt!(sea_query::InsertStatement); build_query_stmt!(sea_query::SelectStatement); build_query_stmt!(sea_query::UpdateStatement); build_query_stmt!(sea_query::DeleteStatement); +build_query_stmt!(sea_query::WithQuery); macro_rules! build_schema_stmt { ($stmt: ty) => { From 285c0b2f4d5b83e986cae378fdb5540587329c36 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 1 Dec 2023 10:38:57 +0000 Subject: [PATCH 0220/1371] Notes --- Cargo.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 84235b2efa..7b44c2695d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -125,3 +125,7 @@ runtime-tokio-rustls = [ ] tests-cfg = ["serde/derive"] seaography = ["sea-orm-macros/seaography"] + +# This allows us to develop using a local version of sea-query +# [patch.crates-io] +# sea-query = { path = "../sea-query" } \ No newline at end of file From 2eb3d8e4651cb2f8186bdc342f6e75f0659f4858 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 1 Dec 2023 11:29:05 +0000 Subject: [PATCH 0221/1371] Cherry pick tests from #1679 --- src/executor/query.rs | 70 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/executor/query.rs b/src/executor/query.rs index 95ec29c42c..a7df40abfc 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -1202,4 +1202,74 @@ mod tests { let expected = "A null value was encountered while decoding column".to_owned(); assert_eq!(DbErr::from(try_get_error), DbErr::Type(expected)); } + + #[test] + fn build_with_query() { + use sea_orm::{DbBackend, Statement}; + use sea_query::*; + + let base_query = SelectStatement::new() + .column(Alias::new("id")) + .expr(1i32) + .column(Alias::new("next")) + .column(Alias::new("value")) + .from(Alias::new("table")) + .to_owned(); + + let cte_referencing = SelectStatement::new() + .column(Alias::new("id")) + .expr(Expr::col(Alias::new("depth")).add(1i32)) + .column(Alias::new("next")) + .column(Alias::new("value")) + .from(Alias::new("table")) + .join( + JoinType::InnerJoin, + Alias::new("cte_traversal"), + Expr::col((Alias::new("cte_traversal"), Alias::new("next"))) + .equals((Alias::new("table"), Alias::new("id"))), + ) + .to_owned(); + + let common_table_expression = CommonTableExpression::new() + .query( + base_query + .clone() + .union(UnionType::All, cte_referencing) + .to_owned(), + ) + .columns([ + Alias::new("id"), + Alias::new("depth"), + Alias::new("next"), + Alias::new("value"), + ]) + .table_name(Alias::new("cte_traversal")) + .to_owned(); + + let select = SelectStatement::new() + .column(ColumnRef::Asterisk) + .from(Alias::new("cte_traversal")) + .to_owned(); + + let with_clause = WithClause::new() + .recursive(true) + .cte(common_table_expression) + .cycle(Cycle::new_from_expr_set_using( + SimpleExpr::Column(ColumnRef::Column(Alias::new("id").into_iden())), + Alias::new("looped"), + Alias::new("traversal_path"), + )) + .to_owned(); + + let with_query = select.with(with_clause).to_owned(); + + assert_eq!( + DbBackend::MySql.build(&with_query), + Statement::from_sql_and_values( + DbBackend::MySql, + r#"WITH RECURSIVE `cte_traversal` (`id`, `depth`, `next`, `value`) AS (SELECT `id`, ?, `next`, `value` FROM `table` UNION ALL (SELECT `id`, `depth` + ?, `next`, `value` FROM `table` INNER JOIN `cte_traversal` ON `cte_traversal`.`next` = `table`.`id`)) SELECT * FROM `cte_traversal`"#, + [1.into(), 1.into()] + ) + ); + } } From e4c31da34f3d87fa580d9313a749e9289db53791 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Mon, 4 Dec 2023 11:20:40 +0000 Subject: [PATCH 0222/1371] 0.12.8 --- CHANGELOG.md | 6 +++++- Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d21b1bffaf..596d12c097 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## Main Branch +## 0.12.8 - 2023-12-04 + +### Enhancements + +* Implement `StatementBuilder` for `sea_query::WithQuery` https://github.com/SeaQL/sea-orm/pull/1960 ### Upgrades diff --git a/Cargo.toml b/Cargo.toml index 7b44c2695d..c0326da847 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [".", "sea-orm-macros", "sea-orm-codegen"] [package] name = "sea-orm" -version = "0.12.7" +version = "0.12.8" authors = ["Chris Tsang "] edition = "2021" description = "🐚 An async & dynamic ORM for Rust" From d763ec08c8bb81518964efad539377511b19e42d Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Mon, 4 Dec 2023 11:27:23 +0000 Subject: [PATCH 0223/1371] Changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 596d12c097..7c1f655b08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,11 +9,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Enhancements -* Implement `StatementBuilder` for `sea_query::WithQuery` https://github.com/SeaQL/sea-orm/pull/1960 +* Implement `StatementBuilder` for `sea_query::WithQuery` https://github.com/SeaQL/sea-orm/issues/1960 ### Upgrades -* Upgrade `axum` example to `0.7` +* Upgrade `axum` example to `0.7` https://github.com/SeaQL/sea-orm/pull/1984 ## 0.12.7 - 2023-11-22 From bb1fff654f32d099320e99f95b2acf5becf28a7b Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 8 Dec 2023 13:16:40 +0000 Subject: [PATCH 0224/1371] Bump sea-query #1996 --- CHANGELOG.md | 4 ++++ Cargo.toml | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c1f655b08..fd6029a730 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## 0.12.9 - 2023-12-08 + +* Updated `sea-query` to `0.30.4` https://github.com/SeaQL/sea-query/releases/tag/0.30.4 + ## 0.12.8 - 2023-12-04 ### Enhancements diff --git a/Cargo.toml b/Cargo.toml index c0326da847..c2aec2c775 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [".", "sea-orm-macros", "sea-orm-codegen"] [package] name = "sea-orm" -version = "0.12.8" +version = "0.12.9" authors = ["Chris Tsang "] edition = "2021" description = "🐚 An async & dynamic ORM for Rust" @@ -34,7 +34,7 @@ tracing = { version = "0.1", default-features = false, features = ["attributes", rust_decimal = { version = "1", default-features = false, optional = true } bigdecimal = { version = "0.3", default-features = false, optional = true } sea-orm-macros = { version = "0.12.6", path = "sea-orm-macros", default-features = false, features = ["strum"] } -sea-query = { version = "0.30.2", default-features = false, features = ["thread-safe", "hashable-value", "backend-mysql", "backend-postgres", "backend-sqlite"] } +sea-query = { version = "0.30.4", default-features = false, features = ["thread-safe", "hashable-value", "backend-mysql", "backend-postgres", "backend-sqlite"] } sea-query-binder = { version = "0.5.0", default-features = false, optional = true } strum = { version = "0.25", default-features = false } serde = { version = "1.0", default-features = false } From 3d6124393ae3989b97209559b6c5e0fd36f03946 Mon Sep 17 00:00:00 2001 From: chayleaf Date: Fri, 8 Dec 2023 20:32:25 +0700 Subject: [PATCH 0225/1371] sea-orm-cli migrate init: don't overwrite files by default (#1829) --- sea-orm-cli/src/cli.rs | 5 ++++- sea-orm-cli/src/commands/migrate.rs | 15 ++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/sea-orm-cli/src/cli.rs b/sea-orm-cli/src/cli.rs index e8611373e2..250072550a 100644 --- a/sea-orm-cli/src/cli.rs +++ b/sea-orm-cli/src/cli.rs @@ -101,7 +101,10 @@ you should provide the directory of that submodule.", #[derive(Subcommand, PartialEq, Eq, Debug)] pub enum MigrateSubcommands { #[command(about = "Initialize migration directory", display_order = 10)] - Init, + Init { + #[arg(short, long, help = "Overwrite files if the folder already exists")] + force: bool, + }, #[command(about = "Generate a new, empty migration", display_order = 20)] Generate { #[arg(required = true, help = "Name of the new migration")] diff --git a/sea-orm-cli/src/commands/migrate.rs b/sea-orm-cli/src/commands/migrate.rs index 5c86364b27..5b744c4104 100644 --- a/sea-orm-cli/src/commands/migrate.rs +++ b/sea-orm-cli/src/commands/migrate.rs @@ -21,7 +21,7 @@ pub fn run_migrate_command( verbose: bool, ) -> Result<(), Box> { match command { - Some(MigrateSubcommands::Init) => run_migrate_init(migration_dir)?, + Some(MigrateSubcommands::Init { force }) => run_migrate_init(migration_dir, force)?, Some(MigrateSubcommands::Generate { migration_name, universal_time: _, @@ -78,11 +78,16 @@ pub fn run_migrate_command( Ok(()) } -pub fn run_migrate_init(migration_dir: &str) -> Result<(), Box> { +pub fn run_migrate_init(migration_dir: &str, force: bool) -> Result<(), Box> { let migration_dir = match migration_dir.ends_with('/') { true => migration_dir.to_string(), false => format!("{migration_dir}/"), }; + if !force && Path::new(&migration_dir).is_dir() { + if fs::read_dir(&migration_dir)?.next().transpose()?.is_some() { + return Err(Box::new(MigrationCommandError::DirAlreadyExists)); + } + } println!("Initializing migration directory..."); macro_rules! write_file { ($filename: literal) => { @@ -252,13 +257,17 @@ fn update_migrator(migration_name: &str, migration_dir: &str) -> Result<(), Box< #[derive(Debug)] enum MigrationCommandError { + DirAlreadyExists, InvalidName(String), } impl Display for MigrationCommandError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - MigrationCommandError::InvalidName(name) => { + Self::DirAlreadyExists => { + write!(f, "Migration directory already exists! Use `--force` flag if you want to overwrite it anyway.") + } + Self::InvalidName(name) => { write!(f, "Invalid migration name: {name}") } } From a1396f367953a3983e99da5418c217509d79c6ac Mon Sep 17 00:00:00 2001 From: Bouke van der Bijl Date: Fri, 8 Dec 2023 14:50:57 +0100 Subject: [PATCH 0226/1371] Add source annotations to errors (#1999) --- src/error.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/error.rs b/src/error.rs index 1ba430dbb8..f9c054c991 100644 --- a/src/error.rs +++ b/src/error.rs @@ -17,7 +17,7 @@ use thiserror::Error; pub enum DbErr { /// This error can happen when the connection pool is fully-utilized #[error("Failed to acquire connection from pool: {0}")] - ConnectionAcquire(ConnAcquireErr), + ConnectionAcquire(#[source] ConnAcquireErr), /// Runtime type conversion error #[error("Error converting `{from}` into `{into}`: {source}")] TryIntoErr { @@ -92,7 +92,7 @@ pub enum RuntimeErr { /// SQLx Error #[cfg(feature = "sqlx-dep")] #[error("{0}")] - SqlxError(sqlx::error::Error), + SqlxError(#[source] sqlx::error::Error), /// Error generated from within SeaORM #[error("{0}")] Internal(String), From c9a25285465b75d25e9f8c0ee330a66790d29238 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 8 Dec 2023 14:16:26 +0000 Subject: [PATCH 0227/1371] Revert "sea-orm-cli migrate init: don't overwrite files by default (#1829)" This seems to be a breaking change may be we should apply later --- sea-orm-cli/src/cli.rs | 5 +---- sea-orm-cli/src/commands/migrate.rs | 15 +++------------ 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/sea-orm-cli/src/cli.rs b/sea-orm-cli/src/cli.rs index 250072550a..e8611373e2 100644 --- a/sea-orm-cli/src/cli.rs +++ b/sea-orm-cli/src/cli.rs @@ -101,10 +101,7 @@ you should provide the directory of that submodule.", #[derive(Subcommand, PartialEq, Eq, Debug)] pub enum MigrateSubcommands { #[command(about = "Initialize migration directory", display_order = 10)] - Init { - #[arg(short, long, help = "Overwrite files if the folder already exists")] - force: bool, - }, + Init, #[command(about = "Generate a new, empty migration", display_order = 20)] Generate { #[arg(required = true, help = "Name of the new migration")] diff --git a/sea-orm-cli/src/commands/migrate.rs b/sea-orm-cli/src/commands/migrate.rs index 5b744c4104..5c86364b27 100644 --- a/sea-orm-cli/src/commands/migrate.rs +++ b/sea-orm-cli/src/commands/migrate.rs @@ -21,7 +21,7 @@ pub fn run_migrate_command( verbose: bool, ) -> Result<(), Box> { match command { - Some(MigrateSubcommands::Init { force }) => run_migrate_init(migration_dir, force)?, + Some(MigrateSubcommands::Init) => run_migrate_init(migration_dir)?, Some(MigrateSubcommands::Generate { migration_name, universal_time: _, @@ -78,16 +78,11 @@ pub fn run_migrate_command( Ok(()) } -pub fn run_migrate_init(migration_dir: &str, force: bool) -> Result<(), Box> { +pub fn run_migrate_init(migration_dir: &str) -> Result<(), Box> { let migration_dir = match migration_dir.ends_with('/') { true => migration_dir.to_string(), false => format!("{migration_dir}/"), }; - if !force && Path::new(&migration_dir).is_dir() { - if fs::read_dir(&migration_dir)?.next().transpose()?.is_some() { - return Err(Box::new(MigrationCommandError::DirAlreadyExists)); - } - } println!("Initializing migration directory..."); macro_rules! write_file { ($filename: literal) => { @@ -257,17 +252,13 @@ fn update_migrator(migration_name: &str, migration_dir: &str) -> Result<(), Box< #[derive(Debug)] enum MigrationCommandError { - DirAlreadyExists, InvalidName(String), } impl Display for MigrationCommandError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::DirAlreadyExists => { - write!(f, "Migration directory already exists! Use `--force` flag if you want to overwrite it anyway.") - } - Self::InvalidName(name) => { + MigrationCommandError::InvalidName(name) => { write!(f, "Invalid migration name: {name}") } } From 544675f24c7f0b7646f86ac941521a2bf1301243 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 8 Dec 2023 14:19:29 +0000 Subject: [PATCH 0228/1371] Changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd6029a730..cff56a36bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## 0.12.9 - 2023-12-08 +### Enhancements + +* Add source annotations to errors https://github.com/SeaQL/sea-orm/pull/1999 + +### Upgrades + * Updated `sea-query` to `0.30.4` https://github.com/SeaQL/sea-query/releases/tag/0.30.4 ## 0.12.8 - 2023-12-04 From 94eac965132c34fa50b27962d49093cc54724f9a Mon Sep 17 00:00:00 2001 From: Caisin Date: Thu, 14 Dec 2023 18:22:36 +0800 Subject: [PATCH 0229/1371] create_table_from_entity support comment (#2009) * support table and column comment * create_table_from_entity support comment * fmt code * fmt code * add comment test * fix test --- sea-orm-codegen/src/entity/base_entity.rs | 6 ++++++ sea-orm-codegen/src/entity/transformer.rs | 2 ++ sea-orm-codegen/src/entity/writer.rs | 16 ++++++++++++++++ sea-orm-macros/src/derives/attributes.rs | 1 + sea-orm-macros/src/derives/entity_model.rs | 17 ++++++++++++++++- sea-orm-macros/src/derives/model.rs | 1 - src/entity/base_entity.rs | 5 +++++ src/entity/column.rs | 7 +++++++ src/schema/entity.rs | 7 +++++++ tests/common/features/applog.rs | 7 +++++-- tests/common/features/schema.rs | 19 ++++++++++++++++--- 11 files changed, 81 insertions(+), 7 deletions(-) diff --git a/sea-orm-codegen/src/entity/base_entity.rs b/sea-orm-codegen/src/entity/base_entity.rs index f261f22e49..c6d24c4f9a 100644 --- a/sea-orm-codegen/src/entity/base_entity.rs +++ b/sea-orm-codegen/src/entity/base_entity.rs @@ -15,9 +15,14 @@ pub struct Entity { pub(crate) relations: Vec, pub(crate) conjunct_relations: Vec, pub(crate) primary_keys: Vec, + pub(crate) comment: Option, } impl Entity { + pub fn get_table_comment(&self) -> Option<&String> { + self.comment.as_ref() + } + pub fn get_table_name_snake_case(&self) -> String { self.table_name.to_snake_case() } @@ -318,6 +323,7 @@ mod tests { primary_keys: vec![PrimaryKey { name: "id".to_owned(), }], + comment: None, } } diff --git a/sea-orm-codegen/src/entity/transformer.rs b/sea-orm-codegen/src/entity/transformer.rs index 20f8047cb6..40401611ff 100644 --- a/sea-orm-codegen/src/entity/transformer.rs +++ b/sea-orm-codegen/src/entity/transformer.rs @@ -117,12 +117,14 @@ impl EntityTransformer { .collect::>() }), ); + let comment = table_create.get_comment().cloned(); let entity = Entity { table_name: table_name.clone(), columns, relations: relations.clone(), conjunct_relations: vec![], primary_keys, + comment, }; entities.insert(table_name.clone(), entity.clone()); for mut rel in relations.into_iter() { diff --git a/sea-orm-codegen/src/entity/writer.rs b/sea-orm-codegen/src/entity/writer.rs index 099a96797d..678964f0aa 100644 --- a/sea-orm-codegen/src/entity/writer.rs +++ b/sea-orm-codegen/src/entity/writer.rs @@ -785,6 +785,7 @@ impl EntityWriter { }, None => quote! {}, }; + let extra_derive = with_serde.extra_derive(); quote! { @@ -882,9 +883,11 @@ mod tests { primary_keys: vec![PrimaryKey { name: "id".to_owned(), }], + comment: None, }, Entity { table_name: "_cake_filling_".to_owned(), + comment: None, columns: vec![ Column { name: "cake_id".to_owned(), @@ -980,6 +983,7 @@ mod tests { name: "filling_id".to_owned(), }, ], + comment: None, }, Entity { table_name: "filling".to_owned(), @@ -1007,6 +1011,7 @@ mod tests { primary_keys: vec![PrimaryKey { name: "id".to_owned(), }], + comment: None, }, Entity { table_name: "fruit".to_owned(), @@ -1061,6 +1066,7 @@ mod tests { primary_keys: vec![PrimaryKey { name: "id".to_owned(), }], + comment: None, }, Entity { table_name: "vendor".to_owned(), @@ -1102,6 +1108,7 @@ mod tests { primary_keys: vec![PrimaryKey { name: "id".to_owned(), }], + comment: None, }, Entity { table_name: "rust_keyword".to_owned(), @@ -1259,6 +1266,7 @@ mod tests { primary_keys: vec![PrimaryKey { name: "id".to_owned(), }], + comment: None, }, Entity { table_name: "cake_with_float".to_owned(), @@ -1303,6 +1311,7 @@ mod tests { primary_keys: vec![PrimaryKey { name: "id".to_owned(), }], + comment: None, }, Entity { table_name: "cake_with_double".to_owned(), @@ -1347,6 +1356,7 @@ mod tests { primary_keys: vec![PrimaryKey { name: "id".to_owned(), }], + comment: None, }, Entity { table_name: "collection".to_owned(), @@ -1378,6 +1388,7 @@ mod tests { primary_keys: vec![PrimaryKey { name: "id".to_owned(), }], + comment: None, }, Entity { table_name: "collection_float".to_owned(), @@ -1409,6 +1420,7 @@ mod tests { primary_keys: vec![PrimaryKey { name: "id".to_owned(), }], + comment: None, }, ] } @@ -1770,6 +1782,7 @@ mod tests { fn test_gen_with_seaography() -> io::Result<()> { let cake_entity = Entity { table_name: "cake".to_owned(), + comment: None, columns: vec![ Column { name: "id".to_owned(), @@ -2225,6 +2238,7 @@ mod tests { primary_keys: vec![PrimaryKey { name: "id".to_owned(), }], + comment: None, }, ]; const ENTITY_FILES: [&str; 1] = [include_str!("../../tests/postgres/binary_json.rs")]; @@ -2308,6 +2322,7 @@ mod tests { let entities = vec![ Entity { table_name: "tea_pairing".to_owned(), + comment: None, columns: vec![ Column { name: "id".to_owned(), @@ -2351,6 +2366,7 @@ mod tests { }, Entity { table_name: "tea_pairing_with_size".to_owned(), + comment: None, columns: vec![ Column { name: "id".to_owned(), diff --git a/sea-orm-macros/src/derives/attributes.rs b/sea-orm-macros/src/derives/attributes.rs index 0b51945b01..9c0c24c4bb 100644 --- a/sea-orm-macros/src/derives/attributes.rs +++ b/sea-orm-macros/src/derives/attributes.rs @@ -11,6 +11,7 @@ pub mod derive_attr { pub relation: Option, pub schema_name: Option, pub table_name: Option, + pub comment: Option, pub table_iden: Option<()>, } } diff --git a/sea-orm-macros/src/derives/entity_model.rs b/sea-orm-macros/src/derives/entity_model.rs index bea168ac3e..6c4f8f2725 100644 --- a/sea-orm-macros/src/derives/entity_model.rs +++ b/sea-orm-macros/src/derives/entity_model.rs @@ -10,6 +10,7 @@ use syn::{ pub fn expand_derive_entity_model(data: Data, attrs: Vec) -> syn::Result { // if #[sea_orm(table_name = "foo", schema_name = "bar")] specified, create Entity struct let mut table_name = None; + let mut comment = quote! {None}; let mut schema_name = quote! { None }; let mut table_iden = false; attrs @@ -17,7 +18,10 @@ pub fn expand_derive_entity_model(data: Data, attrs: Vec) -> syn::Res .filter(|attr| attr.path().is_ident("sea_orm")) .try_for_each(|attr| { attr.parse_nested_meta(|meta| { - if meta.path.is_ident("table_name") { + if meta.path.is_ident("comment") { + let name: Lit = meta.value()?.parse()?; + comment = quote! { Some(#name) }; + } else if meta.path.is_ident("table_name") { table_name = Some(meta.value()?.parse::()?); } else if meta.path.is_ident("schema_name") { let name: Lit = meta.value()?.parse()?; @@ -51,6 +55,10 @@ pub fn expand_derive_entity_model(data: Data, attrs: Vec) -> syn::Res fn table_name(&self) -> &str { #table_name } + + fn comment(&self) -> Option<&str> { + #comment + } } } }) @@ -90,6 +98,7 @@ pub fn expand_derive_entity_model(data: Data, attrs: Vec) -> syn::Res let mut nullable = false; let mut default_value = None; + let mut comment = None; let mut default_expr = None; let mut select_as = None; let mut save_as = None; @@ -134,6 +143,8 @@ pub fn expand_derive_entity_model(data: Data, attrs: Vec) -> syn::Res meta.error(format!("Invalid auto_increment = {:?}", lit)) ); } + } else if meta.path.is_ident("comment") { + comment = Some(meta.value()?.parse::()?); } else if meta.path.is_ident("default_value") { default_value = Some(meta.value()?.parse::()?); } else if meta.path.is_ident("default_expr") { @@ -274,9 +285,13 @@ pub fn expand_derive_entity_model(data: Data, attrs: Vec) -> syn::Res if let Some(default_value) = default_value { match_row = quote! { #match_row.default_value(#default_value) }; } + if let Some(comment) = comment { + match_row = quote! { #match_row.comment(#comment) }; + } if let Some(default_expr) = default_expr { match_row = quote! { #match_row.default(#default_expr) }; } + // match_row = quote! { #match_row.comment() }; columns_trait.push(match_row); } } diff --git a/sea-orm-macros/src/derives/model.rs b/sea-orm-macros/src/derives/model.rs index b28c62cf2d..6884098187 100644 --- a/sea-orm-macros/src/derives/model.rs +++ b/sea-orm-macros/src/derives/model.rs @@ -182,7 +182,6 @@ impl DeriveModel { /// Method to derive an ActiveModel pub fn expand_derive_model(input: syn::DeriveInput) -> syn::Result { let ident_span = input.ident.span(); - match DeriveModel::new(input) { Ok(model) => model.expand(), Err(Error::InputNotStruct) => Ok(quote_spanned! { diff --git a/src/entity/base_entity.rs b/src/entity/base_entity.rs index 5f7422e338..6cd8b1052f 100644 --- a/src/entity/base_entity.rs +++ b/src/entity/base_entity.rs @@ -20,6 +20,11 @@ pub trait EntityName: IdenStatic + Default { None } + /// Method to get the comment for the schema, defaults to [Option::None] if not set + fn comment(&self) -> Option<&str> { + None + } + /// Get the name of the table fn table_name(&self) -> &str; diff --git a/src/entity/column.rs b/src/entity/column.rs index 2117182be8..54e73c35e9 100644 --- a/src/entity/column.rs +++ b/src/entity/column.rs @@ -16,6 +16,7 @@ pub struct ColumnDef { pub(crate) unique: bool, pub(crate) indexed: bool, pub(crate) default: Option, + pub(crate) comment: Option, } macro_rules! bind_oper { @@ -312,6 +313,7 @@ impl ColumnTypeTrait for ColumnType { unique: false, indexed: false, default: None, + comment: None, } } @@ -344,6 +346,11 @@ impl ColumnDef { self.unique = true; self } + /// Set column comment + pub fn comment(mut self, v: &str) -> Self { + self.comment = Some(v.into()); + self + } /// Mark the column as nullable pub fn null(self) -> Self { diff --git a/src/schema/entity.rs b/src/schema/entity.rs index f6e9ead016..5e42d7b132 100644 --- a/src/schema/entity.rs +++ b/src/schema/entity.rs @@ -163,6 +163,10 @@ where { let mut stmt = TableCreateStatement::new(); + if let Some(comment) = entity.comment() { + stmt.comment(comment); + } + for column in E::Column::iter() { let mut column_def = column_def_from_entity_column::(column, backend); stmt.col(&mut column_def); @@ -213,6 +217,9 @@ where if let Some(default) = orm_column_def.default { column_def.default(default); } + if let Some(comment) = orm_column_def.comment { + column_def.comment(comment); + } for primary_key in E::PrimaryKey::iter() { if column.to_string() == primary_key.into_column().to_string() { if E::PrimaryKey::auto_increment() { diff --git a/tests/common/features/applog.rs b/tests/common/features/applog.rs index 98fd186900..f103d2de97 100644 --- a/tests/common/features/applog.rs +++ b/tests/common/features/applog.rs @@ -1,12 +1,15 @@ use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] -#[sea_orm(table_name = "applog")] +#[sea_orm(table_name = "applog", comment = "app logs")] pub struct Model { - #[sea_orm(primary_key)] + #[sea_orm(primary_key, comment = "ID")] pub id: i32, + #[sea_orm(comment = "action")] pub action: String, + #[sea_orm(comment = "action data")] pub json: Json, + #[sea_orm(comment = "create time")] pub created_at: DateTimeWithTimeZone, } diff --git a/tests/common/features/schema.rs b/tests/common/features/schema.rs index 0fb15cf02d..e74614c709 100644 --- a/tests/common/features/schema.rs +++ b/tests/common/features/schema.rs @@ -67,19 +67,32 @@ pub async fn create_tables(db: &DatabaseConnection) -> Result<(), DbErr> { pub async fn create_log_table(db: &DbConn) -> Result { let stmt = sea_query::Table::create() .table(applog::Entity) + .comment("app logs") .col( ColumnDef::new(applog::Column::Id) .integer() .not_null() + .comment("ID") .auto_increment() .primary_key(), ) - .col(ColumnDef::new(applog::Column::Action).string().not_null()) - .col(ColumnDef::new(applog::Column::Json).json().not_null()) + .col( + ColumnDef::new(applog::Column::Action) + .string() + .not_null() + .comment("action"), + ) + .col( + ColumnDef::new(applog::Column::Json) + .json() + .not_null() + .comment("action data"), + ) .col( ColumnDef::new(applog::Column::CreatedAt) .timestamp_with_time_zone() - .not_null(), + .not_null() + .comment("create time"), ) .to_owned(); From d4f8e7226efac42e747ff185d5f7442ec867acd2 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 14 Dec 2023 10:44:31 +0000 Subject: [PATCH 0230/1371] Revert sea-orm-codegen --- sea-orm-codegen/src/entity/base_entity.rs | 6 ------ sea-orm-codegen/src/entity/transformer.rs | 2 -- sea-orm-codegen/src/entity/writer.rs | 16 ---------------- 3 files changed, 24 deletions(-) diff --git a/sea-orm-codegen/src/entity/base_entity.rs b/sea-orm-codegen/src/entity/base_entity.rs index c6d24c4f9a..f261f22e49 100644 --- a/sea-orm-codegen/src/entity/base_entity.rs +++ b/sea-orm-codegen/src/entity/base_entity.rs @@ -15,14 +15,9 @@ pub struct Entity { pub(crate) relations: Vec, pub(crate) conjunct_relations: Vec, pub(crate) primary_keys: Vec, - pub(crate) comment: Option, } impl Entity { - pub fn get_table_comment(&self) -> Option<&String> { - self.comment.as_ref() - } - pub fn get_table_name_snake_case(&self) -> String { self.table_name.to_snake_case() } @@ -323,7 +318,6 @@ mod tests { primary_keys: vec![PrimaryKey { name: "id".to_owned(), }], - comment: None, } } diff --git a/sea-orm-codegen/src/entity/transformer.rs b/sea-orm-codegen/src/entity/transformer.rs index 40401611ff..20f8047cb6 100644 --- a/sea-orm-codegen/src/entity/transformer.rs +++ b/sea-orm-codegen/src/entity/transformer.rs @@ -117,14 +117,12 @@ impl EntityTransformer { .collect::>() }), ); - let comment = table_create.get_comment().cloned(); let entity = Entity { table_name: table_name.clone(), columns, relations: relations.clone(), conjunct_relations: vec![], primary_keys, - comment, }; entities.insert(table_name.clone(), entity.clone()); for mut rel in relations.into_iter() { diff --git a/sea-orm-codegen/src/entity/writer.rs b/sea-orm-codegen/src/entity/writer.rs index 678964f0aa..099a96797d 100644 --- a/sea-orm-codegen/src/entity/writer.rs +++ b/sea-orm-codegen/src/entity/writer.rs @@ -785,7 +785,6 @@ impl EntityWriter { }, None => quote! {}, }; - let extra_derive = with_serde.extra_derive(); quote! { @@ -883,11 +882,9 @@ mod tests { primary_keys: vec![PrimaryKey { name: "id".to_owned(), }], - comment: None, }, Entity { table_name: "_cake_filling_".to_owned(), - comment: None, columns: vec![ Column { name: "cake_id".to_owned(), @@ -983,7 +980,6 @@ mod tests { name: "filling_id".to_owned(), }, ], - comment: None, }, Entity { table_name: "filling".to_owned(), @@ -1011,7 +1007,6 @@ mod tests { primary_keys: vec![PrimaryKey { name: "id".to_owned(), }], - comment: None, }, Entity { table_name: "fruit".to_owned(), @@ -1066,7 +1061,6 @@ mod tests { primary_keys: vec![PrimaryKey { name: "id".to_owned(), }], - comment: None, }, Entity { table_name: "vendor".to_owned(), @@ -1108,7 +1102,6 @@ mod tests { primary_keys: vec![PrimaryKey { name: "id".to_owned(), }], - comment: None, }, Entity { table_name: "rust_keyword".to_owned(), @@ -1266,7 +1259,6 @@ mod tests { primary_keys: vec![PrimaryKey { name: "id".to_owned(), }], - comment: None, }, Entity { table_name: "cake_with_float".to_owned(), @@ -1311,7 +1303,6 @@ mod tests { primary_keys: vec![PrimaryKey { name: "id".to_owned(), }], - comment: None, }, Entity { table_name: "cake_with_double".to_owned(), @@ -1356,7 +1347,6 @@ mod tests { primary_keys: vec![PrimaryKey { name: "id".to_owned(), }], - comment: None, }, Entity { table_name: "collection".to_owned(), @@ -1388,7 +1378,6 @@ mod tests { primary_keys: vec![PrimaryKey { name: "id".to_owned(), }], - comment: None, }, Entity { table_name: "collection_float".to_owned(), @@ -1420,7 +1409,6 @@ mod tests { primary_keys: vec![PrimaryKey { name: "id".to_owned(), }], - comment: None, }, ] } @@ -1782,7 +1770,6 @@ mod tests { fn test_gen_with_seaography() -> io::Result<()> { let cake_entity = Entity { table_name: "cake".to_owned(), - comment: None, columns: vec![ Column { name: "id".to_owned(), @@ -2238,7 +2225,6 @@ mod tests { primary_keys: vec![PrimaryKey { name: "id".to_owned(), }], - comment: None, }, ]; const ENTITY_FILES: [&str; 1] = [include_str!("../../tests/postgres/binary_json.rs")]; @@ -2322,7 +2308,6 @@ mod tests { let entities = vec![ Entity { table_name: "tea_pairing".to_owned(), - comment: None, columns: vec![ Column { name: "id".to_owned(), @@ -2366,7 +2351,6 @@ mod tests { }, Entity { table_name: "tea_pairing_with_size".to_owned(), - comment: None, columns: vec![ Column { name: "id".to_owned(), From 955bbcbc128d5739b8da33db739061edc09fb699 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 14 Dec 2023 11:40:55 +0000 Subject: [PATCH 0231/1371] Database Proxy (#2000) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: Add proxy connection type * feat: Add proxy database's proxy functions trait. * fix: Remove some unused impl to fix the unit test * test: Create the proxy by empty declaration. * test: Try to genereate query and exec commands. * perf: Add more query debug trait for debugging. * chore: Add the example for wasi + proxy. * chore: Try to read string from wasmtime vm. * chore: Sucks, but how to do without tokio::spawn? * chore: Complete the basic memory read logic. * chore: Abandon the WASI demo, native demo first... * refactor: Use single proxy connection generator to avoid stack overflow * refactor: Rename the inner structs' name * fix: Fix CI clippy and unit test * fix: Rename the example. * chore: Try to embed surrealdb for proxy test. * fix: Transfer the query result correctly. * refactor: Rename the example. * chore: Ready to add example for wasmtime proxy. * feat: Try to compile sea-orm into wasm binary. But it would failed on wasm32-wasi target because of the socket deps. It can be compiled on wasm32-unknown-unknown target. * fix: WASM targets can't use sqlx. * fix: Try to fix CI by remove toml. * fix: Try to fix CI by remove toml. * fix: Move vm to the example's root dir. * fix: Add a pre-build script. * chore: Add README. * fix: Try to fix CI. * feat: Add proxy logic in wasm module. * fix: Try to run the wasi module. But WASI cannot support multi threads.. so the module was run failed. * refactor: Bump wasmtime to 14. * fix: Now we can use async traits on wasmtime. The solution is add the current thread tag to tokio-wasi. * build: Use build.rs instead of dynamic command. * feat: Add the execute result's transfer logic. * fix: Convert sqlx query result for sea-query. * fix: Now we can transfer wasm's query to outside. * refactor: Convert to ProxyRow first. It's the solution to know the type information about the value. * fix: Multiple time library reference. * feat: Add a new proxy example which uses GlueSQL. * test: Add the test cases for three new examples. Just try to run once... * ci: Add wasm component's compiler for unit test. * ci: Add wasi target. * ci: It may needs wasi target twice... * feat: Add more keys for proxy execute result. To transfer the fully information of the execute result. * fix: Use custom id type instead of json value. * fix: Wrong reference type. * fix: Rewrite the transformer. * perf: Add ToString trait for proxy exec result. * revert: Again. Refs: 9bac6e91ca9df04ccd8368906e1613cfc5b96218 * revert: Back to the basic proxy exec result. Refs: e0330dde73a54d461d5f38c69eec5e13bcc928d4 * refactor: Update GlueSQL and SurrealDB examples. (#1980) * refactor: Bump gluesql to 0.15 Relate to https://github.com/gluesql/gluesql/issues/1438 * Use SQLParser to parse and replace placeholders. * Use SQLParser for surrealdb demo. * Transform the query by SQLParser. * Tweaks * Remove wasmtime example. (#2001) * ci: Add additional targets. * Remove proxy wasmtime example. * Format --------- Co-authored-by: 伊欧 Co-authored-by: 伊欧 --- Cargo.toml | 3 +- examples/proxy_gluesql_example/Cargo.toml | 29 + examples/proxy_gluesql_example/README.md | 7 + .../proxy_gluesql_example/src/entity/mod.rs | 1 + .../proxy_gluesql_example/src/entity/post.rs | 17 + examples/proxy_gluesql_example/src/main.rs | 190 ++++ examples/proxy_surrealdb_example/Cargo.toml | 27 + examples/proxy_surrealdb_example/README.md | 7 + .../proxy_surrealdb_example/src/entity/mod.rs | 1 + .../src/entity/post.rs | 17 + examples/proxy_surrealdb_example/src/main.rs | 271 +++++ src/database/db_connection.rs | 84 +- src/database/mock.rs | 3 +- src/database/mod.rs | 36 + src/database/proxy.rs | 925 ++++++++++++++++++ src/database/stream/query.rs | 28 +- src/database/stream/transaction.rs | 7 + src/database/transaction.rs | 16 + src/driver/mod.rs | 4 + src/driver/proxy.rs | 140 +++ src/executor/execute.rs | 7 + src/executor/query.rs | 77 +- src/query/json.rs | 12 + 23 files changed, 1900 insertions(+), 9 deletions(-) create mode 100644 examples/proxy_gluesql_example/Cargo.toml create mode 100644 examples/proxy_gluesql_example/README.md create mode 100644 examples/proxy_gluesql_example/src/entity/mod.rs create mode 100644 examples/proxy_gluesql_example/src/entity/post.rs create mode 100644 examples/proxy_gluesql_example/src/main.rs create mode 100644 examples/proxy_surrealdb_example/Cargo.toml create mode 100644 examples/proxy_surrealdb_example/README.md create mode 100644 examples/proxy_surrealdb_example/src/entity/mod.rs create mode 100644 examples/proxy_surrealdb_example/src/entity/post.rs create mode 100644 examples/proxy_surrealdb_example/src/main.rs create mode 100644 src/database/proxy.rs create mode 100644 src/driver/proxy.rs diff --git a/Cargo.toml b/Cargo.toml index c2aec2c775..d4944d627c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ keywords = ["async", "orm", "mysql", "postgres", "sqlite"] rust-version = "1.65" [package.metadata.docs.rs] -features = ["default", "sqlx-all", "mock", "runtime-async-std-native-tls", "postgres-array", "sea-orm-internal"] +features = ["default", "sqlx-all", "mock", "proxy", "runtime-async-std-native-tls", "postgres-array", "sea-orm-internal"] rustdoc-args = ["--cfg", "docsrs"] [lib] @@ -76,6 +76,7 @@ default = [ ] macros = ["sea-orm-macros/derive"] mock = [] +proxy = ["serde_json"] with-json = ["serde_json", "sea-query/with-json", "chrono?/serde", "time?/serde", "uuid?/serde", "sea-query-binder?/with-json", "sqlx?/json"] with-chrono = ["chrono", "sea-query/with-chrono", "sea-query-binder?/with-chrono", "sqlx?/chrono"] with-rust_decimal = ["rust_decimal", "sea-query/with-rust_decimal", "sea-query-binder?/with-rust_decimal", "sqlx?/rust_decimal"] diff --git a/examples/proxy_gluesql_example/Cargo.toml b/examples/proxy_gluesql_example/Cargo.toml new file mode 100644 index 0000000000..bc495135ad --- /dev/null +++ b/examples/proxy_gluesql_example/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "sea-orm-proxy-gluesql-example" +version = "0.1.0" +authors = ["Langyo "] +edition = "2021" +publish = false + +[workspace] + +[dependencies] +async-std = { version = "1.12", features = ["attributes", "tokio1"] } +serde_json = { version = "1" } +serde = { version = "1" } +futures = { version = "0.3" } +async-stream = { version = "0.3" } +futures-util = { version = "0.3" } + +sqlparser = "0.40" +sea-orm = { path = "../../", features = [ + "proxy", + "debug-print", +] } +gluesql = { version = "0.15", default-features = false, features = [ + "memory-storage", +] } + +[dev-dependencies] +smol = { version = "1.2" } +smol-potat = { version = "1.1" } diff --git a/examples/proxy_gluesql_example/README.md b/examples/proxy_gluesql_example/README.md new file mode 100644 index 0000000000..2269c471eb --- /dev/null +++ b/examples/proxy_gluesql_example/README.md @@ -0,0 +1,7 @@ +# SeaORM Proxy Demo for GlueSQL + +Run this demo for [GlueSQL](https://gluesql.org/) with the following command: + +```bash +cargo run +``` diff --git a/examples/proxy_gluesql_example/src/entity/mod.rs b/examples/proxy_gluesql_example/src/entity/mod.rs new file mode 100644 index 0000000000..e8b6291ac3 --- /dev/null +++ b/examples/proxy_gluesql_example/src/entity/mod.rs @@ -0,0 +1 @@ +pub mod post; diff --git a/examples/proxy_gluesql_example/src/entity/post.rs b/examples/proxy_gluesql_example/src/entity/post.rs new file mode 100644 index 0000000000..8688460464 --- /dev/null +++ b/examples/proxy_gluesql_example/src/entity/post.rs @@ -0,0 +1,17 @@ +use sea_orm::entity::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Deserialize, Serialize)] +#[sea_orm(table_name = "posts")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i64, + + pub title: String, + pub text: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/examples/proxy_gluesql_example/src/main.rs b/examples/proxy_gluesql_example/src/main.rs new file mode 100644 index 0000000000..17c942d713 --- /dev/null +++ b/examples/proxy_gluesql_example/src/main.rs @@ -0,0 +1,190 @@ +//! Proxy connection example. + +#![deny(missing_docs)] + +mod entity; + +use std::{ + collections::BTreeMap, + sync::{Arc, Mutex}, +}; + +use gluesql::{memory_storage::MemoryStorage, prelude::Glue}; +use sea_orm::{ + ActiveValue::Set, Database, DbBackend, DbErr, EntityTrait, ProxyDatabaseTrait, ProxyExecResult, + ProxyRow, Statement, +}; + +use entity::post::{ActiveModel, Entity}; + +struct ProxyDb { + mem: Mutex>, +} + +impl std::fmt::Debug for ProxyDb { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ProxyDb").finish() + } +} + +impl ProxyDatabaseTrait for ProxyDb { + fn query(&self, statement: Statement) -> Result, DbErr> { + println!("SQL query: {:?}", statement); + let sql = statement.sql.clone(); + + let mut ret: Vec = vec![]; + async_std::task::block_on(async { + for payload in self.mem.lock().unwrap().execute(sql).await.unwrap().iter() { + match payload { + gluesql::prelude::Payload::Select { labels, rows } => { + for row in rows.iter() { + let mut map = BTreeMap::new(); + for (label, column) in labels.iter().zip(row.iter()) { + map.insert( + label.to_owned(), + match column { + gluesql::prelude::Value::I64(val) => { + sea_orm::Value::BigInt(Some(*val)) + } + gluesql::prelude::Value::Str(val) => { + sea_orm::Value::String(Some(Box::new(val.to_owned()))) + } + _ => unreachable!("Unsupported value: {:?}", column), + }, + ); + } + ret.push(map.into()); + } + } + _ => unreachable!("Unsupported payload: {:?}", payload), + } + } + }); + + Ok(ret) + } + + fn execute(&self, statement: Statement) -> Result { + let sql = if let Some(values) = statement.values { + // Replace all the '?' with the statement values + use sqlparser::ast::{Expr, Value}; + use sqlparser::dialect::GenericDialect; + use sqlparser::parser::Parser; + + let dialect = GenericDialect {}; + let mut ast = Parser::parse_sql(&dialect, statement.sql.as_str()).unwrap(); + match &mut ast[0] { + sqlparser::ast::Statement::Insert { + columns, source, .. + } => { + for item in columns.iter_mut() { + item.quote_style = Some('"'); + } + + if let Some(obj) = source { + match &mut *obj.body { + sqlparser::ast::SetExpr::Values(obj) => { + for (mut item, val) in obj.rows[0].iter_mut().zip(values.0.iter()) { + match &mut item { + Expr::Value(item) => { + *item = match val { + sea_orm::Value::String(val) => { + Value::SingleQuotedString(match val { + Some(val) => val.to_string(), + None => "".to_string(), + }) + } + sea_orm::Value::BigInt(val) => Value::Number( + val.unwrap_or(0).to_string(), + false, + ), + _ => todo!(), + }; + } + _ => todo!(), + } + } + } + _ => todo!(), + } + } + } + _ => todo!(), + } + + let statement = &ast[0]; + statement.to_string() + } else { + statement.sql + }; + + println!("SQL execute: {}", sql); + async_std::task::block_on(async { + self.mem.lock().unwrap().execute(sql).await.unwrap(); + }); + + Ok(ProxyExecResult { + last_insert_id: 1, + rows_affected: 1, + }) + } +} + +#[async_std::main] +async fn main() { + let mem = MemoryStorage::default(); + let mut glue = Glue::new(mem); + + glue.execute( + r#" + CREATE TABLE IF NOT EXISTS posts ( + id INTEGER PRIMARY KEY, + title TEXT NOT NULL, + text TEXT NOT NULL + ) + "#, + ) + .await + .unwrap(); + + let db = Database::connect_proxy( + DbBackend::Sqlite, + Arc::new(Mutex::new(Box::new(ProxyDb { + mem: Mutex::new(glue), + }))), + ) + .await + .unwrap(); + + println!("Initialized"); + + let data = ActiveModel { + id: Set(11), + title: Set("Homo".to_owned()), + text: Set("いいよ、来いよ".to_owned()), + }; + Entity::insert(data).exec(&db).await.unwrap(); + let data = ActiveModel { + id: Set(45), + title: Set("Homo".to_owned()), + text: Set("そうだよ".to_owned()), + }; + Entity::insert(data).exec(&db).await.unwrap(); + let data = ActiveModel { + id: Set(14), + title: Set("Homo".to_owned()), + text: Set("悔い改めて".to_owned()), + }; + Entity::insert(data).exec(&db).await.unwrap(); + + let list = Entity::find().all(&db).await.unwrap().to_vec(); + println!("Result: {:?}", list); +} + +#[cfg(test)] +mod tests { + #[smol_potat::test] + async fn try_run() { + crate::main() + } +} diff --git a/examples/proxy_surrealdb_example/Cargo.toml b/examples/proxy_surrealdb_example/Cargo.toml new file mode 100644 index 0000000000..4b16af5159 --- /dev/null +++ b/examples/proxy_surrealdb_example/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "sea-orm-proxy-surrealdb-example" +version = "0.1.0" +authors = ["Langyo "] +edition = "2021" +publish = false + +[workspace] + +[dependencies] +async-std = { version = "1.12", features = ["attributes", "tokio1"] } +serde_json = { version = "1" } +serde = { version = "1" } +futures = { version = "0.3" } +async-stream = { version = "0.3" } +futures-util = { version = "0.3" } + +sqlparser = "0.40" +sea-orm = { path = "../../", features = [ + "proxy", + "debug-print", +] } +surrealdb = { version = "1", features = ["kv-mem"] } + +[dev-dependencies] +smol = { version = "1.2" } +smol-potat = { version = "1.1" } diff --git a/examples/proxy_surrealdb_example/README.md b/examples/proxy_surrealdb_example/README.md new file mode 100644 index 0000000000..773850bc3a --- /dev/null +++ b/examples/proxy_surrealdb_example/README.md @@ -0,0 +1,7 @@ +# SeaORM Proxy Demo for SurrealDB + +Run this demo for [SurrealDB](https://surrealdb.com/) with the following command: + +```bash +cargo run +``` diff --git a/examples/proxy_surrealdb_example/src/entity/mod.rs b/examples/proxy_surrealdb_example/src/entity/mod.rs new file mode 100644 index 0000000000..e8b6291ac3 --- /dev/null +++ b/examples/proxy_surrealdb_example/src/entity/mod.rs @@ -0,0 +1 @@ +pub mod post; diff --git a/examples/proxy_surrealdb_example/src/entity/post.rs b/examples/proxy_surrealdb_example/src/entity/post.rs new file mode 100644 index 0000000000..1fde0c4cc4 --- /dev/null +++ b/examples/proxy_surrealdb_example/src/entity/post.rs @@ -0,0 +1,17 @@ +use sea_orm::entity::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Deserialize, Serialize)] +#[sea_orm(table_name = "posts")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: String, + + pub title: String, + pub text: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/examples/proxy_surrealdb_example/src/main.rs b/examples/proxy_surrealdb_example/src/main.rs new file mode 100644 index 0000000000..b23f33c5f6 --- /dev/null +++ b/examples/proxy_surrealdb_example/src/main.rs @@ -0,0 +1,271 @@ +//! Proxy connection example. + +#![deny(missing_docs)] + +mod entity; + +use std::{ + collections::BTreeMap, + sync::{Arc, Mutex}, +}; + +use sea_orm::{ + ActiveValue::Set, Database, DbBackend, DbErr, EntityTrait, ProxyDatabaseTrait, ProxyExecResult, + ProxyRow, Statement, +}; +use surrealdb::{ + engine::local::{Db, Mem}, + Surreal, +}; + +use entity::post::{ActiveModel, Entity}; + +#[derive(Debug)] +struct ProxyDb { + mem: Surreal, +} + +impl ProxyDatabaseTrait for ProxyDb { + fn query(&self, statement: Statement) -> Result, DbErr> { + println!("SQL query: {:?}", statement); + let mut ret = async_std::task::block_on(async { + // Surrealdb's grammar is not compatible with sea-orm's + // so we need to remove the extra clauses + // from "SELECT `from`.`col` FROM `from` WHERE `from`.`col` = xx" + // to "SELECT `col` FROM `from` WHERE `col` = xx" + + use sqlparser::ast::{Expr, SelectItem, SetExpr, TableFactor}; + use sqlparser::dialect::GenericDialect; + use sqlparser::parser::Parser; + + let dialect = GenericDialect {}; + let mut ast = Parser::parse_sql(&dialect, statement.sql.as_str()).unwrap(); + match &mut ast[0] { + sqlparser::ast::Statement::Query(query) => match &mut *query.body { + SetExpr::Select(body) => { + body.projection.iter_mut().for_each(|item| { + match item { + SelectItem::UnnamedExpr(expr) => { + match expr { + Expr::CompoundIdentifier(idents) => { + // Remove the head of the identifier + // e.g. `from`.`col` -> `col` + let ident = idents.pop().unwrap(); + *expr = Expr::Identifier(ident); + } + _ => todo!(), + } + } + _ => todo!(), + } + }); + body.from.iter_mut().for_each(|item| { + match &mut item.relation { + TableFactor::Table { name, .. } => { + // Remove the head of the identifier + // e.g. `from`.`col` -> `col` + let ident = name.0.pop().unwrap(); + name.0 = vec![ident]; + } + _ => todo!(), + } + }); + } + _ => todo!(), + }, + _ => todo!(), + }; + + let statement = &ast[0]; + let sql = statement.to_string(); + println!("SQL: {}", sql); + self.mem.query(sql).await + }) + .unwrap(); + + // Convert the result to sea-orm's format + let ret: Vec = ret.take(0).unwrap(); + println!("SQL query result: {}", serde_json::to_string(&ret).unwrap()); + let ret = ret + .iter() + .map(|row| { + let mut map = serde_json::Map::new(); + for (k, v) in row.as_object().unwrap().iter() { + if k == "id" { + // Get `tb` and `id` columns from surrealdb + // and convert them to sea-orm's `id` + let tb = v.as_object().unwrap().get("tb").unwrap().to_string(); + let id = v + .as_object() + .unwrap() + .get("id") + .unwrap() + .get("String") + .unwrap(); + + // Remove the quotes + let tb = tb.to_string().replace("\"", ""); + let id = id.to_string().replace("\"", ""); + + map.insert("id".to_owned(), format!("{}:{}", tb, id).into()); + continue; + } + + map.insert(k.to_owned(), v.to_owned()); + } + serde_json::Value::Object(map) + }) + .map(|v: serde_json::Value| { + let mut ret: BTreeMap = BTreeMap::new(); + for (k, v) in v.as_object().unwrap().iter() { + ret.insert( + k.to_owned(), + match v { + serde_json::Value::Bool(b) => { + sea_orm::Value::TinyInt(if *b { Some(1) } else { Some(0) }) + } + serde_json::Value::Number(n) => { + if n.is_i64() { + sea_orm::Value::BigInt(Some(n.as_i64().unwrap())) + } else if n.is_u64() { + sea_orm::Value::BigUnsigned(Some(n.as_u64().unwrap())) + } else if n.is_f64() { + sea_orm::Value::Double(Some(n.as_f64().unwrap())) + } else { + unreachable!() + } + } + serde_json::Value::String(s) => { + sea_orm::Value::String(Some(Box::new(s.to_owned()))) + } + _ => sea_orm::Value::Json(Some(Box::new(v.to_owned()))), + }, + ); + } + ProxyRow { values: ret } + }) + .collect::>(); + + Ok(ret) + } + + fn execute(&self, statement: Statement) -> Result { + async_std::task::block_on(async { + if let Some(values) = statement.values { + // Replace all the '?' with the statement values + use sqlparser::ast::{Expr, Value}; + use sqlparser::dialect::GenericDialect; + use sqlparser::parser::Parser; + + let dialect = GenericDialect {}; + let mut ast = Parser::parse_sql(&dialect, statement.sql.as_str()).unwrap(); + match &mut ast[0] { + sqlparser::ast::Statement::Insert { + table_name, + columns, + source, + .. + } => { + // Replace the table name's quote style + table_name.0[0].quote_style = Some('`'); + + // Replace all the column names' quote style + for item in columns.iter_mut() { + item.quote_style = Some('`'); + } + + // Convert the values to sea-orm's format + if let Some(obj) = source { + match &mut *obj.body { + sqlparser::ast::SetExpr::Values(obj) => { + for (mut item, val) in + obj.rows[0].iter_mut().zip(values.0.iter()) + { + match &mut item { + Expr::Value(item) => { + *item = match val { + sea_orm::Value::String(val) => { + Value::SingleQuotedString(match val { + Some(val) => val.to_string(), + None => "".to_string(), + }) + } + sea_orm::Value::BigInt(val) => Value::Number( + val.unwrap_or(0).to_string(), + false, + ), + _ => todo!(), + }; + } + _ => todo!(), + } + } + } + _ => todo!(), + } + } + } + _ => todo!(), + } + + let statement = &ast[0]; + let sql = statement.to_string(); + println!("SQL: {}", sql); + self.mem.query(sql).await + } else { + self.mem.query(statement.sql).await + } + }) + .unwrap(); + + Ok(ProxyExecResult { + last_insert_id: 1, + rows_affected: 1, + }) + } +} + +#[async_std::main] +async fn main() { + let mem = Surreal::new::(()).await.unwrap(); + mem.use_ns("test").use_db("post").await.unwrap(); + + let db = Database::connect_proxy( + DbBackend::MySql, + Arc::new(Mutex::new(Box::new(ProxyDb { mem }))), + ) + .await + .unwrap(); + + println!("Initialized"); + + let data = ActiveModel { + title: Set("Homo".to_owned()), + text: Set("いいよ、来いよ".to_owned()), + ..Default::default() + }; + Entity::insert(data).exec(&db).await.unwrap(); + let data = ActiveModel { + title: Set("Homo".to_owned()), + text: Set("そうだよ".to_owned()), + ..Default::default() + }; + Entity::insert(data).exec(&db).await.unwrap(); + let data = ActiveModel { + title: Set("Homo".to_owned()), + text: Set("悔い改めて".to_owned()), + ..Default::default() + }; + Entity::insert(data).exec(&db).await.unwrap(); + + let list = Entity::find().all(&db).await.unwrap().to_vec(); + println!("Result: {:?}", list); +} + +#[cfg(test)] +mod tests { + #[smol_potat::test] + async fn try_run() { + crate::main() + } +} diff --git a/src/database/db_connection.rs b/src/database/db_connection.rs index ad2b56900e..29815ec9f0 100644 --- a/src/database/db_connection.rs +++ b/src/database/db_connection.rs @@ -10,7 +10,7 @@ use url::Url; #[cfg(feature = "sqlx-dep")] use sqlx::pool::PoolConnection; -#[cfg(feature = "mock")] +#[cfg(any(feature = "mock", feature = "proxy"))] use std::sync::Arc; /// Handle a database connection depending on the backend @@ -20,15 +20,23 @@ pub enum DatabaseConnection { /// Create a MYSQL database connection and pool #[cfg(feature = "sqlx-mysql")] SqlxMySqlPoolConnection(crate::SqlxMySqlPoolConnection), - /// Create a PostgreSQL database connection and pool + + /// Create a PostgreSQL database connection and pool #[cfg(feature = "sqlx-postgres")] SqlxPostgresPoolConnection(crate::SqlxPostgresPoolConnection), - /// Create a SQLite database connection and pool + + /// Create a SQLite database connection and pool #[cfg(feature = "sqlx-sqlite")] SqlxSqlitePoolConnection(crate::SqlxSqlitePoolConnection), - /// Create a Mock database connection useful for testing + + /// Create a Mock database connection useful for testing #[cfg(feature = "mock")] MockDatabaseConnection(Arc), + + /// Create a Proxy database connection useful for proxying + #[cfg(feature = "proxy")] + ProxyDatabaseConnection(Arc), + /// The connection to the database has been severed Disconnected, } @@ -66,7 +74,9 @@ pub(crate) enum InnerConnection { #[cfg(feature = "sqlx-sqlite")] Sqlite(PoolConnection), #[cfg(feature = "mock")] - Mock(std::sync::Arc), + Mock(Arc), + #[cfg(feature = "proxy")] + Proxy(Arc), } impl std::fmt::Debug for DatabaseConnection { @@ -83,6 +93,8 @@ impl std::fmt::Debug for DatabaseConnection { Self::SqlxSqlitePoolConnection(_) => "SqlxSqlitePoolConnection", #[cfg(feature = "mock")] Self::MockDatabaseConnection(_) => "MockDatabaseConnection", + #[cfg(feature = "proxy")] + Self::ProxyDatabaseConnection(_) => "ProxyDatabaseConnection", Self::Disconnected => "Disconnected", } ) @@ -101,6 +113,8 @@ impl ConnectionTrait for DatabaseConnection { DatabaseConnection::SqlxSqlitePoolConnection(_) => DbBackend::Sqlite, #[cfg(feature = "mock")] DatabaseConnection::MockDatabaseConnection(conn) => conn.get_database_backend(), + #[cfg(feature = "proxy")] + DatabaseConnection::ProxyDatabaseConnection(conn) => conn.get_database_backend(), DatabaseConnection::Disconnected => panic!("Disconnected"), } } @@ -117,6 +131,8 @@ impl ConnectionTrait for DatabaseConnection { DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.execute(stmt).await, #[cfg(feature = "mock")] DatabaseConnection::MockDatabaseConnection(conn) => conn.execute(stmt), + #[cfg(feature = "proxy")] + DatabaseConnection::ProxyDatabaseConnection(conn) => conn.execute(stmt), DatabaseConnection::Disconnected => Err(conn_err("Disconnected")), } } @@ -141,6 +157,12 @@ impl ConnectionTrait for DatabaseConnection { let stmt = Statement::from_string(db_backend, sql); conn.execute(stmt) } + #[cfg(feature = "proxy")] + DatabaseConnection::ProxyDatabaseConnection(conn) => { + let db_backend = conn.get_database_backend(); + let stmt = Statement::from_string(db_backend, sql); + conn.execute(stmt) + } DatabaseConnection::Disconnected => Err(conn_err("Disconnected")), } } @@ -157,6 +179,8 @@ impl ConnectionTrait for DatabaseConnection { DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.query_one(stmt).await, #[cfg(feature = "mock")] DatabaseConnection::MockDatabaseConnection(conn) => conn.query_one(stmt), + #[cfg(feature = "proxy")] + DatabaseConnection::ProxyDatabaseConnection(conn) => conn.query_one(stmt), DatabaseConnection::Disconnected => Err(conn_err("Disconnected")), } } @@ -173,6 +197,8 @@ impl ConnectionTrait for DatabaseConnection { DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.query_all(stmt).await, #[cfg(feature = "mock")] DatabaseConnection::MockDatabaseConnection(conn) => conn.query_all(stmt), + #[cfg(feature = "proxy")] + DatabaseConnection::ProxyDatabaseConnection(conn) => conn.query_all(stmt), DatabaseConnection::Disconnected => Err(conn_err("Disconnected")), } } @@ -205,6 +231,10 @@ impl StreamTrait for DatabaseConnection { DatabaseConnection::MockDatabaseConnection(conn) => { Ok(crate::QueryStream::from((Arc::clone(conn), stmt, None))) } + #[cfg(feature = "proxy")] + DatabaseConnection::ProxyDatabaseConnection(conn) => { + Ok(crate::QueryStream::from((Arc::clone(conn), stmt, None))) + } DatabaseConnection::Disconnected => Err(conn_err("Disconnected")), } }) @@ -226,6 +256,10 @@ impl TransactionTrait for DatabaseConnection { DatabaseConnection::MockDatabaseConnection(conn) => { DatabaseTransaction::new_mock(Arc::clone(conn), None).await } + #[cfg(feature = "proxy")] + DatabaseConnection::ProxyDatabaseConnection(conn) => { + DatabaseTransaction::new_proxy(conn.clone(), None).await + } DatabaseConnection::Disconnected => Err(conn_err("Disconnected")), } } @@ -253,6 +287,10 @@ impl TransactionTrait for DatabaseConnection { DatabaseConnection::MockDatabaseConnection(conn) => { DatabaseTransaction::new_mock(Arc::clone(conn), None).await } + #[cfg(feature = "proxy")] + DatabaseConnection::ProxyDatabaseConnection(conn) => { + DatabaseTransaction::new_proxy(conn.clone(), None).await + } DatabaseConnection::Disconnected => Err(conn_err("Disconnected")), } } @@ -289,6 +327,13 @@ impl TransactionTrait for DatabaseConnection { .map_err(TransactionError::Connection)?; transaction.run(_callback).await } + #[cfg(feature = "proxy")] + DatabaseConnection::ProxyDatabaseConnection(conn) => { + let transaction = DatabaseTransaction::new_proxy(conn.clone(), None) + .await + .map_err(TransactionError::Connection)?; + transaction.run(_callback).await + } DatabaseConnection::Disconnected => Err(conn_err("Disconnected").into()), } } @@ -333,6 +378,13 @@ impl TransactionTrait for DatabaseConnection { .map_err(TransactionError::Connection)?; transaction.run(_callback).await } + #[cfg(feature = "proxy")] + DatabaseConnection::ProxyDatabaseConnection(conn) => { + let transaction = DatabaseTransaction::new_proxy(conn.clone(), None) + .await + .map_err(TransactionError::Connection)?; + transaction.run(_callback).await + } DatabaseConnection::Disconnected => Err(conn_err("Disconnected").into()), } } @@ -367,6 +419,21 @@ impl DatabaseConnection { } } +#[cfg(feature = "proxy")] +impl DatabaseConnection { + /// Generate a database connection for testing the Proxy database + /// + /// # Panics + /// + /// Panics if [DbConn] is not a proxy connection. + pub fn as_proxy_connection(&self) -> &crate::ProxyDatabaseConnection { + match self { + DatabaseConnection::ProxyDatabaseConnection(proxy_conn) => proxy_conn, + _ => panic!("Not proxy connection"), + } + } +} + impl DatabaseConnection { /// Sets a callback to metric this connection pub fn set_metric_callback(&mut self, _callback: F) @@ -401,6 +468,8 @@ impl DatabaseConnection { DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.ping().await, #[cfg(feature = "mock")] DatabaseConnection::MockDatabaseConnection(conn) => conn.ping(), + #[cfg(feature = "proxy")] + DatabaseConnection::ProxyDatabaseConnection(conn) => conn.ping(), DatabaseConnection::Disconnected => Err(conn_err("Disconnected")), } } @@ -419,6 +488,11 @@ impl DatabaseConnection { // Nothing to cleanup, we just consume the `DatabaseConnection` Ok(()) } + #[cfg(feature = "proxy")] + DatabaseConnection::ProxyDatabaseConnection(_) => { + // Nothing to cleanup, we just consume the `DatabaseConnection` + Ok(()) + } DatabaseConnection::Disconnected => Err(conn_err("Disconnected")), } } diff --git a/src/database/mock.rs b/src/database/mock.rs index e760ceaf7e..6462766f2e 100644 --- a/src/database/mock.rs +++ b/src/database/mock.rs @@ -30,7 +30,8 @@ pub struct MockExecResult { /// which is just a [BTreeMap]<[String], [Value]> #[derive(Clone, Debug)] pub struct MockRow { - values: BTreeMap, + /// The values of the single row + pub values: BTreeMap, } /// A trait to get a [MockRow] from a type useful for testing in the [MockDatabase] diff --git a/src/database/mod.rs b/src/database/mod.rs index 36fe5709ff..a844d1c85d 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -5,6 +5,9 @@ mod db_connection; #[cfg(feature = "mock")] #[cfg_attr(docsrs, doc(cfg(feature = "mock")))] mod mock; +#[cfg(feature = "proxy")] +#[cfg_attr(docsrs, doc(cfg(feature = "proxy")))] +mod proxy; mod statement; mod stream; mod transaction; @@ -14,6 +17,9 @@ pub use db_connection::*; #[cfg(feature = "mock")] #[cfg_attr(docsrs, doc(cfg(feature = "mock")))] pub use mock::*; +#[cfg(feature = "proxy")] +#[cfg_attr(docsrs, doc(cfg(feature = "proxy")))] +pub use proxy::*; pub use statement::*; use std::borrow::Cow; pub use stream::*; @@ -79,11 +85,41 @@ impl Database { if crate::MockDatabaseConnector::accepts(&opt.url) { return crate::MockDatabaseConnector::connect(&opt.url).await; } + Err(conn_err(format!( "The connection string '{}' has no supporting driver.", opt.url ))) } + + /// Method to create a [DatabaseConnection] on a proxy database + #[cfg(feature = "proxy")] + #[instrument(level = "trace", skip(proxy_func_arc))] + pub async fn connect_proxy( + db_type: DbBackend, + proxy_func_arc: std::sync::Arc>>, + ) -> Result { + match db_type { + DbBackend::MySql => { + return crate::ProxyDatabaseConnector::connect( + DbBackend::MySql, + proxy_func_arc.to_owned(), + ); + } + DbBackend::Postgres => { + return crate::ProxyDatabaseConnector::connect( + DbBackend::Postgres, + proxy_func_arc.to_owned(), + ); + } + DbBackend::Sqlite => { + return crate::ProxyDatabaseConnector::connect( + DbBackend::Sqlite, + proxy_func_arc.to_owned(), + ); + } + } + } } impl From for ConnectOptions diff --git a/src/database/proxy.rs b/src/database/proxy.rs new file mode 100644 index 0000000000..4708106701 --- /dev/null +++ b/src/database/proxy.rs @@ -0,0 +1,925 @@ +use crate::{error::*, ExecResult, ExecResultHolder, QueryResult, QueryResultRow, Statement}; + +use sea_query::{Value, ValueType}; +use std::{collections::BTreeMap, fmt::Debug}; + +/// Defines the [ProxyDatabaseTrait] to save the functions +pub trait ProxyDatabaseTrait: Send + Sync + std::fmt::Debug { + /// Execute a query in the [ProxyDatabase], and return the query results + fn query(&self, statement: Statement) -> Result, DbErr>; + + /// Execute a command in the [ProxyDatabase], and report the number of rows affected + fn execute(&self, statement: Statement) -> Result; + + /// Begin a transaction in the [ProxyDatabase] + fn begin(&self) {} + + /// Commit a transaction in the [ProxyDatabase] + fn commit(&self) {} + + /// Rollback a transaction in the [ProxyDatabase] + fn rollback(&self) {} + + /// Ping the [ProxyDatabase], it should return an error if the database is not available + fn ping(&self) -> Result<(), DbErr> { + Ok(()) + } +} + +/// Defines the results obtained from a [ProxyDatabase] +#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)] +pub struct ProxyExecResult { + /// The last inserted id on auto-increment + pub last_insert_id: u64, + /// The number of rows affected by the database operation + pub rows_affected: u64, +} + +impl ProxyExecResult { + /// Create a new [ProxyExecResult] from the last inserted id and the number of rows affected + pub fn new(last_insert_id: u64, rows_affected: u64) -> Self { + Self { + last_insert_id, + rows_affected, + } + } +} + +impl Default for ExecResultHolder { + fn default() -> Self { + Self::Proxy(ProxyExecResult::default()) + } +} + +impl From for ExecResult { + fn from(result: ProxyExecResult) -> Self { + Self { + result: ExecResultHolder::Proxy(result), + } + } +} + +impl From for ProxyExecResult { + fn from(result: ExecResult) -> Self { + match result.result { + #[cfg(feature = "sqlx-mysql")] + ExecResultHolder::SqlxMySql(result) => Self { + last_insert_id: result.last_insert_id() as u64, + rows_affected: result.rows_affected(), + }, + #[cfg(feature = "sqlx-postgres")] + ExecResultHolder::SqlxPostgres(result) => Self { + last_insert_id: 0, + rows_affected: result.rows_affected(), + }, + #[cfg(feature = "sqlx-sqlite")] + ExecResultHolder::SqlxSqlite(result) => Self { + last_insert_id: result.last_insert_rowid() as u64, + rows_affected: result.rows_affected(), + }, + #[cfg(feature = "mock")] + ExecResultHolder::Mock(result) => Self { + last_insert_id: result.last_insert_id, + rows_affected: result.rows_affected, + }, + ExecResultHolder::Proxy(result) => result, + } + } +} + +/// Defines the structure of a Row for the [ProxyDatabase] +/// which is just a [BTreeMap]<[String], [Value]> +#[derive(Clone, Debug)] +pub struct ProxyRow { + /// The values of the single row + pub values: BTreeMap, +} + +impl ProxyRow { + /// Create a new [ProxyRow] from a [BTreeMap]<[String], [Value]> + pub fn new(values: BTreeMap) -> Self { + Self { values } + } +} + +impl Default for ProxyRow { + fn default() -> Self { + Self { + values: BTreeMap::new(), + } + } +} + +impl From> for ProxyRow { + fn from(values: BTreeMap) -> Self { + Self { values } + } +} + +impl From for BTreeMap { + fn from(row: ProxyRow) -> Self { + row.values + } +} + +impl From for Vec<(String, Value)> { + fn from(row: ProxyRow) -> Self { + row.values.into_iter().collect() + } +} + +impl From for QueryResult { + fn from(row: ProxyRow) -> Self { + QueryResult { + row: QueryResultRow::Proxy(row), + } + } +} + +#[cfg(feature = "with-json")] +impl Into for ProxyRow { + fn into(self) -> serde_json::Value { + self.values + .into_iter() + .map(|(k, v)| (k, sea_query::sea_value_to_json_value(&v))) + .collect() + } +} + +/// Convert [QueryResult] to [ProxyRow] +pub fn from_query_result_to_proxy_row(result: &QueryResult) -> ProxyRow { + match &result.row { + #[cfg(feature = "sqlx-mysql")] + QueryResultRow::SqlxMySql(row) => from_sqlx_mysql_row_to_proxy_row(&row), + #[cfg(feature = "sqlx-postgres")] + QueryResultRow::SqlxPostgres(row) => from_sqlx_postgres_row_to_proxy_row(&row), + #[cfg(feature = "sqlx-sqlite")] + QueryResultRow::SqlxSqlite(row) => from_sqlx_sqlite_row_to_proxy_row(&row), + #[cfg(feature = "mock")] + QueryResultRow::Mock(row) => ProxyRow { + values: row.values.clone(), + }, + QueryResultRow::Proxy(row) => row.to_owned(), + } +} + +#[cfg(feature = "sqlx-mysql")] +pub(crate) fn from_sqlx_mysql_row_to_proxy_row(row: &sqlx::mysql::MySqlRow) -> ProxyRow { + // https://docs.rs/sqlx-mysql/0.7.2/src/sqlx_mysql/protocol/text/column.rs.html + // https://docs.rs/sqlx-mysql/0.7.2/sqlx_mysql/types/index.html + use sqlx::{Column, Row, TypeInfo}; + ProxyRow { + values: row + .columns() + .iter() + .map(|c| { + ( + c.name().to_string(), + match c.type_info().name() { + "TINYINT(1)" | "BOOLEAN" => Value::Bool(Some( + row.try_get(c.ordinal()).expect("Failed to get boolean"), + )), + "TINYINT UNSIGNED" => Value::TinyUnsigned(Some( + row.try_get(c.ordinal()) + .expect("Failed to get unsigned tiny integer"), + )), + "SMALLINT UNSIGNED" => Value::SmallUnsigned(Some( + row.try_get(c.ordinal()) + .expect("Failed to get unsigned small integer"), + )), + "INT UNSIGNED" => Value::Unsigned(Some( + row.try_get(c.ordinal()) + .expect("Failed to get unsigned integer"), + )), + "MEDIUMINT UNSIGNED" | "BIGINT UNSIGNED" => Value::BigUnsigned(Some( + row.try_get(c.ordinal()) + .expect("Failed to get unsigned big integer"), + )), + "TINYINT" => Value::TinyInt(Some( + row.try_get(c.ordinal()) + .expect("Failed to get tiny integer"), + )), + "SMALLINT" => Value::SmallInt(Some( + row.try_get(c.ordinal()) + .expect("Failed to get small integer"), + )), + "INT" => Value::Int(Some( + row.try_get(c.ordinal()).expect("Failed to get integer"), + )), + "MEDIUMINT" | "BIGINT" => Value::BigInt(Some( + row.try_get(c.ordinal()).expect("Failed to get big integer"), + )), + "FLOAT" => Value::Float(Some( + row.try_get(c.ordinal()).expect("Failed to get float"), + )), + "DOUBLE" => Value::Double(Some( + row.try_get(c.ordinal()).expect("Failed to get double"), + )), + + "BIT" | "BINARY" | "VARBINARY" | "TINYBLOB" | "BLOB" | "MEDIUMBLOB" + | "LONGBLOB" => Value::Bytes(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get bytes"), + ))), + + "CHAR" | "VARCHAR" | "TINYTEXT" | "TEXT" | "MEDIUMTEXT" | "LONGTEXT" => { + Value::String(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get string"), + ))) + } + + #[cfg(feature = "with-chrono")] + "TIMESTAMP" => Value::ChronoDateTimeUtc(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get timestamp"), + ))), + #[cfg(all(feature = "with-time", not(feature = "with-chrono")))] + "TIMESTAMP" => Value::TimeDateTime(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get timestamp"), + ))), + + #[cfg(feature = "with-chrono")] + "DATE" => Value::ChronoDate(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get date"), + ))), + #[cfg(all(feature = "with-time", not(feature = "with-chrono")))] + "DATE" => Value::TimeDate(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get date"), + ))), + + #[cfg(feature = "with-chrono")] + "TIME" => Value::ChronoTime(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get time"), + ))), + #[cfg(all(feature = "with-time", not(feature = "with-chrono")))] + "TIME" => Value::TimeTime(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get time"), + ))), + + #[cfg(feature = "with-chrono")] + "DATETIME" => Value::ChronoDateTime(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get datetime"), + ))), + #[cfg(all(feature = "with-time", not(feature = "with-chrono")))] + "DATETIME" => Value::TimeDateTime(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get datetime"), + ))), + + #[cfg(feature = "with-chrono")] + "YEAR" => Value::ChronoDate(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get year"), + ))), + #[cfg(all(feature = "with-time", not(feature = "with-chrono")))] + "YEAR" => Value::TimeDate(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get year"), + ))), + + "ENUM" | "SET" | "GEOMETRY" => Value::String(Some(Box::new( + row.try_get(c.ordinal()) + .expect("Failed to get serialized string"), + ))), + + #[cfg(feature = "with-bigdecimal")] + "DECIMAL" => Value::BigDecimal(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get decimal"), + ))), + #[cfg(all( + feature = "with-rust_decimal", + not(feature = "with-bigdecimal") + ))] + "DECIMAL" => Value::Decimal(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get decimal"), + ))), + + #[cfg(feature = "with-json")] + "JSON" => Value::Json(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get json"), + ))), + + _ => unreachable!("Unknown column type: {}", c.type_info().name()), + }, + ) + }) + .collect(), + } +} + +#[cfg(feature = "sqlx-postgres")] +pub(crate) fn from_sqlx_postgres_row_to_proxy_row(row: &sqlx::postgres::PgRow) -> ProxyRow { + // https://docs.rs/sqlx-postgres/0.7.2/src/sqlx_postgres/type_info.rs.html + // https://docs.rs/sqlx-postgres/0.7.2/sqlx_postgres/types/index.html + use sqlx::{Column, Row, TypeInfo}; + ProxyRow { + values: row + .columns() + .iter() + .map(|c| { + ( + c.name().to_string(), + match c.type_info().name() { + "BOOL" => Value::Bool(Some( + row.try_get(c.ordinal()).expect("Failed to get boolean"), + )), + #[cfg(feature = "postgres-array")] + "BOOL[]" => Value::Array( + sea_query::ArrayType::Bool, + Some(Box::new( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get boolean array") + .iter() + .map(|val| Value::Bool(Some(*val))) + .collect(), + )), + ), + + "\"CHAR\"" => Value::TinyInt(Some( + row.try_get(c.ordinal()) + .expect("Failed to get small integer"), + )), + #[cfg(feature = "postgres-array")] + "\"CHAR\"[]" => Value::Array( + sea_query::ArrayType::TinyInt, + Some(Box::new( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get small integer array") + .iter() + .map(|val| Value::TinyInt(Some(*val))) + .collect(), + )), + ), + + "SMALLINT" | "SMALLSERIAL" | "INT2" => Value::SmallInt(Some( + row.try_get(c.ordinal()) + .expect("Failed to get small integer"), + )), + #[cfg(feature = "postgres-array")] + "SMALLINT[]" | "SMALLSERIAL[]" | "INT2[]" => Value::Array( + sea_query::ArrayType::SmallInt, + Some(Box::new( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get small integer array") + .iter() + .map(|val| Value::SmallInt(Some(*val))) + .collect(), + )), + ), + + "INT" | "SERIAL" | "INT4" => Value::Int(Some( + row.try_get(c.ordinal()).expect("Failed to get integer"), + )), + #[cfg(feature = "postgres-array")] + "INT[]" | "SERIAL[]" | "INT4[]" => Value::Array( + sea_query::ArrayType::Int, + Some(Box::new( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get integer array") + .iter() + .map(|val| Value::Int(Some(*val))) + .collect(), + )), + ), + + "BIGINT" | "BIGSERIAL" | "INT8" => Value::BigInt(Some( + row.try_get(c.ordinal()).expect("Failed to get big integer"), + )), + #[cfg(feature = "postgres-array")] + "BIGINT[]" | "BIGSERIAL[]" | "INT8[]" => Value::Array( + sea_query::ArrayType::BigInt, + Some(Box::new( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get big integer array") + .iter() + .map(|val| Value::BigInt(Some(*val))) + .collect(), + )), + ), + + "FLOAT4" | "REAL" => Value::Float(Some( + row.try_get(c.ordinal()).expect("Failed to get float"), + )), + #[cfg(feature = "postgres-array")] + "FLOAT4[]" | "REAL[]" => Value::Array( + sea_query::ArrayType::Float, + Some(Box::new( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get float array") + .iter() + .map(|val| Value::Float(Some(*val))) + .collect(), + )), + ), + + "FLOAT8" | "DOUBLE PRECISION" => Value::Double(Some( + row.try_get(c.ordinal()).expect("Failed to get double"), + )), + #[cfg(feature = "postgres-array")] + "FLOAT8[]" | "DOUBLE PRECISION[]" => Value::Array( + sea_query::ArrayType::Double, + Some(Box::new( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get double array") + .iter() + .map(|val| Value::Double(Some(*val))) + .collect(), + )), + ), + + "VARCHAR" | "CHAR" | "TEXT" | "NAME" => Value::String(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get string"), + ))), + #[cfg(feature = "postgres-array")] + "VARCHAR[]" | "CHAR[]" | "TEXT[]" | "NAME[]" => Value::Array( + sea_query::ArrayType::String, + Some(Box::new( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get string array") + .iter() + .map(|val| Value::String(Some(Box::new(val.clone())))) + .collect(), + )), + ), + + "BYTEA" => Value::Bytes(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get bytes"), + ))), + #[cfg(feature = "postgres-array")] + "BYTEA[]" => Value::Array( + sea_query::ArrayType::Bytes, + Some(Box::new( + row.try_get::>, _>(c.ordinal()) + .expect("Failed to get bytes array") + .iter() + .map(|val| Value::Bytes(Some(Box::new(val.clone())))) + .collect(), + )), + ), + + #[cfg(feature = "with-bigdecimal")] + "NUMERIC" => Value::BigDecimal(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get numeric"), + ))), + #[cfg(all( + feature = "with-rust_decimal", + not(feature = "with-bigdecimal") + ))] + "NUMERIC" => Value::Decimal(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get numeric"), + ))), + + #[cfg(all(feature = "with-bigdecimal", feature = "postgres-array"))] + "NUMERIC[]" => Value::Array( + sea_query::ArrayType::BigDecimal, + Some(Box::new( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get numeric array") + .iter() + .map(|val| Value::BigDecimal(Some(Box::new(val.clone())))) + .collect(), + )), + ), + #[cfg(all( + feature = "with-rust_decimal", + not(feature = "with-bigdecimal"), + feature = "postgres-array" + ))] + "NUMERIC[]" => Value::Array( + sea_query::ArrayType::Decimal, + Some(Box::new( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get numeric array") + .iter() + .map(|val| Value::Decimal(Some(Box::new(val.clone())))) + .collect(), + )), + ), + + "OID" => Value::BigInt(Some( + row.try_get(c.ordinal()).expect("Failed to get oid"), + )), + #[cfg(feature = "postgres-array")] + "OID[]" => Value::Array( + sea_query::ArrayType::BigInt, + Some(Box::new( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get oid array") + .iter() + .map(|val| Value::BigInt(Some(*val))) + .collect(), + )), + ), + + "JSON" | "JSONB" => Value::Json(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get json"), + ))), + #[cfg(any(feature = "json-array", feature = "postgres-array"))] + "JSON[]" | "JSONB[]" => Value::Array( + sea_query::ArrayType::Json, + Some(Box::new( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get json array") + .iter() + .map(|val| Value::Json(Some(Box::new(val.clone())))) + .collect(), + )), + ), + + #[cfg(feature = "with-ipnetwork")] + "INET" | "CIDR" => Value::IpNetwork(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get ip address"), + ))), + #[cfg(feature = "with-ipnetwork")] + "INET[]" | "CIDR[]" => Value::Array( + sea_query::ArrayType::IpNetwork, + Some(Box::new( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get ip address array") + .iter() + .map(|val| Value::IpNetwork(Some(Box::new(val.clone())))) + .collect(), + )), + ), + + #[cfg(feature = "with-mac_address")] + "MACADDR" | "MACADDR8" => Value::MacAddress(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get mac address"), + ))), + #[cfg(all(feature = "with-mac_address", feature = "postgres-array"))] + "MACADDR[]" | "MACADDR8[]" => Value::Array( + sea_query::ArrayType::MacAddress, + Some(Box::new( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get mac address array") + .iter() + .map(|val| Value::MacAddress(Some(Box::new(val.clone())))) + .collect(), + )), + ), + + #[cfg(feature = "with-chrono")] + "TIMESTAMP" => Value::ChronoDateTime(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get timestamp"), + ))), + #[cfg(all(feature = "with-time", not(feature = "with-chrono")))] + "TIMESTAMP" => Value::TimeDateTime(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get timestamp"), + ))), + + #[cfg(all(feature = "with-chrono", feature = "postgres-array"))] + "TIMESTAMP[]" => Value::Array( + sea_query::ArrayType::ChronoDateTime, + Some(Box::new( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get timestamp array") + .iter() + .map(|val| Value::ChronoDateTime(Some(Box::new(val.clone())))) + .collect(), + )), + ), + #[cfg(all( + feature = "with-time", + not(feature = "with-chrono"), + feature = "postgres-array" + ))] + "TIMESTAMP[]" => Value::Array( + sea_query::ArrayType::TimeDateTime, + Some(Box::new( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get timestamp array") + .iter() + .map(|val| Value::TimeDateTime(Some(Box::new(val.clone())))) + .collect(), + )), + ), + + #[cfg(feature = "with-chrono")] + "DATE" => Value::ChronoDate(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get date"), + ))), + #[cfg(all(feature = "with-time", not(feature = "with-chrono")))] + "DATE" => Value::TimeDate(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get date"), + ))), + + #[cfg(all(feature = "with-chrono", feature = "postgres-array"))] + "DATE[]" => Value::Array( + sea_query::ArrayType::ChronoDate, + Some(Box::new( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get date array") + .iter() + .map(|val| Value::ChronoDate(Some(Box::new(val.clone())))) + .collect(), + )), + ), + #[cfg(all( + feature = "with-time", + not(feature = "with-chrono"), + feature = "postgres-array" + ))] + "DATE[]" => Value::Array( + sea_query::ArrayType::TimeDate, + Some(Box::new( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get date array") + .iter() + .map(|val| Value::TimeDate(Some(Box::new(val.clone())))) + .collect(), + )), + ), + + #[cfg(feature = "with-chrono")] + "TIME" => Value::ChronoTime(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get time"), + ))), + #[cfg(all(feature = "with-time", not(feature = "with-chrono")))] + "TIME" => Value::TimeTime(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get time"), + ))), + + #[cfg(all(feature = "with-chrono", feature = "postgres-array"))] + "TIME[]" => Value::Array( + sea_query::ArrayType::ChronoTime, + Some(Box::new( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get time array") + .iter() + .map(|val| Value::ChronoTime(Some(Box::new(val.clone())))) + .collect(), + )), + ), + #[cfg(all( + feature = "with-time", + not(feature = "with-chrono"), + feature = "postgres-array" + ))] + "TIME[]" => Value::Array( + sea_query::ArrayType::TimeTime, + Some(Box::new( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get time array") + .iter() + .map(|val| Value::TimeTime(Some(Box::new(val.clone())))) + .collect(), + )), + ), + + #[cfg(feature = "with-chrono")] + "TIMESTAMPTZ" => Value::ChronoDateTimeUtc(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get timestamptz"), + ))), + #[cfg(all(feature = "with-time", not(feature = "with-chrono")))] + "TIMESTAMPTZ" => Value::TimeDateTime(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get timestamptz"), + ))), + + #[cfg(all(feature = "with-chrono", feature = "postgres-array"))] + "TIMESTAMPTZ[]" => Value::Array( + sea_query::ArrayType::ChronoDateTimeUtc, + Some(Box::new( + row.try_get::>, _>(c.ordinal()) + .expect("Failed to get timestamptz array") + .iter() + .map(|val| { + Value::ChronoDateTimeUtc(Some(Box::new(val.clone()))) + }) + .collect(), + )), + ), + #[cfg(all( + feature = "with-time", + not(feature = "with-chrono"), + feature = "postgres-array" + ))] + "TIMESTAMPTZ[]" => Value::Array( + sea_query::ArrayType::TimeDateTime, + Some(Box::new( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get timestamptz array") + .iter() + .map(|val| Value::TimeDateTime(Some(Box::new(val.clone())))) + .collect(), + )), + ), + + #[cfg(feature = "with-chrono")] + "TIMETZ" => Value::ChronoTime(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get timetz"), + ))), + #[cfg(all(feature = "with-time", not(feature = "with-chrono")))] + "TIMETZ" => Value::TimeTime(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get timetz"), + ))), + + #[cfg(all(feature = "with-chrono", feature = "postgres-array"))] + "TIMETZ[]" => Value::Array( + sea_query::ArrayType::ChronoTime, + Some(Box::new( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get timetz array") + .iter() + .map(|val| Value::ChronoTime(Some(Box::new(val.clone())))) + .collect(), + )), + ), + #[cfg(all( + feature = "with-time", + not(feature = "with-chrono"), + feature = "postgres-array" + ))] + "TIMETZ[]" => Value::Array( + sea_query::ArrayType::TimeTime, + Some(Box::new( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get timetz array") + .iter() + .map(|val| Value::TimeTime(Some(Box::new(val.clone())))) + .collect(), + )), + ), + + #[cfg(feature = "with-uuid")] + "UUID" => Value::Uuid(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get uuid"), + ))), + + #[cfg(all(feature = "with-uuid", feature = "postgres-array"))] + "UUID[]" => Value::Array( + sea_query::ArrayType::Uuid, + Some(Box::new( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get uuid array") + .iter() + .map(|val| Value::Uuid(Some(Box::new(val.clone())))) + .collect(), + )), + ), + + _ => unreachable!("Unknown column type: {}", c.type_info().name()), + }, + ) + }) + .collect(), + } +} + +#[cfg(feature = "sqlx-sqlite")] +pub(crate) fn from_sqlx_sqlite_row_to_proxy_row(row: &sqlx::sqlite::SqliteRow) -> ProxyRow { + // https://docs.rs/sqlx-sqlite/0.7.2/src/sqlx_sqlite/type_info.rs.html + // https://docs.rs/sqlx-sqlite/0.7.2/sqlx_sqlite/types/index.html + use sqlx::{Column, Row, TypeInfo}; + ProxyRow { + values: row + .columns() + .iter() + .map(|c| { + ( + c.name().to_string(), + match c.type_info().name() { + "BOOLEAN" => Value::Bool(Some( + row.try_get(c.ordinal()).expect("Failed to get boolean"), + )), + + "INTEGER" => Value::Int(Some( + row.try_get(c.ordinal()).expect("Failed to get integer"), + )), + + "BIGINT" | "INT8" => Value::BigInt(Some( + row.try_get(c.ordinal()).expect("Failed to get big integer"), + )), + + "REAL" => Value::Double(Some( + row.try_get(c.ordinal()).expect("Failed to get double"), + )), + + "TEXT" => Value::String(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get string"), + ))), + + "BLOB" => Value::Bytes(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get bytes"), + ))), + + #[cfg(feature = "with-chrono")] + "DATETIME" => Value::ChronoDateTimeUtc(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get timestamp"), + ))), + #[cfg(all(feature = "with-time", not(feature = "with-chrono")))] + "DATETIME" => Value::TimeDateTimeWithTimeZone(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get timestamp"), + ))), + + #[cfg(feature = "with-chrono")] + "DATE" => Value::ChronoDate(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get date"), + ))), + #[cfg(all(feature = "with-time", not(feature = "with-chrono")))] + "DATE" => Value::TimeDate(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get date"), + ))), + + #[cfg(feature = "with-chrono")] + "TIME" => Value::ChronoTime(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get time"), + ))), + #[cfg(all(feature = "with-time", not(feature = "with-chrono")))] + "TIME" => Value::TimeTime(Some(Box::new( + row.try_get(c.ordinal()).expect("Failed to get time"), + ))), + + _ => unreachable!("Unknown column type: {}", c.type_info().name()), + }, + ) + }) + .collect(), + } +} + +impl ProxyRow { + /// Get a value from the [ProxyRow] + pub fn try_get(&self, index: I) -> Result + where + T: ValueType, + { + if let Some(index) = index.as_str() { + T::try_from( + self.values + .get(index) + .ok_or_else(|| query_err(format!("No column for ColIdx {index:?}")))? + .clone(), + ) + .map_err(type_err) + } else if let Some(index) = index.as_usize() { + let (_, value) = self + .values + .iter() + .nth(*index) + .ok_or_else(|| query_err(format!("Column at index {index} not found")))?; + T::try_from(value.clone()).map_err(type_err) + } else { + unreachable!("Missing ColIdx implementation for ProxyRow"); + } + } + + /// An iterator over the keys and values of a proxy row + pub fn into_column_value_tuples(self) -> impl Iterator { + self.values.into_iter() + } +} + +#[cfg(test)] +mod tests { + use crate::{ + entity::*, tests_cfg::*, Database, DbBackend, DbErr, ProxyDatabaseTrait, ProxyExecResult, + ProxyRow, Statement, + }; + use std::sync::{Arc, Mutex}; + + #[derive(Debug)] + struct ProxyDb {} + + impl ProxyDatabaseTrait for ProxyDb { + fn query(&self, statement: Statement) -> Result, DbErr> { + println!("SQL query: {}", statement.sql); + Ok(vec![].into()) + } + + fn execute(&self, statement: Statement) -> Result { + println!("SQL execute: {}", statement.sql); + Ok(ProxyExecResult { + last_insert_id: 1, + rows_affected: 1, + }) + } + } + + #[smol_potat::test] + async fn create_proxy_conn() { + let _db = + Database::connect_proxy(DbBackend::MySql, Arc::new(Mutex::new(Box::new(ProxyDb {})))) + .await + .unwrap(); + } + + #[smol_potat::test] + async fn select_rows() { + let db = + Database::connect_proxy(DbBackend::MySql, Arc::new(Mutex::new(Box::new(ProxyDb {})))) + .await + .unwrap(); + + let _ = cake::Entity::find().all(&db).await; + } + + #[smol_potat::test] + async fn insert_one_row() { + let db = + Database::connect_proxy(DbBackend::MySql, Arc::new(Mutex::new(Box::new(ProxyDb {})))) + .await + .unwrap(); + + let item = cake::ActiveModel { + id: NotSet, + name: Set("Alice".to_string()), + }; + + cake::Entity::insert(item).exec(&db).await.unwrap(); + } +} diff --git a/src/database/stream/query.rs b/src/database/stream/query.rs index c38dc3b347..30642bde01 100644 --- a/src/database/stream/query.rs +++ b/src/database/stream/query.rs @@ -2,7 +2,7 @@ use tracing::instrument; -#[cfg(feature = "mock")] +#[cfg(any(feature = "mock", feature = "proxy"))] use std::sync::Arc; use std::{pin::Pin, task::Poll}; @@ -105,6 +105,25 @@ impl } } +#[cfg(feature = "proxy")] +impl + From<( + Arc, + Statement, + Option, + )> for QueryStream +{ + fn from( + (conn, stmt, metric_callback): ( + Arc, + Statement, + Option, + ), + ) -> Self { + QueryStream::build(stmt, InnerConnection::Proxy(conn), metric_callback) + } +} + impl std::fmt::Debug for QueryStream { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "QueryStream") @@ -163,6 +182,13 @@ impl QueryStream { let elapsed = _start.map(|s| s.elapsed().unwrap_or_default()); MetricStream::new(_metric_callback, stmt, elapsed, stream) } + #[cfg(feature = "proxy")] + InnerConnection::Proxy(c) => { + let _start = _metric_callback.is_some().then(std::time::SystemTime::now); + let stream = c.fetch(stmt); + let elapsed = _start.map(|s| s.elapsed().unwrap_or_default()); + MetricStream::new(_metric_callback, stmt, elapsed, stream) + } #[allow(unreachable_patterns)] _ => unreachable!(), }, diff --git a/src/database/stream/transaction.rs b/src/database/stream/transaction.rs index d64feaba47..3e9285d154 100644 --- a/src/database/stream/transaction.rs +++ b/src/database/stream/transaction.rs @@ -86,6 +86,13 @@ impl<'a> TransactionStream<'a> { let elapsed = _start.map(|s| s.elapsed().unwrap_or_default()); MetricStream::new(_metric_callback, stmt, elapsed, stream) } + #[cfg(feature = "proxy")] + InnerConnection::Proxy(c) => { + let _start = _metric_callback.is_some().then(std::time::SystemTime::now); + let stream = c.fetch(stmt); + let elapsed = _start.map(|s| s.elapsed().unwrap_or_default()); + MetricStream::new(_metric_callback, stmt, elapsed, stream) + } #[allow(unreachable_patterns)] _ => unreachable!(), }, diff --git a/src/database/transaction.rs b/src/database/transaction.rs index 14765b214b..3216ffd996 100644 --- a/src/database/transaction.rs +++ b/src/database/transaction.rs @@ -95,6 +95,22 @@ impl DatabaseTransaction { .await } + #[cfg(feature = "proxy")] + pub(crate) async fn new_proxy( + inner: Arc, + metric_callback: Option, + ) -> Result { + let backend = inner.get_database_backend(); + Self::begin( + Arc::new(Mutex::new(InnerConnection::Proxy(inner))), + backend, + metric_callback, + None, + None, + ) + .await + } + #[instrument(level = "trace", skip(metric_callback))] async fn begin( conn: Arc>, diff --git a/src/driver/mod.rs b/src/driver/mod.rs index 33b6c847c4..5e456e687e 100644 --- a/src/driver/mod.rs +++ b/src/driver/mod.rs @@ -1,5 +1,7 @@ #[cfg(feature = "mock")] mod mock; +#[cfg(feature = "proxy")] +mod proxy; #[cfg(feature = "sqlx-dep")] mod sqlx_common; #[cfg(feature = "sqlx-mysql")] @@ -11,6 +13,8 @@ pub(crate) mod sqlx_sqlite; #[cfg(feature = "mock")] pub use mock::*; +#[cfg(feature = "proxy")] +pub use proxy::*; #[cfg(feature = "sqlx-dep")] pub use sqlx_common::*; #[cfg(feature = "sqlx-mysql")] diff --git a/src/driver/proxy.rs b/src/driver/proxy.rs new file mode 100644 index 0000000000..1ed1ab81b3 --- /dev/null +++ b/src/driver/proxy.rs @@ -0,0 +1,140 @@ +use crate::{ + debug_print, error::*, DatabaseConnection, DbBackend, ExecResult, ProxyDatabaseTrait, + QueryResult, Statement, +}; +use futures::Stream; +use std::{ + fmt::Debug, + pin::Pin, + sync::{Arc, Mutex}, +}; +use tracing::instrument; + +/// Defines a database driver for the [ProxyDatabase] +#[derive(Debug)] +pub struct ProxyDatabaseConnector; + +/// Defines a connection for the [ProxyDatabase] +#[derive(Debug)] +pub struct ProxyDatabaseConnection { + db_backend: DbBackend, + proxy: Arc>>, +} + +impl ProxyDatabaseConnector { + /// Check if the database URI given and the [DatabaseBackend](crate::DatabaseBackend) selected are the same + #[allow(unused_variables)] + pub fn accepts(string: &str) -> bool { + // As this is a proxy database, it accepts any URI + true + } + + /// Connect to the [ProxyDatabase] + #[allow(unused_variables)] + #[instrument(level = "trace")] + pub fn connect( + db_type: DbBackend, + func: Arc>>, + ) -> Result { + Ok(DatabaseConnection::ProxyDatabaseConnection(Arc::new( + ProxyDatabaseConnection::new(db_type, func), + ))) + } +} + +impl ProxyDatabaseConnection { + /// Create a connection to the [ProxyDatabase] + pub fn new(db_backend: DbBackend, funcs: Arc>>) -> Self { + Self { + db_backend, + proxy: funcs.to_owned(), + } + } + + /// Get the [DatabaseBackend](crate::DatabaseBackend) being used by the [ProxyDatabase] + pub fn get_database_backend(&self) -> DbBackend { + self.db_backend + } + + /// Execute the SQL statement in the [ProxyDatabase] + #[instrument(level = "trace")] + pub fn execute(&self, statement: Statement) -> Result { + debug_print!("{}", statement); + Ok(self + .proxy + .lock() + .map_err(exec_err)? + .execute(statement)? + .into()) + } + + /// Return one [QueryResult] if the query was successful + #[instrument(level = "trace")] + pub fn query_one(&self, statement: Statement) -> Result, DbErr> { + debug_print!("{}", statement); + let result = self.proxy.lock().map_err(query_err)?.query(statement)?; + + if let Some(first) = result.first() { + return Ok(Some(QueryResult { + row: crate::QueryResultRow::Proxy(first.to_owned()), + })); + } else { + return Ok(None); + } + } + + /// Return all [QueryResult]s if the query was successful + #[instrument(level = "trace")] + pub fn query_all(&self, statement: Statement) -> Result, DbErr> { + debug_print!("{}", statement); + let result = self.proxy.lock().map_err(query_err)?.query(statement)?; + + Ok(result + .into_iter() + .map(|row| QueryResult { + row: crate::QueryResultRow::Proxy(row), + }) + .collect()) + } + + /// Return [QueryResult]s from a multi-query operation + #[instrument(level = "trace")] + pub fn fetch( + &self, + statement: &Statement, + ) -> Pin> + Send>> { + match self.query_all(statement.clone()) { + Ok(v) => Box::pin(futures::stream::iter(v.into_iter().map(Ok))), + Err(e) => Box::pin(futures::stream::iter(Some(Err(e)).into_iter())), + } + } + + /// Create a statement block of SQL statements that execute together. + #[instrument(level = "trace")] + pub fn begin(&self) { + self.proxy.lock().expect("Failed to acquire mocker").begin() + } + + /// Commit a transaction atomically to the database + #[instrument(level = "trace")] + pub fn commit(&self) { + self.proxy + .lock() + .expect("Failed to acquire mocker") + .commit() + } + + /// Roll back a faulty transaction + #[instrument(level = "trace")] + pub fn rollback(&self) { + self.proxy + .lock() + .expect("Failed to acquire mocker") + .rollback() + } + + /// Checks if a connection to the database is still valid. + pub fn ping(&self) -> Result<(), DbErr> { + self.proxy.lock().map_err(query_err)?.ping() + } +} diff --git a/src/executor/execute.rs b/src/executor/execute.rs index 487ca41aa4..626dd81b8a 100644 --- a/src/executor/execute.rs +++ b/src/executor/execute.rs @@ -22,6 +22,9 @@ pub(crate) enum ExecResultHolder { /// Holds the result of executing an operation on the Mock database #[cfg(feature = "mock")] Mock(crate::MockExecResult), + /// Holds the result of executing an operation on the Proxy database + #[cfg(feature = "proxy")] + Proxy(crate::ProxyExecResult), } // ExecResult // @@ -51,6 +54,8 @@ impl ExecResult { } #[cfg(feature = "mock")] ExecResultHolder::Mock(result) => result.last_insert_id, + #[cfg(feature = "proxy")] + ExecResultHolder::Proxy(result) => result.last_insert_id, #[allow(unreachable_patterns)] _ => unreachable!(), } @@ -67,6 +72,8 @@ impl ExecResult { ExecResultHolder::SqlxSqlite(result) => result.rows_affected(), #[cfg(feature = "mock")] ExecResultHolder::Mock(result) => result.rows_affected, + #[cfg(feature = "proxy")] + ExecResultHolder::Proxy(result) => result.rows_affected, #[allow(unreachable_patterns)] _ => unreachable!(), } diff --git a/src/executor/query.rs b/src/executor/query.rs index a7df40abfc..01c3a1d0eb 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -1,7 +1,7 @@ use crate::{error::*, SelectGetableValue, SelectorRaw, Statement}; use std::fmt; -#[cfg(feature = "mock")] +#[cfg(any(feature = "mock", feature = "proxy"))] use crate::debug_print; #[cfg(feature = "sqlx-dep")] @@ -25,6 +25,8 @@ pub(crate) enum QueryResultRow { SqlxSqlite(sqlx::sqlite::SqliteRow), #[cfg(feature = "mock")] Mock(crate::MockRow), + #[cfg(feature = "proxy")] + Proxy(crate::ProxyRow), } /// An interface to get a value from the query result @@ -127,6 +129,8 @@ impl fmt::Debug for QueryResultRow { Self::SqlxSqlite(_) => write!(f, "QueryResultRow::SqlxSqlite cannot be inspected"), #[cfg(feature = "mock")] Self::Mock(row) => write!(f, "{row:?}"), + #[cfg(feature = "proxy")] + Self::Proxy(row) => write!(f, "{row:?}"), #[allow(unreachable_patterns)] _ => unreachable!(), } @@ -271,6 +275,11 @@ macro_rules! try_getable_all { 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!(), } @@ -306,6 +315,11 @@ macro_rules! try_getable_unsigned { 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!(), } @@ -342,6 +356,11 @@ macro_rules! try_getable_mysql { 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!(), } @@ -383,6 +402,11 @@ macro_rules! try_getable_date_time { 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!(), } @@ -478,6 +502,12 @@ impl TryGetable for Decimal { 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!(), } @@ -525,6 +555,12 @@ impl TryGetable for BigDecimal { 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!(), } @@ -559,6 +595,12 @@ macro_rules! try_getable_uuid { 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!(), }; @@ -613,6 +655,12 @@ impl TryGetable for u32 { 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!(), } @@ -658,6 +706,12 @@ mod postgres_array { 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!(), } @@ -745,6 +799,13 @@ mod postgres_array { 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!(), }; @@ -799,6 +860,12 @@ mod postgres_array { 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!(), } @@ -1014,6 +1081,14 @@ where err_null_idx_col(idx) }) .and_then(|json| serde_json::from_value(json).map_err(|e| json_err(e).into())), + #[cfg(feature = "proxy")] + QueryResultRow::Proxy(row) => row + .try_get::(idx) + .map_err(|e| { + debug_print!("{:#?}", e.to_string()); + err_null_idx_col(idx) + }) + .and_then(|json| serde_json::from_value(json).map_err(|e| json_err(e).into())), #[allow(unreachable_patterns)] _ => unreachable!(), } diff --git a/src/query/json.rs b/src/query/json.rs index 66b4dc9785..da91fb3b6b 100644 --- a/src/query/json.rs +++ b/src/query/json.rs @@ -214,6 +214,18 @@ impl FromQueryResult for JsonValue { } Ok(JsonValue::Object(map)) } + #[cfg(feature = "proxy")] + crate::QueryResultRow::Proxy(row) => { + for (column, value) in row.clone().into_column_value_tuples() { + let col = if !column.starts_with(pre) { + continue; + } else { + column.replacen(pre, "", 1) + }; + map.insert(col, sea_query::sea_value_to_json_value(&value)); + } + Ok(JsonValue::Object(map)) + } #[allow(unreachable_patterns)] _ => unreachable!(), } From 5539e7c5adaa75574b99a61ea7c8acc589b5f475 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 14 Dec 2023 01:10:13 +0000 Subject: [PATCH 0232/1371] Stash proxy_surrealdb_example for now --- examples/proxy_surrealdb_example/Cargo.toml | 27 -- examples/proxy_surrealdb_example/README.md | 7 - .../proxy_surrealdb_example/src/entity/mod.rs | 1 - .../src/entity/post.rs | 17 -- examples/proxy_surrealdb_example/src/main.rs | 271 ------------------ 5 files changed, 323 deletions(-) delete mode 100644 examples/proxy_surrealdb_example/Cargo.toml delete mode 100644 examples/proxy_surrealdb_example/README.md delete mode 100644 examples/proxy_surrealdb_example/src/entity/mod.rs delete mode 100644 examples/proxy_surrealdb_example/src/entity/post.rs delete mode 100644 examples/proxy_surrealdb_example/src/main.rs diff --git a/examples/proxy_surrealdb_example/Cargo.toml b/examples/proxy_surrealdb_example/Cargo.toml deleted file mode 100644 index 4b16af5159..0000000000 --- a/examples/proxy_surrealdb_example/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "sea-orm-proxy-surrealdb-example" -version = "0.1.0" -authors = ["Langyo "] -edition = "2021" -publish = false - -[workspace] - -[dependencies] -async-std = { version = "1.12", features = ["attributes", "tokio1"] } -serde_json = { version = "1" } -serde = { version = "1" } -futures = { version = "0.3" } -async-stream = { version = "0.3" } -futures-util = { version = "0.3" } - -sqlparser = "0.40" -sea-orm = { path = "../../", features = [ - "proxy", - "debug-print", -] } -surrealdb = { version = "1", features = ["kv-mem"] } - -[dev-dependencies] -smol = { version = "1.2" } -smol-potat = { version = "1.1" } diff --git a/examples/proxy_surrealdb_example/README.md b/examples/proxy_surrealdb_example/README.md deleted file mode 100644 index 773850bc3a..0000000000 --- a/examples/proxy_surrealdb_example/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# SeaORM Proxy Demo for SurrealDB - -Run this demo for [SurrealDB](https://surrealdb.com/) with the following command: - -```bash -cargo run -``` diff --git a/examples/proxy_surrealdb_example/src/entity/mod.rs b/examples/proxy_surrealdb_example/src/entity/mod.rs deleted file mode 100644 index e8b6291ac3..0000000000 --- a/examples/proxy_surrealdb_example/src/entity/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod post; diff --git a/examples/proxy_surrealdb_example/src/entity/post.rs b/examples/proxy_surrealdb_example/src/entity/post.rs deleted file mode 100644 index 1fde0c4cc4..0000000000 --- a/examples/proxy_surrealdb_example/src/entity/post.rs +++ /dev/null @@ -1,17 +0,0 @@ -use sea_orm::entity::prelude::*; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Deserialize, Serialize)] -#[sea_orm(table_name = "posts")] -pub struct Model { - #[sea_orm(primary_key)] - pub id: String, - - pub title: String, - pub text: String, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation {} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/examples/proxy_surrealdb_example/src/main.rs b/examples/proxy_surrealdb_example/src/main.rs deleted file mode 100644 index b23f33c5f6..0000000000 --- a/examples/proxy_surrealdb_example/src/main.rs +++ /dev/null @@ -1,271 +0,0 @@ -//! Proxy connection example. - -#![deny(missing_docs)] - -mod entity; - -use std::{ - collections::BTreeMap, - sync::{Arc, Mutex}, -}; - -use sea_orm::{ - ActiveValue::Set, Database, DbBackend, DbErr, EntityTrait, ProxyDatabaseTrait, ProxyExecResult, - ProxyRow, Statement, -}; -use surrealdb::{ - engine::local::{Db, Mem}, - Surreal, -}; - -use entity::post::{ActiveModel, Entity}; - -#[derive(Debug)] -struct ProxyDb { - mem: Surreal, -} - -impl ProxyDatabaseTrait for ProxyDb { - fn query(&self, statement: Statement) -> Result, DbErr> { - println!("SQL query: {:?}", statement); - let mut ret = async_std::task::block_on(async { - // Surrealdb's grammar is not compatible with sea-orm's - // so we need to remove the extra clauses - // from "SELECT `from`.`col` FROM `from` WHERE `from`.`col` = xx" - // to "SELECT `col` FROM `from` WHERE `col` = xx" - - use sqlparser::ast::{Expr, SelectItem, SetExpr, TableFactor}; - use sqlparser::dialect::GenericDialect; - use sqlparser::parser::Parser; - - let dialect = GenericDialect {}; - let mut ast = Parser::parse_sql(&dialect, statement.sql.as_str()).unwrap(); - match &mut ast[0] { - sqlparser::ast::Statement::Query(query) => match &mut *query.body { - SetExpr::Select(body) => { - body.projection.iter_mut().for_each(|item| { - match item { - SelectItem::UnnamedExpr(expr) => { - match expr { - Expr::CompoundIdentifier(idents) => { - // Remove the head of the identifier - // e.g. `from`.`col` -> `col` - let ident = idents.pop().unwrap(); - *expr = Expr::Identifier(ident); - } - _ => todo!(), - } - } - _ => todo!(), - } - }); - body.from.iter_mut().for_each(|item| { - match &mut item.relation { - TableFactor::Table { name, .. } => { - // Remove the head of the identifier - // e.g. `from`.`col` -> `col` - let ident = name.0.pop().unwrap(); - name.0 = vec![ident]; - } - _ => todo!(), - } - }); - } - _ => todo!(), - }, - _ => todo!(), - }; - - let statement = &ast[0]; - let sql = statement.to_string(); - println!("SQL: {}", sql); - self.mem.query(sql).await - }) - .unwrap(); - - // Convert the result to sea-orm's format - let ret: Vec = ret.take(0).unwrap(); - println!("SQL query result: {}", serde_json::to_string(&ret).unwrap()); - let ret = ret - .iter() - .map(|row| { - let mut map = serde_json::Map::new(); - for (k, v) in row.as_object().unwrap().iter() { - if k == "id" { - // Get `tb` and `id` columns from surrealdb - // and convert them to sea-orm's `id` - let tb = v.as_object().unwrap().get("tb").unwrap().to_string(); - let id = v - .as_object() - .unwrap() - .get("id") - .unwrap() - .get("String") - .unwrap(); - - // Remove the quotes - let tb = tb.to_string().replace("\"", ""); - let id = id.to_string().replace("\"", ""); - - map.insert("id".to_owned(), format!("{}:{}", tb, id).into()); - continue; - } - - map.insert(k.to_owned(), v.to_owned()); - } - serde_json::Value::Object(map) - }) - .map(|v: serde_json::Value| { - let mut ret: BTreeMap = BTreeMap::new(); - for (k, v) in v.as_object().unwrap().iter() { - ret.insert( - k.to_owned(), - match v { - serde_json::Value::Bool(b) => { - sea_orm::Value::TinyInt(if *b { Some(1) } else { Some(0) }) - } - serde_json::Value::Number(n) => { - if n.is_i64() { - sea_orm::Value::BigInt(Some(n.as_i64().unwrap())) - } else if n.is_u64() { - sea_orm::Value::BigUnsigned(Some(n.as_u64().unwrap())) - } else if n.is_f64() { - sea_orm::Value::Double(Some(n.as_f64().unwrap())) - } else { - unreachable!() - } - } - serde_json::Value::String(s) => { - sea_orm::Value::String(Some(Box::new(s.to_owned()))) - } - _ => sea_orm::Value::Json(Some(Box::new(v.to_owned()))), - }, - ); - } - ProxyRow { values: ret } - }) - .collect::>(); - - Ok(ret) - } - - fn execute(&self, statement: Statement) -> Result { - async_std::task::block_on(async { - if let Some(values) = statement.values { - // Replace all the '?' with the statement values - use sqlparser::ast::{Expr, Value}; - use sqlparser::dialect::GenericDialect; - use sqlparser::parser::Parser; - - let dialect = GenericDialect {}; - let mut ast = Parser::parse_sql(&dialect, statement.sql.as_str()).unwrap(); - match &mut ast[0] { - sqlparser::ast::Statement::Insert { - table_name, - columns, - source, - .. - } => { - // Replace the table name's quote style - table_name.0[0].quote_style = Some('`'); - - // Replace all the column names' quote style - for item in columns.iter_mut() { - item.quote_style = Some('`'); - } - - // Convert the values to sea-orm's format - if let Some(obj) = source { - match &mut *obj.body { - sqlparser::ast::SetExpr::Values(obj) => { - for (mut item, val) in - obj.rows[0].iter_mut().zip(values.0.iter()) - { - match &mut item { - Expr::Value(item) => { - *item = match val { - sea_orm::Value::String(val) => { - Value::SingleQuotedString(match val { - Some(val) => val.to_string(), - None => "".to_string(), - }) - } - sea_orm::Value::BigInt(val) => Value::Number( - val.unwrap_or(0).to_string(), - false, - ), - _ => todo!(), - }; - } - _ => todo!(), - } - } - } - _ => todo!(), - } - } - } - _ => todo!(), - } - - let statement = &ast[0]; - let sql = statement.to_string(); - println!("SQL: {}", sql); - self.mem.query(sql).await - } else { - self.mem.query(statement.sql).await - } - }) - .unwrap(); - - Ok(ProxyExecResult { - last_insert_id: 1, - rows_affected: 1, - }) - } -} - -#[async_std::main] -async fn main() { - let mem = Surreal::new::(()).await.unwrap(); - mem.use_ns("test").use_db("post").await.unwrap(); - - let db = Database::connect_proxy( - DbBackend::MySql, - Arc::new(Mutex::new(Box::new(ProxyDb { mem }))), - ) - .await - .unwrap(); - - println!("Initialized"); - - let data = ActiveModel { - title: Set("Homo".to_owned()), - text: Set("いいよ、来いよ".to_owned()), - ..Default::default() - }; - Entity::insert(data).exec(&db).await.unwrap(); - let data = ActiveModel { - title: Set("Homo".to_owned()), - text: Set("そうだよ".to_owned()), - ..Default::default() - }; - Entity::insert(data).exec(&db).await.unwrap(); - let data = ActiveModel { - title: Set("Homo".to_owned()), - text: Set("悔い改めて".to_owned()), - ..Default::default() - }; - Entity::insert(data).exec(&db).await.unwrap(); - - let list = Entity::find().all(&db).await.unwrap().to_vec(); - println!("Result: {:?}", list); -} - -#[cfg(test)] -mod tests { - #[smol_potat::test] - async fn try_run() { - crate::main() - } -} From b401851511c2122b2a26e377826d3fed48dfebfb Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Thu, 14 Dec 2023 19:56:31 +0800 Subject: [PATCH 0233/1371] CI: whitelist examples runs --- .github/workflows/rust.yml | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 7be8cd0f85..280b3701d3 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -226,24 +226,26 @@ jobs: - uses: dtolnay/rust-toolchain@stable - run: cargo install --path sea-orm-cli --debug - examples-matrix: - name: Examples Matrix - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - id: set-matrix - run: echo "path_matrix=$(find examples -mindepth 1 -maxdepth 1 -type d -not -path 'examples/rocket_okapi_example' -printf '%P\0' | jq -Rc '[ split("\u0000") | .[] | "examples/\(.)" ]')" >> $GITHUB_OUTPUT - outputs: - path_matrix: ${{ steps.set-matrix.outputs.path_matrix }} - examples: name: Examples runs-on: ubuntu-latest - needs: examples-matrix strategy: fail-fast: false matrix: - path: ${{ fromJson(needs.examples-matrix.outputs.path_matrix) }} + path: [ + examples/actix_example, + examples/actix3_example, + # examples/rocket_okapi_example, + examples/jsonrpsee_example, + examples/basic, + examples/salvo_example, + examples/rocket_example, + examples/axum_example, + examples/tonic_example, + examples/seaography_example, + examples/graphql_example, + examples/poem_example, + ] steps: - uses: actions/checkout@v3 - if: ${{ contains(matrix.path, 'tonic_example') }} From b043f84aecaa0ac16bf6d390389fe920896486e5 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 14 Dec 2023 16:43:35 +0000 Subject: [PATCH 0234/1371] Update example list --- .github/workflows/rust.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 280b3701d3..60ec6c8fcc 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -235,16 +235,17 @@ jobs: path: [ examples/actix_example, examples/actix3_example, - # examples/rocket_okapi_example, - examples/jsonrpsee_example, - examples/basic, - examples/salvo_example, - examples/rocket_example, examples/axum_example, - examples/tonic_example, - examples/seaography_example, + examples/basic, examples/graphql_example, + examples/jsonrpsee_example, examples/poem_example, + examples/proxy_gluesql_example, + examples/rocket_example, + examples/rocket_okapi_example, + examples/salvo_example, + examples/seaography_example, + examples/tonic_example, ] steps: - uses: actions/checkout@v3 From 5859d261f1476d1e84ebf9cfd5989813efd078e9 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 14 Dec 2023 17:03:16 +0000 Subject: [PATCH 0235/1371] Cleanup feature flags --- CHANGELOG.md | 5 +++++ Cargo.toml | 2 +- src/database/mock.rs | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cff56a36bd..f413813eaf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## 0.12.10 - 2023-12-14 + +* [sea-orm-macro] Comment attribute for Entity (`#[sea_orm(comment = "action")]`); `create_table_from_entity` supports comment https://github.com/SeaQL/sea-orm/pull/2009 +* Added "proxy" (feature flag `proxy`) to database backend https://github.com/SeaQL/sea-orm/pull/1881, https://github.com/SeaQL/sea-orm/pull/2000 + ## 0.12.9 - 2023-12-08 ### Enhancements diff --git a/Cargo.toml b/Cargo.toml index d4944d627c..63c1595675 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,7 +76,7 @@ default = [ ] macros = ["sea-orm-macros/derive"] mock = [] -proxy = ["serde_json"] +proxy = ["serde_json", "serde/derive"] with-json = ["serde_json", "sea-query/with-json", "chrono?/serde", "time?/serde", "uuid?/serde", "sea-query-binder?/with-json", "sqlx?/json"] with-chrono = ["chrono", "sea-query/with-chrono", "sea-query-binder?/with-chrono", "sqlx?/chrono"] with-rust_decimal = ["rust_decimal", "sea-query/with-rust_decimal", "sea-query-binder?/with-rust_decimal", "sqlx?/rust_decimal"] diff --git a/src/database/mock.rs b/src/database/mock.rs index 6462766f2e..f038d8933a 100644 --- a/src/database/mock.rs +++ b/src/database/mock.rs @@ -31,7 +31,7 @@ pub struct MockExecResult { #[derive(Clone, Debug)] pub struct MockRow { /// The values of the single row - pub values: BTreeMap, + pub(crate) values: BTreeMap, } /// A trait to get a [MockRow] from a type useful for testing in the [MockDatabase] From 4b5fd9388e81c9273a74ee7e2c6d2d6d7aac2e7b Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 14 Dec 2023 17:06:51 +0000 Subject: [PATCH 0236/1371] Cleanup feature flags --- .github/workflows/rust.yml | 2 +- CHANGELOG.md | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 60ec6c8fcc..73ba4ce7e9 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -242,7 +242,7 @@ jobs: examples/poem_example, examples/proxy_gluesql_example, examples/rocket_example, - examples/rocket_okapi_example, + # examples/rocket_okapi_example, examples/salvo_example, examples/seaography_example, examples/tonic_example, diff --git a/CHANGELOG.md b/CHANGELOG.md index f413813eaf..823e1da0fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,9 +7,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## 0.12.10 - 2023-12-14 +### New Features + * [sea-orm-macro] Comment attribute for Entity (`#[sea_orm(comment = "action")]`); `create_table_from_entity` supports comment https://github.com/SeaQL/sea-orm/pull/2009 * Added "proxy" (feature flag `proxy`) to database backend https://github.com/SeaQL/sea-orm/pull/1881, https://github.com/SeaQL/sea-orm/pull/2000 +### Upgrades + +* Updated `sea-query` to `0.30.5` https://github.com/SeaQL/sea-query/releases/tag/0.30.5 + ## 0.12.9 - 2023-12-08 ### Enhancements From 1abc47b5f13f67439f3a54250f7844a4476430a3 Mon Sep 17 00:00:00 2001 From: Dmitrii Aleksandrov Date: Thu, 14 Dec 2023 23:41:05 +0400 Subject: [PATCH 0237/1371] Cast enums in `is_in` and `is_not_in` (#1527) (#2002) * Cast enums in `is_in` and `is_not_in` (#1527) * Restore original tests, add tests for generated SQL --- src/entity/column.rs | 3 ++- tests/active_enum_tests.rs | 41 ++++++++++++++++++++++++++++++++- tests/enum_primary_key_tests.rs | 9 ++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/entity/column.rs b/src/entity/column.rs index 54e73c35e9..4f895f0de2 100644 --- a/src/entity/column.rs +++ b/src/entity/column.rs @@ -62,7 +62,8 @@ macro_rules! bind_vec_func { V: Into, I: IntoIterator, { - Expr::col((self.entity_name(), *self)).$func(v) + let v_with_enum_cast = v.into_iter().map(|v| self.save_as(Expr::val(v))); + Expr::col((self.entity_name(), *self)).$func(v_with_enum_cast) } }; } diff --git a/tests/active_enum_tests.rs b/tests/active_enum_tests.rs index 547532052f..27babd6c06 100644 --- a/tests/active_enum_tests.rs +++ b/tests/active_enum_tests.rs @@ -8,7 +8,7 @@ use sea_orm::{ entity::prelude::*, entity::*, sea_query::{BinOper, Expr}, - ActiveEnum as ActiveEnumTrait, DatabaseConnection, + ActiveEnum as ActiveEnumTrait, DatabaseConnection, QueryTrait, }; #[sea_orm_macros::test] @@ -98,6 +98,7 @@ pub async fn insert_active_enum(db: &DatabaseConnection) -> Result<(), DbErr> { .await? .unwrap() ); + assert_eq!( model, Entity::find() @@ -109,6 +110,24 @@ pub async fn insert_active_enum(db: &DatabaseConnection) -> Result<(), DbErr> { .await? .unwrap() ); + // Equivalent to the above. + let select_with_tea_in = Entity::find().filter(Column::Tea.is_in([Tea::EverydayTea])); + assert_eq!( + select_with_tea_in + .build(sea_orm::DatabaseBackend::Postgres) + .to_string(), + [ + "SELECT \"active_enum\".\"id\",", + "\"active_enum\".\"category\",", + "\"active_enum\".\"color\",", + "CAST(\"active_enum\".\"tea\" AS text)", + "FROM \"active_enum\"", + "WHERE \"active_enum\".\"tea\" IN (CAST('EverydayTea' AS tea))", + ] + .join(" ") + ); + assert_eq!(model, select_with_tea_in.one(db).await?.unwrap()); + assert_eq!( model, Entity::find() @@ -121,6 +140,26 @@ pub async fn insert_active_enum(db: &DatabaseConnection) -> Result<(), DbErr> { .await? .unwrap() ); + // Equivalent to the above. + let select_with_tea_not_in = Entity::find() + .filter(Column::Tea.is_not_null()) + .filter(Column::Tea.is_not_in([Tea::BreakfastTea])); + assert_eq!( + select_with_tea_not_in + .build(sea_orm::DatabaseBackend::Postgres) + .to_string(), + [ + "SELECT \"active_enum\".\"id\",", + "\"active_enum\".\"category\",", + "\"active_enum\".\"color\",", + "CAST(\"active_enum\".\"tea\" AS text)", + "FROM \"active_enum\"", + "WHERE \"active_enum\".\"tea\" IS NOT NULL", + "AND \"active_enum\".\"tea\" NOT IN (CAST('BreakfastTea' AS tea))", + ] + .join(" ") + ); + assert_eq!(model, select_with_tea_not_in.one(db).await?.unwrap()); let res = model.delete(db).await?; diff --git a/tests/enum_primary_key_tests.rs b/tests/enum_primary_key_tests.rs index ce487bbc0b..8364865d42 100644 --- a/tests/enum_primary_key_tests.rs +++ b/tests/enum_primary_key_tests.rs @@ -113,6 +113,15 @@ pub async fn insert_teas(db: &DatabaseConnection) -> Result<(), DbErr> { .await? .unwrap() ); + // Equivalent to the above. + assert_eq!( + model, + Entity::find() + .filter(Column::Id.is_in([Tea::EverydayTea])) + .one(db) + .await? + .unwrap() + ); let res = model.delete(db).await?; From 4a59ca681bdeab33ea6f67c0cb5c83bee7ec9875 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 14 Dec 2023 19:25:50 +0000 Subject: [PATCH 0238/1371] 0.12.10 --- Cargo.toml | 4 ++-- sea-orm-cli/Cargo.toml | 4 ++-- sea-orm-codegen/Cargo.toml | 2 +- sea-orm-macros/Cargo.toml | 2 +- sea-orm-migration/Cargo.toml | 6 +++--- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 63c1595675..f65113578a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [".", "sea-orm-macros", "sea-orm-codegen"] [package] name = "sea-orm" -version = "0.12.9" +version = "0.12.10" authors = ["Chris Tsang "] edition = "2021" description = "🐚 An async & dynamic ORM for Rust" @@ -33,7 +33,7 @@ log = { version = "0.4", default-features = false } tracing = { version = "0.1", default-features = false, features = ["attributes", "log"] } rust_decimal = { version = "1", default-features = false, optional = true } bigdecimal = { version = "0.3", default-features = false, optional = true } -sea-orm-macros = { version = "0.12.6", path = "sea-orm-macros", default-features = false, features = ["strum"] } +sea-orm-macros = { version = "0.12.10", path = "sea-orm-macros", default-features = false, features = ["strum"] } sea-query = { version = "0.30.4", default-features = false, features = ["thread-safe", "hashable-value", "backend-mysql", "backend-postgres", "backend-sqlite"] } sea-query-binder = { version = "0.5.0", default-features = false, optional = true } strum = { version = "0.25", default-features = false } diff --git a/sea-orm-cli/Cargo.toml b/sea-orm-cli/Cargo.toml index 5db16904ae..ecb522ae81 100644 --- a/sea-orm-cli/Cargo.toml +++ b/sea-orm-cli/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "sea-orm-cli" -version = "0.12.6" +version = "0.12.10" authors = [ "Chris Tsang ", "Billy Chan ", @@ -37,7 +37,7 @@ required-features = ["cli", "codegen"] clap = { version = "4.3", features = ["env", "derive"], optional = true } dotenvy = { version = "0.15", default-features = false, optional = true } async-std = { version = "1.9", default-features = false, features = ["attributes", "tokio1"], optional = true } -sea-orm-codegen = { version = "=0.12.6", path = "../sea-orm-codegen", default-features = false, optional = true } +sea-orm-codegen = { version = "=0.12.10", path = "../sea-orm-codegen", default-features = false, optional = true } sea-schema = { version = "0.14.0" } sqlx = { version = "0.7", default-features = false, features = ["mysql", "postgres"], optional = true } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["env-filter", "fmt"] } diff --git a/sea-orm-codegen/Cargo.toml b/sea-orm-codegen/Cargo.toml index e7f6ffd1a5..26bdcbaaee 100644 --- a/sea-orm-codegen/Cargo.toml +++ b/sea-orm-codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sea-orm-codegen" -version = "0.12.6" +version = "0.12.10" authors = ["Billy Chan "] edition = "2021" description = "Code Generator for SeaORM" diff --git a/sea-orm-macros/Cargo.toml b/sea-orm-macros/Cargo.toml index 49b1653aac..70492c5ac7 100644 --- a/sea-orm-macros/Cargo.toml +++ b/sea-orm-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sea-orm-macros" -version = "0.12.6" +version = "0.12.10" authors = [ "Billy Chan " ] edition = "2021" description = "Derive macros for SeaORM" diff --git a/sea-orm-migration/Cargo.toml b/sea-orm-migration/Cargo.toml index dcb5f83a6a..d32f1a7a09 100644 --- a/sea-orm-migration/Cargo.toml +++ b/sea-orm-migration/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "sea-orm-migration" -version = "0.12.6" +version = "0.12.10" authors = ["Billy Chan "] edition = "2021" description = "Migration utility for SeaORM" @@ -23,8 +23,8 @@ path = "src/lib.rs" async-trait = { version = "0.1", default-features = false } clap = { version = "4.3", features = ["env", "derive"], optional = true } dotenvy = { version = "0.15", default-features = false, optional = true } -sea-orm = { version = "0.12.6", path = "../", default-features = false, features = ["macros"] } -sea-orm-cli = { version = "0.12.6", path = "../sea-orm-cli", default-features = false, optional = true } +sea-orm = { version = "0.12.10", path = "../", default-features = false, features = ["macros"] } +sea-orm-cli = { version = "0.12.10", path = "../sea-orm-cli", default-features = false, optional = true } sea-schema = { version = "0.14.1" } tracing = { version = "0.1", default-features = false, features = ["log"] } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["env-filter", "fmt"] } From 4ecf47e08733274ad9d587401400cc4819073d3a Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 14 Dec 2023 19:25:50 +0000 Subject: [PATCH 0239/1371] update examples --- examples/actix3_example/entity/Cargo.toml | 2 +- examples/actix3_example/migration/Cargo.toml | 2 +- examples/actix3_example/service/Cargo.toml | 2 +- examples/actix_example/entity/Cargo.toml | 2 +- examples/actix_example/migration/Cargo.toml | 2 +- examples/actix_example/service/Cargo.toml | 2 +- examples/axum_example/entity/Cargo.toml | 2 +- examples/axum_example/migration/Cargo.toml | 2 +- examples/axum_example/service/Cargo.toml | 2 +- examples/graphql_example/entity/Cargo.toml | 2 +- examples/graphql_example/migration/Cargo.toml | 2 +- examples/graphql_example/service/Cargo.toml | 2 +- examples/jsonrpsee_example/entity/Cargo.toml | 2 +- examples/jsonrpsee_example/migration/Cargo.toml | 2 +- examples/jsonrpsee_example/service/Cargo.toml | 2 +- examples/poem_example/entity/Cargo.toml | 2 +- examples/poem_example/migration/Cargo.toml | 2 +- examples/poem_example/service/Cargo.toml | 2 +- examples/rocket_example/entity/Cargo.toml | 2 +- examples/rocket_example/migration/Cargo.toml | 2 +- examples/rocket_example/service/Cargo.toml | 2 +- examples/rocket_okapi_example/entity/Cargo.toml | 2 +- examples/rocket_okapi_example/migration/Cargo.toml | 2 +- examples/rocket_okapi_example/service/Cargo.toml | 2 +- examples/salvo_example/entity/Cargo.toml | 2 +- examples/salvo_example/migration/Cargo.toml | 2 +- examples/salvo_example/service/Cargo.toml | 2 +- examples/seaography_example/migration/Cargo.toml | 4 ++-- examples/tonic_example/entity/Cargo.toml | 2 +- examples/tonic_example/migration/Cargo.toml | 2 +- examples/tonic_example/service/Cargo.toml | 2 +- 31 files changed, 32 insertions(+), 32 deletions(-) diff --git a/examples/actix3_example/entity/Cargo.toml b/examples/actix3_example/entity/Cargo.toml index 41f742f6db..a50073b434 100644 --- a/examples/actix3_example/entity/Cargo.toml +++ b/examples/actix3_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.6" # sea-orm version +version = "0.12.10" # sea-orm version diff --git a/examples/actix3_example/migration/Cargo.toml b/examples/actix3_example/migration/Cargo.toml index f115e64c15..96530b57e8 100644 --- a/examples/actix3_example/migration/Cargo.toml +++ b/examples/actix3_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.6" # sea-orm-migration version +version = "0.12.10" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-async-std-native-tls", diff --git a/examples/actix3_example/service/Cargo.toml b/examples/actix3_example/service/Cargo.toml index ccb4100700..ee7e475e4a 100644 --- a/examples/actix3_example/service/Cargo.toml +++ b/examples/actix3_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.6" # sea-orm version +version = "0.12.10" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/actix_example/entity/Cargo.toml b/examples/actix_example/entity/Cargo.toml index 41f742f6db..a50073b434 100644 --- a/examples/actix_example/entity/Cargo.toml +++ b/examples/actix_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.6" # sea-orm version +version = "0.12.10" # sea-orm version diff --git a/examples/actix_example/migration/Cargo.toml b/examples/actix_example/migration/Cargo.toml index ae056a10ff..a81d8215ad 100644 --- a/examples/actix_example/migration/Cargo.toml +++ b/examples/actix_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.6" # sea-orm-migration version +version = "0.12.10" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-actix-native-tls", diff --git a/examples/actix_example/service/Cargo.toml b/examples/actix_example/service/Cargo.toml index c8092b3620..2ecf9a0e37 100644 --- a/examples/actix_example/service/Cargo.toml +++ b/examples/actix_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.6" # sea-orm version +version = "0.12.10" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/axum_example/entity/Cargo.toml b/examples/axum_example/entity/Cargo.toml index 41f742f6db..a50073b434 100644 --- a/examples/axum_example/entity/Cargo.toml +++ b/examples/axum_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.6" # sea-orm version +version = "0.12.10" # sea-orm version diff --git a/examples/axum_example/migration/Cargo.toml b/examples/axum_example/migration/Cargo.toml index 518046de3d..9bc4966803 100644 --- a/examples/axum_example/migration/Cargo.toml +++ b/examples/axum_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.6" # sea-orm-migration version +version = "0.12.10" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/axum_example/service/Cargo.toml b/examples/axum_example/service/Cargo.toml index 261b451b8b..24a9a0b548 100644 --- a/examples/axum_example/service/Cargo.toml +++ b/examples/axum_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.6" # sea-orm version +version = "0.12.10" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/graphql_example/entity/Cargo.toml b/examples/graphql_example/entity/Cargo.toml index 66bd454be4..db5f354d34 100644 --- a/examples/graphql_example/entity/Cargo.toml +++ b/examples/graphql_example/entity/Cargo.toml @@ -16,4 +16,4 @@ version = "5.0.10" [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.6" # sea-orm version +version = "0.12.10" # sea-orm version diff --git a/examples/graphql_example/migration/Cargo.toml b/examples/graphql_example/migration/Cargo.toml index e03da112a4..e25efb7d3e 100644 --- a/examples/graphql_example/migration/Cargo.toml +++ b/examples/graphql_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.6" # sea-orm-migration version +version = "0.12.10" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/graphql_example/service/Cargo.toml b/examples/graphql_example/service/Cargo.toml index 1158114a59..2533739d74 100644 --- a/examples/graphql_example/service/Cargo.toml +++ b/examples/graphql_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.6" # sea-orm version +version = "0.12.10" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/jsonrpsee_example/entity/Cargo.toml b/examples/jsonrpsee_example/entity/Cargo.toml index 41f742f6db..a50073b434 100644 --- a/examples/jsonrpsee_example/entity/Cargo.toml +++ b/examples/jsonrpsee_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.6" # sea-orm version +version = "0.12.10" # sea-orm version diff --git a/examples/jsonrpsee_example/migration/Cargo.toml b/examples/jsonrpsee_example/migration/Cargo.toml index f1efa6ea4b..c0de8f405d 100644 --- a/examples/jsonrpsee_example/migration/Cargo.toml +++ b/examples/jsonrpsee_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.6" # sea-orm-migration version +version = "0.12.10" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/jsonrpsee_example/service/Cargo.toml b/examples/jsonrpsee_example/service/Cargo.toml index f056221a34..81be9f5a17 100644 --- a/examples/jsonrpsee_example/service/Cargo.toml +++ b/examples/jsonrpsee_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.6" # sea-orm version +version = "0.12.10" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/poem_example/entity/Cargo.toml b/examples/poem_example/entity/Cargo.toml index 41f742f6db..a50073b434 100644 --- a/examples/poem_example/entity/Cargo.toml +++ b/examples/poem_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.6" # sea-orm version +version = "0.12.10" # sea-orm version diff --git a/examples/poem_example/migration/Cargo.toml b/examples/poem_example/migration/Cargo.toml index f1efa6ea4b..c0de8f405d 100644 --- a/examples/poem_example/migration/Cargo.toml +++ b/examples/poem_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.6" # sea-orm-migration version +version = "0.12.10" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/poem_example/service/Cargo.toml b/examples/poem_example/service/Cargo.toml index 8dbb7bf1d6..e7009f7d49 100644 --- a/examples/poem_example/service/Cargo.toml +++ b/examples/poem_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.6" # sea-orm version +version = "0.12.10" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/rocket_example/entity/Cargo.toml b/examples/rocket_example/entity/Cargo.toml index 60cf1e8a48..68eec76e9a 100644 --- a/examples/rocket_example/entity/Cargo.toml +++ b/examples/rocket_example/entity/Cargo.toml @@ -13,4 +13,4 @@ rocket = { version = "0.5.0-rc.4", features = ["json"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.6" # sea-orm version +version = "0.12.10" # sea-orm version diff --git a/examples/rocket_example/migration/Cargo.toml b/examples/rocket_example/migration/Cargo.toml index b5bda5b0a9..b9f136927a 100644 --- a/examples/rocket_example/migration/Cargo.toml +++ b/examples/rocket_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.6" # sea-orm-migration version +version = "0.12.10" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/rocket_example/service/Cargo.toml b/examples/rocket_example/service/Cargo.toml index 8b72ed7971..e1fa4975a1 100644 --- a/examples/rocket_example/service/Cargo.toml +++ b/examples/rocket_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.6" # sea-orm version +version = "0.12.10" # sea-orm version features = [ "runtime-tokio-native-tls", "sqlx-postgres", diff --git a/examples/rocket_okapi_example/entity/Cargo.toml b/examples/rocket_okapi_example/entity/Cargo.toml index 91dd207951..9357c06e5f 100644 --- a/examples/rocket_okapi_example/entity/Cargo.toml +++ b/examples/rocket_okapi_example/entity/Cargo.toml @@ -13,7 +13,7 @@ rocket = { version = "0.5.0-rc.4", features = ["json"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.6" # sea-orm version +version = "0.12.10" # sea-orm version [dependencies.rocket_okapi] version = "0.8.0-rc.2" diff --git a/examples/rocket_okapi_example/migration/Cargo.toml b/examples/rocket_okapi_example/migration/Cargo.toml index b5bda5b0a9..b9f136927a 100644 --- a/examples/rocket_okapi_example/migration/Cargo.toml +++ b/examples/rocket_okapi_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.6" # sea-orm-migration version +version = "0.12.10" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/rocket_okapi_example/service/Cargo.toml b/examples/rocket_okapi_example/service/Cargo.toml index cde5208b21..6f50ca8e8e 100644 --- a/examples/rocket_okapi_example/service/Cargo.toml +++ b/examples/rocket_okapi_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.6" # sea-orm version +version = "0.12.10" # sea-orm version features = [ "runtime-tokio-native-tls", "sqlx-postgres", diff --git a/examples/salvo_example/entity/Cargo.toml b/examples/salvo_example/entity/Cargo.toml index 058b355f4b..2a4f98f8a1 100644 --- a/examples/salvo_example/entity/Cargo.toml +++ b/examples/salvo_example/entity/Cargo.toml @@ -14,4 +14,4 @@ salvo = { version = "0.50" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.6" # sea-orm version +version = "0.12.10" # sea-orm version diff --git a/examples/salvo_example/migration/Cargo.toml b/examples/salvo_example/migration/Cargo.toml index f1efa6ea4b..c0de8f405d 100644 --- a/examples/salvo_example/migration/Cargo.toml +++ b/examples/salvo_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.6" # sea-orm-migration version +version = "0.12.10" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/salvo_example/service/Cargo.toml b/examples/salvo_example/service/Cargo.toml index d778d8b4b8..bbe435b7c7 100644 --- a/examples/salvo_example/service/Cargo.toml +++ b/examples/salvo_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.6" # sea-orm version +version = "0.12.10" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/seaography_example/migration/Cargo.toml b/examples/seaography_example/migration/Cargo.toml index 7424628660..2c4857803e 100644 --- a/examples/seaography_example/migration/Cargo.toml +++ b/examples/seaography_example/migration/Cargo.toml @@ -15,11 +15,11 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm] path = "../../.." # remove this line in your own project -version = "0.12.6" # sea-orm version +version = "0.12.10" # sea-orm version [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.6" # sea-orm-migration version +version = "0.12.10" # sea-orm-migration version features = [ "runtime-async-std-native-tls", "sqlx-mysql", diff --git a/examples/tonic_example/entity/Cargo.toml b/examples/tonic_example/entity/Cargo.toml index 41f742f6db..a50073b434 100644 --- a/examples/tonic_example/entity/Cargo.toml +++ b/examples/tonic_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.6" # sea-orm version +version = "0.12.10" # sea-orm version diff --git a/examples/tonic_example/migration/Cargo.toml b/examples/tonic_example/migration/Cargo.toml index 3cb2baf5ca..653d0d41b0 100644 --- a/examples/tonic_example/migration/Cargo.toml +++ b/examples/tonic_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.6" # sea-orm-migration version +version = "0.12.10" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-rustls", diff --git a/examples/tonic_example/service/Cargo.toml b/examples/tonic_example/service/Cargo.toml index e8fb93ec2f..cfb76930f2 100644 --- a/examples/tonic_example/service/Cargo.toml +++ b/examples/tonic_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.6" # sea-orm version +version = "0.12.10" # sea-orm version features = [ "debug-print", "runtime-tokio-rustls", From b8ad6303d8309b722a68520b69aa8fead5379daa Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 14 Dec 2023 19:47:52 +0000 Subject: [PATCH 0240/1371] Fixup --- tests/active_enum_tests.rs | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/tests/active_enum_tests.rs b/tests/active_enum_tests.rs index 27babd6c06..e06e0ab24a 100644 --- a/tests/active_enum_tests.rs +++ b/tests/active_enum_tests.rs @@ -111,18 +111,19 @@ pub async fn insert_active_enum(db: &DatabaseConnection) -> Result<(), DbErr> { .unwrap() ); // Equivalent to the above. - let select_with_tea_in = Entity::find().filter(Column::Tea.is_in([Tea::EverydayTea])); + let select_with_tea_in = + Entity::find().filter(Column::Tea.is_in([Tea::EverydayTea, Tea::BreakfastTea])); assert_eq!( select_with_tea_in .build(sea_orm::DatabaseBackend::Postgres) .to_string(), [ - "SELECT \"active_enum\".\"id\",", - "\"active_enum\".\"category\",", - "\"active_enum\".\"color\",", - "CAST(\"active_enum\".\"tea\" AS text)", - "FROM \"active_enum\"", - "WHERE \"active_enum\".\"tea\" IN (CAST('EverydayTea' AS tea))", + r#"SELECT "active_enum"."id","#, + r#""active_enum"."category","#, + r#""active_enum"."color","#, + r#"CAST("active_enum"."tea" AS text)"#, + r#"FROM "public"."active_enum""#, + r#"WHERE "active_enum"."tea" IN (CAST('EverydayTea' AS tea), CAST('BreakfastTea' AS tea))"#, ] .join(" ") ); @@ -149,13 +150,13 @@ pub async fn insert_active_enum(db: &DatabaseConnection) -> Result<(), DbErr> { .build(sea_orm::DatabaseBackend::Postgres) .to_string(), [ - "SELECT \"active_enum\".\"id\",", - "\"active_enum\".\"category\",", - "\"active_enum\".\"color\",", - "CAST(\"active_enum\".\"tea\" AS text)", - "FROM \"active_enum\"", - "WHERE \"active_enum\".\"tea\" IS NOT NULL", - "AND \"active_enum\".\"tea\" NOT IN (CAST('BreakfastTea' AS tea))", + r#"SELECT "active_enum"."id","#, + r#""active_enum"."category","#, + r#""active_enum"."color","#, + r#"CAST("active_enum"."tea" AS text)"#, + r#"FROM "public"."active_enum""#, + r#"WHERE "active_enum"."tea" IS NOT NULL"#, + r#"AND "active_enum"."tea" NOT IN (CAST('BreakfastTea' AS tea))"#, ] .join(" ") ); From 91b6f79ba76a22a636b1fb36c1ce0063bb37842d Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 14 Dec 2023 19:50:50 +0000 Subject: [PATCH 0241/1371] Changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 823e1da0fe..ef8de6d98b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). * [sea-orm-macro] Comment attribute for Entity (`#[sea_orm(comment = "action")]`); `create_table_from_entity` supports comment https://github.com/SeaQL/sea-orm/pull/2009 * Added "proxy" (feature flag `proxy`) to database backend https://github.com/SeaQL/sea-orm/pull/1881, https://github.com/SeaQL/sea-orm/pull/2000 +### Enhancements + +* Cast enums in `is_in` and `is_not_in` https://github.com/SeaQL/sea-orm/pull/2002 + ### Upgrades * Updated `sea-query` to `0.30.5` https://github.com/SeaQL/sea-query/releases/tag/0.30.5 From 8a29cba78a45fc6c7fa578dfb0163ec44a77de98 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 14 Dec 2023 20:03:02 +0000 Subject: [PATCH 0242/1371] Fixup --- .../duplicated_many_to_many_paths/bills.rs | 4 ++-- .../users_saved_bills.rs | 4 ++-- .../duplicated_many_to_many_paths/users_votes.rs | 4 ++-- .../src/tests_cfg/many_to_many/bills.rs | 4 ++-- .../src/tests_cfg/many_to_many/users_votes.rs | 4 ++-- .../src/tests_cfg/many_to_many_multiple/bills.rs | 4 ++-- .../many_to_many_multiple/users_votes.rs | 12 ++++++------ .../src/tests_cfg/self_referencing/bills.rs | 8 ++------ .../src/tests_cfg/self_referencing/users.rs | 16 ++++------------ tests/active_enum_tests.rs | 2 ++ 10 files changed, 26 insertions(+), 36 deletions(-) diff --git a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/bills.rs b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/bills.rs index a3a6911b98..baf33426eb 100644 --- a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/bills.rs +++ b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/bills.rs @@ -5,7 +5,7 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub user_id: Option , + pub user_id: Option, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] @@ -15,7 +15,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "NoAction", - on_delete = "NoAction", + on_delete = "NoAction" )] Users, #[sea_orm(has_many = "super::users_saved_bills::Entity")] diff --git a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_saved_bills.rs b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_saved_bills.rs index 4ef0281cd2..575bac58f1 100644 --- a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_saved_bills.rs +++ b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_saved_bills.rs @@ -16,7 +16,7 @@ pub enum Relation { from = "Column::BillId", to = "super::bills::Column::Id", on_update = "Cascade", - on_delete = "Cascade", + on_delete = "Cascade" )] Bills, #[sea_orm( @@ -24,7 +24,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "Cascade", - on_delete = "Cascade", + on_delete = "Cascade" )] Users, } diff --git a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_votes.rs b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_votes.rs index a04ad7fa73..62f9cf7d00 100644 --- a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_votes.rs +++ b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_votes.rs @@ -17,7 +17,7 @@ pub enum Relation { from = "Column::BillId", to = "super::bills::Column::Id", on_update = "Cascade", - on_delete = "Cascade", + on_delete = "Cascade" )] Bills, #[sea_orm( @@ -25,7 +25,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "Cascade", - on_delete = "Cascade", + on_delete = "Cascade" )] Users, } diff --git a/sea-orm-codegen/src/tests_cfg/many_to_many/bills.rs b/sea-orm-codegen/src/tests_cfg/many_to_many/bills.rs index 5a3acef9cb..f2de469f0a 100644 --- a/sea-orm-codegen/src/tests_cfg/many_to_many/bills.rs +++ b/sea-orm-codegen/src/tests_cfg/many_to_many/bills.rs @@ -5,7 +5,7 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub user_id: Option , + pub user_id: Option, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] @@ -15,7 +15,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "NoAction", - on_delete = "NoAction", + on_delete = "NoAction" )] Users, #[sea_orm(has_many = "super::users_votes::Entity")] diff --git a/sea-orm-codegen/src/tests_cfg/many_to_many/users_votes.rs b/sea-orm-codegen/src/tests_cfg/many_to_many/users_votes.rs index a04ad7fa73..62f9cf7d00 100644 --- a/sea-orm-codegen/src/tests_cfg/many_to_many/users_votes.rs +++ b/sea-orm-codegen/src/tests_cfg/many_to_many/users_votes.rs @@ -17,7 +17,7 @@ pub enum Relation { from = "Column::BillId", to = "super::bills::Column::Id", on_update = "Cascade", - on_delete = "Cascade", + on_delete = "Cascade" )] Bills, #[sea_orm( @@ -25,7 +25,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "Cascade", - on_delete = "Cascade", + on_delete = "Cascade" )] Users, } diff --git a/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/bills.rs b/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/bills.rs index 5094e5885f..6874aeed0e 100644 --- a/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/bills.rs +++ b/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/bills.rs @@ -5,7 +5,7 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub user_id: Option , + pub user_id: Option, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] @@ -15,7 +15,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "NoAction", - on_delete = "NoAction", + on_delete = "NoAction" )] Users, } diff --git a/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/users_votes.rs b/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/users_votes.rs index 87187f4c0b..29fb173cb5 100644 --- a/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/users_votes.rs +++ b/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/users_votes.rs @@ -7,8 +7,8 @@ pub struct Model { pub user_id: i32, #[sea_orm(primary_key, auto_increment = false)] pub bill_id: i32, - pub user_idd: Option , - pub bill_idd: Option , + pub user_idd: Option, + pub bill_idd: Option, pub vote: bool, } @@ -17,25 +17,25 @@ pub enum Relation { #[sea_orm( belongs_to = "super::bills::Entity", from = "Column::BillIdd", - to = "super::bills::Column::Id", + to = "super::bills::Column::Id" )] Bills2, #[sea_orm( belongs_to = "super::bills::Entity", from = "Column::BillId", - to = "super::bills::Column::Id", + to = "super::bills::Column::Id" )] Bills1, #[sea_orm( belongs_to = "super::users::Entity", from = "Column::UserIdd", - to = "super::users::Column::Id", + to = "super::users::Column::Id" )] Users2, #[sea_orm( belongs_to = "super::users::Entity", from = "Column::UserId", - to = "super::users::Column::Id", + to = "super::users::Column::Id" )] Users1, } diff --git a/sea-orm-codegen/src/tests_cfg/self_referencing/bills.rs b/sea-orm-codegen/src/tests_cfg/self_referencing/bills.rs index 583655f673..22cfc46908 100644 --- a/sea-orm-codegen/src/tests_cfg/self_referencing/bills.rs +++ b/sea-orm-codegen/src/tests_cfg/self_referencing/bills.rs @@ -5,16 +5,12 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub self_id: Option , + pub self_id: Option, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { - #[sea_orm( - belongs_to = "Entity", - from = "Column::SelfId", - to = "Column::Id", - )] + #[sea_orm(belongs_to = "Entity", from = "Column::SelfId", to = "Column::Id")] SelfRef, } diff --git a/sea-orm-codegen/src/tests_cfg/self_referencing/users.rs b/sea-orm-codegen/src/tests_cfg/self_referencing/users.rs index b54f3e5e2d..487ba9409a 100644 --- a/sea-orm-codegen/src/tests_cfg/self_referencing/users.rs +++ b/sea-orm-codegen/src/tests_cfg/self_referencing/users.rs @@ -5,23 +5,15 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub self_id: Option , - pub self_idd: Option , + pub self_id: Option, + pub self_idd: Option, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { - #[sea_orm( - belongs_to = "Entity", - from = "Column::SelfId", - to = "Column::Id", - )] + #[sea_orm(belongs_to = "Entity", from = "Column::SelfId", to = "Column::Id")] SelfRef2, - #[sea_orm( - belongs_to = "Entity", - from = "Column::SelfIdd", - to = "Column::Id", - )] + #[sea_orm(belongs_to = "Entity", from = "Column::SelfIdd", to = "Column::Id")] SelfRef1, } diff --git a/tests/active_enum_tests.rs b/tests/active_enum_tests.rs index e06e0ab24a..1a38f3b712 100644 --- a/tests/active_enum_tests.rs +++ b/tests/active_enum_tests.rs @@ -113,6 +113,7 @@ pub async fn insert_active_enum(db: &DatabaseConnection) -> Result<(), DbErr> { // Equivalent to the above. let select_with_tea_in = Entity::find().filter(Column::Tea.is_in([Tea::EverydayTea, Tea::BreakfastTea])); + #[cfg(feature = "sqlx-postgres")] assert_eq!( select_with_tea_in .build(sea_orm::DatabaseBackend::Postgres) @@ -145,6 +146,7 @@ pub async fn insert_active_enum(db: &DatabaseConnection) -> Result<(), DbErr> { let select_with_tea_not_in = Entity::find() .filter(Column::Tea.is_not_null()) .filter(Column::Tea.is_not_in([Tea::BreakfastTea])); + #[cfg(feature = "sqlx-postgres")] assert_eq!( select_with_tea_not_in .build(sea_orm::DatabaseBackend::Postgres) From e10dcd012fdbfcd137c5a034c8a4c2c18df92137 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 14 Dec 2023 20:20:25 +0000 Subject: [PATCH 0243/1371] Revert "Fixup" This reverts commit 8a29cba78a45fc6c7fa578dfb0163ec44a77de98. --- .../duplicated_many_to_many_paths/bills.rs | 4 ++-- .../users_saved_bills.rs | 4 ++-- .../duplicated_many_to_many_paths/users_votes.rs | 4 ++-- .../src/tests_cfg/many_to_many/bills.rs | 4 ++-- .../src/tests_cfg/many_to_many/users_votes.rs | 4 ++-- .../src/tests_cfg/many_to_many_multiple/bills.rs | 4 ++-- .../many_to_many_multiple/users_votes.rs | 12 ++++++------ .../src/tests_cfg/self_referencing/bills.rs | 8 ++++++-- .../src/tests_cfg/self_referencing/users.rs | 16 ++++++++++++---- tests/active_enum_tests.rs | 2 -- 10 files changed, 36 insertions(+), 26 deletions(-) diff --git a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/bills.rs b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/bills.rs index baf33426eb..a3a6911b98 100644 --- a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/bills.rs +++ b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/bills.rs @@ -5,7 +5,7 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub user_id: Option, + pub user_id: Option , } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] @@ -15,7 +15,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "NoAction", - on_delete = "NoAction" + on_delete = "NoAction", )] Users, #[sea_orm(has_many = "super::users_saved_bills::Entity")] diff --git a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_saved_bills.rs b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_saved_bills.rs index 575bac58f1..4ef0281cd2 100644 --- a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_saved_bills.rs +++ b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_saved_bills.rs @@ -16,7 +16,7 @@ pub enum Relation { from = "Column::BillId", to = "super::bills::Column::Id", on_update = "Cascade", - on_delete = "Cascade" + on_delete = "Cascade", )] Bills, #[sea_orm( @@ -24,7 +24,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "Cascade", - on_delete = "Cascade" + on_delete = "Cascade", )] Users, } diff --git a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_votes.rs b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_votes.rs index 62f9cf7d00..a04ad7fa73 100644 --- a/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_votes.rs +++ b/sea-orm-codegen/src/tests_cfg/duplicated_many_to_many_paths/users_votes.rs @@ -17,7 +17,7 @@ pub enum Relation { from = "Column::BillId", to = "super::bills::Column::Id", on_update = "Cascade", - on_delete = "Cascade" + on_delete = "Cascade", )] Bills, #[sea_orm( @@ -25,7 +25,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "Cascade", - on_delete = "Cascade" + on_delete = "Cascade", )] Users, } diff --git a/sea-orm-codegen/src/tests_cfg/many_to_many/bills.rs b/sea-orm-codegen/src/tests_cfg/many_to_many/bills.rs index f2de469f0a..5a3acef9cb 100644 --- a/sea-orm-codegen/src/tests_cfg/many_to_many/bills.rs +++ b/sea-orm-codegen/src/tests_cfg/many_to_many/bills.rs @@ -5,7 +5,7 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub user_id: Option, + pub user_id: Option , } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] @@ -15,7 +15,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "NoAction", - on_delete = "NoAction" + on_delete = "NoAction", )] Users, #[sea_orm(has_many = "super::users_votes::Entity")] diff --git a/sea-orm-codegen/src/tests_cfg/many_to_many/users_votes.rs b/sea-orm-codegen/src/tests_cfg/many_to_many/users_votes.rs index 62f9cf7d00..a04ad7fa73 100644 --- a/sea-orm-codegen/src/tests_cfg/many_to_many/users_votes.rs +++ b/sea-orm-codegen/src/tests_cfg/many_to_many/users_votes.rs @@ -17,7 +17,7 @@ pub enum Relation { from = "Column::BillId", to = "super::bills::Column::Id", on_update = "Cascade", - on_delete = "Cascade" + on_delete = "Cascade", )] Bills, #[sea_orm( @@ -25,7 +25,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "Cascade", - on_delete = "Cascade" + on_delete = "Cascade", )] Users, } diff --git a/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/bills.rs b/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/bills.rs index 6874aeed0e..5094e5885f 100644 --- a/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/bills.rs +++ b/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/bills.rs @@ -5,7 +5,7 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub user_id: Option, + pub user_id: Option , } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] @@ -15,7 +15,7 @@ pub enum Relation { from = "Column::UserId", to = "super::users::Column::Id", on_update = "NoAction", - on_delete = "NoAction" + on_delete = "NoAction", )] Users, } diff --git a/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/users_votes.rs b/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/users_votes.rs index 29fb173cb5..87187f4c0b 100644 --- a/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/users_votes.rs +++ b/sea-orm-codegen/src/tests_cfg/many_to_many_multiple/users_votes.rs @@ -7,8 +7,8 @@ pub struct Model { pub user_id: i32, #[sea_orm(primary_key, auto_increment = false)] pub bill_id: i32, - pub user_idd: Option, - pub bill_idd: Option, + pub user_idd: Option , + pub bill_idd: Option , pub vote: bool, } @@ -17,25 +17,25 @@ pub enum Relation { #[sea_orm( belongs_to = "super::bills::Entity", from = "Column::BillIdd", - to = "super::bills::Column::Id" + to = "super::bills::Column::Id", )] Bills2, #[sea_orm( belongs_to = "super::bills::Entity", from = "Column::BillId", - to = "super::bills::Column::Id" + to = "super::bills::Column::Id", )] Bills1, #[sea_orm( belongs_to = "super::users::Entity", from = "Column::UserIdd", - to = "super::users::Column::Id" + to = "super::users::Column::Id", )] Users2, #[sea_orm( belongs_to = "super::users::Entity", from = "Column::UserId", - to = "super::users::Column::Id" + to = "super::users::Column::Id", )] Users1, } diff --git a/sea-orm-codegen/src/tests_cfg/self_referencing/bills.rs b/sea-orm-codegen/src/tests_cfg/self_referencing/bills.rs index 22cfc46908..583655f673 100644 --- a/sea-orm-codegen/src/tests_cfg/self_referencing/bills.rs +++ b/sea-orm-codegen/src/tests_cfg/self_referencing/bills.rs @@ -5,12 +5,16 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub self_id: Option, + pub self_id: Option , } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { - #[sea_orm(belongs_to = "Entity", from = "Column::SelfId", to = "Column::Id")] + #[sea_orm( + belongs_to = "Entity", + from = "Column::SelfId", + to = "Column::Id", + )] SelfRef, } diff --git a/sea-orm-codegen/src/tests_cfg/self_referencing/users.rs b/sea-orm-codegen/src/tests_cfg/self_referencing/users.rs index 487ba9409a..b54f3e5e2d 100644 --- a/sea-orm-codegen/src/tests_cfg/self_referencing/users.rs +++ b/sea-orm-codegen/src/tests_cfg/self_referencing/users.rs @@ -5,15 +5,23 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub self_id: Option, - pub self_idd: Option, + pub self_id: Option , + pub self_idd: Option , } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { - #[sea_orm(belongs_to = "Entity", from = "Column::SelfId", to = "Column::Id")] + #[sea_orm( + belongs_to = "Entity", + from = "Column::SelfId", + to = "Column::Id", + )] SelfRef2, - #[sea_orm(belongs_to = "Entity", from = "Column::SelfIdd", to = "Column::Id")] + #[sea_orm( + belongs_to = "Entity", + from = "Column::SelfIdd", + to = "Column::Id", + )] SelfRef1, } diff --git a/tests/active_enum_tests.rs b/tests/active_enum_tests.rs index 1a38f3b712..e06e0ab24a 100644 --- a/tests/active_enum_tests.rs +++ b/tests/active_enum_tests.rs @@ -113,7 +113,6 @@ pub async fn insert_active_enum(db: &DatabaseConnection) -> Result<(), DbErr> { // Equivalent to the above. let select_with_tea_in = Entity::find().filter(Column::Tea.is_in([Tea::EverydayTea, Tea::BreakfastTea])); - #[cfg(feature = "sqlx-postgres")] assert_eq!( select_with_tea_in .build(sea_orm::DatabaseBackend::Postgres) @@ -146,7 +145,6 @@ pub async fn insert_active_enum(db: &DatabaseConnection) -> Result<(), DbErr> { let select_with_tea_not_in = Entity::find() .filter(Column::Tea.is_not_null()) .filter(Column::Tea.is_not_in([Tea::BreakfastTea])); - #[cfg(feature = "sqlx-postgres")] assert_eq!( select_with_tea_not_in .build(sea_orm::DatabaseBackend::Postgres) From 1554db50001356ee3ff036b4a98ad4da742cf4c2 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 14 Dec 2023 20:21:11 +0000 Subject: [PATCH 0244/1371] Fixup --- tests/active_enum_tests.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/active_enum_tests.rs b/tests/active_enum_tests.rs index e06e0ab24a..1a38f3b712 100644 --- a/tests/active_enum_tests.rs +++ b/tests/active_enum_tests.rs @@ -113,6 +113,7 @@ pub async fn insert_active_enum(db: &DatabaseConnection) -> Result<(), DbErr> { // Equivalent to the above. let select_with_tea_in = Entity::find().filter(Column::Tea.is_in([Tea::EverydayTea, Tea::BreakfastTea])); + #[cfg(feature = "sqlx-postgres")] assert_eq!( select_with_tea_in .build(sea_orm::DatabaseBackend::Postgres) @@ -145,6 +146,7 @@ pub async fn insert_active_enum(db: &DatabaseConnection) -> Result<(), DbErr> { let select_with_tea_not_in = Entity::find() .filter(Column::Tea.is_not_null()) .filter(Column::Tea.is_not_in([Tea::BreakfastTea])); + #[cfg(feature = "sqlx-postgres")] assert_eq!( select_with_tea_not_in .build(sea_orm::DatabaseBackend::Postgres) From 84a24bab0135c63a6b68c4ddb3a980890a82cc5b Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 14 Dec 2023 21:13:04 +0000 Subject: [PATCH 0245/1371] Rename things --- README.md | 2 +- build-tools/{bump-version.sh => bump.sh} | 0 build-tools/{cargo-clean.sh => clean.sh} | 0 build-tools/{cargo-publish.sh => publish.sh} | 0 build-tools/{cargo-readme.sh => readme.sh} | 0 src/lib.rs | 2 +- 6 files changed, 2 insertions(+), 2 deletions(-) rename build-tools/{bump-version.sh => bump.sh} (100%) rename build-tools/{cargo-clean.sh => clean.sh} (100%) rename build-tools/{cargo-publish.sh => publish.sh} (100%) rename build-tools/{cargo-readme.sh => readme.sh} (100%) diff --git a/README.md b/README.md index 1f357e2782..60fb738e25 100644 --- a/README.md +++ b/README.md @@ -218,7 +218,7 @@ The following products are powered by SeaORM:

A lightweight web security auditing toolkit
The enterprise ready webhooks service -
A personal search engine +
A personal search engine diff --git a/build-tools/bump-version.sh b/build-tools/bump.sh similarity index 100% rename from build-tools/bump-version.sh rename to build-tools/bump.sh diff --git a/build-tools/cargo-clean.sh b/build-tools/clean.sh similarity index 100% rename from build-tools/cargo-clean.sh rename to build-tools/clean.sh diff --git a/build-tools/cargo-publish.sh b/build-tools/publish.sh similarity index 100% rename from build-tools/cargo-publish.sh rename to build-tools/publish.sh diff --git a/build-tools/cargo-readme.sh b/build-tools/readme.sh similarity index 100% rename from build-tools/cargo-readme.sh rename to build-tools/readme.sh diff --git a/src/lib.rs b/src/lib.rs index 75fb1c133a..db78e24500 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -291,7 +291,7 @@ //! //!

A lightweight web security auditing toolkit //!
The enterprise ready webhooks service -//!
A personal search engine +//!
A personal search engine //! //! //! From 0ad6c2fb148309c9f785132ca1cf919e80cd6d06 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sat, 16 Dec 2023 19:44:32 +0000 Subject: [PATCH 0246/1371] Readme --- README.md | 16 ++-------------- src/lib.rs | 16 ++-------------- 2 files changed, 4 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 60fb738e25..a4d85fa6d4 100644 --- a/README.md +++ b/README.md @@ -209,21 +209,9 @@ Look at the [Seaography Example](https://github.com/SeaQL/sea-orm/tree/master/ex 1. [Engineering](https://www.sea-ql.org/blog/2022-07-30-engineering/) 1. [Change Log](https://github.com/SeaQL/sea-orm/tree/master/CHANGELOG.md) -## Who's using SeaORM? +### Who's using SeaORM? -The following products are powered by SeaORM: - - - - - - - - - -


A lightweight web security auditing toolkit

The enterprise ready webhooks service

A personal search engine
- -For more projects, see [Built with SeaORM](https://github.com/SeaQL/sea-orm/blob/master/COMMUNITY.md#built-with-seaorm). Feel free to [submit yours](https://github.com/SeaQL/sea-orm/issues/403)! +See [Built with SeaORM](https://github.com/SeaQL/sea-orm/blob/master/COMMUNITY.md#built-with-seaorm). Feel free to [submit yours](https://github.com/SeaQL/sea-orm/issues/403)! ## License diff --git a/src/lib.rs b/src/lib.rs index db78e24500..4026b5636d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -282,21 +282,9 @@ //! 1. [Engineering](https://www.sea-ql.org/blog/2022-07-30-engineering/) //! 1. [Change Log](https://github.com/SeaQL/sea-orm/tree/master/CHANGELOG.md) //! -//! ## Who's using SeaORM? +//! ### Who's using SeaORM? //! -//! The following products are powered by SeaORM: -//! -//! -//! -//! -//! -//! -//! -//! -//! -//!


A lightweight web security auditing toolkit

The enterprise ready webhooks service

A personal search engine
-//! -//! For more projects, see [Built with SeaORM](https://github.com/SeaQL/sea-orm/blob/master/COMMUNITY.md#built-with-seaorm). Feel free to [submit yours](https://github.com/SeaQL/sea-orm/issues/403)! +//! See [Built with SeaORM](https://github.com/SeaQL/sea-orm/blob/master/COMMUNITY.md#built-with-seaorm). Feel free to [submit yours](https://github.com/SeaQL/sea-orm/issues/403)! //! //! ## License //! From 3af70f4aff8985882446efe2afe11d38f5c8ad49 Mon Sep 17 00:00:00 2001 From: "Dotan J. Nahum" Date: Tue, 19 Dec 2023 13:37:53 +0200 Subject: [PATCH 0247/1371] Community: add Loco.rs framework (#2026) --- COMMUNITY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/COMMUNITY.md b/COMMUNITY.md index 37e8635155..957392c0cf 100644 --- a/COMMUNITY.md +++ b/COMMUNITY.md @@ -101,6 +101,7 @@ If you have built an app using SeaORM and want to showcase it, feel free to open - [awto](https://github.com/awto-rs/awto) | Awtomate your 🦀 microservices with awto | DB: Postgres - [poem_admin](https://github.com/lingdu1234/poem_admin) | An admin panel built with poems, SeaORM and Vue 3. | DB: MySQL, Postgres, SQLite - [Quasar](https://github.com/Technik97/Quasar) | Rust REST API using Actix Web, SeaORM and Postgres with Svelte Typescript Frontend | DB: Postgres +- [Loco.rs](https://github.com/loco-rs/loco) | A full stack Web and API productivity framework similar to Rails, based on SeaORM with db tooling and migrations code generation | DB: Postgres, SQLite - [tardis](https://github.com/ideal-world/tardis) | Elegant, Clean Rust development framework🛸 | DB: MySQL, Postgres, SQLite - [ZAPP](https://zapp.epics.dev) ([repository](https://github.com/EpicsDAO/zapp)) | ZAPP is a serverless framework made by Rust. Quickly build a scalable GraphQL API web server. From 53ec0e15488ea71d5da71b1d3ac3f2d9a3b2b97d Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Tue, 19 Dec 2023 01:35:26 +0000 Subject: [PATCH 0248/1371] Community --- COMMUNITY.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/COMMUNITY.md b/COMMUNITY.md index 957392c0cf..b96f6f61af 100644 --- a/COMMUNITY.md +++ b/COMMUNITY.md @@ -97,13 +97,10 @@ If you have built an app using SeaORM and want to showcase it, feel free to open ### Frameworks -- [actix-admin](https://github.com/mgugger/actix-admin/) | An admin panel for Actix Web built with Tera, HTMX and SeaOrm | DB: MySQL, Postgres, SQLite -- [awto](https://github.com/awto-rs/awto) | Awtomate your 🦀 microservices with awto | DB: Postgres +- [actix-admin](https://github.com/mgugger/actix-admin) | An admin panel for Actix Web built with Tera, HTMX and SeaOrm | DB: MySQL, Postgres, SQLite - [poem_admin](https://github.com/lingdu1234/poem_admin) | An admin panel built with poems, SeaORM and Vue 3. | DB: MySQL, Postgres, SQLite -- [Quasar](https://github.com/Technik97/Quasar) | Rust REST API using Actix Web, SeaORM and Postgres with Svelte Typescript Frontend | DB: Postgres - [Loco.rs](https://github.com/loco-rs/loco) | A full stack Web and API productivity framework similar to Rails, based on SeaORM with db tooling and migrations code generation | DB: Postgres, SQLite - [tardis](https://github.com/ideal-world/tardis) | Elegant, Clean Rust development framework🛸 | DB: MySQL, Postgres, SQLite -- [ZAPP](https://zapp.epics.dev) ([repository](https://github.com/EpicsDAO/zapp)) | ZAPP is a serverless framework made by Rust. Quickly build a scalable GraphQL API web server. ### Scaffolding From c3344347f75441069dc5a812c32ed671cad1d4b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=97=8D+85CD?= <50108258+kwaa@users.noreply.github.com> Date: Sun, 31 Dec 2023 08:08:41 +0800 Subject: [PATCH 0249/1371] Add hatsu to COMMUNITY.md (#2036) * Add hatsu to COMMUNITY.md * Update COMMUNITY.md --- COMMUNITY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/COMMUNITY.md b/COMMUNITY.md index b96f6f61af..01c75f739e 100644 --- a/COMMUNITY.md +++ b/COMMUNITY.md @@ -37,6 +37,7 @@ If you have built an app using SeaORM and want to showcase it, feel free to open #### Social - [aeroFans](https://github.com/naryand/aerofans) | Full stack forum-like social media platform in Rust and WebAssembly | DB: Postgres - [Crab Fit](https://crab.fit) [![GitHub stars](https://img.shields.io/github/stars/GRA0007/crab.fit.svg?style=social)](https://github.com/GRA0007/crab.fit) | Align your schedules to find the perfect time that works for everyone. | DB: MySQL, Postgres, SQLite +- [Hatsu](https://github.com/importantimport/hatsu) | 🩵 Self-hosted & Fully-automated ActivityPub Bridge for Static Sites. | DB: SQLite, Postgres - [JinShu](https://github.com/gengteng/jinshu) | A cross-platform **I**nstant **M**essaging system written in 🦀 | DB: MySQL, Postgres - [Portfolio](https://github.com/admisio/Portfolio) | Encrypted high school 🏫 admissions service | DB: Postgres, SQLite - [THUBurrow](https://github.com/BobAnkh/THUBurrow) | A campus forum built by Next.js and Rocket | DB: Postgres From 3dc66aa17da4ba36a2cc99aa43037cce421b13ab Mon Sep 17 00:00:00 2001 From: Shogo Nakano <61229807+shogo-nakano-desu@users.noreply.github.com> Date: Wed, 10 Jan 2024 19:30:30 +0900 Subject: [PATCH 0250/1371] refactor: add clear_order_by for num_items (#2030) --- src/executor/paginator.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/executor/paginator.rs b/src/executor/paginator.rs index 6907457e62..71b55bc45f 100644 --- a/src/executor/paginator.rs +++ b/src/executor/paginator.rs @@ -71,7 +71,12 @@ where SelectStatement::new() .expr(Expr::cust("COUNT(*) AS num_items")) .from_subquery( - self.query.clone().reset_limit().reset_offset().to_owned(), + self.query + .clone() + .reset_limit() + .reset_offset() + .clear_order_by() + .to_owned(), Alias::new("sub_query"), ), ); From f54683d3653b064ba35118aa6a56e5309ed23af8 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Wed, 10 Jan 2024 16:21:22 +0000 Subject: [PATCH 0251/1371] Refactor/fix clippy errors (#2056) (#2057) * chore: add clippy config file * refactor: fix clippy errors and wornings of runtime-async-std-native-tls,sqlx-all * refactor: fix clippy errors and wornings of sqlx-sqlite, sqlx-mysql, sqlx-postgres * chore: format * refactor: fix clippy * fix: import path * refactor: fix clippy errors and wornings of sqlx-sqlite, sqlx-mysql, sqlx-postgres * fix: revert some space and comma removal * fix: revert some space and comma removal * refactor: add feature flag * fix: import path * test: remove mismatch feature flag * test: remove mismatch feature flag * chore: add proper feature flag * chore: remove feature flag * refactor: remove clippy.toml file * fix: re-export driver * fix: re-export JoinType * fix: remove feature flag * chore: add #[allow(unused_imports)] for driver Co-authored-by: Shogo Nakano <61229807+shogo-nakano-desu@users.noreply.github.com> --- sea-orm-macros/src/strum/helpers/mod.rs | 1 - src/driver/mock.rs | 4 ++++ src/lib.rs | 1 + src/query/combine.rs | 1 - src/query/mod.rs | 1 - src/query/select.rs | 1 - tests/active_enum_tests.rs | 21 ++++++++++++--------- tests/common/features/json_vec.rs | 6 +----- tests/common/setup/mod.rs | 1 - tests/cursor_tests.rs | 11 ++++------- tests/empty_insert_tests.rs | 3 +-- tests/returning_tests.rs | 2 +- 12 files changed, 24 insertions(+), 29 deletions(-) diff --git a/sea-orm-macros/src/strum/helpers/mod.rs b/sea-orm-macros/src/strum/helpers/mod.rs index 77049eb4b6..5f076579be 100644 --- a/sea-orm-macros/src/strum/helpers/mod.rs +++ b/sea-orm-macros/src/strum/helpers/mod.rs @@ -1,4 +1,3 @@ -pub use self::case_style::CaseStyleHelpers; pub use self::type_props::HasTypeProperties; pub use self::variant_props::HasStrumVariantProperties; diff --git a/src/driver/mock.rs b/src/driver/mock.rs index af3145cbb9..6b2fd08e6c 100644 --- a/src/driver/mock.rs +++ b/src/driver/mock.rs @@ -117,6 +117,10 @@ impl MockDatabaseConnection { } /// Get the [DatabaseBackend](crate::DatabaseBackend) being used by the [MockDatabase] + /// + /// # Panics + /// + /// Will panic if the lock cannot be acquired. pub fn get_database_backend(&self) -> DbBackend { self.mocker .lock() diff --git a/src/lib.rs b/src/lib.rs index 4026b5636d..65e0d1cac6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -345,6 +345,7 @@ pub mod tests_cfg; mod util; pub use database::*; +#[allow(unused_imports)] pub use driver::*; pub use entity::*; pub use error::*; diff --git a/src/query/combine.rs b/src/query/combine.rs index 61d957f74f..0f2d0290c5 100644 --- a/src/query/combine.rs +++ b/src/query/combine.rs @@ -2,7 +2,6 @@ use crate::{ ColumnTrait, EntityTrait, IdenStatic, Iterable, QueryTrait, Select, SelectTwo, SelectTwoMany, }; use core::marker::PhantomData; -pub use sea_query::JoinType; use sea_query::{Alias, ColumnRef, Iden, Order, SeaRc, SelectExpr, SelectStatement, SimpleExpr}; macro_rules! select_def { diff --git a/src/query/mod.rs b/src/query/mod.rs index 559eba1763..227ba48c01 100644 --- a/src/query/mod.rs +++ b/src/query/mod.rs @@ -15,7 +15,6 @@ pub use combine::{SelectA, SelectB}; pub use delete::*; pub use helper::*; pub use insert::*; -pub use join::*; #[cfg(feature = "with-json")] pub use json::*; pub use loader::*; diff --git a/src/query/select.rs b/src/query/select.rs index f747fb582d..4ac04aeb9e 100644 --- a/src/query/select.rs +++ b/src/query/select.rs @@ -1,7 +1,6 @@ use crate::{ColumnTrait, EntityTrait, Iterable, QueryFilter, QueryOrder, QuerySelect, QueryTrait}; use core::fmt::Debug; use core::marker::PhantomData; -pub use sea_query::JoinType; use sea_query::{Expr, IntoColumnRef, SelectStatement, SimpleExpr}; /// Defines a structure to perform select operations diff --git a/tests/active_enum_tests.rs b/tests/active_enum_tests.rs index 1a38f3b712..81daddd600 100644 --- a/tests/active_enum_tests.rs +++ b/tests/active_enum_tests.rs @@ -1,14 +1,15 @@ pub mod common; -use active_enum::Entity as ActiveEnum; -use active_enum_child::Entity as ActiveEnumChild; +use active_enum::Entity as ActiveEnumEntity; pub use common::{features::*, setup::*, TestContext}; use pretty_assertions::assert_eq; +#[cfg(feature = "sqlx-postgres")] +use sea_orm::QueryTrait; use sea_orm::{ entity::prelude::*, entity::*, sea_query::{BinOper, Expr}, - ActiveEnum as ActiveEnumTrait, DatabaseConnection, QueryTrait, + ActiveEnum as ActiveEnumTrait, DatabaseConnection, }; #[sea_orm_macros::test] @@ -146,6 +147,7 @@ pub async fn insert_active_enum(db: &DatabaseConnection) -> Result<(), DbErr> { let select_with_tea_not_in = Entity::find() .filter(Column::Tea.is_not_null()) .filter(Column::Tea.is_not_in([Tea::BreakfastTea])); + #[cfg(feature = "sqlx-postgres")] assert_eq!( select_with_tea_not_in @@ -162,6 +164,7 @@ pub async fn insert_active_enum(db: &DatabaseConnection) -> Result<(), DbErr> { ] .join(" ") ); + assert_eq!(model, select_with_tea_not_in.one(db).await?.unwrap()); let res = model.delete(db).await?; @@ -338,7 +341,7 @@ pub async fn find_related_active_enum(db: &DatabaseConnection) -> Result<(), DbE }] ); assert_eq!( - ActiveEnum::find() + ActiveEnumEntity::find() .find_with_related(ActiveEnumChild) .all(db) .await?, @@ -359,7 +362,7 @@ pub async fn find_related_active_enum(db: &DatabaseConnection) -> Result<(), DbE )] ); assert_eq!( - ActiveEnum::find() + ActiveEnumEntity::find() .find_also_related(ActiveEnumChild) .all(db) .await?, @@ -464,7 +467,7 @@ pub async fn find_linked_active_enum(db: &DatabaseConnection) -> Result<(), DbEr }] ); assert_eq!( - ActiveEnum::find() + ActiveEnumEntity::find() .find_also_linked(active_enum::ActiveEnumChildLink) .all(db) .await?, @@ -485,7 +488,7 @@ pub async fn find_linked_active_enum(db: &DatabaseConnection) -> Result<(), DbEr )] ); assert_eq!( - ActiveEnum::find() + ActiveEnumEntity::find() .find_with_linked(active_enum::ActiveEnumChildLink) .all(db) .await?, @@ -620,7 +623,7 @@ mod tests { .join(" ") ); - let _select = ActiveEnum::find().find_also_related(ActiveEnumChild); + let _select = ActiveEnumEntity::find().find_also_related(ActiveEnumChild); #[cfg(any(feature = "sqlx-mysql", feature = "sqlx-sqlite"))] { assert_eq!( @@ -707,7 +710,7 @@ mod tests { .join(" ") ); - let _select = ActiveEnum::find().find_also_linked(active_enum::ActiveEnumChildLink); + let _select = ActiveEnumEntity::find().find_also_linked(active_enum::ActiveEnumChildLink); #[cfg(any(feature = "sqlx-mysql", feature = "sqlx-sqlite"))] { assert_eq!( diff --git a/tests/common/features/json_vec.rs b/tests/common/features/json_vec.rs index 5eb6234964..a1263b79e5 100644 --- a/tests/common/features/json_vec.rs +++ b/tests/common/features/json_vec.rs @@ -22,11 +22,7 @@ impl TryGetableFromJson for StringVec {} impl From for Value { fn from(source: StringVec) -> Self { - sea_orm::Value::Json( - serde_json::to_value(&source) - .ok() - .map(|s| std::boxed::Box::new(s)), - ) + sea_orm::Value::Json(serde_json::to_value(source).ok().map(std::boxed::Box::new)) } } diff --git a/tests/common/setup/mod.rs b/tests/common/setup/mod.rs index 2cdc00d210..2385cfb0c4 100644 --- a/tests/common/setup/mod.rs +++ b/tests/common/setup/mod.rs @@ -74,7 +74,6 @@ pub async fn tear_down(base_url: &str, db_name: &str) { format!("DROP DATABASE IF EXISTS \"{db_name}\";"), )) .await; - } else { }; } diff --git a/tests/cursor_tests.rs b/tests/cursor_tests.rs index ef82805736..6b90c34f4f 100644 --- a/tests/cursor_tests.rs +++ b/tests/cursor_tests.rs @@ -2,9 +2,7 @@ pub mod common; pub use common::{features::*, setup::*, TestContext}; use pretty_assertions::assert_eq; -use sea_orm::{ - entity::prelude::*, DerivePartialModel, FromQueryResult, QueryOrder, QuerySelect, Set, -}; +use sea_orm::{entity::prelude::*, DerivePartialModel, FromQueryResult, QuerySelect, Set}; use serde_json::json; #[sea_orm_macros::test] @@ -18,7 +16,7 @@ async fn main() -> Result<(), DbErr> { create_tables(&ctx.db).await?; create_insert_default(&ctx.db).await?; cursor_pagination(&ctx.db).await?; - schema::create_tables(&ctx.db).await?; + bakery_chain_schema::create_tables(&ctx.db).await?; create_baker_cake(&ctx.db).await?; cursor_related_pagination(&ctx.db).await?; ctx.delete().await; @@ -281,7 +279,8 @@ pub async fn cursor_pagination(db: &DatabaseConnection) -> Result<(), DbErr> { } use common::bakery_chain::{ - baker, bakery, cake, cakes_bakers, schema, Baker, Bakery, Cake, CakesBakers, + baker, bakery, cake, cakes_bakers, schema as bakery_chain_schema, Baker, Bakery, Cake, + CakesBakers, }; fn bakery(i: i32) -> bakery::Model { @@ -318,8 +317,6 @@ fn cakebaker(cake: char, baker: char) -> CakeBakerlite { } pub async fn create_baker_cake(db: &DatabaseConnection) -> Result<(), DbErr> { - use sea_orm::IntoActiveModel; - let mut bakeries: Vec = vec![]; // bakeries named from 1 to 10 for i in 1..=10 { diff --git a/tests/empty_insert_tests.rs b/tests/empty_insert_tests.rs index ea61a37fba..6bc01f3a1c 100644 --- a/tests/empty_insert_tests.rs +++ b/tests/empty_insert_tests.rs @@ -9,7 +9,6 @@ pub use sea_orm::{ pub use crud::*; // use common::bakery_chain::*; use sea_orm::{DbConn, TryInsertResult}; -use sea_query::OnConflict; #[sea_orm_macros::test] #[cfg(any( @@ -38,7 +37,7 @@ pub async fn test(db: &DbConn) { assert!(matches!(res, Ok(TryInsertResult::Inserted(_)))); - let double_seaside_bakery = bakery::ActiveModel { + let _double_seaside_bakery = bakery::ActiveModel { name: Set("SeaSide Bakery".to_owned()), profit_margin: Set(10.4), id: Set(1), diff --git a/tests/returning_tests.rs b/tests/returning_tests.rs index bcdcc38d6b..68abbf7750 100644 --- a/tests/returning_tests.rs +++ b/tests/returning_tests.rs @@ -79,7 +79,7 @@ async fn main() -> Result<(), DbErr> { should_panic(expected = "Database backend doesn't support RETURNING") )] async fn update_many() { - pub use common::{features::*, setup::*, TestContext}; + pub use common::{features::*, TestContext}; use edit_log::*; let run = || async { From 358e31a09b8d1b07ef7ef4d7bbf4df8261db8741 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Wed, 10 Jan 2024 11:05:00 +0000 Subject: [PATCH 0252/1371] Changelog --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef8de6d98b..b4389175e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## 0.12.11 - Pending + +### Enhancements + +* Improve query performance of `Paginator`'s `COUNT` query https://github.com/SeaQL/sea-orm/pull/2030 + +### House keeping + +* Fix clippy warnings on 1.75 https://github.com/SeaQL/sea-orm/pull/2057 + ## 0.12.10 - 2023-12-14 ### New Features From f019d6ab22935bdb39faa63c6dacc64240bc9dbf Mon Sep 17 00:00:00 2001 From: Anshul Sanghi Date: Fri, 12 Jan 2024 16:46:24 +0530 Subject: [PATCH 0253/1371] Add Ability To Configure SQLx Slow Statement Logging Settings (#2055) * Add Ability To Configure SQLx Slow Statement Logging Settings * Disable SQLX Slow Statement Logging By Default --- src/database/mod.rs | 26 ++++++++++++++++++++++++++ src/driver/sqlx_mysql.rs | 7 +++++++ src/driver/sqlx_postgres.rs | 7 +++++++ src/driver/sqlx_sqlite.rs | 7 +++++++ 4 files changed, 47 insertions(+) diff --git a/src/database/mod.rs b/src/database/mod.rs index a844d1c85d..1cd41a53d7 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -54,6 +54,10 @@ pub struct ConnectOptions { pub(crate) sqlx_logging: bool, /// SQLx statement logging level (ignored if `sqlx_logging` is false) pub(crate) sqlx_logging_level: log::LevelFilter, + /// SQLx slow statements logging level (ignored if `sqlx_logging` is false) + pub(crate) sqlx_slow_statements_logging_level: log::LevelFilter, + /// SQLx slow statements duration threshold (ignored if `sqlx_logging` is false) + pub(crate) sqlx_slow_statements_logging_threshold: Duration, /// set sqlcipher key pub(crate) sqlcipher_key: Option>, /// Schema search path (PostgreSQL only) @@ -147,6 +151,8 @@ impl ConnectOptions { max_lifetime: None, sqlx_logging: true, sqlx_logging_level: log::LevelFilter::Info, + sqlx_slow_statements_logging_level: log::LevelFilter::Off, + sqlx_slow_statements_logging_threshold: Duration::from_secs(1), sqlcipher_key: None, schema_search_path: None, } @@ -269,11 +275,31 @@ impl ConnectOptions { self } + /// Set SQLx slow statements logging level and duration threshold + /// (ignored if `sqlx_logging` is `false`) + pub fn sqlx_slow_statements_logging_settings( + &mut self, + level: log::LevelFilter, + duration: Duration, + ) -> &mut Self { + self.sqlx_slow_statements_logging_level = level; + self.sqlx_slow_statements_logging_threshold = duration; + self + } + /// Get the level of SQLx statement logging pub fn get_sqlx_logging_level(&self) -> log::LevelFilter { self.sqlx_logging_level } + /// Get the SQLx slow statements logging settings + pub fn get_sqlx_slow_statements_logging_settings(&self) -> (log::LevelFilter, Duration) { + ( + self.sqlx_slow_statements_logging_level, + self.sqlx_slow_statements_logging_threshold, + ) + } + /// set key for sqlcipher pub fn sqlcipher_key(&mut self, value: T) -> &mut Self where diff --git a/src/driver/sqlx_mysql.rs b/src/driver/sqlx_mysql.rs index 51a1842a76..0385b76add 100644 --- a/src/driver/sqlx_mysql.rs +++ b/src/driver/sqlx_mysql.rs @@ -1,3 +1,4 @@ +use log::LevelFilter; use sea_query::Values; use std::{future::Future, pin::Pin, sync::Arc}; @@ -52,6 +53,12 @@ impl SqlxMySqlConnector { opt = opt.disable_statement_logging(); } else { opt = opt.log_statements(options.sqlx_logging_level); + if options.sqlx_slow_statements_logging_level != LevelFilter::Off { + opt = opt.log_slow_statements( + options.sqlx_slow_statements_logging_level, + options.sqlx_slow_statements_logging_threshold, + ); + } } match options.pool_options().connect_with(opt).await { Ok(pool) => Ok(DatabaseConnection::SqlxMySqlPoolConnection( diff --git a/src/driver/sqlx_postgres.rs b/src/driver/sqlx_postgres.rs index 1214f6c29c..b623c60b76 100644 --- a/src/driver/sqlx_postgres.rs +++ b/src/driver/sqlx_postgres.rs @@ -1,3 +1,4 @@ +use log::LevelFilter; use sea_query::Values; use std::{future::Future, pin::Pin, sync::Arc}; @@ -52,6 +53,12 @@ impl SqlxPostgresConnector { opt = opt.disable_statement_logging(); } else { opt = opt.log_statements(options.sqlx_logging_level); + if options.sqlx_slow_statements_logging_level != LevelFilter::Off { + opt = opt.log_slow_statements( + options.sqlx_slow_statements_logging_level, + options.sqlx_slow_statements_logging_threshold, + ); + } } let set_search_path_sql = options .schema_search_path diff --git a/src/driver/sqlx_sqlite.rs b/src/driver/sqlx_sqlite.rs index 8a9924cf56..1755b46fed 100644 --- a/src/driver/sqlx_sqlite.rs +++ b/src/driver/sqlx_sqlite.rs @@ -1,3 +1,4 @@ +use log::LevelFilter; use sea_query::Values; use std::{future::Future, pin::Pin, sync::Arc}; @@ -56,6 +57,12 @@ impl SqlxSqliteConnector { opt = opt.disable_statement_logging(); } else { opt = opt.log_statements(options.sqlx_logging_level); + if options.sqlx_slow_statements_logging_level != LevelFilter::Off { + opt = opt.log_slow_statements( + options.sqlx_slow_statements_logging_level, + options.sqlx_slow_statements_logging_threshold, + ); + } } if options.get_max_connections().is_none() { options.max_connections(1); From 914ae2e1661aa91fb89e9ba7f5b6808ab513a9b7 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Fri, 12 Jan 2024 11:16:48 +0000 Subject: [PATCH 0254/1371] Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4389175e8..2891f5fdb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Enhancements * Improve query performance of `Paginator`'s `COUNT` query https://github.com/SeaQL/sea-orm/pull/2030 +* Added SQLx slow statements logging to `ConnectOptions` https://github.com/SeaQL/sea-orm/pull/2055 ### House keeping From a73f69957836919ba7cf64b687c32eb2b7b90faf Mon Sep 17 00:00:00 2001 From: Dmitrii Aleksandrov Date: Fri, 12 Jan 2024 18:42:29 +0400 Subject: [PATCH 0255/1371] Cast enums in `lt()`, `lte()`, `gt()`, `gte()` (#2014) * Cast enums in lt, lte, gt, gte (revert 10f3de0, discussed under #1527) * Add tests for enum order comparisons --- src/entity/column.rs | 24 +++++------------- tests/active_enum_tests.rs | 51 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 18 deletions(-) diff --git a/src/entity/column.rs b/src/entity/column.rs index 4f895f0de2..b59f410e73 100644 --- a/src/entity/column.rs +++ b/src/entity/column.rs @@ -20,18 +20,6 @@ pub struct ColumnDef { } macro_rules! bind_oper { - ( $op: ident ) => { - #[allow(missing_docs)] - fn $op(&self, v: V) -> SimpleExpr - where - V: Into, - { - Expr::col((self.entity_name(), *self)).$op(v) - } - }; -} - -macro_rules! bind_oper_with_enum_casting { ( $op: ident, $bin_op: ident ) => { #[allow(missing_docs)] fn $op(&self, v: V) -> SimpleExpr @@ -97,12 +85,12 @@ pub trait ColumnTrait: IdenStatic + Iterable + FromStr { (self.entity_name(), SeaRc::new(*self) as DynIden) } - bind_oper_with_enum_casting!(eq, Equal); - bind_oper_with_enum_casting!(ne, NotEqual); - bind_oper!(gt); - bind_oper!(gte); - bind_oper!(lt); - bind_oper!(lte); + bind_oper!(eq, Equal); + bind_oper!(ne, NotEqual); + bind_oper!(gt, GreaterThan); + bind_oper!(gte, GreaterThanOrEqual); + bind_oper!(lt, SmallerThan); + bind_oper!(lte, SmallerThanOrEqual); /// ``` /// use sea_orm::{entity::*, query::*, tests_cfg::cake, DbBackend}; diff --git a/tests/active_enum_tests.rs b/tests/active_enum_tests.rs index 81daddd600..f31663fad2 100644 --- a/tests/active_enum_tests.rs +++ b/tests/active_enum_tests.rs @@ -167,6 +167,57 @@ pub async fn insert_active_enum(db: &DatabaseConnection) -> Result<(), DbErr> { assert_eq!(model, select_with_tea_not_in.one(db).await?.unwrap()); + // String enums should be compared alphabetically in all supported DBs. + // 'B' < 'S', so Big is considered "smaller" than Small. + assert_eq!( + model, + Entity::find() + .filter(Column::Category.lt(Category::Small)) + .one(db) + .await? + .unwrap() + ); + + // Integer enums should be compared by value in all supported DBs. + // 0 <= 1, so Black is considered "smaller or equal to" White. + assert_eq!( + model, + Entity::find() + .filter(Column::Color.lte(Color::White)) + .one(db) + .await? + .unwrap() + ); + + // Native enum comparisons are not portable. + // + // Postgres enums are compared by their definition order + // (see https://www.postgresql.org/docs/current/datatype-enum.html#DATATYPE-ENUM-ORDERING). + // Tea was defined as ('EverydayTea', 'BreakfastTea'), so EverydayTea is considered "smaller" than BreakfastTea. + // + // SQLite doesn't support enum types and SeaORM works around this limitation by storing them as strings. + // When treated as strings, EverydayTea is not "smaller" than BreakfastTea! + // + // MySQL should be the same as Postgres (see https://dev.mysql.com/doc/refman/8.0/en/enum.html#enum-sorting), + // but in practice this test case behaves like SQLite. I'm not sure why. + #[cfg(feature = "sqlx-postgres")] + assert_eq!( + model, + Entity::find() + .filter(Column::Tea.lt(Tea::BreakfastTea)) + .one(db) + .await? + .unwrap() + ); + #[cfg(any(feature = "sqlx-mysql", feature = "sqlx-sqlite"))] + assert_eq!( + None, + Entity::find() + .filter(Column::Tea.lt(Tea::BreakfastTea)) + .one(db) + .await? + ); + let res = model.delete(db).await?; assert_eq!(res.rows_affected, 1); From e3c94a7708540e78ae7916eaebecdfdfe1050c51 Mon Sep 17 00:00:00 2001 From: Dmitrii Aleksandrov Date: Fri, 12 Jan 2024 18:43:13 +0400 Subject: [PATCH 0256/1371] Export `main()` of `sea-orm-cli` (#1889) (#2034) * Export main() of sea-orm-cli (#1889) * Fix compile error: require "codegen" feature for main() --- sea-orm-cli/src/bin/main.rs | 30 +----------------------------- sea-orm-cli/src/bin/sea.rs | 30 +----------------------------- sea-orm-cli/src/cli.rs | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 58 deletions(-) diff --git a/sea-orm-cli/src/bin/main.rs b/sea-orm-cli/src/bin/main.rs index 3a4e28ac85..96e7c42b1e 100644 --- a/sea-orm-cli/src/bin/main.rs +++ b/sea-orm-cli/src/bin/main.rs @@ -1,32 +1,4 @@ -use clap::Parser; -use dotenvy::dotenv; -use sea_orm_cli::{handle_error, run_generate_command, run_migrate_command, Cli, Commands}; - #[async_std::main] async fn main() { - dotenv().ok(); - - let cli = Cli::parse(); - let verbose = cli.verbose; - - match cli.command { - Commands::Generate { command } => { - run_generate_command(command, verbose) - .await - .unwrap_or_else(handle_error); - } - Commands::Migrate { - migration_dir, - database_schema, - database_url, - command, - } => run_migrate_command( - command, - &migration_dir, - database_schema, - database_url, - verbose, - ) - .unwrap_or_else(handle_error), - } + sea_orm_cli::main().await } diff --git a/sea-orm-cli/src/bin/sea.rs b/sea-orm-cli/src/bin/sea.rs index cef653910c..f2a1f18e6e 100644 --- a/sea-orm-cli/src/bin/sea.rs +++ b/sea-orm-cli/src/bin/sea.rs @@ -1,34 +1,6 @@ //! COPY FROM bin/main.rs -use clap::Parser; -use dotenvy::dotenv; -use sea_orm_cli::{handle_error, run_generate_command, run_migrate_command, Cli, Commands}; - #[async_std::main] async fn main() { - dotenv().ok(); - - let cli = Cli::parse(); - let verbose = cli.verbose; - - match cli.command { - Commands::Generate { command } => { - run_generate_command(command, verbose) - .await - .unwrap_or_else(handle_error); - } - Commands::Migrate { - migration_dir, - database_schema, - database_url, - command, - } => run_migrate_command( - command, - &migration_dir, - database_schema, - database_url, - verbose, - ) - .unwrap_or_else(handle_error), - } + sea_orm_cli::main().await } diff --git a/sea-orm-cli/src/cli.rs b/sea-orm-cli/src/cli.rs index e8611373e2..79af1190f9 100644 --- a/sea-orm-cli/src/cli.rs +++ b/sea-orm-cli/src/cli.rs @@ -1,4 +1,9 @@ use clap::{ArgGroup, Parser, Subcommand, ValueEnum}; +#[cfg(feature = "codegen")] +use dotenvy::dotenv; + +#[cfg(feature = "codegen")] +use crate::{handle_error, run_generate_command, run_migrate_command}; #[derive(Parser, Debug)] #[command( @@ -309,3 +314,34 @@ pub enum DateTimeCrate { Chrono, Time, } + +/// Use this to build a local, version-controlled `sea-orm-cli` in dependent projects +/// (see [example use case](https://github.com/SeaQL/sea-orm/discussions/1889)). +#[cfg(feature = "codegen")] +pub async fn main() { + dotenv().ok(); + + let cli = Cli::parse(); + let verbose = cli.verbose; + + match cli.command { + Commands::Generate { command } => { + run_generate_command(command, verbose) + .await + .unwrap_or_else(handle_error); + } + Commands::Migrate { + migration_dir, + database_schema, + database_url, + command, + } => run_migrate_command( + command, + &migration_dir, + database_schema, + database_url, + verbose, + ) + .unwrap_or_else(handle_error), + } +} From 13d383e43ce06a8e75d12cbf84a1cdb307e7f85e Mon Sep 17 00:00:00 2001 From: bobogei81123 <74331647+bobogei81123@users.noreply.github.com> Date: Sun, 14 Jan 2024 07:06:40 -0800 Subject: [PATCH 0257/1371] fix: fully qualify types in DeriveValueType macro (#2054) `DeriveValueType` proc macro relies on several sea-orm types to be imported. This is partially fixed in #1855 by fully qualifying `sea_orm::QueryResult`, but others like `sea_orm::Value` is still not being qualified. Also, if use defined a type alias on `std::result::Result`, it can cause the proc macro to generate code that won't compile. - Fully qualify `sea_orm::Value`, `std::string::String`, and `std::result::Result` in `DeriveValueType` proc macro. - Add some simple integration tests for this. - Add doc and doc test in `sea_orm_macro::derive_value_type`. --- sea-orm-macros/src/derives/value_type.rs | 9 +++++---- sea-orm-macros/src/lib.rs | 13 +++++++++++++ sea-orm-macros/tests/derive_value_type_test.rs | 13 +++++++++++++ 3 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 sea-orm-macros/tests/derive_value_type_test.rs diff --git a/sea-orm-macros/src/derives/value_type.rs b/sea-orm-macros/src/derives/value_type.rs index e6cee9e6b9..286a9f732d 100644 --- a/sea-orm-macros/src/derives/value_type.rs +++ b/sea-orm-macros/src/derives/value_type.rs @@ -100,7 +100,7 @@ impl DeriveValueType { quote!( #[automatically_derived] - impl std::convert::From<#name> for Value { + impl std::convert::From<#name> for sea_orm::Value { fn from(source: #name) -> Self { source.0.into() } @@ -108,18 +108,19 @@ impl DeriveValueType { #[automatically_derived] impl sea_orm::TryGetable for #name { - fn try_get_by(res: &sea_orm::QueryResult, idx: I) -> Result { + fn try_get_by(res: &sea_orm::QueryResult, idx: I) + -> std::result::Result { <#field_type as sea_orm::TryGetable>::try_get_by(res, idx).map(|v| #name(v)) } } #[automatically_derived] impl sea_orm::sea_query::ValueType for #name { - fn try_from(v: Value) -> Result { + fn try_from(v: sea_orm::Value) -> std::result::Result { <#field_type as sea_orm::sea_query::ValueType>::try_from(v).map(|v| #name(v)) } - fn type_name() -> String { + fn type_name() -> std::string::String { stringify!(#name).to_owned() } diff --git a/sea-orm-macros/src/lib.rs b/sea-orm-macros/src/lib.rs index 52f2e734ab..6d2db2d08f 100644 --- a/sea-orm-macros/src/lib.rs +++ b/sea-orm-macros/src/lib.rs @@ -838,6 +838,19 @@ pub fn enum_iter(input: TokenStream) -> TokenStream { .into() } +/// Implements traits for types that wrap a database value type. +/// +/// This procedure macro implements `From for Value`, `sea_orm::TryGetTable`, and +/// `sea_query::ValueType` for the wrapper type `T`. +/// +/// ## Usage +/// +/// ```rust +/// use sea_orm::DeriveValueType; +/// +/// #[derive(DeriveValueType)] +/// struct MyString(String); +/// ``` #[cfg(feature = "derive")] #[proc_macro_derive(DeriveValueType, attributes(sea_orm))] pub fn derive_value_type(input: TokenStream) -> TokenStream { diff --git a/sea-orm-macros/tests/derive_value_type_test.rs b/sea-orm-macros/tests/derive_value_type_test.rs new file mode 100644 index 0000000000..b1de7f15e4 --- /dev/null +++ b/sea-orm-macros/tests/derive_value_type_test.rs @@ -0,0 +1,13 @@ +#[test] +fn when_user_import_nothing_macro_still_works_test() { + #[derive(sea_orm::DeriveValueType)] + struct MyString(String); +} + +#[test] +fn when_user_alias_result_macro_still_works_test() { + #[allow(dead_code)] + type Result = std::result::Result; + #[derive(sea_orm::DeriveValueType)] + struct MyString(String); +} From 5f9c4500270744843bbea63f1ac81517cbfca0f5 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sun, 14 Jan 2024 15:05:14 +0000 Subject: [PATCH 0258/1371] Changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2891f5fdb1..bb7c6fc5e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). * Fix clippy warnings on 1.75 https://github.com/SeaQL/sea-orm/pull/2057 +### Bug Fixes + +* [sea-orm-macro] Qualify types in `DeriveValueType` macro https://github.com/SeaQL/sea-orm/pull/2054 + ## 0.12.10 - 2023-12-14 ### New Features From c5adb9d1d1627b5bd1fe9a5016f45e4a7587398e Mon Sep 17 00:00:00 2001 From: Maksim Zaborovskiy Date: Sun, 14 Jan 2024 16:23:55 +0100 Subject: [PATCH 0259/1371] Add cursor pagination desc order (#2037) * feat: add cursor pagination desc order * feat: add cursor pagination desc order * test: add cursor integration tests * test: add cursor integration tests * test: add query cursor_by tests * test: add query cursor_by tests * test: add query cursor_by tests * test: add query cursor_by tests * test: add query cursor_by tests * test: add query cursor_by sort tests * chore: remove unused import --- src/executor/cursor.rs | 1054 ++++++++++++++++++++++++++++++++++++++-- tests/cursor_tests.rs | 413 +++++++++++++++- 2 files changed, 1428 insertions(+), 39 deletions(-) diff --git a/src/executor/cursor.rs b/src/executor/cursor.rs index 50f282ef46..78d86a7aec 100644 --- a/src/executor/cursor.rs +++ b/src/executor/cursor.rs @@ -23,7 +23,12 @@ where table: DynIden, order_columns: Identity, secondary_order_by: Vec<(DynIden, Identity)>, - last: bool, + first: Option, + last: Option, + before: Option, + after: Option, + sort_asc: bool, + is_result_reversed: bool, phantom: PhantomData, } @@ -40,7 +45,12 @@ where query, table, order_columns: order_columns.into_identity(), - last: false, + last: None, + first: None, + after: None, + before: None, + sort_asc: true, + is_result_reversed: false, phantom: PhantomData, secondary_order_by: Default::default(), } @@ -51,10 +61,7 @@ where where V: IntoValueTuple, { - let condition = self.apply_filter(values, |c, v| { - Expr::col((SeaRc::clone(&self.table), SeaRc::clone(c))).lt(v) - }); - self.query.cond_where(condition); + self.before = Some(values.into_value_tuple()); self } @@ -63,19 +70,43 @@ where where V: IntoValueTuple, { - let condition = self.apply_filter(values, |c, v| { - Expr::col((SeaRc::clone(&self.table), SeaRc::clone(c))).gt(v) - }); - self.query.cond_where(condition); + self.after = Some(values.into_value_tuple()); self } - fn apply_filter(&self, values: V, f: F) -> Condition + fn apply_filters(&mut self) -> &mut Self { + if let Some(values) = self.after.clone() { + let condition = self.apply_filter(values, |c, v| { + let exp = Expr::col((SeaRc::clone(&self.table), SeaRc::clone(c))); + if self.sort_asc { + exp.gt(v) + } else { + exp.lt(v) + } + }); + self.query.cond_where(condition); + } + + if let Some(values) = self.before.clone() { + let condition = self.apply_filter(values, |c, v| { + let exp = Expr::col((SeaRc::clone(&self.table), SeaRc::clone(c))); + if self.sort_asc { + exp.lt(v) + } else { + exp.gt(v) + } + }); + self.query.cond_where(condition); + } + + self + } + + fn apply_filter(&self, values: ValueTuple, f: F) -> Condition where - V: IntoValueTuple, F: Fn(&DynIden, Value) -> SimpleExpr, { - match (&self.order_columns, values.into_value_tuple()) { + match (&self.order_columns, values) { (Identity::Unary(c1), ValueTuple::One(v1)) => Condition::all().add(f(c1, v1)), (Identity::Binary(c1, c2), ValueTuple::Two(v1, v2)) => Condition::any() .add( @@ -159,26 +190,65 @@ where } } + /// Use ascending sort order + pub fn asc(&mut self) -> &mut Self { + self.sort_asc = true; + self + } + + /// Use descending sort order + pub fn desc(&mut self) -> &mut Self { + self.sort_asc = false; + self + } + /// Limit result set to only first N rows in ascending order of the order by column pub fn first(&mut self, num_rows: u64) -> &mut Self { - self.query.limit(num_rows).clear_order_by(); - self.apply_order_by(self.table.clone(), Order::Asc); - self.last = false; + self.last = None; + self.first = Some(num_rows); self } /// Limit result set to only last N rows in ascending order of the order by column pub fn last(&mut self, num_rows: u64) -> &mut Self { - self.query.limit(num_rows).clear_order_by(); - self.apply_order_by(self.table.clone(), Order::Desc); - self.last = true; + self.first = None; + self.last = Some(num_rows); self } - fn apply_order_by(&mut self, table: DynIden, ord: Order) { + fn resolve_sort_order(&mut self) -> Order { + let should_reverse_order = self.last.is_some(); + + if should_reverse_order { + self.is_result_reversed = true; + } else { + self.is_result_reversed = false; + } + + if (self.sort_asc && !should_reverse_order) || (!self.sort_asc && should_reverse_order) { + Order::Asc + } else { + Order::Desc + } + } + + fn apply_limit(&mut self) -> &mut Self { + if let Some(num_rows) = self.first { + self.query.limit(num_rows); + } else if let Some(num_rows) = self.last { + self.query.limit(num_rows); + } + + self + } + + fn apply_order_by(&mut self) -> &mut Self { + self.query.clear_order_by(); + let ord = self.resolve_sort_order(); + let query = &mut self.query; let order = |query: &mut SelectStatement, col| { - query.order_by((SeaRc::clone(&table), SeaRc::clone(col)), ord.clone()); + query.order_by((SeaRc::clone(&self.table), SeaRc::clone(col)), ord.clone()); }; match &self.order_columns { Identity::Unary(c1) => { @@ -205,6 +275,8 @@ where query.order_by((tbl, c1), ord.clone()); }; } + + self } /// Fetch the paginated result @@ -212,13 +284,17 @@ where where C: ConnectionTrait, { + self.apply_limit(); + self.apply_order_by(); + self.apply_filters(); + let stmt = db.get_database_backend().build(&self.query); let rows = db.query_all(stmt).await?; let mut buffer = Vec::with_capacity(rows.len()); for row in rows.into_iter() { buffer.push(S::from_raw_query_result(row)?); } - if self.last { + if self.is_result_reversed { buffer.reverse() } Ok(buffer) @@ -234,6 +310,11 @@ where table: self.table, order_columns: self.order_columns, last: self.last, + first: self.first, + after: self.after, + before: self.before, + sort_asc: self.sort_asc, + is_result_reversed: self.is_result_reversed, phantom: PhantomData, secondary_order_by: self.secondary_order_by, } @@ -255,6 +336,11 @@ where table: self.table, order_columns: self.order_columns, last: self.last, + first: self.first, + after: self.after, + before: self.before, + sort_asc: self.sort_asc, + is_result_reversed: self.is_result_reversed, phantom: PhantomData, secondary_order_by: self.secondary_order_by, } @@ -438,6 +524,60 @@ mod tests { Ok(()) } + #[smol_potat::test] + async fn last_2_after_10_desc() -> Result<(), DbErr> { + use fruit::*; + + let mut models = [ + Model { + id: 1, + name: "Blueberry".into(), + cake_id: Some(1), + }, + Model { + id: 2, + name: "Rasberry".into(), + cake_id: Some(1), + }, + ]; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([models.clone()]) + .into_connection(); + + models.reverse(); + + assert_eq!( + Entity::find() + .cursor_by(Column::Id) + .after(10) + .last(2) + .desc() + .all(&db) + .await?, + models + ); + + assert_eq!( + db.into_transaction_log(), + [Transaction::many([Statement::from_sql_and_values( + DbBackend::Postgres, + [ + r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id""#, + r#"FROM "fruit""#, + r#"WHERE "fruit"."id" < $1"#, + r#"ORDER BY "fruit"."id" ASC"#, + r#"LIMIT $2"#, + ] + .join(" ") + .as_str(), + [10_i32.into(), 2_u64.into()] + ),])] + ); + + Ok(()) + } + #[smol_potat::test] async fn first_2_before_10_also_related_select() -> Result<(), DbErr> { let models = [ @@ -501,6 +641,72 @@ mod tests { Ok(()) } + #[smol_potat::test] + async fn last_2_after_10_also_related_select_desc() -> Result<(), DbErr> { + let mut models = [ + ( + cake::Model { + id: 2, + name: "Rasberry Cheese Cake".into(), + }, + Some(fruit::Model { + id: 10, + name: "Rasberry".into(), + cake_id: Some(1), + }), + ), + ( + cake::Model { + id: 1, + name: "Blueberry Cheese Cake".into(), + }, + Some(fruit::Model { + id: 9, + name: "Blueberry".into(), + cake_id: Some(1), + }), + ), + ]; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([models.clone()]) + .into_connection(); + + models.reverse(); + + assert_eq!( + cake::Entity::find() + .find_also_related(Fruit) + .cursor_by(cake::Column::Id) + .after(10) + .last(2) + .desc() + .all(&db) + .await?, + models + ); + + assert_eq!( + db.into_transaction_log(), + [Transaction::many([Statement::from_sql_and_values( + DbBackend::Postgres, + [ + r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#, + r#""fruit"."id" AS "B_id", "fruit"."name" AS "B_name", "fruit"."cake_id" AS "B_cake_id""#, + r#"FROM "cake""#, + r#"LEFT JOIN "fruit" ON "cake"."id" = "fruit"."cake_id""#, + r#"WHERE "cake"."id" < $1"#, + r#"ORDER BY "cake"."id" ASC, "fruit"."id" ASC LIMIT $2"#, + ] + .join(" ") + .as_str(), + [10_i32.into(), 2_u64.into()] + ),])] + ); + + Ok(()) + } + #[smol_potat::test] async fn first_2_before_10_also_related_select_cursor_other() -> Result<(), DbErr> { let models = [( @@ -551,6 +757,57 @@ mod tests { Ok(()) } + #[smol_potat::test] + async fn last_2_after_10_also_related_select_cursor_other_desc() -> Result<(), DbErr> { + let models = [( + cake::Model { + id: 1, + name: "Blueberry Cheese Cake".into(), + }, + Some(fruit::Model { + id: 9, + name: "Blueberry".into(), + cake_id: Some(1), + }), + )]; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([models.clone()]) + .into_connection(); + + assert_eq!( + cake::Entity::find() + .find_also_related(Fruit) + .cursor_by_other(fruit::Column::Id) + .after(10) + .last(2) + .desc() + .all(&db) + .await?, + models + ); + + assert_eq!( + db.into_transaction_log(), + [Transaction::many([Statement::from_sql_and_values( + DbBackend::Postgres, + [ + r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#, + r#""fruit"."id" AS "B_id", "fruit"."name" AS "B_name", "fruit"."cake_id" AS "B_cake_id""#, + r#"FROM "cake""#, + r#"LEFT JOIN "fruit" ON "cake"."id" = "fruit"."cake_id""#, + r#"WHERE "fruit"."id" < $1"#, + r#"ORDER BY "fruit"."id" ASC, "cake"."id" ASC LIMIT $2"#, + ] + .join(" ") + .as_str(), + [10_i32.into(), 2_u64.into()] + ),])] + ); + + Ok(()) + } + #[smol_potat::test] async fn first_2_before_10_also_linked_select() -> Result<(), DbErr> { let models = [ @@ -613,6 +870,71 @@ mod tests { Ok(()) } + #[smol_potat::test] + async fn last_2_after_10_also_linked_select_desc() -> Result<(), DbErr> { + let mut models = [ + ( + cake::Model { + id: 2, + name: "Rasberry Cheese Cake".into(), + }, + Some(vendor::Model { + id: 10, + name: "Rasberry".into(), + }), + ), + ( + cake::Model { + id: 1, + name: "Blueberry Cheese Cake".into(), + }, + Some(vendor::Model { + id: 9, + name: "Blueberry".into(), + }), + ), + ]; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([models.clone()]) + .into_connection(); + + models.reverse(); + + assert_eq!( + cake::Entity::find() + .find_also_linked(entity_linked::CakeToFillingVendor) + .cursor_by(cake::Column::Id) + .after(10) + .last(2) + .desc() + .all(&db) + .await?, + models + ); + + assert_eq!( + db.into_transaction_log(), + [Transaction::many([Statement::from_sql_and_values( + DbBackend::Postgres, + [ + r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#, + r#""r2"."id" AS "B_id", "r2"."name" AS "B_name""#, + r#"FROM "cake""#, + r#"LEFT JOIN "cake_filling" AS "r0" ON "cake"."id" = "r0"."cake_id""#, + r#"LEFT JOIN "filling" AS "r1" ON "r0"."filling_id" = "r1"."id""#, + r#"LEFT JOIN "vendor" AS "r2" ON "r1"."vendor_id" = "r2"."id""#, + r#"WHERE "cake"."id" < $1 ORDER BY "cake"."id" ASC, "vendor"."id" ASC LIMIT $2"#, + ] + .join(" ") + .as_str(), + [10_i32.into(), 2_u64.into()] + ),])] + ); + + Ok(()) + } + #[smol_potat::test] async fn first_2_before_10_also_linked_select_cursor_other() -> Result<(), DbErr> { let models = [( @@ -664,9 +986,62 @@ mod tests { } #[smol_potat::test] - async fn last_2_after_10() -> Result<(), DbErr> { - use fruit::*; - + async fn last_2_after_10_also_linked_select_cursor_other_desc() -> Result<(), DbErr> { + let mut models = [( + cake::Model { + id: 1, + name: "Blueberry Cheese Cake".into(), + }, + Some(vendor::Model { + id: 9, + name: "Blueberry".into(), + }), + )]; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([models.clone()]) + .into_connection(); + + models.reverse(); + + assert_eq!( + cake::Entity::find() + .find_also_linked(entity_linked::CakeToFillingVendor) + .cursor_by_other(vendor::Column::Id) + .after(10) + .last(2) + .desc() + .all(&db) + .await?, + models + ); + + assert_eq!( + db.into_transaction_log(), + [Transaction::many([Statement::from_sql_and_values( + DbBackend::Postgres, + [ + r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#, + r#""r2"."id" AS "B_id", "r2"."name" AS "B_name""#, + r#"FROM "cake""#, + r#"LEFT JOIN "cake_filling" AS "r0" ON "cake"."id" = "r0"."cake_id""#, + r#"LEFT JOIN "filling" AS "r1" ON "r0"."filling_id" = "r1"."id""#, + r#"LEFT JOIN "vendor" AS "r2" ON "r1"."vendor_id" = "r2"."id""#, + r#"WHERE "vendor"."id" < $1 ORDER BY "vendor"."id" ASC, "cake"."id" ASC LIMIT $2"#, + ] + .join(" ") + .as_str(), + [10_i32.into(), 2_u64.into()] + ),])] + ); + + Ok(()) + } + + #[smol_potat::test] + async fn last_2_after_10() -> Result<(), DbErr> { + use fruit::*; + let db = MockDatabase::new(DbBackend::Postgres) .append_query_results([[ Model { @@ -723,6 +1098,58 @@ mod tests { Ok(()) } + #[smol_potat::test] + async fn first_2_before_10_desc() -> Result<(), DbErr> { + use fruit::*; + + let models = [ + Model { + id: 22, + name: "Rasberry".into(), + cake_id: Some(1), + }, + Model { + id: 21, + name: "Blueberry".into(), + cake_id: Some(1), + }, + ]; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([models.clone()]) + .into_connection(); + + assert_eq!( + Entity::find() + .cursor_by(Column::Id) + .before(10) + .first(2) + .desc() + .all(&db) + .await?, + models + ); + + assert_eq!( + db.into_transaction_log(), + [Transaction::many([Statement::from_sql_and_values( + DbBackend::Postgres, + [ + r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id""#, + r#"FROM "fruit""#, + r#"WHERE "fruit"."id" > $1"#, + r#"ORDER BY "fruit"."id" DESC"#, + r#"LIMIT $2"#, + ] + .join(" ") + .as_str(), + [10_i32.into(), 2_u64.into()] + ),])] + ); + + Ok(()) + } + #[smol_potat::test] async fn last_2_after_25_before_30() -> Result<(), DbErr> { use fruit::*; @@ -785,6 +1212,60 @@ mod tests { Ok(()) } + #[smol_potat::test] + async fn first_2_after_30_before_25_desc() -> Result<(), DbErr> { + use fruit::*; + + let models = [ + Model { + id: 27, + name: "Rasberry".into(), + cake_id: Some(1), + }, + Model { + id: 26, + name: "Blueberry".into(), + cake_id: Some(1), + }, + ]; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([models.clone()]) + .into_connection(); + + assert_eq!( + Entity::find() + .cursor_by(Column::Id) + .after(30) + .before(25) + .first(2) + .desc() + .all(&db) + .await?, + models + ); + + assert_eq!( + db.into_transaction_log(), + [Transaction::many([Statement::from_sql_and_values( + DbBackend::Postgres, + [ + r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id""#, + r#"FROM "fruit""#, + r#"WHERE "fruit"."id" < $1"#, + r#"AND "fruit"."id" > $2"#, + r#"ORDER BY "fruit"."id" DESC"#, + r#"LIMIT $3"#, + ] + .join(" ") + .as_str(), + [30_i32.into(), 25_i32.into(), 2_u64.into()] + ),])] + ); + + Ok(()) + } + mod test_entity { use crate as sea_orm; use crate::entity::prelude::*; @@ -862,6 +1343,44 @@ mod tests { Ok(()) } + #[smol_potat::test] + async fn composite_keys_1_desc() -> Result<(), DbErr> { + use test_entity::*; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[Model { + id: 1, + category: "CAT".into(), + }]]) + .into_connection(); + + assert!(!Entity::find() + .cursor_by((Column::Category, Column::Id)) + .last(3) + .desc() + .all(&db) + .await? + .is_empty()); + + assert_eq!( + db.into_transaction_log(), + [Transaction::many([Statement::from_sql_and_values( + DbBackend::Postgres, + [ + r#"SELECT "example"."id", "example"."category""#, + r#"FROM "example""#, + r#"ORDER BY "example"."category" ASC, "example"."id" ASC"#, + r#"LIMIT $1"#, + ] + .join(" ") + .as_str(), + [3_u64.into()] + ),])] + ); + + Ok(()) + } + #[smol_potat::test] async fn composite_keys_2() -> Result<(), DbErr> { use test_entity::*; @@ -907,6 +1426,52 @@ mod tests { Ok(()) } + #[smol_potat::test] + async fn composite_keys_2_desc() -> Result<(), DbErr> { + use test_entity::*; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[Model { + id: 1, + category: "CAT".into(), + }]]) + .into_connection(); + + assert!(!Entity::find() + .cursor_by((Column::Category, Column::Id)) + .before(("A".to_owned(), 2)) + .last(3) + .desc() + .all(&db) + .await? + .is_empty()); + + assert_eq!( + db.into_transaction_log(), + [Transaction::many([Statement::from_sql_and_values( + DbBackend::Postgres, + [ + r#"SELECT "example"."id", "example"."category""#, + r#"FROM "example""#, + r#"WHERE ("example"."category" = $1 AND "example"."id" > $2)"#, + r#"OR "example"."category" > $3"#, + r#"ORDER BY "example"."category" ASC, "example"."id" ASC"#, + r#"LIMIT $4"#, + ] + .join(" ") + .as_str(), + [ + "A".to_string().into(), + 2i32.into(), + "A".to_string().into(), + 3_u64.into(), + ] + )])] + ); + + Ok(()) + } + #[smol_potat::test] async fn composite_keys_3() -> Result<(), DbErr> { use test_entity::*; @@ -952,6 +1517,52 @@ mod tests { Ok(()) } + #[smol_potat::test] + async fn composite_keys_3_desc() -> Result<(), DbErr> { + use test_entity::*; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[Model { + id: 1, + category: "CAT".into(), + }]]) + .into_connection(); + + assert!(!Entity::find() + .cursor_by((Column::Category, Column::Id)) + .after(("A".to_owned(), 2)) + .first(3) + .desc() + .all(&db) + .await? + .is_empty()); + + assert_eq!( + db.into_transaction_log(), + [Transaction::many([Statement::from_sql_and_values( + DbBackend::Postgres, + [ + r#"SELECT "example"."id", "example"."category""#, + r#"FROM "example""#, + r#"WHERE ("example"."category" = $1 AND "example"."id" < $2)"#, + r#"OR "example"."category" < $3"#, + r#"ORDER BY "example"."category" DESC, "example"."id" DESC"#, + r#"LIMIT $4"#, + ] + .join(" ") + .as_str(), + [ + "A".to_string().into(), + 2i32.into(), + "A".to_string().into(), + 3_u64.into(), + ] + )])] + ); + + Ok(()) + } + #[smol_potat::test] async fn composite_keys_4() -> Result<(), DbErr> { use xyz_entity::*; @@ -981,9 +1592,98 @@ mod tests { r#"ORDER BY "m"."x" ASC, "m"."y" ASC, "m"."z" ASC"#, r#"LIMIT $1"#, ] - .join(" ") - .as_str(), - [4_u64.into()] + .join(" ") + .as_str(), + [4_u64.into()] + ),])] + ); + + Ok(()) + } + + #[smol_potat::test] + async fn composite_keys_4_desc() -> Result<(), DbErr> { + use xyz_entity::*; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[Model { + x: 'x' as i32, + y: "y".into(), + z: 'z' as i64, + }]]) + .into_connection(); + + assert!(!Entity::find() + .cursor_by((Column::X, Column::Y, Column::Z)) + .last(4) + .desc() + .all(&db) + .await? + .is_empty()); + + assert_eq!( + db.into_transaction_log(), + [Transaction::many([Statement::from_sql_and_values( + DbBackend::Postgres, + [ + r#"SELECT "m"."x", "m"."y", "m"."z""#, + r#"FROM "m""#, + r#"ORDER BY "m"."x" ASC, "m"."y" ASC, "m"."z" ASC"#, + r#"LIMIT $1"#, + ] + .join(" ") + .as_str(), + [4_u64.into()] + ),])] + ); + + Ok(()) + } + + #[smol_potat::test] + async fn composite_keys_5() -> Result<(), DbErr> { + use xyz_entity::*; + + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[Model { + x: 'x' as i32, + y: "y".into(), + z: 'z' as i64, + }]]) + .into_connection(); + + assert!(!Entity::find() + .cursor_by((Column::X, Column::Y, Column::Z)) + .after(('x' as i32, "y".to_owned(), 'z' as i64)) + .first(4) + .all(&db) + .await? + .is_empty()); + + assert_eq!( + db.into_transaction_log(), + [Transaction::many([Statement::from_sql_and_values( + DbBackend::Postgres, + [ + r#"SELECT "m"."x", "m"."y", "m"."z""#, + r#"FROM "m""#, + r#"WHERE ("m"."x" = $1 AND "m"."y" = $2 AND "m"."z" > $3)"#, + r#"OR ("m"."x" = $4 AND "m"."y" > $5)"#, + r#"OR "m"."x" > $6"#, + r#"ORDER BY "m"."x" ASC, "m"."y" ASC, "m"."z" ASC"#, + r#"LIMIT $7"#, + ] + .join(" ") + .as_str(), + [ + ('x' as i32).into(), + "y".into(), + ('z' as i64).into(), + ('x' as i32).into(), + "y".into(), + ('x' as i32).into(), + 4_u64.into(), + ] ),])] ); @@ -991,7 +1691,7 @@ mod tests { } #[smol_potat::test] - async fn composite_keys_5() -> Result<(), DbErr> { + async fn composite_keys_5_desc() -> Result<(), DbErr> { use xyz_entity::*; let db = MockDatabase::new(DbBackend::Postgres) @@ -1004,8 +1704,9 @@ mod tests { assert!(!Entity::find() .cursor_by((Column::X, Column::Y, Column::Z)) - .after(('x' as i32, "y".to_owned(), 'z' as i64)) - .first(4) + .before(('x' as i32, "y".to_owned(), 'z' as i64)) + .last(4) + .desc() .all(&db) .await? .is_empty()); @@ -1092,14 +1793,136 @@ mod tests { DbBackend::Postgres.build(& Entity::find() .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4)) - .after(("val_1", "val_2", "val_3", "val_4")) + .after(("val_1", "val_2", "val_3", "val_4")).apply_limit().apply_order_by().apply_filters().query + ).to_string(), + format!("{base_sql} {}", [ + r#"("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" > 'val_4')"#, + r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" > 'val_3')"#, + r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" > 'val_2')"#, + r#"OR "t"."col_1" > 'val_1'"#, + r#"ORDER BY "t"."col_1" ASC, "t"."col_2" ASC, "t"."col_3" ASC, "t"."col_4" ASC"#, + ].join(" ")) + ); + + assert_eq!( + DbBackend::Postgres.build(& + Entity::find() + .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4, Column::Col5)) + .after(("val_1", "val_2", "val_3", "val_4", "val_5")).apply_limit().apply_order_by().apply_filters() + .query + ).to_string(), + format!("{base_sql} {}", [ + r#"("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" > 'val_5')"#, + r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" > 'val_4')"#, + r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" > 'val_3')"#, + r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" > 'val_2')"#, + r#"OR "t"."col_1" > 'val_1'"#, + r#"ORDER BY "t"."col_1" ASC, "t"."col_2" ASC, "t"."col_3" ASC, "t"."col_4" ASC, "t"."col_5" ASC"#, + ].join(" ")) + ); + + assert_eq!( + DbBackend::Postgres.build(& + Entity::find() + .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4, Column::Col5, Column::Col6)) + .after(("val_1", "val_2", "val_3", "val_4", "val_5", "val_6")).apply_limit().apply_order_by().apply_filters() + .query + ).to_string(), + format!("{base_sql} {}", [ + r#"("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" = 'val_5' AND "t"."col_6" > 'val_6')"#, + r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" > 'val_5')"#, + r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" > 'val_4')"#, + r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" > 'val_3')"#, + r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" > 'val_2')"#, + r#"OR "t"."col_1" > 'val_1'"#, + r#"ORDER BY "t"."col_1" ASC, "t"."col_2" ASC, "t"."col_3" ASC, "t"."col_4" ASC, "t"."col_5" ASC, "t"."col_6" ASC"#, + ].join(" ")) + ); + + assert_eq!( + DbBackend::Postgres.build(& + Entity::find() + .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4, Column::Col5, Column::Col6, Column::Col7)) + .before(("val_1", "val_2", "val_3", "val_4", "val_5", "val_6", "val_7")).apply_limit().apply_order_by().apply_filters() + .query + ).to_string(), + format!("{base_sql} {}", [ + r#"("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" = 'val_5' AND "t"."col_6" = 'val_6' AND "t"."col_7" < 'val_7')"#, + r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" = 'val_5' AND "t"."col_6" < 'val_6')"#, + r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" < 'val_5')"#, + r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" < 'val_4')"#, + r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" < 'val_3')"#, + r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" < 'val_2')"#, + r#"OR "t"."col_1" < 'val_1'"#, + r#"ORDER BY "t"."col_1" ASC, "t"."col_2" ASC, "t"."col_3" ASC, "t"."col_4" ASC, "t"."col_5" ASC, "t"."col_6" ASC, "t"."col_7" ASC"#, + ].join(" ")) + ); + + assert_eq!( + DbBackend::Postgres.build(& + Entity::find() + .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4, Column::Col5, Column::Col6, Column::Col7, Column::Col8)) + .before(("val_1", "val_2", "val_3", "val_4", "val_5", "val_6", "val_7", "val_8")).apply_limit().apply_order_by().apply_filters() + .query + ).to_string(), + format!("{base_sql} {}", [ + r#"("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" = 'val_5' AND "t"."col_6" = 'val_6' AND "t"."col_7" = 'val_7' AND "t"."col_8" < 'val_8')"#, + r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" = 'val_5' AND "t"."col_6" = 'val_6' AND "t"."col_7" < 'val_7')"#, + r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" = 'val_5' AND "t"."col_6" < 'val_6')"#, + r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" < 'val_5')"#, + r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" < 'val_4')"#, + r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" < 'val_3')"#, + r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" < 'val_2')"#, + r#"OR "t"."col_1" < 'val_1'"#, + r#"ORDER BY "t"."col_1" ASC, "t"."col_2" ASC, "t"."col_3" ASC, "t"."col_4" ASC, "t"."col_5" ASC, "t"."col_6" ASC, "t"."col_7" ASC, "t"."col_8" ASC"#, + ].join(" ")) + ); + + assert_eq!( + DbBackend::Postgres.build(& + Entity::find() + .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4, Column::Col5, Column::Col6, Column::Col7, Column::Col8, Column::Col9)) + .before(("val_1", "val_2", "val_3", "val_4", "val_5", "val_6", "val_7", "val_8", "val_9")).apply_limit().apply_order_by().apply_filters() .query ).to_string(), + format!("{base_sql} {}", [ + r#"("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" = 'val_5' AND "t"."col_6" = 'val_6' AND "t"."col_7" = 'val_7' AND "t"."col_8" = 'val_8' AND "t"."col_9" < 'val_9')"#, + r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" = 'val_5' AND "t"."col_6" = 'val_6' AND "t"."col_7" = 'val_7' AND "t"."col_8" < 'val_8')"#, + r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" = 'val_5' AND "t"."col_6" = 'val_6' AND "t"."col_7" < 'val_7')"#, + r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" = 'val_5' AND "t"."col_6" < 'val_6')"#, + r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" < 'val_5')"#, + r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" < 'val_4')"#, + r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" < 'val_3')"#, + r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" < 'val_2')"#, + r#"OR "t"."col_1" < 'val_1'"#, + r#"ORDER BY "t"."col_1" ASC, "t"."col_2" ASC, "t"."col_3" ASC, "t"."col_4" ASC, "t"."col_5" ASC, "t"."col_6" ASC, "t"."col_7" ASC, "t"."col_8" ASC, "t"."col_9" ASC"#, + ].join(" ")) + ); + + Ok(()) + } + + #[smol_potat::test] + async fn cursor_by_many_desc() -> Result<(), DbErr> { + use composite_entity::*; + + let base_sql = [ + r#"SELECT "t"."col_1", "t"."col_2", "t"."col_3", "t"."col_4", "t"."col_5", "t"."col_6", "t"."col_7", "t"."col_8", "t"."col_9", "t"."col_10", "t"."col_11", "t"."col_12""#, + r#"FROM "t" WHERE"#, + ].join(" "); + + assert_eq!( + DbBackend::Postgres.build(& + Entity::find() + .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4)) + .before(("val_1", "val_2", "val_3", "val_4")).desc().apply_limit().apply_order_by().apply_filters().query + ).to_string(), format!("{base_sql} {}", [ r#"("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" > 'val_4')"#, r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" > 'val_3')"#, r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" > 'val_2')"#, r#"OR "t"."col_1" > 'val_1'"#, + r#"ORDER BY "t"."col_1" DESC, "t"."col_2" DESC, "t"."col_3" DESC, "t"."col_4" DESC"#, ].join(" ")) ); @@ -1107,7 +1930,7 @@ mod tests { DbBackend::Postgres.build(& Entity::find() .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4, Column::Col5)) - .after(("val_1", "val_2", "val_3", "val_4", "val_5")) + .before(("val_1", "val_2", "val_3", "val_4", "val_5")).desc().apply_limit().apply_order_by().apply_filters() .query ).to_string(), format!("{base_sql} {}", [ @@ -1116,6 +1939,7 @@ mod tests { r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" > 'val_3')"#, r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" > 'val_2')"#, r#"OR "t"."col_1" > 'val_1'"#, + r#"ORDER BY "t"."col_1" DESC, "t"."col_2" DESC, "t"."col_3" DESC, "t"."col_4" DESC, "t"."col_5" DESC"#, ].join(" ")) ); @@ -1123,7 +1947,7 @@ mod tests { DbBackend::Postgres.build(& Entity::find() .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4, Column::Col5, Column::Col6)) - .after(("val_1", "val_2", "val_3", "val_4", "val_5", "val_6")) + .before(("val_1", "val_2", "val_3", "val_4", "val_5", "val_6")).desc().apply_limit().apply_order_by().apply_filters() .query ).to_string(), format!("{base_sql} {}", [ @@ -1133,6 +1957,7 @@ mod tests { r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" > 'val_3')"#, r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" > 'val_2')"#, r#"OR "t"."col_1" > 'val_1'"#, + r#"ORDER BY "t"."col_1" DESC, "t"."col_2" DESC, "t"."col_3" DESC, "t"."col_4" DESC, "t"."col_5" DESC, "t"."col_6" DESC"#, ].join(" ")) ); @@ -1140,7 +1965,7 @@ mod tests { DbBackend::Postgres.build(& Entity::find() .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4, Column::Col5, Column::Col6, Column::Col7)) - .before(("val_1", "val_2", "val_3", "val_4", "val_5", "val_6", "val_7")) + .after(("val_1", "val_2", "val_3", "val_4", "val_5", "val_6", "val_7")).desc().apply_limit().apply_order_by().apply_filters() .query ).to_string(), format!("{base_sql} {}", [ @@ -1151,6 +1976,7 @@ mod tests { r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" < 'val_3')"#, r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" < 'val_2')"#, r#"OR "t"."col_1" < 'val_1'"#, + r#"ORDER BY "t"."col_1" DESC, "t"."col_2" DESC, "t"."col_3" DESC, "t"."col_4" DESC, "t"."col_5" DESC, "t"."col_6" DESC, "t"."col_7" DESC"#, ].join(" ")) ); @@ -1158,7 +1984,7 @@ mod tests { DbBackend::Postgres.build(& Entity::find() .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4, Column::Col5, Column::Col6, Column::Col7, Column::Col8)) - .before(("val_1", "val_2", "val_3", "val_4", "val_5", "val_6", "val_7", "val_8")) + .after(("val_1", "val_2", "val_3", "val_4", "val_5", "val_6", "val_7", "val_8")).desc().apply_limit().apply_order_by().apply_filters() .query ).to_string(), format!("{base_sql} {}", [ @@ -1170,6 +1996,7 @@ mod tests { r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" < 'val_3')"#, r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" < 'val_2')"#, r#"OR "t"."col_1" < 'val_1'"#, + r#"ORDER BY "t"."col_1" DESC, "t"."col_2" DESC, "t"."col_3" DESC, "t"."col_4" DESC, "t"."col_5" DESC, "t"."col_6" DESC, "t"."col_7" DESC, "t"."col_8" DESC"#, ].join(" ")) ); @@ -1177,7 +2004,7 @@ mod tests { DbBackend::Postgres.build(& Entity::find() .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4, Column::Col5, Column::Col6, Column::Col7, Column::Col8, Column::Col9)) - .before(("val_1", "val_2", "val_3", "val_4", "val_5", "val_6", "val_7", "val_8", "val_9")) + .after(("val_1", "val_2", "val_3", "val_4", "val_5", "val_6", "val_7", "val_8", "val_9")).desc().apply_limit().apply_order_by().apply_filters() .query ).to_string(), format!("{base_sql} {}", [ @@ -1190,6 +2017,7 @@ mod tests { r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" < 'val_3')"#, r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" < 'val_2')"#, r#"OR "t"."col_1" < 'val_1'"#, + r#"ORDER BY "t"."col_1" DESC, "t"."col_2" DESC, "t"."col_3" DESC, "t"."col_4" DESC, "t"."col_5" DESC, "t"."col_6" DESC, "t"."col_7" DESC, "t"."col_8" DESC, "t"."col_9" DESC"#, ].join(" ")) ); @@ -1302,6 +2130,51 @@ mod tests { Ok(()) } + #[smol_potat::test] + async fn related_composite_keys_1_desc() -> Result<(), DbErr> { + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[( + test_base_entity::Model { + id: 1, + name: "CAT".into(), + }, + test_related_entity::Model { + id: 1, + name: "CATE".into(), + test_id: 1, + }, + )]]) + .into_connection(); + + assert!(!test_base_entity::Entity::find() + .find_also_related(test_related_entity::Entity) + .cursor_by((test_base_entity::Column::Id, test_base_entity::Column::Name)) + .last(1) + .desc() + .all(&db) + .await? + .is_empty()); + + assert_eq!( + db.into_transaction_log(), + [Transaction::many([Statement::from_sql_and_values( + DbBackend::Postgres, + [ + r#"SELECT "base"."id" AS "A_id", "base"."name" AS "A_name","#, + r#""related"."id" AS "B_id", "related"."name" AS "B_name", "related"."test_id" AS "B_test_id""#, + r#"FROM "base""#, + r#"LEFT JOIN "related" ON "base"."id" = "related"."test_id""#, + r#"ORDER BY "base"."id" ASC, "base"."name" ASC, "related"."id" ASC, "related"."name" ASC LIMIT $1"#, + ] + .join(" ") + .as_str(), + [1_u64.into()] + ),])] + ); + + Ok(()) + } + #[smol_potat::test] async fn related_composite_keys_2() -> Result<(), DbErr> { let db = MockDatabase::new(DbBackend::Postgres) @@ -1353,6 +2226,58 @@ mod tests { Ok(()) } + #[smol_potat::test] + async fn related_composite_keys_2_desc() -> Result<(), DbErr> { + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[( + test_base_entity::Model { + id: 1, + name: "CAT".into(), + }, + test_related_entity::Model { + id: 1, + name: "CATE".into(), + test_id: 1, + }, + )]]) + .into_connection(); + + assert!(!test_base_entity::Entity::find() + .find_also_related(test_related_entity::Entity) + .cursor_by((test_base_entity::Column::Id, test_base_entity::Column::Name)) + .before((1, "C".to_string())) + .last(2) + .desc() + .all(&db) + .await? + .is_empty()); + + assert_eq!( + db.into_transaction_log(), + [Transaction::many([Statement::from_sql_and_values( + DbBackend::Postgres, + [ + r#"SELECT "base"."id" AS "A_id", "base"."name" AS "A_name","#, + r#""related"."id" AS "B_id", "related"."name" AS "B_name", "related"."test_id" AS "B_test_id""#, + r#"FROM "base""#, + r#"LEFT JOIN "related" ON "base"."id" = "related"."test_id""#, + r#"WHERE ("base"."id" = $1 AND "base"."name" > $2) OR "base"."id" > $3"#, + r#"ORDER BY "base"."id" ASC, "base"."name" ASC, "related"."id" ASC, "related"."name" ASC LIMIT $4"#, + ] + .join(" ") + .as_str(), + [ + 1_i32.into(), + "C".into(), + 1_i32.into(), + 2_u64.into(), + ] + ),])] + ); + + Ok(()) + } + #[smol_potat::test] async fn related_composite_keys_3() -> Result<(), DbErr> { let db = MockDatabase::new(DbBackend::Postgres) @@ -1406,4 +2331,59 @@ mod tests { Ok(()) } + + #[smol_potat::test] + async fn related_composite_keys_3_desc() -> Result<(), DbErr> { + let db = MockDatabase::new(DbBackend::Postgres) + .append_query_results([[( + test_base_entity::Model { + id: 1, + name: "CAT".into(), + }, + test_related_entity::Model { + id: 1, + name: "CATE".into(), + test_id: 1, + }, + )]]) + .into_connection(); + + assert!(!test_base_entity::Entity::find() + .find_also_related(test_related_entity::Entity) + .cursor_by_other(( + test_related_entity::Column::Id, + test_related_entity::Column::Name + )) + .before((1, "CAT".to_string())) + .last(2) + .desc() + .all(&db) + .await? + .is_empty()); + + assert_eq!( + db.into_transaction_log(), + [Transaction::many([Statement::from_sql_and_values( + DbBackend::Postgres, + [ + r#"SELECT "base"."id" AS "A_id", "base"."name" AS "A_name","#, + r#""related"."id" AS "B_id", "related"."name" AS "B_name", "related"."test_id" AS "B_test_id""#, + r#"FROM "base""#, + r#"LEFT JOIN "related" ON "base"."id" = "related"."test_id""#, + r#"WHERE ("related"."id" = $1 AND "related"."name" > $2) OR "related"."id" > $3"#, + r#"ORDER BY "related"."id" ASC, "related"."name" ASC, "base"."id" ASC, "base"."name" ASC LIMIT $4"#, + ] + .join(" ") + .as_str(), + [ + 1_i32.into(), + "CAT".into(), + 1_i32.into(), + 2_u64.into(), + ] + ),])] + ); + + Ok(()) + } } diff --git a/tests/cursor_tests.rs b/tests/cursor_tests.rs index 6b90c34f4f..e843086894 100644 --- a/tests/cursor_tests.rs +++ b/tests/cursor_tests.rs @@ -11,7 +11,7 @@ use serde_json::json; feature = "sqlx-sqlite", feature = "sqlx-postgres" ))] -async fn main() -> Result<(), DbErr> { +async fn cursor_tests() -> Result<(), DbErr> { let ctx = TestContext::new("cursor_tests").await; create_tables(&ctx.db).await?; create_insert_default(&ctx.db).await?; @@ -103,6 +103,54 @@ pub async fn cursor_pagination(db: &DatabaseConnection) -> Result<(), DbErr> { ] ); + // Before 5 DESC, i.e. id > 5 + + let mut cursor = Entity::find().cursor_by(Column::Id); + + cursor.before(5).desc(); + + assert_eq!( + cursor.first(4).all(db).await?, + [ + Model { id: 10 }, + Model { id: 9 }, + Model { id: 8 }, + Model { id: 7 }, + ] + ); + + assert_eq!( + cursor.first(5).all(db).await?, + [ + Model { id: 10 }, + Model { id: 9 }, + Model { id: 8 }, + Model { id: 7 }, + Model { id: 6 }, + ] + ); + + assert_eq!( + cursor.last(4).all(db).await?, + [ + Model { id: 9 }, + Model { id: 8 }, + Model { id: 7 }, + Model { id: 6 }, + ] + ); + + assert_eq!( + cursor.last(5).all(db).await?, + [ + Model { id: 10 }, + Model { id: 9 }, + Model { id: 8 }, + Model { id: 7 }, + Model { id: 6 }, + ] + ); + // After 5, i.e. id > 5 let mut cursor = Entity::find().cursor_by(Column::Id); @@ -173,6 +221,62 @@ pub async fn cursor_pagination(db: &DatabaseConnection) -> Result<(), DbErr> { ] ); + // After 5 DESC, i.e. id < 5 + + let mut cursor = Entity::find().cursor_by(Column::Id); + + cursor.after(5).desc(); + + assert_eq!( + cursor.first(3).all(db).await?, + [Model { id: 4 }, Model { id: 3 }, Model { id: 2 },] + ); + + assert_eq!( + cursor.first(4).all(db).await?, + [ + Model { id: 4 }, + Model { id: 3 }, + Model { id: 2 }, + Model { id: 1 }, + ] + ); + + assert_eq!( + cursor.first(5).all(db).await?, + [ + Model { id: 4 }, + Model { id: 3 }, + Model { id: 2 }, + Model { id: 1 }, + ] + ); + + assert_eq!( + cursor.last(3).all(db).await?, + [Model { id: 3 }, Model { id: 2 }, Model { id: 1 },] + ); + + assert_eq!( + cursor.last(4).all(db).await?, + [ + Model { id: 4 }, + Model { id: 3 }, + Model { id: 2 }, + Model { id: 1 }, + ] + ); + + assert_eq!( + cursor.last(5).all(db).await?, + [ + Model { id: 4 }, + Model { id: 3 }, + Model { id: 2 }, + Model { id: 1 }, + ] + ); + // Between 5 and 8, i.e. id > 5 AND id < 8 let mut cursor = Entity::find().cursor_by(Column::Id); @@ -203,6 +307,64 @@ pub async fn cursor_pagination(db: &DatabaseConnection) -> Result<(), DbErr> { [Model { id: 6 }, Model { id: 7 }] ); + // Between 8 and 5 DESC, i.e. id < 8 AND id > 5 + + let mut cursor = Entity::find().cursor_by(Column::Id); + + cursor.after(8).before(5).desc(); + + assert_eq!(cursor.first(1).all(db).await?, [Model { id: 7 }]); + + assert_eq!( + cursor.first(2).all(db).await?, + [Model { id: 7 }, Model { id: 6 }] + ); + + assert_eq!( + cursor.first(3).all(db).await?, + [Model { id: 7 }, Model { id: 6 }] + ); + + assert_eq!(cursor.last(1).all(db).await?, [Model { id: 6 }]); + + assert_eq!( + cursor.last(2).all(db).await?, + [Model { id: 7 }, Model { id: 6 }] + ); + + assert_eq!( + cursor.last(3).all(db).await?, + [Model { id: 7 }, Model { id: 6 }] + ); + + // Ensure asc/desc order can be changed + + let mut cursor = Entity::find().cursor_by(Column::Id); + + cursor.first(2); + + assert_eq!(cursor.all(db).await?, [Model { id: 1 }, Model { id: 2 },]); + + assert_eq!( + cursor.asc().all(db).await?, + [Model { id: 1 }, Model { id: 2 },] + ); + + assert_eq!( + cursor.desc().all(db).await?, + [Model { id: 10 }, Model { id: 9 },] + ); + + assert_eq!( + cursor.asc().all(db).await?, + [Model { id: 1 }, Model { id: 2 },] + ); + + assert_eq!( + cursor.desc().all(db).await?, + [Model { id: 10 }, Model { id: 9 },] + ); + // Fetch custom struct #[derive(FromQueryResult, Debug, PartialEq, Clone)] @@ -210,6 +372,10 @@ pub async fn cursor_pagination(db: &DatabaseConnection) -> Result<(), DbErr> { id: i32, } + let mut cursor = Entity::find().cursor_by(Column::Id); + + cursor.after(5).before(8); + let mut cursor = cursor.into_model::(); assert_eq!( @@ -222,8 +388,30 @@ pub async fn cursor_pagination(db: &DatabaseConnection) -> Result<(), DbErr> { [Row { id: 6 }, Row { id: 7 }] ); + // Fetch custom struct desc + + let mut cursor = Entity::find().cursor_by(Column::Id); + + cursor.after(8).before(5).desc(); + + let mut cursor = cursor.into_model::(); + + assert_eq!( + cursor.first(2).all(db).await?, + [Row { id: 7 }, Row { id: 6 }] + ); + + assert_eq!( + cursor.first(3).all(db).await?, + [Row { id: 7 }, Row { id: 6 }] + ); + // Fetch JSON value + let mut cursor = Entity::find().cursor_by(Column::Id); + + cursor.after(5).before(8); + let mut cursor = cursor.into_json(); assert_eq!( @@ -275,6 +463,54 @@ pub async fn cursor_pagination(db: &DatabaseConnection) -> Result<(), DbErr> { ] ); + // Fetch JSON value desc + + let mut cursor = Entity::find().cursor_by(Column::Id); + + cursor.after(8).before(5).desc(); + + let mut cursor = cursor.into_json(); + + assert_eq!( + cursor.first(2).all(db).await?, + [json!({ "id": 7 }), json!({ "id": 6 })] + ); + + assert_eq!( + cursor.first(3).all(db).await?, + [json!({ "id": 7 }), json!({ "id": 6 })] + ); + + let mut cursor = cursor.into_partial_model::(); + + assert_eq!( + cursor.first(2).all(db).await?, + [ + PartialRow { + id: 7, + id_shifted: 1007, + }, + PartialRow { + id: 6, + id_shifted: 1006, + } + ] + ); + + assert_eq!( + cursor.first(3).all(db).await?, + [ + PartialRow { + id: 7, + id_shifted: 1007, + }, + PartialRow { + id: 6, + id_shifted: 1006, + }, + ] + ); + Ok(()) } @@ -387,6 +623,17 @@ pub async fn cursor_related_pagination(db: &DatabaseConnection) -> Result<(), Db [bakery(1), bakery(2), bakery(3), bakery(4),] ); + assert_eq!( + bakery::Entity::find() + .cursor_by(bakery::Column::Id) + .before(5) + .first(4) + .desc() + .all(db) + .await?, + [bakery(10), bakery(9), bakery(8), bakery(7),] + ); + assert_eq!( bakery::Entity::find() .find_also_related(Baker) @@ -411,6 +658,31 @@ pub async fn cursor_related_pagination(db: &DatabaseConnection) -> Result<(), Db ] ); + assert_eq!( + bakery::Entity::find() + .find_also_related(Baker) + .cursor_by(bakery::Column::Id) + .after(5) + .last(20) + .desc() + .all(db) + .await?, + [ + (bakery(4), Some(baker('X'))), + (bakery(4), Some(baker('N'))), + (bakery(4), Some(baker('D'))), + (bakery(3), Some(baker('W'))), + (bakery(3), Some(baker('M'))), + (bakery(3), Some(baker('C'))), + (bakery(2), Some(baker('V'))), + (bakery(2), Some(baker('L'))), + (bakery(2), Some(baker('B'))), + (bakery(1), Some(baker('U'))), + (bakery(1), Some(baker('K'))), + (bakery(1), Some(baker('A'))), + ] + ); + assert_eq!( bakery::Entity::find() .find_also_related(Baker) @@ -427,6 +699,41 @@ pub async fn cursor_related_pagination(db: &DatabaseConnection) -> Result<(), Db ] ); + assert_eq!( + bakery::Entity::find() + .find_also_related(Baker) + .cursor_by(bakery::Column::Id) + .after(5) + .last(4) + .desc() + .all(db) + .await?, + [ + (bakery(2), Some(baker('B'))), + (bakery(1), Some(baker('U'))), + (bakery(1), Some(baker('K'))), + (bakery(1), Some(baker('A'))), + ] + ); + + // since "10" is before "2" lexicologically, it return that first + assert_eq!( + bakery::Entity::find() + .find_also_related(Baker) + .cursor_by(bakery::Column::Name) + .after("3") + .last(4) + .desc() + .all(db) + .await?, + [ + (bakery(10), Some(baker('J'))), + (bakery(1), Some(baker('U'))), + (bakery(1), Some(baker('K'))), + (bakery(1), Some(baker('A'))), + ] + ); + // since "10" is before "2" lexicologically, it return that first assert_eq!( bakery::Entity::find() @@ -444,6 +751,24 @@ pub async fn cursor_related_pagination(db: &DatabaseConnection) -> Result<(), Db ] ); + // since "10" is before "2" lexicologically, it return that first + assert_eq!( + bakery::Entity::find() + .find_also_related(Baker) + .cursor_by(bakery::Column::Name) + .after("3") + .last(4) + .desc() + .all(db) + .await?, + [ + (bakery(10), Some(baker('J'))), + (bakery(1), Some(baker('U'))), + (bakery(1), Some(baker('K'))), + (bakery(1), Some(baker('A'))), + ] + ); + #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] enum QueryAs { CakeId, @@ -473,6 +798,29 @@ pub async fn cursor_related_pagination(db: &DatabaseConnection) -> Result<(), Db ] ); + assert_eq!( + cake::Entity::find() + .find_also_related(Baker) + .select_only() + .column_as(cake::Column::Id, QueryAs::CakeId) + .column_as(cake::Column::Name, QueryAs::CakeName) + .column_as(baker::Column::Name, QueryAs::BakerName) + .cursor_by(cake::Column::Name) + .after("e") + .last(4) + .desc() + .clone() + .into_model::() + .all(db) + .await?, + vec![ + cakebaker('b', 'B'), + cakebaker('b', 'A'), + cakebaker('a', 'B'), + cakebaker('a', 'A') + ] + ); + assert_eq!( cake::Entity::find() .find_also_related(Baker) @@ -487,7 +835,25 @@ pub async fn cursor_related_pagination(db: &DatabaseConnection) -> Result<(), Db .into_model::() .all(db) .await?, - vec![cakebaker('a', 'A'), cakebaker('a', 'B'),] + vec![cakebaker('a', 'A'), cakebaker('a', 'B')] + ); + + assert_eq!( + cake::Entity::find() + .find_also_related(Baker) + .select_only() + .column_as(cake::Column::Id, QueryAs::CakeId) + .column_as(cake::Column::Name, QueryAs::CakeName) + .column_as(baker::Column::Name, QueryAs::BakerName) + .cursor_by(cake::Column::Name) + .after("b") + .last(4) + .desc() + .clone() + .into_model::() + .all(db) + .await?, + vec![cakebaker('a', 'B'), cakebaker('a', 'A')] ); assert_eq!( @@ -507,6 +873,24 @@ pub async fn cursor_related_pagination(db: &DatabaseConnection) -> Result<(), Db vec![cakebaker('a', 'A'), cakebaker('b', 'A'),] ); + assert_eq!( + cake::Entity::find() + .find_also_related(Baker) + .select_only() + .column_as(cake::Column::Id, QueryAs::CakeId) + .column_as(cake::Column::Name, QueryAs::CakeName) + .column_as(baker::Column::Name, QueryAs::BakerName) + .cursor_by_other(baker::Column::Name) + .after("B") + .last(4) + .desc() + .clone() + .into_model::() + .all(db) + .await?, + vec![cakebaker('b', 'A'), cakebaker('a', 'A'),] + ); + assert_eq!( cake::Entity::find() .find_also_related(Baker) @@ -531,5 +915,30 @@ pub async fn cursor_related_pagination(db: &DatabaseConnection) -> Result<(), Db ] ); + assert_eq!( + cake::Entity::find() + .find_also_related(Baker) + .select_only() + .column_as(cake::Column::Id, QueryAs::CakeId) + .column_as(cake::Column::Name, QueryAs::CakeName) + .column_as(baker::Column::Name, QueryAs::BakerName) + .cursor_by_other(baker::Column::Name) + .after("E") + .last(20) + .desc() + .clone() + .into_model::() + .all(db) + .await?, + vec![ + cakebaker('d', 'D'), + cakebaker('c', 'C'), + cakebaker('b', 'B'), + cakebaker('a', 'B'), + cakebaker('b', 'A'), + cakebaker('a', 'A'), + ] + ); + Ok(()) } From 3b0896b5272294ce94a8acd99d7a4e0f12eeb8fa Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sun, 14 Jan 2024 15:17:27 +0000 Subject: [PATCH 0260/1371] Changelog --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb7c6fc5e4..80540c4557 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## 0.12.11 - Pending +## 0.12.11 - 2023-01-14 + +### New Features + +* Added `desc` to `Cursor` paginator https://github.com/SeaQL/sea-orm/pull/2037 ### Enhancements From 7eb3e12294976585683ce12f86d074751d1fbe45 Mon Sep 17 00:00:00 2001 From: Francis Le Roy <36069799+GrandChaman@users.noreply.github.com> Date: Sun, 14 Jan 2024 16:51:51 +0100 Subject: [PATCH 0261/1371] Updated the 'QuerySelect' trait to expose 'lock_with_behavior' (#1867) --- src/query/helper.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/query/helper.rs b/src/query/helper.rs index 976220c1f9..c5511cd609 100644 --- a/src/query/helper.rs +++ b/src/query/helper.rs @@ -3,8 +3,8 @@ use crate::{ PrimaryKeyToColumn, RelationDef, }; use sea_query::{ - Alias, ConditionType, Expr, Iden, IntoCondition, IntoIden, LockType, SeaRc, SelectExpr, - SelectStatement, SimpleExpr, TableRef, + Alias, ConditionType, Expr, Iden, IntoCondition, IntoIden, LockBehavior, LockType, SeaRc, + SelectExpr, SelectStatement, SimpleExpr, TableRef, }; pub use sea_query::{Condition, ConditionalStatement, DynIden, JoinType, Order, OrderedStatement}; @@ -447,6 +447,14 @@ pub trait QuerySelect: Sized { self } + /// Row locking with behavior (if supported). + /// + /// See [`SelectStatement::lock_with_behavior`]. + fn lock_with_behavior(mut self, r#type: LockType, behavior: LockBehavior) -> Self { + self.query().lock_with_behavior(r#type, behavior); + self + } + /// Add an expression to the select expression list. /// ``` /// use sea_orm::sea_query::Expr; From f27526b79d952916251d136ea54cfb6ad5572810 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sun, 14 Jan 2024 15:43:57 +0000 Subject: [PATCH 0262/1371] Changelog --- CHANGELOG.md | 1 + src/query/helper.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80540c4557..735ec00d04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). * Improve query performance of `Paginator`'s `COUNT` query https://github.com/SeaQL/sea-orm/pull/2030 * Added SQLx slow statements logging to `ConnectOptions` https://github.com/SeaQL/sea-orm/pull/2055 +* Added `QuerySelect::lock_with_behavior` https://github.com/SeaQL/sea-orm/pull/1867 ### House keeping diff --git a/src/query/helper.rs b/src/query/helper.rs index c5511cd609..a4f180e1a5 100644 --- a/src/query/helper.rs +++ b/src/query/helper.rs @@ -449,7 +449,7 @@ pub trait QuerySelect: Sized { /// Row locking with behavior (if supported). /// - /// See [`SelectStatement::lock_with_behavior`]. + /// See [`SelectStatement::lock_with_behavior`](https://docs.rs/sea-query/*/sea_query/query/struct.SelectStatement.html#method.lock_with_behavior). fn lock_with_behavior(mut self, r#type: LockType, behavior: LockBehavior) -> Self { self.query().lock_with_behavior(r#type, behavior); self From 361924336d49593608204bcc2922fac18be26e59 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sun, 14 Jan 2024 15:55:08 +0000 Subject: [PATCH 0263/1371] clippy --- src/executor/cursor.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/executor/cursor.rs b/src/executor/cursor.rs index 78d86a7aec..9d5ee91f27 100644 --- a/src/executor/cursor.rs +++ b/src/executor/cursor.rs @@ -218,12 +218,7 @@ where fn resolve_sort_order(&mut self) -> Order { let should_reverse_order = self.last.is_some(); - - if should_reverse_order { - self.is_result_reversed = true; - } else { - self.is_result_reversed = false; - } + self.is_result_reversed = should_reverse_order; if (self.sort_asc && !should_reverse_order) || (!self.sort_asc && should_reverse_order) { Order::Asc From 260b8a40768c9621c35e0c430754e4039688c347 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sun, 14 Jan 2024 16:10:33 +0000 Subject: [PATCH 0264/1371] 0.12.11 --- Cargo.toml | 4 ++-- sea-orm-cli/Cargo.toml | 4 ++-- sea-orm-codegen/Cargo.toml | 2 +- sea-orm-macros/Cargo.toml | 2 +- sea-orm-migration/Cargo.toml | 6 +++--- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f65113578a..22676c2305 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [".", "sea-orm-macros", "sea-orm-codegen"] [package] name = "sea-orm" -version = "0.12.10" +version = "0.12.11" authors = ["Chris Tsang "] edition = "2021" description = "🐚 An async & dynamic ORM for Rust" @@ -33,7 +33,7 @@ log = { version = "0.4", default-features = false } tracing = { version = "0.1", default-features = false, features = ["attributes", "log"] } rust_decimal = { version = "1", default-features = false, optional = true } bigdecimal = { version = "0.3", default-features = false, optional = true } -sea-orm-macros = { version = "0.12.10", path = "sea-orm-macros", default-features = false, features = ["strum"] } +sea-orm-macros = { version = "0.12.11", path = "sea-orm-macros", default-features = false, features = ["strum"] } sea-query = { version = "0.30.4", default-features = false, features = ["thread-safe", "hashable-value", "backend-mysql", "backend-postgres", "backend-sqlite"] } sea-query-binder = { version = "0.5.0", default-features = false, optional = true } strum = { version = "0.25", default-features = false } diff --git a/sea-orm-cli/Cargo.toml b/sea-orm-cli/Cargo.toml index ecb522ae81..9d3f36f089 100644 --- a/sea-orm-cli/Cargo.toml +++ b/sea-orm-cli/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "sea-orm-cli" -version = "0.12.10" +version = "0.12.11" authors = [ "Chris Tsang ", "Billy Chan ", @@ -37,7 +37,7 @@ required-features = ["cli", "codegen"] clap = { version = "4.3", features = ["env", "derive"], optional = true } dotenvy = { version = "0.15", default-features = false, optional = true } async-std = { version = "1.9", default-features = false, features = ["attributes", "tokio1"], optional = true } -sea-orm-codegen = { version = "=0.12.10", path = "../sea-orm-codegen", default-features = false, optional = true } +sea-orm-codegen = { version = "=0.12.11", path = "../sea-orm-codegen", default-features = false, optional = true } sea-schema = { version = "0.14.0" } sqlx = { version = "0.7", default-features = false, features = ["mysql", "postgres"], optional = true } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["env-filter", "fmt"] } diff --git a/sea-orm-codegen/Cargo.toml b/sea-orm-codegen/Cargo.toml index 26bdcbaaee..511cfa23e7 100644 --- a/sea-orm-codegen/Cargo.toml +++ b/sea-orm-codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sea-orm-codegen" -version = "0.12.10" +version = "0.12.11" authors = ["Billy Chan "] edition = "2021" description = "Code Generator for SeaORM" diff --git a/sea-orm-macros/Cargo.toml b/sea-orm-macros/Cargo.toml index 70492c5ac7..2f68b77f0d 100644 --- a/sea-orm-macros/Cargo.toml +++ b/sea-orm-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sea-orm-macros" -version = "0.12.10" +version = "0.12.11" authors = [ "Billy Chan " ] edition = "2021" description = "Derive macros for SeaORM" diff --git a/sea-orm-migration/Cargo.toml b/sea-orm-migration/Cargo.toml index d32f1a7a09..ec9346ef70 100644 --- a/sea-orm-migration/Cargo.toml +++ b/sea-orm-migration/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "sea-orm-migration" -version = "0.12.10" +version = "0.12.11" authors = ["Billy Chan "] edition = "2021" description = "Migration utility for SeaORM" @@ -23,8 +23,8 @@ path = "src/lib.rs" async-trait = { version = "0.1", default-features = false } clap = { version = "4.3", features = ["env", "derive"], optional = true } dotenvy = { version = "0.15", default-features = false, optional = true } -sea-orm = { version = "0.12.10", path = "../", default-features = false, features = ["macros"] } -sea-orm-cli = { version = "0.12.10", path = "../sea-orm-cli", default-features = false, optional = true } +sea-orm = { version = "0.12.11", path = "../", default-features = false, features = ["macros"] } +sea-orm-cli = { version = "0.12.11", path = "../sea-orm-cli", default-features = false, optional = true } sea-schema = { version = "0.14.1" } tracing = { version = "0.1", default-features = false, features = ["log"] } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["env-filter", "fmt"] } From ca6f381d2887e073ab172fc9c7bb4f52fea210f3 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sun, 14 Jan 2024 16:10:33 +0000 Subject: [PATCH 0265/1371] update examples --- examples/actix3_example/entity/Cargo.toml | 2 +- examples/actix3_example/migration/Cargo.toml | 2 +- examples/actix3_example/service/Cargo.toml | 2 +- examples/actix_example/entity/Cargo.toml | 2 +- examples/actix_example/migration/Cargo.toml | 2 +- examples/actix_example/service/Cargo.toml | 2 +- examples/axum_example/entity/Cargo.toml | 2 +- examples/axum_example/migration/Cargo.toml | 2 +- examples/axum_example/service/Cargo.toml | 2 +- examples/graphql_example/entity/Cargo.toml | 2 +- examples/graphql_example/migration/Cargo.toml | 2 +- examples/graphql_example/service/Cargo.toml | 2 +- examples/jsonrpsee_example/entity/Cargo.toml | 2 +- examples/jsonrpsee_example/migration/Cargo.toml | 2 +- examples/jsonrpsee_example/service/Cargo.toml | 2 +- examples/poem_example/entity/Cargo.toml | 2 +- examples/poem_example/migration/Cargo.toml | 2 +- examples/poem_example/service/Cargo.toml | 2 +- examples/rocket_example/entity/Cargo.toml | 2 +- examples/rocket_example/migration/Cargo.toml | 2 +- examples/rocket_example/service/Cargo.toml | 2 +- examples/rocket_okapi_example/entity/Cargo.toml | 2 +- examples/rocket_okapi_example/migration/Cargo.toml | 2 +- examples/rocket_okapi_example/service/Cargo.toml | 2 +- examples/salvo_example/entity/Cargo.toml | 2 +- examples/salvo_example/migration/Cargo.toml | 2 +- examples/salvo_example/service/Cargo.toml | 2 +- examples/seaography_example/migration/Cargo.toml | 4 ++-- examples/tonic_example/entity/Cargo.toml | 2 +- examples/tonic_example/migration/Cargo.toml | 2 +- examples/tonic_example/service/Cargo.toml | 2 +- 31 files changed, 32 insertions(+), 32 deletions(-) diff --git a/examples/actix3_example/entity/Cargo.toml b/examples/actix3_example/entity/Cargo.toml index a50073b434..4463794a64 100644 --- a/examples/actix3_example/entity/Cargo.toml +++ b/examples/actix3_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.10" # sea-orm version +version = "0.12.11" # sea-orm version diff --git a/examples/actix3_example/migration/Cargo.toml b/examples/actix3_example/migration/Cargo.toml index 96530b57e8..731403b022 100644 --- a/examples/actix3_example/migration/Cargo.toml +++ b/examples/actix3_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.10" # sea-orm-migration version +version = "0.12.11" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-async-std-native-tls", diff --git a/examples/actix3_example/service/Cargo.toml b/examples/actix3_example/service/Cargo.toml index ee7e475e4a..816a66ab1f 100644 --- a/examples/actix3_example/service/Cargo.toml +++ b/examples/actix3_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.10" # sea-orm version +version = "0.12.11" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/actix_example/entity/Cargo.toml b/examples/actix_example/entity/Cargo.toml index a50073b434..4463794a64 100644 --- a/examples/actix_example/entity/Cargo.toml +++ b/examples/actix_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.10" # sea-orm version +version = "0.12.11" # sea-orm version diff --git a/examples/actix_example/migration/Cargo.toml b/examples/actix_example/migration/Cargo.toml index a81d8215ad..cd10bd850c 100644 --- a/examples/actix_example/migration/Cargo.toml +++ b/examples/actix_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.10" # sea-orm-migration version +version = "0.12.11" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-actix-native-tls", diff --git a/examples/actix_example/service/Cargo.toml b/examples/actix_example/service/Cargo.toml index 2ecf9a0e37..5f4ba3c83b 100644 --- a/examples/actix_example/service/Cargo.toml +++ b/examples/actix_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.10" # sea-orm version +version = "0.12.11" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/axum_example/entity/Cargo.toml b/examples/axum_example/entity/Cargo.toml index a50073b434..4463794a64 100644 --- a/examples/axum_example/entity/Cargo.toml +++ b/examples/axum_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.10" # sea-orm version +version = "0.12.11" # sea-orm version diff --git a/examples/axum_example/migration/Cargo.toml b/examples/axum_example/migration/Cargo.toml index 9bc4966803..6f1b794df5 100644 --- a/examples/axum_example/migration/Cargo.toml +++ b/examples/axum_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.10" # sea-orm-migration version +version = "0.12.11" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/axum_example/service/Cargo.toml b/examples/axum_example/service/Cargo.toml index 24a9a0b548..372b0832c2 100644 --- a/examples/axum_example/service/Cargo.toml +++ b/examples/axum_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.10" # sea-orm version +version = "0.12.11" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/graphql_example/entity/Cargo.toml b/examples/graphql_example/entity/Cargo.toml index db5f354d34..1279b4cf16 100644 --- a/examples/graphql_example/entity/Cargo.toml +++ b/examples/graphql_example/entity/Cargo.toml @@ -16,4 +16,4 @@ version = "5.0.10" [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.10" # sea-orm version +version = "0.12.11" # sea-orm version diff --git a/examples/graphql_example/migration/Cargo.toml b/examples/graphql_example/migration/Cargo.toml index e25efb7d3e..3d13bbef1d 100644 --- a/examples/graphql_example/migration/Cargo.toml +++ b/examples/graphql_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.10" # sea-orm-migration version +version = "0.12.11" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/graphql_example/service/Cargo.toml b/examples/graphql_example/service/Cargo.toml index 2533739d74..a9a2519f4e 100644 --- a/examples/graphql_example/service/Cargo.toml +++ b/examples/graphql_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.10" # sea-orm version +version = "0.12.11" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/jsonrpsee_example/entity/Cargo.toml b/examples/jsonrpsee_example/entity/Cargo.toml index a50073b434..4463794a64 100644 --- a/examples/jsonrpsee_example/entity/Cargo.toml +++ b/examples/jsonrpsee_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.10" # sea-orm version +version = "0.12.11" # sea-orm version diff --git a/examples/jsonrpsee_example/migration/Cargo.toml b/examples/jsonrpsee_example/migration/Cargo.toml index c0de8f405d..898ad7b8a8 100644 --- a/examples/jsonrpsee_example/migration/Cargo.toml +++ b/examples/jsonrpsee_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.10" # sea-orm-migration version +version = "0.12.11" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/jsonrpsee_example/service/Cargo.toml b/examples/jsonrpsee_example/service/Cargo.toml index 81be9f5a17..45bffe8c57 100644 --- a/examples/jsonrpsee_example/service/Cargo.toml +++ b/examples/jsonrpsee_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.10" # sea-orm version +version = "0.12.11" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/poem_example/entity/Cargo.toml b/examples/poem_example/entity/Cargo.toml index a50073b434..4463794a64 100644 --- a/examples/poem_example/entity/Cargo.toml +++ b/examples/poem_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.10" # sea-orm version +version = "0.12.11" # sea-orm version diff --git a/examples/poem_example/migration/Cargo.toml b/examples/poem_example/migration/Cargo.toml index c0de8f405d..898ad7b8a8 100644 --- a/examples/poem_example/migration/Cargo.toml +++ b/examples/poem_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.10" # sea-orm-migration version +version = "0.12.11" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/poem_example/service/Cargo.toml b/examples/poem_example/service/Cargo.toml index e7009f7d49..aeeddabdbd 100644 --- a/examples/poem_example/service/Cargo.toml +++ b/examples/poem_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.10" # sea-orm version +version = "0.12.11" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/rocket_example/entity/Cargo.toml b/examples/rocket_example/entity/Cargo.toml index 68eec76e9a..67ee1f2350 100644 --- a/examples/rocket_example/entity/Cargo.toml +++ b/examples/rocket_example/entity/Cargo.toml @@ -13,4 +13,4 @@ rocket = { version = "0.5.0-rc.4", features = ["json"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.10" # sea-orm version +version = "0.12.11" # sea-orm version diff --git a/examples/rocket_example/migration/Cargo.toml b/examples/rocket_example/migration/Cargo.toml index b9f136927a..1d11fc040f 100644 --- a/examples/rocket_example/migration/Cargo.toml +++ b/examples/rocket_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.10" # sea-orm-migration version +version = "0.12.11" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/rocket_example/service/Cargo.toml b/examples/rocket_example/service/Cargo.toml index e1fa4975a1..89155d2c9d 100644 --- a/examples/rocket_example/service/Cargo.toml +++ b/examples/rocket_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.10" # sea-orm version +version = "0.12.11" # sea-orm version features = [ "runtime-tokio-native-tls", "sqlx-postgres", diff --git a/examples/rocket_okapi_example/entity/Cargo.toml b/examples/rocket_okapi_example/entity/Cargo.toml index 9357c06e5f..653acebe4f 100644 --- a/examples/rocket_okapi_example/entity/Cargo.toml +++ b/examples/rocket_okapi_example/entity/Cargo.toml @@ -13,7 +13,7 @@ rocket = { version = "0.5.0-rc.4", features = ["json"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.10" # sea-orm version +version = "0.12.11" # sea-orm version [dependencies.rocket_okapi] version = "0.8.0-rc.2" diff --git a/examples/rocket_okapi_example/migration/Cargo.toml b/examples/rocket_okapi_example/migration/Cargo.toml index b9f136927a..1d11fc040f 100644 --- a/examples/rocket_okapi_example/migration/Cargo.toml +++ b/examples/rocket_okapi_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.10" # sea-orm-migration version +version = "0.12.11" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/rocket_okapi_example/service/Cargo.toml b/examples/rocket_okapi_example/service/Cargo.toml index 6f50ca8e8e..39b8004e04 100644 --- a/examples/rocket_okapi_example/service/Cargo.toml +++ b/examples/rocket_okapi_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.10" # sea-orm version +version = "0.12.11" # sea-orm version features = [ "runtime-tokio-native-tls", "sqlx-postgres", diff --git a/examples/salvo_example/entity/Cargo.toml b/examples/salvo_example/entity/Cargo.toml index 2a4f98f8a1..14df9e1f63 100644 --- a/examples/salvo_example/entity/Cargo.toml +++ b/examples/salvo_example/entity/Cargo.toml @@ -14,4 +14,4 @@ salvo = { version = "0.50" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.10" # sea-orm version +version = "0.12.11" # sea-orm version diff --git a/examples/salvo_example/migration/Cargo.toml b/examples/salvo_example/migration/Cargo.toml index c0de8f405d..898ad7b8a8 100644 --- a/examples/salvo_example/migration/Cargo.toml +++ b/examples/salvo_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.10" # sea-orm-migration version +version = "0.12.11" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/salvo_example/service/Cargo.toml b/examples/salvo_example/service/Cargo.toml index bbe435b7c7..a527f1bf30 100644 --- a/examples/salvo_example/service/Cargo.toml +++ b/examples/salvo_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.10" # sea-orm version +version = "0.12.11" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/seaography_example/migration/Cargo.toml b/examples/seaography_example/migration/Cargo.toml index 2c4857803e..eaa5dcbd06 100644 --- a/examples/seaography_example/migration/Cargo.toml +++ b/examples/seaography_example/migration/Cargo.toml @@ -15,11 +15,11 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm] path = "../../.." # remove this line in your own project -version = "0.12.10" # sea-orm version +version = "0.12.11" # sea-orm version [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.10" # sea-orm-migration version +version = "0.12.11" # sea-orm-migration version features = [ "runtime-async-std-native-tls", "sqlx-mysql", diff --git a/examples/tonic_example/entity/Cargo.toml b/examples/tonic_example/entity/Cargo.toml index a50073b434..4463794a64 100644 --- a/examples/tonic_example/entity/Cargo.toml +++ b/examples/tonic_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.10" # sea-orm version +version = "0.12.11" # sea-orm version diff --git a/examples/tonic_example/migration/Cargo.toml b/examples/tonic_example/migration/Cargo.toml index 653d0d41b0..9aefa76077 100644 --- a/examples/tonic_example/migration/Cargo.toml +++ b/examples/tonic_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.10" # sea-orm-migration version +version = "0.12.11" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-rustls", diff --git a/examples/tonic_example/service/Cargo.toml b/examples/tonic_example/service/Cargo.toml index cfb76930f2..78366a8ebb 100644 --- a/examples/tonic_example/service/Cargo.toml +++ b/examples/tonic_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.10" # sea-orm version +version = "0.12.11" # sea-orm version features = [ "debug-print", "runtime-tokio-rustls", From 131f9f11230b7fd2d318870fd0c2d441c80ed734 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sun, 14 Jan 2024 16:15:21 +0000 Subject: [PATCH 0266/1371] Changelog --- CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 735ec00d04..825c24e579 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,14 +17,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/). * Added SQLx slow statements logging to `ConnectOptions` https://github.com/SeaQL/sea-orm/pull/2055 * Added `QuerySelect::lock_with_behavior` https://github.com/SeaQL/sea-orm/pull/1867 -### House keeping - -* Fix clippy warnings on 1.75 https://github.com/SeaQL/sea-orm/pull/2057 - ### Bug Fixes * [sea-orm-macro] Qualify types in `DeriveValueType` macro https://github.com/SeaQL/sea-orm/pull/2054 +### House keeping + +* Fix clippy warnings on 1.75 https://github.com/SeaQL/sea-orm/pull/2057 + ## 0.12.10 - 2023-12-14 ### New Features From d8582c30ab4c6a28edbd1e03645699f0d1750d87 Mon Sep 17 00:00:00 2001 From: Zhang Yao Date: Fri, 19 Jan 2024 18:39:57 +0800 Subject: [PATCH 0267/1371] Fix problems in #1819 (#1821) * Fix problems in #1819 * Add test cases --------- Co-authored-by: Billy Chan --- sea-orm-codegen/src/entity/active_enum.rs | 62 ++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/sea-orm-codegen/src/entity/active_enum.rs b/sea-orm-codegen/src/entity/active_enum.rs index e7e0480c7b..e6a0b64b5c 100644 --- a/sea-orm-codegen/src/entity/active_enum.rs +++ b/sea-orm-codegen/src/entity/active_enum.rs @@ -26,7 +26,18 @@ impl ActiveEnum { if v.chars().next().map(char::is_numeric).unwrap_or(false) { format_ident!("_{}", v) } else { - format_ident!("{}", v.to_upper_camel_case()) + let variant_name = v.to_upper_camel_case(); + if variant_name.is_empty() { + println!("Warning: item '{}' in the enumeration '{}' cannot be converted into a valid Rust enum member name. It will be converted to its corresponding UTF-8 encoding. You can modify it later as needed.", v, enum_name); + let mut utf_string = String::new(); + for c in v.chars() { + utf_string.push('U'); + utf_string.push_str(&format!("{:04X}", c as u32)); + } + format_ident!("{}", utf_string) + } else { + format_ident!("{}", variant_name) + } } }); @@ -223,4 +234,53 @@ mod tests { .to_string() ) } + + #[test] + fn test_enum_variant_utf8_encode() { + assert_eq!( + ActiveEnum { + enum_name: Alias::new("ty").into_iden(), + values: vec![ + "Question", + "QuestionsAdditional", + "Answer", + "Other", + "/", + "//", + "A-B-C", + ] + .into_iter() + .map(|variant| Alias::new(variant).into_iden()) + .collect(), + } + .impl_active_enum( + &WithSerde::None, + true, + &TokenStream::new(), + &TokenStream::new(), + ) + .to_string(), + quote!( + #[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Copy)] + #[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "ty")] + pub enum Ty { + #[sea_orm(string_value = "Question")] + Question, + #[sea_orm(string_value = "QuestionsAdditional")] + QuestionsAdditional, + #[sea_orm(string_value = "Answer")] + Answer, + #[sea_orm(string_value = "Other")] + Other, + #[sea_orm(string_value = "/")] + U002F, + #[sea_orm(string_value = "//")] + U002FU002F, + #[sea_orm(string_value = "A-B-C")] + ABC, + } + ) + .to_string() + ) + } } From f2bda8a4c22b2f83ea876f9425c9ee8a592da98b Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Wed, 17 Jan 2024 20:55:12 +0000 Subject: [PATCH 0268/1371] Changelog --- CHANGELOG.md | 7 +++++++ sea-orm-cli/Cargo.toml | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 825c24e579..04effd54cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## 0.12.12 - Pending + +### Bug Fixes + +* [sea-orm-cli] Fix entity generation for non-alphanumeric enum variants https://github.com/SeaQL/sea-orm/pull/1821 +* [sea-orm-cli] Fix entity generation for relations with composite keys https://github.com/SeaQL/sea-orm/pull/2071 + ## 0.12.11 - 2023-01-14 ### New Features diff --git a/sea-orm-cli/Cargo.toml b/sea-orm-cli/Cargo.toml index 9d3f36f089..f100b0d794 100644 --- a/sea-orm-cli/Cargo.toml +++ b/sea-orm-cli/Cargo.toml @@ -38,7 +38,7 @@ clap = { version = "4.3", features = ["env", "derive"], optional = true } dotenvy = { version = "0.15", default-features = false, optional = true } async-std = { version = "1.9", default-features = false, features = ["attributes", "tokio1"], optional = true } sea-orm-codegen = { version = "=0.12.11", path = "../sea-orm-codegen", default-features = false, optional = true } -sea-schema = { version = "0.14.0" } +sea-schema = { version = "0.14.2" } sqlx = { version = "0.7", default-features = false, features = ["mysql", "postgres"], optional = true } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["env-filter", "fmt"] } tracing = { version = "0.1", default-features = false } From c6e1a84a120dff5a73abe450fb56ed4483d9413d Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Fri, 19 Jan 2024 18:47:49 +0800 Subject: [PATCH 0269/1371] codegen: testing entity generation of composite foreign key (#2071) --- sea-orm-codegen/src/entity/writer.rs | 96 ++++++++++++++++++- sea-orm-codegen/tests/compact/child.rs | 30 ++++++ sea-orm-codegen/tests/compact/parent.rs | 26 +++++ .../tests/compact_with_schema_name/child.rs | 30 ++++++ .../tests/compact_with_schema_name/parent.rs | 26 +++++ sea-orm-codegen/tests/expanded/child.rs | 73 ++++++++++++++ sea-orm-codegen/tests/expanded/parent.rs | 68 +++++++++++++ .../tests/expanded_with_schema_name/child.rs | 77 +++++++++++++++ .../tests/expanded_with_schema_name/parent.rs | 72 ++++++++++++++ 9 files changed, 494 insertions(+), 4 deletions(-) create mode 100644 sea-orm-codegen/tests/compact/child.rs create mode 100644 sea-orm-codegen/tests/compact/parent.rs create mode 100644 sea-orm-codegen/tests/compact_with_schema_name/child.rs create mode 100644 sea-orm-codegen/tests/compact_with_schema_name/parent.rs create mode 100644 sea-orm-codegen/tests/expanded/child.rs create mode 100644 sea-orm-codegen/tests/expanded/parent.rs create mode 100644 sea-orm-codegen/tests/expanded_with_schema_name/child.rs create mode 100644 sea-orm-codegen/tests/expanded_with_schema_name/parent.rs diff --git a/sea-orm-codegen/src/entity/writer.rs b/sea-orm-codegen/src/entity/writer.rs index 099a96797d..3c4c17eebe 100644 --- a/sea-orm-codegen/src/entity/writer.rs +++ b/sea-orm-codegen/src/entity/writer.rs @@ -1410,6 +1410,86 @@ mod tests { name: "id".to_owned(), }], }, + Entity { + table_name: "parent".to_owned(), + columns: vec![ + Column { + name: "id1".to_owned(), + col_type: ColumnType::Integer, + auto_increment: false, + not_null: true, + unique: false, + }, + Column { + name: "id2".to_owned(), + col_type: ColumnType::Integer, + auto_increment: false, + not_null: true, + unique: false, + }, + ], + relations: vec![Relation { + ref_table: "child".to_owned(), + columns: vec![], + ref_columns: vec![], + rel_type: RelationType::HasMany, + on_delete: None, + on_update: None, + self_referencing: false, + num_suffix: 0, + impl_related: true, + }], + conjunct_relations: vec![], + primary_keys: vec![ + PrimaryKey { + name: "id1".to_owned(), + }, + PrimaryKey { + name: "id2".to_owned(), + }, + ], + }, + Entity { + table_name: "child".to_owned(), + columns: vec![ + Column { + name: "id".to_owned(), + col_type: ColumnType::Integer, + auto_increment: true, + not_null: true, + unique: false, + }, + Column { + name: "parent_id1".to_owned(), + col_type: ColumnType::Integer, + auto_increment: false, + not_null: true, + unique: false, + }, + Column { + name: "parent_id2".to_owned(), + col_type: ColumnType::Integer, + auto_increment: false, + not_null: true, + unique: false, + }, + ], + relations: vec![Relation { + ref_table: "parent".to_owned(), + columns: vec!["parent_id1".to_owned(), "parent_id2".to_owned()], + ref_columns: vec!["id1".to_owned(), "id2".to_owned()], + rel_type: RelationType::BelongsTo, + on_delete: None, + on_update: None, + self_referencing: false, + num_suffix: 0, + impl_related: true, + }], + conjunct_relations: vec![], + primary_keys: vec![PrimaryKey { + name: "id".to_owned(), + }], + }, ] } @@ -1434,7 +1514,7 @@ mod tests { #[test] fn test_gen_expanded_code_blocks() -> io::Result<()> { let entities = setup(); - const ENTITY_FILES: [&str; 11] = [ + const ENTITY_FILES: [&str; 13] = [ include_str!("../../tests/expanded/cake.rs"), include_str!("../../tests/expanded/cake_filling.rs"), include_str!("../../tests/expanded/cake_filling_price.rs"), @@ -1446,8 +1526,10 @@ mod tests { include_str!("../../tests/expanded/cake_with_double.rs"), include_str!("../../tests/expanded/collection.rs"), include_str!("../../tests/expanded/collection_float.rs"), + include_str!("../../tests/expanded/parent.rs"), + include_str!("../../tests/expanded/child.rs"), ]; - const ENTITY_FILES_WITH_SCHEMA_NAME: [&str; 11] = [ + const ENTITY_FILES_WITH_SCHEMA_NAME: [&str; 13] = [ include_str!("../../tests/expanded_with_schema_name/cake.rs"), include_str!("../../tests/expanded_with_schema_name/cake_filling.rs"), include_str!("../../tests/expanded_with_schema_name/cake_filling_price.rs"), @@ -1459,6 +1541,8 @@ mod tests { include_str!("../../tests/expanded_with_schema_name/cake_with_double.rs"), include_str!("../../tests/expanded_with_schema_name/collection.rs"), include_str!("../../tests/expanded_with_schema_name/collection_float.rs"), + include_str!("../../tests/expanded_with_schema_name/parent.rs"), + include_str!("../../tests/expanded_with_schema_name/child.rs"), ]; assert_eq!(entities.len(), ENTITY_FILES.len()); @@ -1535,7 +1619,7 @@ mod tests { #[test] fn test_gen_compact_code_blocks() -> io::Result<()> { let entities = setup(); - const ENTITY_FILES: [&str; 11] = [ + const ENTITY_FILES: [&str; 13] = [ include_str!("../../tests/compact/cake.rs"), include_str!("../../tests/compact/cake_filling.rs"), include_str!("../../tests/compact/cake_filling_price.rs"), @@ -1547,8 +1631,10 @@ mod tests { include_str!("../../tests/compact/cake_with_double.rs"), include_str!("../../tests/compact/collection.rs"), include_str!("../../tests/compact/collection_float.rs"), + include_str!("../../tests/compact/parent.rs"), + include_str!("../../tests/compact/child.rs"), ]; - const ENTITY_FILES_WITH_SCHEMA_NAME: [&str; 11] = [ + const ENTITY_FILES_WITH_SCHEMA_NAME: [&str; 13] = [ include_str!("../../tests/compact_with_schema_name/cake.rs"), include_str!("../../tests/compact_with_schema_name/cake_filling.rs"), include_str!("../../tests/compact_with_schema_name/cake_filling_price.rs"), @@ -1560,6 +1646,8 @@ mod tests { include_str!("../../tests/compact_with_schema_name/cake_with_double.rs"), include_str!("../../tests/compact_with_schema_name/collection.rs"), include_str!("../../tests/compact_with_schema_name/collection_float.rs"), + include_str!("../../tests/compact_with_schema_name/parent.rs"), + include_str!("../../tests/compact_with_schema_name/child.rs"), ]; assert_eq!(entities.len(), ENTITY_FILES.len()); diff --git a/sea-orm-codegen/tests/compact/child.rs b/sea-orm-codegen/tests/compact/child.rs new file mode 100644 index 0000000000..428d09631d --- /dev/null +++ b/sea-orm-codegen/tests/compact/child.rs @@ -0,0 +1,30 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "child")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub parent_id1: i32, + pub parent_id2: i32, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::parent::Entity", + from = "(Column::ParentId1, Column::ParentId2)", + to = "(super::parent::Column::Id1, super::parent::Column::Id2)", + )] + Parent, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Parent.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/sea-orm-codegen/tests/compact/parent.rs b/sea-orm-codegen/tests/compact/parent.rs new file mode 100644 index 0000000000..25dc9aeea0 --- /dev/null +++ b/sea-orm-codegen/tests/compact/parent.rs @@ -0,0 +1,26 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "parent")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id1: i32, + #[sea_orm(primary_key, auto_increment = false)] + pub id2: i32, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::child::Entity")] + Child, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Child.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/sea-orm-codegen/tests/compact_with_schema_name/child.rs b/sea-orm-codegen/tests/compact_with_schema_name/child.rs new file mode 100644 index 0000000000..28e687d614 --- /dev/null +++ b/sea-orm-codegen/tests/compact_with_schema_name/child.rs @@ -0,0 +1,30 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(schema_name = "schema_name", table_name = "child")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub parent_id1: i32, + pub parent_id2: i32, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::parent::Entity", + from = "(Column::ParentId1, Column::ParentId2)", + to = "(super::parent::Column::Id1, super::parent::Column::Id2)", + )] + Parent, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Parent.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/sea-orm-codegen/tests/compact_with_schema_name/parent.rs b/sea-orm-codegen/tests/compact_with_schema_name/parent.rs new file mode 100644 index 0000000000..3bd4ac071c --- /dev/null +++ b/sea-orm-codegen/tests/compact_with_schema_name/parent.rs @@ -0,0 +1,26 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(schema_name = "schema_name", table_name = "parent")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id1: i32, + #[sea_orm(primary_key, auto_increment = false)] + pub id2: i32, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::child::Entity")] + Child, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Child.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/sea-orm-codegen/tests/expanded/child.rs b/sea-orm-codegen/tests/expanded/child.rs new file mode 100644 index 0000000000..7073fffb30 --- /dev/null +++ b/sea-orm-codegen/tests/expanded/child.rs @@ -0,0 +1,73 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.10.0 + +use sea_orm::entity::prelude::*; + +#[derive(Copy, Clone, Default, Debug, DeriveEntity)] +pub struct Entity; + +impl EntityName for Entity { + fn table_name(&self) -> &str { + "child" + } +} + +#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Eq)] +pub struct Model { + pub id: i32, + pub parent_id1: i32, + pub parent_id2: i32, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] +pub enum Column { + Id, + ParentId1, + ParentId2, +} + +#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] +pub enum PrimaryKey { + Id, +} + +impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { + true + } +} + +#[derive(Copy, Clone, Debug, EnumIter)] +pub enum Relation { + Parent, +} + +impl ColumnTrait for Column { + type EntityName = Entity; + fn def(&self) -> ColumnDef { + match self { + Self::Id => ColumnType::Integer.def(), + Self::ParentId1 => ColumnType::Integer.def(), + Self::ParentId2 => ColumnType::Integer.def(), + } + } +} + +impl RelationTrait for Relation { + fn def(&self) -> RelationDef { + match self { + Self::Parent => Entity::belongs_to(super::parent::Entity) + .from((Column::ParentId1, Column::ParentId2)) + .to((super::parent::Column::Id1, super::parent::Column::Id2)) + .into(), + } + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Parent.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/sea-orm-codegen/tests/expanded/parent.rs b/sea-orm-codegen/tests/expanded/parent.rs new file mode 100644 index 0000000000..2ecaa5916c --- /dev/null +++ b/sea-orm-codegen/tests/expanded/parent.rs @@ -0,0 +1,68 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.10.0 + +use sea_orm::entity::prelude::*; + +#[derive(Copy, Clone, Default, Debug, DeriveEntity)] +pub struct Entity; + +impl EntityName for Entity { + fn table_name(&self) -> &str { + "parent" + } +} + +#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Eq)] +pub struct Model { + pub id1: i32, + pub id2: i32, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] +pub enum Column { + Id1, + Id2, +} + +#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] +pub enum PrimaryKey { + Id1, + Id2, +} + +impl PrimaryKeyTrait for PrimaryKey { + type ValueType = (i32, i32); + fn auto_increment() -> bool { + false + } +} + +#[derive(Copy, Clone, Debug, EnumIter)] +pub enum Relation { + Child, +} + +impl ColumnTrait for Column { + type EntityName = Entity; + fn def(&self) -> ColumnDef { + match self { + Self::Id1 => ColumnType::Integer.def(), + Self::Id2 => ColumnType::Integer.def(), + } + } +} + +impl RelationTrait for Relation { + fn def(&self) -> RelationDef { + match self { + Self::Child => Entity::has_many(super::child::Entity).into(), + } + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Child.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/sea-orm-codegen/tests/expanded_with_schema_name/child.rs b/sea-orm-codegen/tests/expanded_with_schema_name/child.rs new file mode 100644 index 0000000000..05ba5bfa58 --- /dev/null +++ b/sea-orm-codegen/tests/expanded_with_schema_name/child.rs @@ -0,0 +1,77 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.10.0 + +use sea_orm::entity::prelude::*; + +#[derive(Copy, Clone, Default, Debug, DeriveEntity)] +pub struct Entity; + +impl EntityName for Entity { + fn schema_name(&self) -> Option< &str > { + Some("schema_name") + } + + fn table_name(&self) -> &str { + "child" + } +} + +#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Eq)] +pub struct Model { + pub id: i32, + pub parent_id1: i32, + pub parent_id2: i32, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] +pub enum Column { + Id, + ParentId1, + ParentId2, +} + +#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] +pub enum PrimaryKey { + Id, +} + +impl PrimaryKeyTrait for PrimaryKey { + type ValueType = i32; + fn auto_increment() -> bool { + true + } +} + +#[derive(Copy, Clone, Debug, EnumIter)] +pub enum Relation { + Parent, +} + +impl ColumnTrait for Column { + type EntityName = Entity; + fn def(&self) -> ColumnDef { + match self { + Self::Id => ColumnType::Integer.def(), + Self::ParentId1 => ColumnType::Integer.def(), + Self::ParentId2 => ColumnType::Integer.def(), + } + } +} + +impl RelationTrait for Relation { + fn def(&self) -> RelationDef { + match self { + Self::Parent => Entity::belongs_to(super::parent::Entity) + .from((Column::ParentId1, Column::ParentId2)) + .to((super::parent::Column::Id1, super::parent::Column::Id2)) + .into(), + } + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Parent.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/sea-orm-codegen/tests/expanded_with_schema_name/parent.rs b/sea-orm-codegen/tests/expanded_with_schema_name/parent.rs new file mode 100644 index 0000000000..1665c69ed4 --- /dev/null +++ b/sea-orm-codegen/tests/expanded_with_schema_name/parent.rs @@ -0,0 +1,72 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.10.0 + +use sea_orm::entity::prelude::*; + +#[derive(Copy, Clone, Default, Debug, DeriveEntity)] +pub struct Entity; + +impl EntityName for Entity { + fn schema_name(&self) -> Option< &str > { + Some("schema_name") + } + + fn table_name(&self) -> &str { + "parent" + } +} + +#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Eq)] +pub struct Model { + pub id1: i32, + pub id2: i32, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] +pub enum Column { + Id1, + Id2, +} + +#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] +pub enum PrimaryKey { + Id1, + Id2, +} + +impl PrimaryKeyTrait for PrimaryKey { + type ValueType = (i32, i32); + fn auto_increment() -> bool { + false + } +} + +#[derive(Copy, Clone, Debug, EnumIter)] +pub enum Relation { + Child, +} + +impl ColumnTrait for Column { + type EntityName = Entity; + fn def(&self) -> ColumnDef { + match self { + Self::Id1 => ColumnType::Integer.def(), + Self::Id2 => ColumnType::Integer.def(), + } + } +} + +impl RelationTrait for Relation { + fn def(&self) -> RelationDef { + match self { + Self::Child => Entity::has_many(super::child::Entity).into(), + } + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Child.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} From 382ffa3054128f80b8500634e97fe9e30a759f63 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sat, 20 Jan 2024 22:05:16 +0000 Subject: [PATCH 0270/1371] Add `ConnectOptions::test_before_acquire` #2066 --- src/database/mod.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/database/mod.rs b/src/database/mod.rs index 1cd41a53d7..6258cb1401 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -62,6 +62,7 @@ pub struct ConnectOptions { pub(crate) sqlcipher_key: Option>, /// Schema search path (PostgreSQL only) pub(crate) schema_search_path: Option, + pub(crate) test_before_acquire: bool, } impl Database { @@ -155,6 +156,7 @@ impl ConnectOptions { sqlx_slow_statements_logging_threshold: Duration::from_secs(1), sqlcipher_key: None, schema_search_path: None, + test_before_acquire: true, } } @@ -183,6 +185,7 @@ impl ConnectOptions { if let Some(max_lifetime) = self.max_lifetime { opt = opt.max_lifetime(Some(max_lifetime)); } + opt = opt.test_before_acquire(self.test_before_acquire); opt } @@ -268,14 +271,14 @@ impl ConnectOptions { self.sqlx_logging } - /// Set SQLx statement logging level (default INFO) + /// Set SQLx statement logging level (default INFO). /// (ignored if `sqlx_logging` is `false`) pub fn sqlx_logging_level(&mut self, level: log::LevelFilter) -> &mut Self { self.sqlx_logging_level = level; self } - /// Set SQLx slow statements logging level and duration threshold + /// Set SQLx slow statements logging level and duration threshold (default `LevelFilter::Off`). /// (ignored if `sqlx_logging` is `false`) pub fn sqlx_slow_statements_logging_settings( &mut self, @@ -317,4 +320,10 @@ impl ConnectOptions { self.schema_search_path = Some(schema_search_path.into()); self } + + /// If true, the connection will be pinged after acquiring from the pool (default true). + pub fn test_before_acquire(&mut self, value: bool) -> &mut Self { + self.test_before_acquire = value; + self + } } From 59afbd4cdf59356670a5e9ecdf1224723d043214 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sat, 20 Jan 2024 22:06:28 +0000 Subject: [PATCH 0271/1371] Changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04effd54cc..4d1676b449 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). * [sea-orm-cli] Fix entity generation for non-alphanumeric enum variants https://github.com/SeaQL/sea-orm/pull/1821 * [sea-orm-cli] Fix entity generation for relations with composite keys https://github.com/SeaQL/sea-orm/pull/2071 +### Enhancements + +* Added `ConnectOptions::test_before_acquire` + ## 0.12.11 - 2023-01-14 ### New Features From 4690bafcbdad901f1d35f0e60e58c7ab15635737 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sat, 20 Jan 2024 22:08:35 +0000 Subject: [PATCH 0272/1371] Edit --- src/database/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/database/mod.rs b/src/database/mod.rs index 6258cb1401..891481a2ea 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -321,7 +321,7 @@ impl ConnectOptions { self } - /// If true, the connection will be pinged after acquiring from the pool (default true). + /// If true, the connection will be pinged upon acquiring from the pool (default true). pub fn test_before_acquire(&mut self, value: bool) -> &mut Self { self.test_before_acquire = value; self From c2e6e54a504c5c09c60a5434466ce0fb9af99e9b Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sun, 21 Jan 2024 16:17:23 +0000 Subject: [PATCH 0273/1371] Update Rocket --- examples/rocket_example/api/Cargo.toml | 2 +- examples/rocket_example/entity/Cargo.toml | 2 +- examples/rocket_example/migration/Cargo.toml | 2 +- examples/rocket_okapi_example/api/Cargo.toml | 2 +- examples/rocket_okapi_example/dto/Cargo.toml | 2 +- examples/rocket_okapi_example/entity/Cargo.toml | 2 +- examples/rocket_okapi_example/migration/Cargo.toml | 2 +- sea-orm-rocket/codegen/Cargo.toml | 2 +- sea-orm-rocket/lib/Cargo.toml | 4 ++-- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/rocket_example/api/Cargo.toml b/examples/rocket_example/api/Cargo.toml index cbcc304788..a9282eb446 100644 --- a/examples/rocket_example/api/Cargo.toml +++ b/examples/rocket_example/api/Cargo.toml @@ -11,7 +11,7 @@ async-trait = { version = "0.1" } rocket-example-service = { path = "../service" } futures = { version = "0.3" } futures-util = { version = "0.3" } -rocket = { version = "0.5.0-rc.4", features = ["json"] } +rocket = { version = "0.5.0", features = ["json"] } rocket_dyn_templates = { version = "0.1.0-rc.1", features = ["tera"] } serde_json = { version = "1" } entity = { path = "../entity" } diff --git a/examples/rocket_example/entity/Cargo.toml b/examples/rocket_example/entity/Cargo.toml index 67ee1f2350..8b55ace602 100644 --- a/examples/rocket_example/entity/Cargo.toml +++ b/examples/rocket_example/entity/Cargo.toml @@ -9,7 +9,7 @@ name = "entity" path = "src/lib.rs" [dependencies] -rocket = { version = "0.5.0-rc.4", features = ["json"] } +rocket = { version = "0.5.0", features = ["json"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project diff --git a/examples/rocket_example/migration/Cargo.toml b/examples/rocket_example/migration/Cargo.toml index 1d11fc040f..8a68bc6ea8 100644 --- a/examples/rocket_example/migration/Cargo.toml +++ b/examples/rocket_example/migration/Cargo.toml @@ -9,7 +9,7 @@ name = "migration" path = "src/lib.rs" [dependencies] -rocket = { version = "0.5.0-rc.4" } +rocket = { version = "0.5.0" } async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] diff --git a/examples/rocket_okapi_example/api/Cargo.toml b/examples/rocket_okapi_example/api/Cargo.toml index 91fe0e6a19..eeb94822df 100644 --- a/examples/rocket_okapi_example/api/Cargo.toml +++ b/examples/rocket_okapi_example/api/Cargo.toml @@ -11,7 +11,7 @@ async-trait = { version = "0.1" } rocket-okapi-example-service = { path = "../service" } futures = { version = "0.3" } futures-util = { version = "0.3" } -rocket = { version = "0.5.0-rc.4", features = ["json"] } +rocket = { version = "0.5.0", features = ["json"] } rocket_dyn_templates = { version = "0.1.0-rc.1", features = ["tera"] } serde_json = { version = "1" } entity = { path = "../entity" } diff --git a/examples/rocket_okapi_example/dto/Cargo.toml b/examples/rocket_okapi_example/dto/Cargo.toml index 233a3dc8cf..1f08ac4e72 100644 --- a/examples/rocket_okapi_example/dto/Cargo.toml +++ b/examples/rocket_okapi_example/dto/Cargo.toml @@ -9,7 +9,7 @@ name = "dto" path = "src/lib.rs" [dependencies] -rocket = { version = "0.5.0-rc.4", features = ["json"] } +rocket = { version = "0.5.0", features = ["json"] } [dependencies.entity] path = "../entity" diff --git a/examples/rocket_okapi_example/entity/Cargo.toml b/examples/rocket_okapi_example/entity/Cargo.toml index 653acebe4f..051056def7 100644 --- a/examples/rocket_okapi_example/entity/Cargo.toml +++ b/examples/rocket_okapi_example/entity/Cargo.toml @@ -9,7 +9,7 @@ name = "entity" path = "src/lib.rs" [dependencies] -rocket = { version = "0.5.0-rc.4", features = ["json"] } +rocket = { version = "0.5.0", features = ["json"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project diff --git a/examples/rocket_okapi_example/migration/Cargo.toml b/examples/rocket_okapi_example/migration/Cargo.toml index 1d11fc040f..8a68bc6ea8 100644 --- a/examples/rocket_okapi_example/migration/Cargo.toml +++ b/examples/rocket_okapi_example/migration/Cargo.toml @@ -9,7 +9,7 @@ name = "migration" path = "src/lib.rs" [dependencies] -rocket = { version = "0.5.0-rc.4" } +rocket = { version = "0.5.0" } async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] diff --git a/sea-orm-rocket/codegen/Cargo.toml b/sea-orm-rocket/codegen/Cargo.toml index 52600884ba..5b41fbed9d 100644 --- a/sea-orm-rocket/codegen/Cargo.toml +++ b/sea-orm-rocket/codegen/Cargo.toml @@ -17,6 +17,6 @@ devise = "0.3" quote = "1" [dev-dependencies] -rocket = { version = "0.5.0-rc.4", default-features = false } +rocket = { version = "0.5.0", default-features = false } trybuild = "1.0" version_check = "0.9" diff --git a/sea-orm-rocket/lib/Cargo.toml b/sea-orm-rocket/lib/Cargo.toml index c9dcaffd62..a8dd6ccffc 100644 --- a/sea-orm-rocket/lib/Cargo.toml +++ b/sea-orm-rocket/lib/Cargo.toml @@ -13,7 +13,7 @@ edition = "2021" all-features = true [dependencies.rocket] -version = "0.5.0-rc.4" +version = "0.5.0" default-features = false [dependencies.sea-orm-rocket-codegen] @@ -26,7 +26,7 @@ version = "0.5.1" # optional = true [dev-dependencies.rocket] -version = "0.5.0-rc.4" +version = "0.5.0" default-features = false features = ["json"] From 00eba94c4f7e42a166357babe0f9b12e97f57814 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sun, 21 Jan 2024 16:22:48 +0000 Subject: [PATCH 0274/1371] Bump sea-orm-rocket --- sea-orm-rocket/codegen/Cargo.toml | 2 +- sea-orm-rocket/lib/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sea-orm-rocket/codegen/Cargo.toml b/sea-orm-rocket/codegen/Cargo.toml index 5b41fbed9d..838f973ef1 100644 --- a/sea-orm-rocket/codegen/Cargo.toml +++ b/sea-orm-rocket/codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sea-orm-rocket-codegen" -version = "0.5.1" +version = "0.5.4" authors = ["Sergio Benitez ", "Jeb Rosen "] description = "Procedural macros for sea_orm_rocket." repository = "https://github.com/SergioBenitez/Rocket/contrib/db_pools" diff --git a/sea-orm-rocket/lib/Cargo.toml b/sea-orm-rocket/lib/Cargo.toml index a8dd6ccffc..095cf8a008 100644 --- a/sea-orm-rocket/lib/Cargo.toml +++ b/sea-orm-rocket/lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sea-orm-rocket" -version = "0.5.3" +version = "0.5.4" authors = ["Sergio Benitez ", "Jeb Rosen "] description = "SeaORM Rocket support crate" repository = "https://github.com/SeaQL/sea-orm" From 9b53db99c325edddeee3b58ce603abc095fe9775 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sun, 21 Jan 2024 17:17:09 +0000 Subject: [PATCH 0275/1371] Edit --- examples/rocket_example/api/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/rocket_example/api/Cargo.toml b/examples/rocket_example/api/Cargo.toml index a9282eb446..1b75877421 100644 --- a/examples/rocket_example/api/Cargo.toml +++ b/examples/rocket_example/api/Cargo.toml @@ -20,4 +20,4 @@ tokio = "1.29.0" [dependencies.sea-orm-rocket] path = "../../../sea-orm-rocket/lib" # remove this line in your own project and uncomment the following line -# version = "0.5.3" +# version = "0.5" From f16bac9a9f1d12fc4f79ea22dcf369f6bfe4732a Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sun, 21 Jan 2024 17:17:17 +0000 Subject: [PATCH 0276/1371] Edit --- examples/rocket_okapi_example/api/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/rocket_okapi_example/api/Cargo.toml b/examples/rocket_okapi_example/api/Cargo.toml index eeb94822df..e9adc091c0 100644 --- a/examples/rocket_okapi_example/api/Cargo.toml +++ b/examples/rocket_okapi_example/api/Cargo.toml @@ -25,7 +25,7 @@ path = "../../../sea-orm-rocket/lib" # remove this line in your own project and features = [ "rocket_okapi", ] # enables rocket_okapi so to have open api features enabled -# version = "0.5.1" +# version = "0.5" [dependencies.rocket_okapi] version = "0.8.0-rc.2" From 70d4278598d762a94c113616c2c68c9a82ab1fe3 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Mon, 22 Jan 2024 09:48:29 +0000 Subject: [PATCH 0277/1371] 0.12.12 --- Cargo.toml | 4 ++-- sea-orm-cli/Cargo.toml | 4 ++-- sea-orm-codegen/Cargo.toml | 2 +- sea-orm-macros/Cargo.toml | 2 +- sea-orm-migration/Cargo.toml | 6 +++--- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 22676c2305..a9e94916ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [".", "sea-orm-macros", "sea-orm-codegen"] [package] name = "sea-orm" -version = "0.12.11" +version = "0.12.12" authors = ["Chris Tsang "] edition = "2021" description = "🐚 An async & dynamic ORM for Rust" @@ -33,7 +33,7 @@ log = { version = "0.4", default-features = false } tracing = { version = "0.1", default-features = false, features = ["attributes", "log"] } rust_decimal = { version = "1", default-features = false, optional = true } bigdecimal = { version = "0.3", default-features = false, optional = true } -sea-orm-macros = { version = "0.12.11", path = "sea-orm-macros", default-features = false, features = ["strum"] } +sea-orm-macros = { version = "0.12.12", path = "sea-orm-macros", default-features = false, features = ["strum"] } sea-query = { version = "0.30.4", default-features = false, features = ["thread-safe", "hashable-value", "backend-mysql", "backend-postgres", "backend-sqlite"] } sea-query-binder = { version = "0.5.0", default-features = false, optional = true } strum = { version = "0.25", default-features = false } diff --git a/sea-orm-cli/Cargo.toml b/sea-orm-cli/Cargo.toml index f100b0d794..2ab7d6fd33 100644 --- a/sea-orm-cli/Cargo.toml +++ b/sea-orm-cli/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "sea-orm-cli" -version = "0.12.11" +version = "0.12.12" authors = [ "Chris Tsang ", "Billy Chan ", @@ -37,7 +37,7 @@ required-features = ["cli", "codegen"] clap = { version = "4.3", features = ["env", "derive"], optional = true } dotenvy = { version = "0.15", default-features = false, optional = true } async-std = { version = "1.9", default-features = false, features = ["attributes", "tokio1"], optional = true } -sea-orm-codegen = { version = "=0.12.11", path = "../sea-orm-codegen", default-features = false, optional = true } +sea-orm-codegen = { version = "=0.12.12", path = "../sea-orm-codegen", default-features = false, optional = true } sea-schema = { version = "0.14.2" } sqlx = { version = "0.7", default-features = false, features = ["mysql", "postgres"], optional = true } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["env-filter", "fmt"] } diff --git a/sea-orm-codegen/Cargo.toml b/sea-orm-codegen/Cargo.toml index 511cfa23e7..7ba52b49ba 100644 --- a/sea-orm-codegen/Cargo.toml +++ b/sea-orm-codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sea-orm-codegen" -version = "0.12.11" +version = "0.12.12" authors = ["Billy Chan "] edition = "2021" description = "Code Generator for SeaORM" diff --git a/sea-orm-macros/Cargo.toml b/sea-orm-macros/Cargo.toml index 2f68b77f0d..ab7384835f 100644 --- a/sea-orm-macros/Cargo.toml +++ b/sea-orm-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sea-orm-macros" -version = "0.12.11" +version = "0.12.12" authors = [ "Billy Chan " ] edition = "2021" description = "Derive macros for SeaORM" diff --git a/sea-orm-migration/Cargo.toml b/sea-orm-migration/Cargo.toml index ec9346ef70..b2848043b9 100644 --- a/sea-orm-migration/Cargo.toml +++ b/sea-orm-migration/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "sea-orm-migration" -version = "0.12.11" +version = "0.12.12" authors = ["Billy Chan "] edition = "2021" description = "Migration utility for SeaORM" @@ -23,8 +23,8 @@ path = "src/lib.rs" async-trait = { version = "0.1", default-features = false } clap = { version = "4.3", features = ["env", "derive"], optional = true } dotenvy = { version = "0.15", default-features = false, optional = true } -sea-orm = { version = "0.12.11", path = "../", default-features = false, features = ["macros"] } -sea-orm-cli = { version = "0.12.11", path = "../sea-orm-cli", default-features = false, optional = true } +sea-orm = { version = "0.12.12", path = "../", default-features = false, features = ["macros"] } +sea-orm-cli = { version = "0.12.12", path = "../sea-orm-cli", default-features = false, optional = true } sea-schema = { version = "0.14.1" } tracing = { version = "0.1", default-features = false, features = ["log"] } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["env-filter", "fmt"] } From b5804551ebe4a3644ae9e1c8a672edfda23a80b2 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Mon, 22 Jan 2024 09:48:29 +0000 Subject: [PATCH 0278/1371] update examples --- examples/actix3_example/entity/Cargo.toml | 2 +- examples/actix3_example/migration/Cargo.toml | 2 +- examples/actix3_example/service/Cargo.toml | 2 +- examples/actix_example/entity/Cargo.toml | 2 +- examples/actix_example/migration/Cargo.toml | 2 +- examples/actix_example/service/Cargo.toml | 2 +- examples/axum_example/entity/Cargo.toml | 2 +- examples/axum_example/migration/Cargo.toml | 2 +- examples/axum_example/service/Cargo.toml | 2 +- examples/graphql_example/entity/Cargo.toml | 2 +- examples/graphql_example/migration/Cargo.toml | 2 +- examples/graphql_example/service/Cargo.toml | 2 +- examples/jsonrpsee_example/entity/Cargo.toml | 2 +- examples/jsonrpsee_example/migration/Cargo.toml | 2 +- examples/jsonrpsee_example/service/Cargo.toml | 2 +- examples/poem_example/entity/Cargo.toml | 2 +- examples/poem_example/migration/Cargo.toml | 2 +- examples/poem_example/service/Cargo.toml | 2 +- examples/rocket_example/entity/Cargo.toml | 2 +- examples/rocket_example/migration/Cargo.toml | 2 +- examples/rocket_example/service/Cargo.toml | 2 +- examples/rocket_okapi_example/entity/Cargo.toml | 2 +- examples/rocket_okapi_example/migration/Cargo.toml | 2 +- examples/rocket_okapi_example/service/Cargo.toml | 2 +- examples/salvo_example/entity/Cargo.toml | 2 +- examples/salvo_example/migration/Cargo.toml | 2 +- examples/salvo_example/service/Cargo.toml | 2 +- examples/seaography_example/migration/Cargo.toml | 4 ++-- examples/tonic_example/entity/Cargo.toml | 2 +- examples/tonic_example/migration/Cargo.toml | 2 +- examples/tonic_example/service/Cargo.toml | 2 +- 31 files changed, 32 insertions(+), 32 deletions(-) diff --git a/examples/actix3_example/entity/Cargo.toml b/examples/actix3_example/entity/Cargo.toml index 4463794a64..15cfbce5df 100644 --- a/examples/actix3_example/entity/Cargo.toml +++ b/examples/actix3_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.11" # sea-orm version +version = "0.12.12" # sea-orm version diff --git a/examples/actix3_example/migration/Cargo.toml b/examples/actix3_example/migration/Cargo.toml index 731403b022..c8c9077c7e 100644 --- a/examples/actix3_example/migration/Cargo.toml +++ b/examples/actix3_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.11" # sea-orm-migration version +version = "0.12.12" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-async-std-native-tls", diff --git a/examples/actix3_example/service/Cargo.toml b/examples/actix3_example/service/Cargo.toml index 816a66ab1f..e9f4fa3668 100644 --- a/examples/actix3_example/service/Cargo.toml +++ b/examples/actix3_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.11" # sea-orm version +version = "0.12.12" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/actix_example/entity/Cargo.toml b/examples/actix_example/entity/Cargo.toml index 4463794a64..15cfbce5df 100644 --- a/examples/actix_example/entity/Cargo.toml +++ b/examples/actix_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.11" # sea-orm version +version = "0.12.12" # sea-orm version diff --git a/examples/actix_example/migration/Cargo.toml b/examples/actix_example/migration/Cargo.toml index cd10bd850c..1663f68fff 100644 --- a/examples/actix_example/migration/Cargo.toml +++ b/examples/actix_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.11" # sea-orm-migration version +version = "0.12.12" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-actix-native-tls", diff --git a/examples/actix_example/service/Cargo.toml b/examples/actix_example/service/Cargo.toml index 5f4ba3c83b..58ca3af821 100644 --- a/examples/actix_example/service/Cargo.toml +++ b/examples/actix_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.11" # sea-orm version +version = "0.12.12" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/axum_example/entity/Cargo.toml b/examples/axum_example/entity/Cargo.toml index 4463794a64..15cfbce5df 100644 --- a/examples/axum_example/entity/Cargo.toml +++ b/examples/axum_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.11" # sea-orm version +version = "0.12.12" # sea-orm version diff --git a/examples/axum_example/migration/Cargo.toml b/examples/axum_example/migration/Cargo.toml index 6f1b794df5..671043d169 100644 --- a/examples/axum_example/migration/Cargo.toml +++ b/examples/axum_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.11" # sea-orm-migration version +version = "0.12.12" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/axum_example/service/Cargo.toml b/examples/axum_example/service/Cargo.toml index 372b0832c2..6e74e79c5a 100644 --- a/examples/axum_example/service/Cargo.toml +++ b/examples/axum_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.11" # sea-orm version +version = "0.12.12" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/graphql_example/entity/Cargo.toml b/examples/graphql_example/entity/Cargo.toml index 1279b4cf16..3a7718cbda 100644 --- a/examples/graphql_example/entity/Cargo.toml +++ b/examples/graphql_example/entity/Cargo.toml @@ -16,4 +16,4 @@ version = "5.0.10" [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.11" # sea-orm version +version = "0.12.12" # sea-orm version diff --git a/examples/graphql_example/migration/Cargo.toml b/examples/graphql_example/migration/Cargo.toml index 3d13bbef1d..67bcb3a7ab 100644 --- a/examples/graphql_example/migration/Cargo.toml +++ b/examples/graphql_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.11" # sea-orm-migration version +version = "0.12.12" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/graphql_example/service/Cargo.toml b/examples/graphql_example/service/Cargo.toml index a9a2519f4e..1de3af0675 100644 --- a/examples/graphql_example/service/Cargo.toml +++ b/examples/graphql_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.11" # sea-orm version +version = "0.12.12" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/jsonrpsee_example/entity/Cargo.toml b/examples/jsonrpsee_example/entity/Cargo.toml index 4463794a64..15cfbce5df 100644 --- a/examples/jsonrpsee_example/entity/Cargo.toml +++ b/examples/jsonrpsee_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.11" # sea-orm version +version = "0.12.12" # sea-orm version diff --git a/examples/jsonrpsee_example/migration/Cargo.toml b/examples/jsonrpsee_example/migration/Cargo.toml index 898ad7b8a8..449b2aaf6e 100644 --- a/examples/jsonrpsee_example/migration/Cargo.toml +++ b/examples/jsonrpsee_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.11" # sea-orm-migration version +version = "0.12.12" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/jsonrpsee_example/service/Cargo.toml b/examples/jsonrpsee_example/service/Cargo.toml index 45bffe8c57..88523ab596 100644 --- a/examples/jsonrpsee_example/service/Cargo.toml +++ b/examples/jsonrpsee_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.11" # sea-orm version +version = "0.12.12" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/poem_example/entity/Cargo.toml b/examples/poem_example/entity/Cargo.toml index 4463794a64..15cfbce5df 100644 --- a/examples/poem_example/entity/Cargo.toml +++ b/examples/poem_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.11" # sea-orm version +version = "0.12.12" # sea-orm version diff --git a/examples/poem_example/migration/Cargo.toml b/examples/poem_example/migration/Cargo.toml index 898ad7b8a8..449b2aaf6e 100644 --- a/examples/poem_example/migration/Cargo.toml +++ b/examples/poem_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.11" # sea-orm-migration version +version = "0.12.12" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/poem_example/service/Cargo.toml b/examples/poem_example/service/Cargo.toml index aeeddabdbd..042404abb3 100644 --- a/examples/poem_example/service/Cargo.toml +++ b/examples/poem_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.11" # sea-orm version +version = "0.12.12" # sea-orm version features = [ "debug-print", "runtime-async-std-native-tls", diff --git a/examples/rocket_example/entity/Cargo.toml b/examples/rocket_example/entity/Cargo.toml index 8b55ace602..bf5d63a75e 100644 --- a/examples/rocket_example/entity/Cargo.toml +++ b/examples/rocket_example/entity/Cargo.toml @@ -13,4 +13,4 @@ rocket = { version = "0.5.0", features = ["json"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.11" # sea-orm version +version = "0.12.12" # sea-orm version diff --git a/examples/rocket_example/migration/Cargo.toml b/examples/rocket_example/migration/Cargo.toml index 8a68bc6ea8..5905cdd352 100644 --- a/examples/rocket_example/migration/Cargo.toml +++ b/examples/rocket_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.11" # sea-orm-migration version +version = "0.12.12" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/rocket_example/service/Cargo.toml b/examples/rocket_example/service/Cargo.toml index 89155d2c9d..a0a12c8a25 100644 --- a/examples/rocket_example/service/Cargo.toml +++ b/examples/rocket_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.11" # sea-orm version +version = "0.12.12" # sea-orm version features = [ "runtime-tokio-native-tls", "sqlx-postgres", diff --git a/examples/rocket_okapi_example/entity/Cargo.toml b/examples/rocket_okapi_example/entity/Cargo.toml index 051056def7..b68625c91a 100644 --- a/examples/rocket_okapi_example/entity/Cargo.toml +++ b/examples/rocket_okapi_example/entity/Cargo.toml @@ -13,7 +13,7 @@ rocket = { version = "0.5.0", features = ["json"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.11" # sea-orm version +version = "0.12.12" # sea-orm version [dependencies.rocket_okapi] version = "0.8.0-rc.2" diff --git a/examples/rocket_okapi_example/migration/Cargo.toml b/examples/rocket_okapi_example/migration/Cargo.toml index 8a68bc6ea8..5905cdd352 100644 --- a/examples/rocket_okapi_example/migration/Cargo.toml +++ b/examples/rocket_okapi_example/migration/Cargo.toml @@ -14,7 +14,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.11" # sea-orm-migration version +version = "0.12.12" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/rocket_okapi_example/service/Cargo.toml b/examples/rocket_okapi_example/service/Cargo.toml index 39b8004e04..cff56beb77 100644 --- a/examples/rocket_okapi_example/service/Cargo.toml +++ b/examples/rocket_okapi_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.11" # sea-orm version +version = "0.12.12" # sea-orm version features = [ "runtime-tokio-native-tls", "sqlx-postgres", diff --git a/examples/salvo_example/entity/Cargo.toml b/examples/salvo_example/entity/Cargo.toml index 14df9e1f63..83e9c9ec42 100644 --- a/examples/salvo_example/entity/Cargo.toml +++ b/examples/salvo_example/entity/Cargo.toml @@ -14,4 +14,4 @@ salvo = { version = "0.50" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.11" # sea-orm version +version = "0.12.12" # sea-orm version diff --git a/examples/salvo_example/migration/Cargo.toml b/examples/salvo_example/migration/Cargo.toml index 898ad7b8a8..449b2aaf6e 100644 --- a/examples/salvo_example/migration/Cargo.toml +++ b/examples/salvo_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.11" # sea-orm-migration version +version = "0.12.12" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-native-tls", diff --git a/examples/salvo_example/service/Cargo.toml b/examples/salvo_example/service/Cargo.toml index a527f1bf30..f5da81ce59 100644 --- a/examples/salvo_example/service/Cargo.toml +++ b/examples/salvo_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.11" # sea-orm version +version = "0.12.12" # sea-orm version features = [ "debug-print", "runtime-tokio-native-tls", diff --git a/examples/seaography_example/migration/Cargo.toml b/examples/seaography_example/migration/Cargo.toml index eaa5dcbd06..78bec745d3 100644 --- a/examples/seaography_example/migration/Cargo.toml +++ b/examples/seaography_example/migration/Cargo.toml @@ -15,11 +15,11 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm] path = "../../.." # remove this line in your own project -version = "0.12.11" # sea-orm version +version = "0.12.12" # sea-orm version [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.11" # sea-orm-migration version +version = "0.12.12" # sea-orm-migration version features = [ "runtime-async-std-native-tls", "sqlx-mysql", diff --git a/examples/tonic_example/entity/Cargo.toml b/examples/tonic_example/entity/Cargo.toml index 4463794a64..15cfbce5df 100644 --- a/examples/tonic_example/entity/Cargo.toml +++ b/examples/tonic_example/entity/Cargo.toml @@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.11" # sea-orm version +version = "0.12.12" # sea-orm version diff --git a/examples/tonic_example/migration/Cargo.toml b/examples/tonic_example/migration/Cargo.toml index 9aefa76077..a615c6026f 100644 --- a/examples/tonic_example/migration/Cargo.toml +++ b/examples/tonic_example/migration/Cargo.toml @@ -13,7 +13,7 @@ async-std = { version = "1", features = ["attributes", "tokio1"] } [dependencies.sea-orm-migration] path = "../../../sea-orm-migration" # remove this line in your own project -version = "0.12.11" # sea-orm-migration version +version = "0.12.12" # sea-orm-migration version features = [ # Enable following runtime and db backend features if you want to run migration via CLI # "runtime-tokio-rustls", diff --git a/examples/tonic_example/service/Cargo.toml b/examples/tonic_example/service/Cargo.toml index 78366a8ebb..22bc0d97c0 100644 --- a/examples/tonic_example/service/Cargo.toml +++ b/examples/tonic_example/service/Cargo.toml @@ -10,7 +10,7 @@ entity = { path = "../entity" } [dependencies.sea-orm] path = "../../../" # remove this line in your own project -version = "0.12.11" # sea-orm version +version = "0.12.12" # sea-orm version features = [ "debug-print", "runtime-tokio-rustls", From 9797da1ee4c09efcf5d4bf1683dca2a9ded8dc8e Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Tue, 23 Jan 2024 22:18:18 +0800 Subject: [PATCH 0279/1371] Drop the use of `rust_decimal_macros` (#2078) --- Cargo.toml | 1 - tests/common/bakery_chain/cake.rs | 7 +++-- tests/common/setup/mod.rs | 5 ++++ tests/crud/create_cake.rs | 5 ++-- tests/crud/create_lineitem.rs | 9 +++---- tests/crud/create_order.rs | 11 ++++---- tests/crud/deletes.rs | 3 +-- tests/crud/error.rs | 3 +-- tests/crud/mod.rs | 1 + tests/crud/updates.rs | 9 +++---- tests/cursor_tests.rs | 2 +- tests/pi_tests.rs | 11 ++++---- tests/relational_tests.rs | 45 +++++++++++++++---------------- tests/sequential_op_tests.rs | 15 +++++------ tests/sql_err_tests.rs | 5 ++-- tests/transaction_tests.rs | 9 +++---- 16 files changed, 67 insertions(+), 74 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a9e94916ba..46e491a0bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,7 +53,6 @@ futures = { version = "0.3" } tokio = { version = "1.6", features = ["full"] } actix-rt = { version = "2.2.0" } maplit = { version = "1" } -rust_decimal_macros = { version = "1" } tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } sea-orm = { path = ".", features = ["mock", "debug-print", "tests-cfg", "postgres-array", "sea-orm-internal"] } pretty_assertions = { version = "0.7" } diff --git a/tests/common/bakery_chain/cake.rs b/tests/common/bakery_chain/cake.rs index 6ba54131fa..1096b420be 100644 --- a/tests/common/bakery_chain/cake.rs +++ b/tests/common/bakery_chain/cake.rs @@ -1,3 +1,4 @@ +use crate::common::setup::rust_dec; use sea_orm::{entity::prelude::*, ConnectionTrait}; #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] @@ -63,8 +64,7 @@ impl ActiveModelBehavior for ActiveModel { where C: ConnectionTrait, { - use rust_decimal_macros::dec; - if self.price.as_ref() == &dec!(0) { + if self.price.as_ref() == &rust_dec(0) { Err(DbErr::Custom(format!( "[before_save] Invalid Price, insert: {insert}" ))) @@ -77,8 +77,7 @@ impl ActiveModelBehavior for ActiveModel { where C: ConnectionTrait, { - use rust_decimal_macros::dec; - if model.price < dec!(0) { + if model.price < rust_dec(0) { Err(DbErr::Custom(format!( "[after_save] Invalid Price, insert: {insert}" ))) diff --git a/tests/common/setup/mod.rs b/tests/common/setup/mod.rs index 2385cfb0c4..c0a91b9976 100644 --- a/tests/common/setup/mod.rs +++ b/tests/common/setup/mod.rs @@ -158,3 +158,8 @@ pub async fn create_table_without_asserts( } db.execute(builder.build(create)).await } + +pub fn rust_dec(v: T) -> rust_decimal::Decimal { + use std::str::FromStr; + rust_decimal::Decimal::from_str(&v.to_string()).unwrap() +} diff --git a/tests/crud/create_cake.rs b/tests/crud/create_cake.rs index cb4b9bd1c0..2e5577f3d0 100644 --- a/tests/crud/create_cake.rs +++ b/tests/crud/create_cake.rs @@ -1,5 +1,4 @@ pub use super::*; -use rust_decimal_macros::dec; use uuid::Uuid; pub async fn test_create_cake(db: &DbConn) { @@ -31,7 +30,7 @@ pub async fn test_create_cake(db: &DbConn) { let mud_cake = cake::ActiveModel { name: Set("Mud Cake".to_owned()), - price: Set(dec!(10.25)), + price: Set(rust_dec(-10.25)), gluten_free: Set(false), serial: Set(uuid), bakery_id: Set(Some(bakery_insert_res.last_insert_id)), @@ -64,7 +63,7 @@ pub async fn test_create_cake(db: &DbConn) { assert!(cake.is_some()); let cake_model = cake.unwrap(); assert_eq!(cake_model.name, "Mud Cake"); - assert_eq!(cake_model.price, dec!(10.25)); + assert_eq!(cake_model.price, rust_dec(-10.25)); assert!(!cake_model.gluten_free); assert_eq!( cake_model diff --git a/tests/crud/create_lineitem.rs b/tests/crud/create_lineitem.rs index fa569a53fa..cb4c0f1fad 100644 --- a/tests/crud/create_lineitem.rs +++ b/tests/crud/create_lineitem.rs @@ -1,6 +1,5 @@ pub use super::*; use chrono::offset::Utc; -use rust_decimal_macros::dec; use uuid::Uuid; pub async fn test_create_lineitem(db: &DbConn) { @@ -34,7 +33,7 @@ pub async fn test_create_lineitem(db: &DbConn) { // Cake let mud_cake = cake::ActiveModel { name: Set("Mud Cake".to_owned()), - price: Set(dec!(10.25)), + price: Set(rust_dec(10.25)), gluten_free: Set(false), serial: Set(Uuid::new_v4()), bakery_id: Set(Some(bakery_insert_res.last_insert_id)), @@ -75,7 +74,7 @@ pub async fn test_create_lineitem(db: &DbConn) { let order_1 = order::ActiveModel { bakery_id: Set(bakery_insert_res.last_insert_id), customer_id: Set(customer_insert_res.last_insert_id), - total: Set(dec!(7.55)), + total: Set(rust_dec(7.55)), placed_at: Set(Utc::now().naive_utc()), ..Default::default() }; @@ -88,7 +87,7 @@ pub async fn test_create_lineitem(db: &DbConn) { let lineitem_1 = lineitem::ActiveModel { cake_id: Set(cake_insert_res.last_insert_id), order_id: Set(order_insert_res.last_insert_id), - price: Set(dec!(7.55)), + price: Set(rust_dec(7.55)), quantity: Set(1), ..Default::default() }; @@ -106,7 +105,7 @@ pub async fn test_create_lineitem(db: &DbConn) { assert!(lineitem.is_some()); let lineitem_model = lineitem.unwrap(); - assert_eq!(lineitem_model.price, dec!(7.55)); + assert_eq!(lineitem_model.price, rust_dec(7.55)); let cake: Option = Cake::find_by_id(lineitem_model.cake_id) .one(db) diff --git a/tests/crud/create_order.rs b/tests/crud/create_order.rs index 950cba50c7..a80550ca79 100644 --- a/tests/crud/create_order.rs +++ b/tests/crud/create_order.rs @@ -1,6 +1,5 @@ pub use super::*; use chrono::offset::Utc; -use rust_decimal_macros::dec; use uuid::Uuid; pub async fn test_create_order(db: &DbConn) { @@ -34,7 +33,7 @@ pub async fn test_create_order(db: &DbConn) { // Cake let mud_cake = cake::ActiveModel { name: Set("Mud Cake".to_owned()), - price: Set(dec!(10.25)), + price: Set(rust_dec(10.25)), gluten_free: Set(false), serial: Set(Uuid::new_v4()), bakery_id: Set(Some(bakery_insert_res.last_insert_id)), @@ -75,7 +74,7 @@ pub async fn test_create_order(db: &DbConn) { let order_1 = order::ActiveModel { bakery_id: Set(bakery_insert_res.last_insert_id), customer_id: Set(customer_insert_res.last_insert_id), - total: Set(dec!(15.10)), + total: Set(rust_dec(15.10)), placed_at: Set(Utc::now().naive_utc()), ..Default::default() }; @@ -88,7 +87,7 @@ pub async fn test_create_order(db: &DbConn) { let lineitem_1 = lineitem::ActiveModel { cake_id: Set(cake_insert_res.last_insert_id), order_id: Set(order_insert_res.last_insert_id), - price: Set(dec!(7.55)), + price: Set(rust_dec(7.55)), quantity: Set(2), ..Default::default() }; @@ -104,7 +103,7 @@ pub async fn test_create_order(db: &DbConn) { assert!(order.is_some()); let order_model = order.unwrap(); - assert_eq!(order_model.total, dec!(15.10)); + assert_eq!(order_model.total, rust_dec(15.10)); let customer: Option = Customer::find_by_id(order_model.customer_id) .one(db) @@ -128,6 +127,6 @@ pub async fn test_create_order(db: &DbConn) { .await .expect("could not find related lineitems"); assert_eq!(related_lineitems.len(), 1); - assert_eq!(related_lineitems[0].price, dec!(7.55)); + assert_eq!(related_lineitems[0].price, rust_dec(7.55)); assert_eq!(related_lineitems[0].quantity, 2); } diff --git a/tests/crud/deletes.rs b/tests/crud/deletes.rs index 70c18901f5..aa4722a6cc 100644 --- a/tests/crud/deletes.rs +++ b/tests/crud/deletes.rs @@ -1,5 +1,4 @@ pub use super::*; -use rust_decimal_macros::dec; use uuid::Uuid; pub async fn test_delete_cake(db: &DbConn) { @@ -17,7 +16,7 @@ pub async fn test_delete_cake(db: &DbConn) { let mud_cake = cake::ActiveModel { name: Set("Mud Cake".to_owned()), - price: Set(dec!(10.25)), + price: Set(rust_dec(10.25)), gluten_free: Set(false), serial: Set(Uuid::new_v4()), bakery_id: Set(Some(bakery_insert_res.last_insert_id)), diff --git a/tests/crud/error.rs b/tests/crud/error.rs index 07bb510093..9e05c7860c 100644 --- a/tests/crud/error.rs +++ b/tests/crud/error.rs @@ -1,5 +1,4 @@ pub use super::*; -use rust_decimal_macros::dec; use sea_orm::error::*; #[cfg(any( feature = "sqlx-mysql", @@ -12,7 +11,7 @@ use uuid::Uuid; pub async fn test_cake_error_sqlx(db: &DbConn) { let mud_cake = cake::ActiveModel { name: Set("Moldy Cake".to_owned()), - price: Set(dec!(10.25)), + price: Set(rust_dec(10.25)), gluten_free: Set(false), serial: Set(Uuid::new_v4()), bakery_id: Set(None), diff --git a/tests/crud/mod.rs b/tests/crud/mod.rs index 3a629d7acf..d1f0f1b9f5 100644 --- a/tests/crud/mod.rs +++ b/tests/crud/mod.rs @@ -15,6 +15,7 @@ pub use error::*; pub use updates::*; pub use super::common::bakery_chain::*; +pub use crate::common::setup::rust_dec; use sea_orm::{entity::*, DbConn}; pub async fn test_create_bakery(db: &DbConn) { diff --git a/tests/crud/updates.rs b/tests/crud/updates.rs index 6dbd29e93c..5e5628fb71 100644 --- a/tests/crud/updates.rs +++ b/tests/crud/updates.rs @@ -1,5 +1,4 @@ pub use super::*; -use rust_decimal_macros::dec; use sea_orm::{query::*, DbErr}; use uuid::Uuid; @@ -16,7 +15,7 @@ pub async fn test_update_cake(db: &DbConn) { let mud_cake = cake::ActiveModel { name: Set("Mud Cake".to_owned()), - price: Set(dec!(10.25)), + price: Set(rust_dec(10.25)), gluten_free: Set(false), serial: Set(Uuid::new_v4()), bakery_id: Set(Some(bakery_insert_res.last_insert_id)), @@ -36,12 +35,12 @@ pub async fn test_update_cake(db: &DbConn) { assert!(cake.is_some()); let cake_model = cake.unwrap(); assert_eq!(cake_model.name, "Mud Cake"); - assert_eq!(cake_model.price, dec!(10.25)); + assert_eq!(cake_model.price, rust_dec(10.25)); assert!(!cake_model.gluten_free); let mut cake_am: cake::ActiveModel = cake_model.into(); cake_am.name = Set("Extra chocolate mud cake".to_owned()); - cake_am.price = Set(dec!(20.00)); + cake_am.price = Set(rust_dec(20.00)); let _cake_update_res: cake::Model = cake_am.update(db).await.expect("could not update cake"); @@ -51,7 +50,7 @@ pub async fn test_update_cake(db: &DbConn) { .expect("could not find cake"); let cake_model = cake.unwrap(); assert_eq!(cake_model.name, "Extra chocolate mud cake"); - assert_eq!(cake_model.price, dec!(20.00)); + assert_eq!(cake_model.price, rust_dec(20.00)); assert!(!cake_model.gluten_free); } diff --git a/tests/cursor_tests.rs b/tests/cursor_tests.rs index e843086894..0d1d54eb78 100644 --- a/tests/cursor_tests.rs +++ b/tests/cursor_tests.rs @@ -579,7 +579,7 @@ pub async fn create_baker_cake(db: &DatabaseConnection) -> Result<(), DbErr> { }); cakes.push(cake::ActiveModel { name: Set(c.to_ascii_lowercase().to_string()), - price: Set(rust_decimal_macros::dec!(10.25)), + price: Set(rust_dec(10.25)), gluten_free: Set(false), serial: Set(Uuid::new_v4()), bakery_id: Set(Some((c as i32 - 65) % 10 + 1)), diff --git a/tests/pi_tests.rs b/tests/pi_tests.rs index 0d344ceee8..31f3b794b2 100644 --- a/tests/pi_tests.rs +++ b/tests/pi_tests.rs @@ -1,8 +1,7 @@ pub mod common; -use common::{features::*, TestContext}; +use common::{features::*, setup::*, TestContext}; use pretty_assertions::assert_eq; -use rust_decimal_macros::dec; use sea_orm::{entity::prelude::*, entity::*, DatabaseConnection}; use std::str::FromStr; @@ -24,7 +23,7 @@ async fn main() -> Result<(), DbErr> { pub async fn create_and_update_pi(db: &DatabaseConnection) -> Result<(), DbErr> { let pi = pi::Model { id: 1, - decimal: dec!(3.1415926536), + decimal: rust_dec(3.1415926536), big_decimal: BigDecimal::from_str("3.1415926536").unwrap(), decimal_opt: None, big_decimal_opt: None, @@ -37,7 +36,7 @@ pub async fn create_and_update_pi(db: &DatabaseConnection) -> Result<(), DbErr> assert_eq!(model, Some(pi.clone())); let res = pi::ActiveModel { - decimal_opt: Set(Some(dec!(3.1415926536))), + decimal_opt: Set(Some(rust_dec(3.1415926536))), big_decimal_opt: Set(Some(BigDecimal::from_str("3.1415926536").unwrap())), ..pi.clone().into_active_model() } @@ -50,9 +49,9 @@ pub async fn create_and_update_pi(db: &DatabaseConnection) -> Result<(), DbErr> model, Some(pi::Model { id: 1, - decimal: dec!(3.1415926536), + decimal: rust_dec(3.1415926536), big_decimal: BigDecimal::from_str("3.1415926536").unwrap(), - decimal_opt: Some(dec!(3.1415926536)), + decimal_opt: Some(rust_dec(3.1415926536)), big_decimal_opt: Some(BigDecimal::from_str("3.1415926536").unwrap()), }) ); diff --git a/tests/relational_tests.rs b/tests/relational_tests.rs index c8c41b17a4..6a51f20364 100644 --- a/tests/relational_tests.rs +++ b/tests/relational_tests.rs @@ -4,7 +4,6 @@ pub use chrono::offset::Utc; pub use common::{bakery_chain::*, setup::*, TestContext}; use pretty_assertions::assert_eq; pub use rust_decimal::prelude::*; -pub use rust_decimal_macros::dec; use sea_orm::{entity::*, query::*, DbErr, DerivePartialModel, FromQueryResult}; use sea_query::{Expr, Func, SimpleExpr}; pub use uuid::Uuid; @@ -151,7 +150,7 @@ pub async fn right_join() { let _order = order::ActiveModel { bakery_id: Set(bakery.id), customer_id: Set(customer_kate.id), - total: Set(dec!(15.10)), + total: Set(rust_dec(15.10)), placed_at: Set(Utc::now().naive_utc()), ..Default::default() @@ -180,7 +179,7 @@ pub async fn right_join() { .await .unwrap() .unwrap(); - assert_eq!(result.order_total, Some(dec!(15.10))); + assert_eq!(result.order_total, Some(rust_dec(15.10))); let select = order::Entity::find() .right_join(customer::Entity) @@ -238,7 +237,7 @@ pub async fn inner_join() { let kate_order_1 = order::ActiveModel { bakery_id: Set(bakery.id), customer_id: Set(customer_kate.id), - total: Set(dec!(15.10)), + total: Set(rust_dec(15.10)), placed_at: Set(Utc::now().naive_utc()), ..Default::default() @@ -250,7 +249,7 @@ pub async fn inner_join() { let kate_order_2 = order::ActiveModel { bakery_id: Set(bakery.id), customer_id: Set(customer_kate.id), - total: Set(dec!(100.00)), + total: Set(rust_dec(100.00)), placed_at: Set(Utc::now().naive_utc()), ..Default::default() @@ -320,7 +319,7 @@ pub async fn group_by() { let kate_order_1 = order::ActiveModel { bakery_id: Set(bakery.id), customer_id: Set(customer_kate.id), - total: Set(dec!(99.95)), + total: Set(rust_dec(99.95)), placed_at: Set(Utc::now().naive_utc()), ..Default::default() @@ -332,7 +331,7 @@ pub async fn group_by() { let kate_order_2 = order::ActiveModel { bakery_id: Set(bakery.id), customer_id: Set(customer_kate.id), - total: Set(dec!(200.00)), + total: Set(rust_dec(200.00)), placed_at: Set(Utc::now().naive_utc()), ..Default::default() @@ -415,7 +414,7 @@ pub async fn having() { let kate_order_1 = order::ActiveModel { bakery_id: Set(bakery.id), customer_id: Set(customer_kate.id), - total: Set(dec!(100.00)), + total: Set(rust_dec(100.00)), placed_at: Set(Utc::now().naive_utc()), ..Default::default() @@ -427,7 +426,7 @@ pub async fn having() { let _kate_order_2 = order::ActiveModel { bakery_id: Set(bakery.id), customer_id: Set(customer_kate.id), - total: Set(dec!(12.00)), + total: Set(rust_dec(12.00)), placed_at: Set(Utc::now().naive_utc()), ..Default::default() @@ -447,7 +446,7 @@ pub async fn having() { let _bob_order_1 = order::ActiveModel { bakery_id: Set(bakery.id), customer_id: Set(customer_bob.id), - total: Set(dec!(50.0)), + total: Set(rust_dec(50.0)), placed_at: Set(Utc::now().naive_utc()), ..Default::default() @@ -459,7 +458,7 @@ pub async fn having() { let _bob_order_2 = order::ActiveModel { bakery_id: Set(bakery.id), customer_id: Set(customer_bob.id), - total: Set(dec!(50.0)), + total: Set(rust_dec(50.0)), placed_at: Set(Utc::now().naive_utc()), ..Default::default() @@ -481,7 +480,7 @@ pub async fn having() { .column_as(order::Column::Total, "order_total") .group_by(customer::Column::Name) .group_by(order::Column::Total) - .having(order::Column::Total.gt(dec!(90.00))) + .having(order::Column::Total.gt(rust_dec(90.00))) .into_model::() .all(&ctx.db) .await @@ -779,7 +778,7 @@ pub async fn linked() -> Result<(), DbErr> { let baker_bob_res = Baker::insert(baker_bob).exec(&ctx.db).await?; let mud_cake = cake::ActiveModel { name: Set("Mud Cake".to_owned()), - price: Set(dec!(10.25)), + price: Set(rust_dec(10.25)), gluten_free: Set(false), serial: Set(Uuid::new_v4()), bakery_id: Set(Some(seaside_bakery_res.last_insert_id)), @@ -804,7 +803,7 @@ pub async fn linked() -> Result<(), DbErr> { let baker_bobby_res = Baker::insert(baker_bobby).exec(&ctx.db).await?; let cheese_cake = cake::ActiveModel { name: Set("Cheese Cake".to_owned()), - price: Set(dec!(20.5)), + price: Set(rust_dec(20.5)), gluten_free: Set(false), serial: Set(Uuid::new_v4()), bakery_id: Set(Some(seaside_bakery_res.last_insert_id)), @@ -820,7 +819,7 @@ pub async fn linked() -> Result<(), DbErr> { .await?; let chocolate_cake = cake::ActiveModel { name: Set("Chocolate Cake".to_owned()), - price: Set(dec!(30.15)), + price: Set(rust_dec(30.15)), gluten_free: Set(false), serial: Set(Uuid::new_v4()), bakery_id: Set(Some(seaside_bakery_res.last_insert_id)), @@ -856,7 +855,7 @@ pub async fn linked() -> Result<(), DbErr> { let kate_order_1 = order::ActiveModel { bakery_id: Set(seaside_bakery_res.last_insert_id), customer_id: Set(customer_kate_res.last_insert_id), - total: Set(dec!(15.10)), + total: Set(rust_dec(15.10)), placed_at: Set(Utc::now().naive_utc()), ..Default::default() }; @@ -864,7 +863,7 @@ pub async fn linked() -> Result<(), DbErr> { lineitem::ActiveModel { cake_id: Set(cheese_cake_res.last_insert_id), order_id: Set(kate_order_1_res.last_insert_id), - price: Set(dec!(7.55)), + price: Set(rust_dec(7.55)), quantity: Set(2), ..Default::default() } @@ -873,7 +872,7 @@ pub async fn linked() -> Result<(), DbErr> { let kate_order_2 = order::ActiveModel { bakery_id: Set(seaside_bakery_res.last_insert_id), customer_id: Set(customer_kate_res.last_insert_id), - total: Set(dec!(29.7)), + total: Set(rust_dec(29.7)), placed_at: Set(Utc::now().naive_utc()), ..Default::default() }; @@ -881,7 +880,7 @@ pub async fn linked() -> Result<(), DbErr> { lineitem::ActiveModel { cake_id: Set(chocolate_cake_res.last_insert_id), order_id: Set(kate_order_2_res.last_insert_id), - price: Set(dec!(9.9)), + price: Set(rust_dec(9.9)), quantity: Set(3), ..Default::default() } @@ -898,7 +897,7 @@ pub async fn linked() -> Result<(), DbErr> { let kara_order_1 = order::ActiveModel { bakery_id: Set(seaside_bakery_res.last_insert_id), customer_id: Set(customer_kara_res.last_insert_id), - total: Set(dec!(15.10)), + total: Set(rust_dec(15.10)), placed_at: Set(Utc::now().naive_utc()), ..Default::default() }; @@ -906,7 +905,7 @@ pub async fn linked() -> Result<(), DbErr> { lineitem::ActiveModel { cake_id: Set(mud_cake_res.last_insert_id), order_id: Set(kara_order_1_res.last_insert_id), - price: Set(dec!(7.55)), + price: Set(rust_dec(7.55)), quantity: Set(2), ..Default::default() } @@ -915,7 +914,7 @@ pub async fn linked() -> Result<(), DbErr> { let kara_order_2 = order::ActiveModel { bakery_id: Set(seaside_bakery_res.last_insert_id), customer_id: Set(customer_kara_res.last_insert_id), - total: Set(dec!(29.7)), + total: Set(rust_dec(29.7)), placed_at: Set(Utc::now().naive_utc()), ..Default::default() }; @@ -923,7 +922,7 @@ pub async fn linked() -> Result<(), DbErr> { lineitem::ActiveModel { cake_id: Set(cheese_cake_res.last_insert_id), order_id: Set(kara_order_2_res.last_insert_id), - price: Set(dec!(9.9)), + price: Set(rust_dec(9.9)), quantity: Set(3), ..Default::default() } diff --git a/tests/sequential_op_tests.rs b/tests/sequential_op_tests.rs index 208b388c6e..d84f6bfed5 100644 --- a/tests/sequential_op_tests.rs +++ b/tests/sequential_op_tests.rs @@ -3,7 +3,6 @@ pub mod common; pub use chrono::offset::Utc; pub use common::{bakery_chain::*, setup::*, TestContext}; pub use rust_decimal::prelude::*; -pub use rust_decimal_macros::dec; pub use uuid::Uuid; #[cfg(any(feature = "sqlx-mysql", feature = "sqlx-postgres"))] @@ -63,7 +62,7 @@ async fn seed_data(db: &DatabaseConnection) { let mud_cake = cake::ActiveModel { name: Set("Mud Cake".to_owned()), - price: Set(dec!(10.25)), + price: Set(rust_dec(10.25)), gluten_free: Set(false), serial: Set(Uuid::new_v4()), bakery_id: Set(Some(bakery.id.clone().unwrap())), @@ -100,7 +99,7 @@ async fn seed_data(db: &DatabaseConnection) { let kate_order_1 = order::ActiveModel { bakery_id: Set(bakery.id.clone().unwrap()), customer_id: Set(customer_kate.id.clone().unwrap()), - total: Set(dec!(99.95)), + total: Set(rust_dec(99.95)), placed_at: Set(Utc::now().naive_utc()), ..Default::default() @@ -111,7 +110,7 @@ async fn seed_data(db: &DatabaseConnection) { let _lineitem = lineitem::ActiveModel { cake_id: Set(cake_insert_res.last_insert_id), - price: Set(dec!(10.00)), + price: Set(rust_dec(10.00)), quantity: Set(12), order_id: Set(kate_order_1.id.clone().unwrap()), ..Default::default() @@ -122,7 +121,7 @@ async fn seed_data(db: &DatabaseConnection) { let _lineitem2 = lineitem::ActiveModel { cake_id: Set(cake_insert_res.last_insert_id), - price: Set(dec!(50.00)), + price: Set(rust_dec(50.00)), quantity: Set(2), order_id: Set(kate_order_1.id.clone().unwrap()), ..Default::default() @@ -199,7 +198,7 @@ async fn find_baker_least_sales(db: &DatabaseConnection) -> Option async fn create_cake(db: &DatabaseConnection, baker: baker::Model) -> Option { let new_cake = cake::ActiveModel { name: Set("New Cake".to_owned()), - price: Set(dec!(8.00)), + price: Set(rust_dec(8.00)), gluten_free: Set(false), serial: Set(Uuid::new_v4()), bakery_id: Set(Some(baker.bakery_id.unwrap())), @@ -244,7 +243,7 @@ async fn create_order(db: &DatabaseConnection, cake: cake::Model) { let order = order::ActiveModel { bakery_id: Set(cake.bakery_id.unwrap()), customer_id: Set(another_customer.id.clone().unwrap()), - total: Set(dec!(200.00)), + total: Set(rust_dec(200.00)), placed_at: Set(Utc::now().naive_utc()), ..Default::default() @@ -255,7 +254,7 @@ async fn create_order(db: &DatabaseConnection, cake: cake::Model) { let _lineitem = lineitem::ActiveModel { cake_id: Set(cake.id), - price: Set(dec!(10.00)), + price: Set(rust_dec(10.00)), quantity: Set(300), order_id: Set(order.id.clone().unwrap()), ..Default::default() diff --git a/tests/sql_err_tests.rs b/tests/sql_err_tests.rs index e086d730c2..4f032a4867 100644 --- a/tests/sql_err_tests.rs +++ b/tests/sql_err_tests.rs @@ -1,6 +1,5 @@ pub mod common; pub use common::{bakery_chain::*, setup::*, TestContext}; -use rust_decimal_macros::dec; pub use sea_orm::{ entity::*, error::DbErr, error::SqlErr, tests_cfg, DatabaseConnection, DbBackend, EntityName, ExecResult, @@ -23,7 +22,7 @@ async fn main() { pub async fn test_error(db: &DatabaseConnection) { let mud_cake = cake::ActiveModel { name: Set("Moldy Cake".to_owned()), - price: Set(dec!(10.25)), + price: Set(rust_dec(10.25)), gluten_free: Set(false), serial: Set(Uuid::new_v4()), bakery_id: Set(None), @@ -48,7 +47,7 @@ pub async fn test_error(db: &DatabaseConnection) { let fk_cake = cake::ActiveModel { name: Set("fk error Cake".to_owned()), - price: Set(dec!(10.25)), + price: Set(rust_dec(10.25)), gluten_free: Set(false), serial: Set(Uuid::new_v4()), bakery_id: Set(Some(1000)), diff --git a/tests/transaction_tests.rs b/tests/transaction_tests.rs index ffc937a6d7..fea3951897 100644 --- a/tests/transaction_tests.rs +++ b/tests/transaction_tests.rs @@ -358,7 +358,6 @@ pub async fn transaction_closure_rollback() -> Result<(), DbErr> { feature = "sqlx-postgres" ))] pub async fn transaction_with_active_model_behaviour() -> Result<(), DbErr> { - use rust_decimal_macros::dec; let ctx = TestContext::new("transaction_with_active_model_behaviour_test").await; create_tables(&ctx.db).await?; @@ -366,7 +365,7 @@ pub async fn transaction_with_active_model_behaviour() -> Result<(), DbErr> { assert_eq!( cake::ActiveModel { name: Set("Cake with invalid price".to_owned()), - price: Set(dec!(0)), + price: Set(rust_dec(0)), gluten_free: Set(false), ..Default::default() } @@ -382,7 +381,7 @@ pub async fn transaction_with_active_model_behaviour() -> Result<(), DbErr> { assert_eq!( cake::ActiveModel { name: Set("Cake with invalid price".to_owned()), - price: Set(dec!(-10)), + price: Set(rust_dec(-10)), gluten_free: Set(false), ..Default::default() } @@ -397,7 +396,7 @@ pub async fn transaction_with_active_model_behaviour() -> Result<(), DbErr> { let readonly_cake_1 = cake::ActiveModel { name: Set("Readonly cake (err_on_before_delete)".to_owned()), - price: Set(dec!(10)), + price: Set(rust_dec(10)), gluten_free: Set(true), ..Default::default() } @@ -417,7 +416,7 @@ pub async fn transaction_with_active_model_behaviour() -> Result<(), DbErr> { let readonly_cake_2 = cake::ActiveModel { name: Set("Readonly cake (err_on_after_delete)".to_owned()), - price: Set(dec!(10)), + price: Set(rust_dec(10)), gluten_free: Set(true), ..Default::default() } From 1abb0b2d913b92a24e50c3fb01c77daac99e301d Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Wed, 24 Jan 2024 18:24:23 +0000 Subject: [PATCH 0280/1371] Fix #1851 --- sea-orm-codegen/src/entity/active_enum.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/sea-orm-codegen/src/entity/active_enum.rs b/sea-orm-codegen/src/entity/active_enum.rs index e6a0b64b5c..5aea51f81a 100644 --- a/sea-orm-codegen/src/entity/active_enum.rs +++ b/sea-orm-codegen/src/entity/active_enum.rs @@ -2,6 +2,7 @@ use heck::ToUpperCamelCase; use proc_macro2::TokenStream; use quote::{format_ident, quote}; use sea_query::DynIden; +use std::fmt::Write; use crate::WithSerde; @@ -29,12 +30,15 @@ impl ActiveEnum { let variant_name = v.to_upper_camel_case(); if variant_name.is_empty() { println!("Warning: item '{}' in the enumeration '{}' cannot be converted into a valid Rust enum member name. It will be converted to its corresponding UTF-8 encoding. You can modify it later as needed.", v, enum_name); - let mut utf_string = String::new(); + let mut ss = String::new(); for c in v.chars() { - utf_string.push('U'); - utf_string.push_str(&format!("{:04X}", c as u32)); + if c.len_utf8() > 1 { + write!(&mut ss, "{c}").unwrap(); + } else { + write!(&mut ss, "U{:04X}", c as u32).unwrap(); + } } - format_ident!("{}", utf_string) + format_ident!("{}", ss) } else { format_ident!("{}", variant_name) } @@ -248,6 +252,7 @@ mod tests { "/", "//", "A-B-C", + "你好", ] .into_iter() .map(|variant| Alias::new(variant).into_iden()) @@ -278,6 +283,8 @@ mod tests { U002FU002F, #[sea_orm(string_value = "A-B-C")] ABC, + #[sea_orm(string_value = "你好")] + 你好, } ) .to_string() From c56c072f27804df2bcc77d7e594416d6b7f6aba4 Mon Sep 17 00:00:00 2001 From: Andrea Corradi Date: Thu, 25 Jan 2024 21:28:59 +0100 Subject: [PATCH 0281/1371] Enable sqlite returning with feature flag (#2070) * Enable sqlite returning with feature flag * Add runtime check for sqlite version * Apply suggestions from code review * Fix clippy * Rename feature * Fix tests when feature flag is not enabled --------- Co-authored-by: Chris Tsang --- .github/workflows/rust.yml | 5 +- Cargo.toml | 3 +- sea-orm-migration/Cargo.toml | 1 + src/database/db_connection.rs | 10 +++- src/driver/sqlx_sqlite.rs | 98 ++++++++++++++++++++++++++++++++--- tests/crud/error.rs | 4 ++ tests/returning_tests.rs | 8 ++- 7 files changed, 118 insertions(+), 11 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 73ba4ce7e9..b170b5ce33 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -308,6 +308,7 @@ jobs: matrix: runtime: [async-std] tls: [native-tls, rustls] + sqlite_flavor: ["", sqlite-use-returning-for-3_35] steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@stable @@ -319,8 +320,8 @@ jobs: Cargo.lock target key: ${{ github.sha }}-${{ github.run_id }}-${{ runner.os }}-sqlite-${{ matrix.runtime }}-${{ matrix.tls }} - - run: cargo test --test '*' --features default,sqlx-sqlite,runtime-${{ matrix.runtime }}-${{ matrix.tls }} - - run: cargo test --manifest-path sea-orm-migration/Cargo.toml --test '*' --features sqlx-sqlite,runtime-${{ matrix.runtime }}-${{ matrix.tls }} + - run: cargo test --test '*' --features default,sqlx-sqlite,runtime-${{ matrix.runtime }}-${{ matrix.tls }},${{ matrix.sqlite_flavor }} + - run: cargo test --manifest-path sea-orm-migration/Cargo.toml --test '*' --features sqlx-sqlite,runtime-${{ matrix.runtime }}-${{ matrix.tls }},${{ matrix.sqlite_flavor }} mysql: name: MySQL diff --git a/Cargo.toml b/Cargo.toml index 46e491a0bb..b14d880186 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,6 +90,7 @@ sqlx-all = ["sqlx-mysql", "sqlx-postgres", "sqlx-sqlite"] sqlx-mysql = ["sqlx-dep", "sea-query-binder/sqlx-mysql", "sqlx/mysql"] sqlx-postgres = ["sqlx-dep", "sea-query-binder/sqlx-postgres", "sqlx/postgres", "postgres-array"] sqlx-sqlite = ["sqlx-dep", "sea-query-binder/sqlx-sqlite", "sqlx/sqlite"] +sqlite-use-returning-for-3_35 = [] runtime-async-std = [] runtime-async-std-native-tls = [ "sqlx?/runtime-async-std-native-tls", @@ -128,4 +129,4 @@ seaography = ["sea-orm-macros/seaography"] # This allows us to develop using a local version of sea-query # [patch.crates-io] -# sea-query = { path = "../sea-query" } \ No newline at end of file +# sea-query = { path = "../sea-query" } diff --git a/sea-orm-migration/Cargo.toml b/sea-orm-migration/Cargo.toml index b2848043b9..71a4b4f33e 100644 --- a/sea-orm-migration/Cargo.toml +++ b/sea-orm-migration/Cargo.toml @@ -39,6 +39,7 @@ cli = ["clap", "dotenvy", "sea-orm-cli/cli"] sqlx-mysql = ["sea-orm/sqlx-mysql"] sqlx-postgres = ["sea-orm/sqlx-postgres"] sqlx-sqlite = ["sea-orm/sqlx-sqlite"] +sqlite-use-returning-for-3_35 = ["sea-orm/sqlite-use-returning-for-3_35"] runtime-actix-native-tls = ["sea-orm/runtime-actix-native-tls"] runtime-async-std-native-tls = ["sea-orm/runtime-async-std-native-tls"] runtime-tokio-native-tls = ["sea-orm/runtime-tokio-native-tls"] diff --git a/src/database/db_connection.rs b/src/database/db_connection.rs index 29815ec9f0..74f95b2380 100644 --- a/src/database/db_connection.rs +++ b/src/database/db_connection.rs @@ -578,7 +578,15 @@ impl DbBackend { /// Check if the database supports `RETURNING` syntax on insert and update pub fn support_returning(&self) -> bool { - matches!(self, Self::Postgres) + #[cfg(not(feature = "sqlite-use-returning-for-3_35"))] + { + matches!(self, Self::Postgres) + } + + #[cfg(feature = "sqlite-use-returning-for-3_35")] + { + matches!(self, Self::Postgres | Self::Sqlite) + } } } diff --git a/src/driver/sqlx_sqlite.rs b/src/driver/sqlx_sqlite.rs index 1755b46fed..0d275c8fac 100644 --- a/src/driver/sqlx_sqlite.rs +++ b/src/driver/sqlx_sqlite.rs @@ -12,8 +12,9 @@ use sea_query_binder::SqlxValues; use tracing::{instrument, warn}; use crate::{ - debug_print, error::*, executor::*, AccessMode, ConnectOptions, DatabaseConnection, - DatabaseTransaction, IsolationLevel, QueryStream, Statement, TransactionError, + debug_print, error::*, executor::*, sqlx_error_to_exec_err, AccessMode, ConnectOptions, + DatabaseConnection, DatabaseTransaction, IsolationLevel, QueryStream, Statement, + TransactionError, }; use super::sqlx_common::*; @@ -68,12 +69,20 @@ impl SqlxSqliteConnector { options.max_connections(1); } match options.pool_options().connect_with(opt).await { - Ok(pool) => Ok(DatabaseConnection::SqlxSqlitePoolConnection( - SqlxSqlitePoolConnection { + Ok(pool) => { + let pool = SqlxSqlitePoolConnection { pool, metric_callback: None, - }, - )), + }; + + #[cfg(feature = "sqlite-use-returning-for-3_35")] + { + let version = get_version(&pool).await?; + ensure_returning_version(&version)?; + } + + Ok(DatabaseConnection::SqlxSqlitePoolConnection(pool)) + } Err(e) => Err(sqlx_error_to_conn_err(e)), } } @@ -268,3 +277,80 @@ pub(crate) async fn set_transaction_config( } Ok(()) } + +#[cfg(feature = "sqlite-use-returning-for-3_35")] +async fn get_version(conn: &SqlxSqlitePoolConnection) -> Result { + let stmt = Statement { + sql: "SELECT sqlite_version()".to_string(), + values: None, + db_backend: crate::DbBackend::Sqlite, + }; + conn.query_one(stmt) + .await? + .ok_or_else(|| { + DbErr::Conn(RuntimeErr::Internal( + "Error reading SQLite version".to_string(), + )) + })? + .try_get_by(0) +} + +#[cfg(feature = "sqlite-use-returning-for-3_35")] +fn ensure_returning_version(version: &str) -> Result<(), DbErr> { + let mut parts = version.trim().split('.').map(|part| { + part.parse::().map_err(|_| { + DbErr::Conn(RuntimeErr::Internal( + "Error parsing SQLite version".to_string(), + )) + }) + }); + + let mut extract_next = || { + parts.next().transpose().and_then(|part| { + part.ok_or_else(|| { + DbErr::Conn(RuntimeErr::Internal("SQLite version too short".to_string())) + }) + }) + }; + + let major = extract_next()?; + let minor = extract_next()?; + + if major > 3 || (major == 3 && minor >= 35) { + Ok(()) + } else { + Err(DbErr::Conn(RuntimeErr::Internal( + "SQLite version does not support returning".to_string(), + ))) + } +} + +#[cfg(all(test, feature = "sqlite-use-returning-for-3_35"))] +mod tests { + use super::*; + + #[test] + fn test_ensure_returning_version() { + assert!(ensure_returning_version("").is_err()); + assert!(ensure_returning_version(".").is_err()); + assert!(ensure_returning_version(".a").is_err()); + assert!(ensure_returning_version(".4.9").is_err()); + assert!(ensure_returning_version("a").is_err()); + assert!(ensure_returning_version("1.").is_err()); + assert!(ensure_returning_version("1.a").is_err()); + + assert!(ensure_returning_version("1.1").is_err()); + assert!(ensure_returning_version("1.0.").is_err()); + assert!(ensure_returning_version("1.0.0").is_err()); + assert!(ensure_returning_version("2.0.0").is_err()); + assert!(ensure_returning_version("3.34.0").is_err()); + assert!(ensure_returning_version("3.34.999").is_err()); + + // valid version + assert!(ensure_returning_version("3.35.0").is_ok()); + assert!(ensure_returning_version("3.35.1").is_ok()); + assert!(ensure_returning_version("3.36.0").is_ok()); + assert!(ensure_returning_version("4.0.0").is_ok()); + assert!(ensure_returning_version("99.0.0").is_ok()); + } +} diff --git a/tests/crud/error.rs b/tests/crud/error.rs index 9e05c7860c..5e38153efa 100644 --- a/tests/crud/error.rs +++ b/tests/crud/error.rs @@ -40,6 +40,10 @@ pub async fn test_cake_error_sqlx(db: &DbConn) { } _ => panic!("Unexpected sqlx-error kind"), }, + #[cfg(all(feature = "sqlx-sqlite", feature = "sqlite-use-returning-for-3_35"))] + DbErr::Query(RuntimeErr::SqlxError(Error::Database(e))) => { + assert_eq!(e.code().unwrap(), "1555"); + } _ => panic!("Unexpected Error kind"), } #[cfg(feature = "sqlx-postgres")] diff --git a/tests/returning_tests.rs b/tests/returning_tests.rs index 68abbf7750..0e0181e9f7 100644 --- a/tests/returning_tests.rs +++ b/tests/returning_tests.rs @@ -75,7 +75,13 @@ async fn main() -> Result<(), DbErr> { feature = "sqlx-postgres" ))] #[cfg_attr( - any(feature = "sqlx-mysql", feature = "sqlx-sqlite"), + any( + feature = "sqlx-mysql", + all( + feature = "sqlx-sqlite", + not(feature = "sqlite-use-returning-for-3_35") + ) + ), should_panic(expected = "Database backend doesn't support RETURNING") )] async fn update_many() { From 387b07e1c51a22a2fde97ae91111dd989d099f3e Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Thu, 25 Jan 2024 10:30:52 +0000 Subject: [PATCH 0282/1371] Changelog --- .github/workflows/rust.yml | 7 ++++--- CHANGELOG.md | 6 +++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index b170b5ce33..97b198a668 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -308,7 +308,6 @@ jobs: matrix: runtime: [async-std] tls: [native-tls, rustls] - sqlite_flavor: ["", sqlite-use-returning-for-3_35] steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@stable @@ -320,8 +319,10 @@ jobs: Cargo.lock target key: ${{ github.sha }}-${{ github.run_id }}-${{ runner.os }}-sqlite-${{ matrix.runtime }}-${{ matrix.tls }} - - run: cargo test --test '*' --features default,sqlx-sqlite,runtime-${{ matrix.runtime }}-${{ matrix.tls }},${{ matrix.sqlite_flavor }} - - run: cargo test --manifest-path sea-orm-migration/Cargo.toml --test '*' --features sqlx-sqlite,runtime-${{ matrix.runtime }}-${{ matrix.tls }},${{ matrix.sqlite_flavor }} + - run: cargo test --test '*' --features default,sqlx-sqlite,runtime-${{ matrix.runtime }}-${{ matrix.tls }} + - run: cargo test --manifest-path sea-orm-migration/Cargo.toml --test '*' --features sqlx-sqlite,runtime-${{ matrix.runtime }}-${{ matrix.tls }} + - run: cargo test --test '*' --features default,sqlx-sqlite,runtime-${{ matrix.runtime }}-${{ matrix.tls }},sqlite-use-returning-for-3_35 + - run: cargo test --manifest-path sea-orm-migration/Cargo.toml --test '*' --features sqlx-sqlite,runtime-${{ matrix.runtime }}-${{ matrix.tls }},sqlite-use-returning-for-3_35 mysql: name: MySQL diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d1676b449..dfae2ab6f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## 0.12.12 - Pending +## Master - Pending + +* Added feature flag `sqlite-use-returning-for-3_35` to use SQLite's returning https://github.com/SeaQL/sea-orm/pull/2070 + +## 0.12.12 - 2023-01-22 ### Bug Fixes From 390ca0f0f6b048f53ab7758297f089754f726e33 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sat, 27 Jan 2024 14:36:26 +0000 Subject: [PATCH 0283/1371] Edit #2083 --- .../common/migration/m20220118_000004_create_tea_enum.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sea-orm-migration/tests/common/migration/m20220118_000004_create_tea_enum.rs b/sea-orm-migration/tests/common/migration/m20220118_000004_create_tea_enum.rs index 26661d4a1e..5a369e6850 100644 --- a/sea-orm-migration/tests/common/migration/m20220118_000004_create_tea_enum.rs +++ b/sea-orm-migration/tests/common/migration/m20220118_000004_create_tea_enum.rs @@ -15,7 +15,7 @@ impl MigrationTrait for Migration { manager .create_type( Type::create() - .as_enum(Tea::Table) + .as_enum(Tea::Enum) .values([Tea::EverydayTea, Tea::BreakfastTea]) .to_owned(), ) @@ -33,7 +33,7 @@ impl MigrationTrait for Migration { DbBackend::MySql | DbBackend::Sqlite => {} DbBackend::Postgres => { manager - .drop_type(Type::drop().name(Tea::Table).to_owned()) + .drop_type(Type::drop().name(Tea::Enum).to_owned()) .await?; } } @@ -44,7 +44,8 @@ impl MigrationTrait for Migration { #[derive(DeriveIden)] pub enum Tea { - Table, + #[sea_orm(iden = "tea")] + Enum, #[sea_orm(iden = "EverydayTea")] EverydayTea, #[sea_orm(iden = "BreakfastTea")] From 3c07c91d54da2599867817dfec2909085211237d Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sat, 27 Jan 2024 19:31:43 +0000 Subject: [PATCH 0284/1371] Doc #2083 --- sea-orm-macros/src/lib.rs | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/sea-orm-macros/src/lib.rs b/sea-orm-macros/src/lib.rs index 6d2db2d08f..de9fde1a21 100644 --- a/sea-orm-macros/src/lib.rs +++ b/sea-orm-macros/src/lib.rs @@ -876,31 +876,25 @@ pub fn derive_active_enum_display(input: TokenStream) -> TokenStream { /// ## Usage /// /// ```rust -/// use sea_orm::DeriveIden; +/// use sea_orm::{Iden, DeriveIden}; /// /// #[derive(DeriveIden)] -/// pub enum Class { +/// pub enum MyClass { +/// Table, // this is a special case, which maps to the enum's name /// Id, +/// #[sea_orm(iden = "turtle")] /// Title, /// Text, /// } /// /// #[derive(DeriveIden)] -/// struct Glyph; -/// ``` -/// -/// You can use iden = "" to customize the name -/// ``` -/// use sea_orm::DeriveIden; -/// -/// #[derive(DeriveIden)] -/// pub enum Class { -/// Id, -/// #[sea_orm(iden = "turtle")] -/// Title, -/// #[sea_orm(iden = "TeXt")] -/// Text, -/// } +/// struct MyOther; +/// +/// assert_eq!(MyClass::Table.to_string(), "my_class"); +/// assert_eq!(MyClass::Id.to_string(), "id"); +/// assert_eq!(MyClass::Title.to_string(), "turtle"); // renamed! +/// assert_eq!(MyClass::Text.to_string(), "text"); +/// assert_eq!(MyOther.to_string(), "my_other"); /// ``` #[cfg(feature = "derive")] #[proc_macro_derive(DeriveIden, attributes(sea_orm))] From 8e6316c90ec747f985b3876bf85b1cfacd871943 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sat, 27 Jan 2024 19:32:39 +0000 Subject: [PATCH 0285/1371] fmt --- sea-orm-macros/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sea-orm-macros/src/lib.rs b/sea-orm-macros/src/lib.rs index de9fde1a21..d889527719 100644 --- a/sea-orm-macros/src/lib.rs +++ b/sea-orm-macros/src/lib.rs @@ -889,7 +889,7 @@ pub fn derive_active_enum_display(input: TokenStream) -> TokenStream { /// /// #[derive(DeriveIden)] /// struct MyOther; -/// +/// /// assert_eq!(MyClass::Table.to_string(), "my_class"); /// assert_eq!(MyClass::Id.to_string(), "id"); /// assert_eq!(MyClass::Title.to_string(), "turtle"); // renamed! From 59e987453dc80ebcee8181e65e2d9a229e48a96f Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sat, 27 Jan 2024 19:43:04 +0000 Subject: [PATCH 0286/1371] fmt --- sea-orm-macros/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sea-orm-macros/src/lib.rs b/sea-orm-macros/src/lib.rs index d889527719..26256700f8 100644 --- a/sea-orm-macros/src/lib.rs +++ b/sea-orm-macros/src/lib.rs @@ -876,7 +876,7 @@ pub fn derive_active_enum_display(input: TokenStream) -> TokenStream { /// ## Usage /// /// ```rust -/// use sea_orm::{Iden, DeriveIden}; +/// use sea_orm::{DeriveIden, Iden}; /// /// #[derive(DeriveIden)] /// pub enum MyClass { From 0016eff73b5b25284633bd9de19d7cbaf43ab5d3 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sun, 28 Jan 2024 01:50:43 +0000 Subject: [PATCH 0287/1371] Update --- README.md | 2 +- sea-orm-cli/src/cli.rs | 2 +- src/lib.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a4d85fa6d4..baf5ec852a 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ [![GitHub stars](https://img.shields.io/github/stars/SeaQL/sea-orm.svg?style=social&label=Star&maxAge=1)](https://github.com/SeaQL/sea-orm/stargazers/) If you like what we do, consider starring, sharing and contributing! -Please help us with maintaining SeaORM by completing the [SeaQL Community Survey 2023](https://sea-ql.org/community-survey)! +Please help us with maintaining SeaORM by completing the [SeaQL Community Survey 2024](https://sea-ql.org/community-survey)! ## Getting Started diff --git a/sea-orm-cli/src/cli.rs b/sea-orm-cli/src/cli.rs index 79af1190f9..3bc9ae9c6e 100644 --- a/sea-orm-cli/src/cli.rs +++ b/sea-orm-cli/src/cli.rs @@ -38,7 +38,7 @@ AUTHORS: Join our Discord server to chat with others in the SeaQL community! - Invitation: https://discord.com/invite/uCPdDXzbdv - SeaQL Community Survey 2023 + SeaQL Community Survey 2024 - Link: https://sea-ql.org/community-survey If you like what we do, consider starring, sharing and contributing! diff --git a/src/lib.rs b/src/lib.rs index 65e0d1cac6..15fce51f09 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,7 +29,7 @@ //! [![GitHub stars](https://img.shields.io/github/stars/SeaQL/sea-orm.svg?style=social&label=Star&maxAge=1)](https://github.com/SeaQL/sea-orm/stargazers/) //! If you like what we do, consider starring, sharing and contributing! //! -//! Please help us with maintaining SeaORM by completing the [SeaQL Community Survey 2023](https://sea-ql.org/community-survey)! +//! Please help us with maintaining SeaORM by completing the [SeaQL Community Survey 2024](https://sea-ql.org/community-survey)! //! //! ## Getting Started //! From 30fcdcbd5eaf1378bdeca00e48aa288b0549d8f6 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Thu, 1 Feb 2024 17:00:32 +0800 Subject: [PATCH 0288/1371] gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 413d53bfc2..7ec785cd4a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ Cargo.lock .vscode .idea/* */.idea/* -.env.local \ No newline at end of file +.env.local +.DS_Store \ No newline at end of file From d79b9590b2396b12bf8eb01e9566e5fcf1da6008 Mon Sep 17 00:00:00 2001 From: SZenglein <13049685+SZenglein@users.noreply.github.com> Date: Thu, 1 Feb 2024 10:20:23 +0100 Subject: [PATCH 0289/1371] Happy New Year! (#2093) --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dfae2ab6f0..477dae3776 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). * Added feature flag `sqlite-use-returning-for-3_35` to use SQLite's returning https://github.com/SeaQL/sea-orm/pull/2070 -## 0.12.12 - 2023-01-22 +## 0.12.12 - 2024-01-22 ### Bug Fixes @@ -20,7 +20,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). * Added `ConnectOptions::test_before_acquire` -## 0.12.11 - 2023-01-14 +## 0.12.11 - 2024-01-14 ### New Features From 2adbf8cd226b90847b4624943d30b3c2b15e2cf4 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Fri, 2 Feb 2024 13:21:35 +0800 Subject: [PATCH 0290/1371] Gold Sponsor --- README.md | 17 +++++++++++++++++ src/lib.rs | 19 ++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index baf5ec852a..4ce5f496fd 100644 --- a/README.md +++ b/README.md @@ -238,10 +238,27 @@ A big shout out to our contributors! ## Sponsorship +### Gold Sponsors + [SeaQL.org](https://www.sea-ql.org/) is an independent open-source organization run by passionate developers. If you enjoy using our libraries, please star and share our repositories. If you feel generous, a small donation via [GitHub Sponsor](https://github.com/sponsors/SeaQL) will be greatly appreciated, and goes a long way towards sustaining the organization. We invite you to participate, contribute and together help build Rust's future. + + + + + + +
+ + + + + + +
+ ## Mascot A friend of Ferris, Terres the hermit crab is the official mascot of SeaORM. His hobby is collecting shells. diff --git a/src/lib.rs b/src/lib.rs index 15fce51f09..12d44b7794 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -310,10 +310,27 @@ //! [![Contributors](https://opencollective.com/sea-orm/contributors.svg?width=1000&button=false)](https://github.com/SeaQL/sea-orm/graphs/contributors) //! //! ## Sponsorship -//! +//! +//! ### Gold Sponsors +//! //! [SeaQL.org](https://www.sea-ql.org/) is an independent open-source organization run by passionate developers. If you enjoy using our libraries, please star and share our repositories. If you feel generous, a small donation via [GitHub Sponsor](https://github.com/sponsors/SeaQL) will be greatly appreciated, and goes a long way towards sustaining the organization. //! //! We invite you to participate, contribute and together help build Rust's future. +//! +//! +//! +//! +//! +//! +//! +//!
+//! +//! +//! +//! +//! +//! +//!
//! //! ## Mascot //! From 116cfebece952d2c2a5ddc50500f22d144ac4145 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Fri, 2 Feb 2024 13:22:13 +0800 Subject: [PATCH 0291/1371] Gold Sponsor --- README.md | 4 ++-- src/lib.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4ce5f496fd..e5d180334d 100644 --- a/README.md +++ b/README.md @@ -238,12 +238,12 @@ A big shout out to our contributors! ## Sponsorship -### Gold Sponsors - [SeaQL.org](https://www.sea-ql.org/) is an independent open-source organization run by passionate developers. If you enjoy using our libraries, please star and share our repositories. If you feel generous, a small donation via [GitHub Sponsor](https://github.com/sponsors/SeaQL) will be greatly appreciated, and goes a long way towards sustaining the organization. We invite you to participate, contribute and together help build Rust's future. +### Gold Sponsors + diff --git a/src/lib.rs b/src/lib.rs index 12d44b7794..68465887fb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -311,12 +311,12 @@ //! //! ## Sponsorship //! -//! ### Gold Sponsors -//! //! [SeaQL.org](https://www.sea-ql.org/) is an independent open-source organization run by passionate developers. If you enjoy using our libraries, please star and share our repositories. If you feel generous, a small donation via [GitHub Sponsor](https://github.com/sponsors/SeaQL) will be greatly appreciated, and goes a long way towards sustaining the organization. //! //! We invite you to participate, contribute and together help build Rust's future. //! +//! ### Gold Sponsors +//! //!
//! //! From 9cd96462e82c7991bae108bef0c29c5b5fefdc64 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Fri, 2 Feb 2024 13:35:30 +0800 Subject: [PATCH 0292/1371] Gold Sponsor --- README.md | 2 +- src/lib.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e5d180334d..9e5866de9b 100644 --- a/README.md +++ b/README.md @@ -251,7 +251,7 @@ We invite you to participate, contribute and together help build Rust's future. - + diff --git a/src/lib.rs b/src/lib.rs index 68465887fb..5d854d0754 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -310,13 +310,13 @@ //! [![Contributors](https://opencollective.com/sea-orm/contributors.svg?width=1000&button=false)](https://github.com/SeaQL/sea-orm/graphs/contributors) //! //! ## Sponsorship -//! +//! //! [SeaQL.org](https://www.sea-ql.org/) is an independent open-source organization run by passionate developers. If you enjoy using our libraries, please star and share our repositories. If you feel generous, a small donation via [GitHub Sponsor](https://github.com/sponsors/SeaQL) will be greatly appreciated, and goes a long way towards sustaining the organization. //! //! We invite you to participate, contribute and together help build Rust's future. -//! +//! //! ### Gold Sponsors -//! +//! //!
//! //! @@ -324,7 +324,7 @@ //! //! //! -//! +//! //! //! //! From ab7de834b913114d1f211516f95fdac218d03be5 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Fri, 2 Feb 2024 16:51:30 +0800 Subject: [PATCH 0293/1371] README --- README.md | 20 ++++++-------------- src/lib.rs | 20 ++++++-------------- 2 files changed, 12 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 9e5866de9b..4d20feb113 100644 --- a/README.md +++ b/README.md @@ -244,20 +244,12 @@ We invite you to participate, contribute and together help build Rust's future. ### Gold Sponsors -
- - - - - -
- - - - - - -
+ + + + + + ## Mascot diff --git a/src/lib.rs b/src/lib.rs index 5d854d0754..fe5d27172f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -317,20 +317,12 @@ //! //! ### Gold Sponsors //! -//! -//! -//! -//! -//! -//! -//!
-//! -//! -//! -//! -//! -//! -//!
+//! +//! +//! +//! +//! +//! //! //! ## Mascot //! From 76f73e778d3213c9882e090c6e7aec26faf2ae4b Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Fri, 2 Feb 2024 17:01:25 +0800 Subject: [PATCH 0294/1371] README --- README.md | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4d20feb113..ac46a7ba26 100644 --- a/README.md +++ b/README.md @@ -244,7 +244,7 @@ We invite you to participate, contribute and together help build Rust's future. ### Gold Sponsors - + diff --git a/src/lib.rs b/src/lib.rs index fe5d27172f..80a9afce0e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -317,7 +317,7 @@ //! //! ### Gold Sponsors //! -//! +//! //! //! //! From 7f25da3e2b6ea14ad551182b0866998c3a2dafe4 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Mon, 5 Feb 2024 14:35:37 +0800 Subject: [PATCH 0295/1371] example: loco-todo-list (#2092) * example: loco-todo-list * fmt * Cargo.lock * Disabled integration test for GitHub CI * fmt * Update Cargo.toml --- .github/workflows/rust.yml | 1 + examples/loco_example/.cargo/config.toml | 3 + examples/loco_example/.dockerignore | 5 + examples/loco_example/.gitignore | 17 + examples/loco_example/.rustfmt.toml | 7 + examples/loco_example/Cargo.lock | 4994 ++++++++++++++++ examples/loco_example/Cargo.toml | 47 + examples/loco_example/README.md | 95 + examples/loco_example/Screenshot.png | Bin 0 -> 648485 bytes examples/loco_example/config/development.yaml | 72 + examples/loco_example/config/production.yaml | 72 + examples/loco_example/config/test.yaml | 76 + examples/loco_example/dockerfile | 18 + examples/loco_example/frontend/.eslintrc.cjs | 20 + examples/loco_example/frontend/.gitignore | 27 + examples/loco_example/frontend/index.html | 13 + examples/loco_example/frontend/package.json | 29 + examples/loco_example/frontend/pnpm-lock.yaml | 2898 ++++++++++ examples/loco_example/frontend/src/App.css | 37 + examples/loco_example/frontend/src/App.jsx | 163 + .../loco_example/frontend/src/assets/loco.svg | 5116 +++++++++++++++++ examples/loco_example/frontend/src/index.css | 68 + examples/loco_example/frontend/src/main.jsx | 21 + examples/loco_example/frontend/vite.config.js | 16 + examples/loco_example/frontend/yarn.lock | 3979 +++++++++++++ examples/loco_example/migration/Cargo.toml | 23 + examples/loco_example/migration/README.md | 41 + examples/loco_example/migration/src/lib.rs | 14 + .../migration/src/m20231103_114510_notes.rs | 36 + examples/loco_example/migration/src/main.rs | 6 + examples/loco_example/src/app.rs | 42 + examples/loco_example/src/bin/main.rs | 8 + examples/loco_example/src/controllers/mod.rs | 1 + .../loco_example/src/controllers/notes.rs | 69 + examples/loco_example/src/fixtures/notes.yaml | 11 + examples/loco_example/src/lib.rs | 3 + .../loco_example/src/models/_entities/mod.rs | 5 + .../src/models/_entities/notes.rs | 18 + .../src/models/_entities/prelude.rs | 3 + examples/loco_example/src/models/mod.rs | 2 + examples/loco_example/src/models/notes.rs | 7 + examples/loco_example/tests/mod.rs | 3 + examples/loco_example/tests/models/mod.rs | 1 + examples/loco_example/tests/requests/mod.rs | 1 + examples/loco_example/tests/requests/notes.rs | 127 + .../snapshots/can_add_note@notes_request.snap | 8 + .../can_delete_note@notes_request.snap | 8 + .../snapshots/can_get_note@notes_request.snap | 8 + .../can_get_notes@notes_request.snap | 8 + examples/loco_example/tests/tasks/mod.rs | 1 + examples/loco_example/tests/tasks/seed.rs | 42 + 51 files changed, 18290 insertions(+) create mode 100644 examples/loco_example/.cargo/config.toml create mode 100644 examples/loco_example/.dockerignore create mode 100644 examples/loco_example/.gitignore create mode 100644 examples/loco_example/.rustfmt.toml create mode 100644 examples/loco_example/Cargo.lock create mode 100644 examples/loco_example/Cargo.toml create mode 100644 examples/loco_example/README.md create mode 100644 examples/loco_example/Screenshot.png create mode 100644 examples/loco_example/config/development.yaml create mode 100644 examples/loco_example/config/production.yaml create mode 100644 examples/loco_example/config/test.yaml create mode 100644 examples/loco_example/dockerfile create mode 100644 examples/loco_example/frontend/.eslintrc.cjs create mode 100644 examples/loco_example/frontend/.gitignore create mode 100644 examples/loco_example/frontend/index.html create mode 100644 examples/loco_example/frontend/package.json create mode 100644 examples/loco_example/frontend/pnpm-lock.yaml create mode 100644 examples/loco_example/frontend/src/App.css create mode 100644 examples/loco_example/frontend/src/App.jsx create mode 100644 examples/loco_example/frontend/src/assets/loco.svg create mode 100644 examples/loco_example/frontend/src/index.css create mode 100644 examples/loco_example/frontend/src/main.jsx create mode 100644 examples/loco_example/frontend/vite.config.js create mode 100644 examples/loco_example/frontend/yarn.lock create mode 100644 examples/loco_example/migration/Cargo.toml create mode 100644 examples/loco_example/migration/README.md create mode 100644 examples/loco_example/migration/src/lib.rs create mode 100644 examples/loco_example/migration/src/m20231103_114510_notes.rs create mode 100644 examples/loco_example/migration/src/main.rs create mode 100644 examples/loco_example/src/app.rs create mode 100644 examples/loco_example/src/bin/main.rs create mode 100644 examples/loco_example/src/controllers/mod.rs create mode 100644 examples/loco_example/src/controllers/notes.rs create mode 100644 examples/loco_example/src/fixtures/notes.yaml create mode 100644 examples/loco_example/src/lib.rs create mode 100644 examples/loco_example/src/models/_entities/mod.rs create mode 100644 examples/loco_example/src/models/_entities/notes.rs create mode 100644 examples/loco_example/src/models/_entities/prelude.rs create mode 100644 examples/loco_example/src/models/mod.rs create mode 100644 examples/loco_example/src/models/notes.rs create mode 100644 examples/loco_example/tests/mod.rs create mode 100644 examples/loco_example/tests/models/mod.rs create mode 100644 examples/loco_example/tests/requests/mod.rs create mode 100644 examples/loco_example/tests/requests/notes.rs create mode 100644 examples/loco_example/tests/requests/snapshots/can_add_note@notes_request.snap create mode 100644 examples/loco_example/tests/requests/snapshots/can_delete_note@notes_request.snap create mode 100644 examples/loco_example/tests/requests/snapshots/can_get_note@notes_request.snap create mode 100644 examples/loco_example/tests/requests/snapshots/can_get_notes@notes_request.snap create mode 100644 examples/loco_example/tests/tasks/mod.rs create mode 100644 examples/loco_example/tests/tasks/seed.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 97b198a668..a84fd3b13f 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -239,6 +239,7 @@ jobs: examples/basic, examples/graphql_example, examples/jsonrpsee_example, + examples/loco_example, examples/poem_example, examples/proxy_gluesql_example, examples/rocket_example, diff --git a/examples/loco_example/.cargo/config.toml b/examples/loco_example/.cargo/config.toml new file mode 100644 index 0000000000..21b762815f --- /dev/null +++ b/examples/loco_example/.cargo/config.toml @@ -0,0 +1,3 @@ +[alias] +loco = "run --" +playground = "run --example playground" diff --git a/examples/loco_example/.dockerignore b/examples/loco_example/.dockerignore new file mode 100644 index 0000000000..41e0a30635 --- /dev/null +++ b/examples/loco_example/.dockerignore @@ -0,0 +1,5 @@ +target +dockerfile +.dockerignore +.git +.gitignore diff --git a/examples/loco_example/.gitignore b/examples/loco_example/.gitignore new file mode 100644 index 0000000000..624c1b572f --- /dev/null +++ b/examples/loco_example/.gitignore @@ -0,0 +1,17 @@ +**/config/local.yaml +**/config/*.local.yaml + +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +!Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb diff --git a/examples/loco_example/.rustfmt.toml b/examples/loco_example/.rustfmt.toml new file mode 100644 index 0000000000..d509e3ae84 --- /dev/null +++ b/examples/loco_example/.rustfmt.toml @@ -0,0 +1,7 @@ +max_width = 100 +comment_width = 80 +wrap_comments = true +imports_granularity = "Crate" +use_small_heuristics = "Default" +group_imports = "StdExternalCrate" +format_strings = true diff --git a/examples/loco_example/Cargo.lock b/examples/loco_example/Cargo.lock new file mode 100644 index 0000000000..285c500055 --- /dev/null +++ b/examples/loco_example/Cargo.lock @@ -0,0 +1,4994 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "aliasable" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" + +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "argon2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ba4cac0a46bc1d2912652a751c47f2a9f3a7fe89bcae2275d418f5270402f9" +dependencies = [ + "base64ct", + "blake2", + "cpufeatures", + "password-hash", +] + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "async-attributes" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c" +dependencies = [ + "concurrent-queue", + "event-listener 4.0.0", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c" +dependencies = [ + "async-lock 3.2.0", + "async-task", + "concurrent-queue", + "fastrand 2.0.1", + "futures-lite 2.1.0", + "slab", +] + +[[package]] +name = "async-global-executor" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" +dependencies = [ + "async-channel 2.1.1", + "async-executor", + "async-io 2.2.2", + "async-lock 3.2.0", + "blocking", + "futures-lite 2.1.0", + "once_cell", + "tokio", +] + +[[package]] +name = "async-io" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +dependencies = [ + "async-lock 2.8.0", + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-lite 1.13.0", + "log", + "parking", + "polling 2.8.0", + "rustix 0.37.27", + "slab", + "socket2 0.4.10", + "waker-fn", +] + +[[package]] +name = "async-io" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6afaa937395a620e33dc6a742c593c01aced20aa376ffb0f628121198578ccc7" +dependencies = [ + "async-lock 3.2.0", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite 2.1.0", + "parking", + "polling 3.3.1", + "rustix 0.38.28", + "slab", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-lock" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener 2.5.3", +] + +[[package]] +name = "async-lock" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7125e42787d53db9dd54261812ef17e937c95a51e4d291373b670342fa44310c" +dependencies = [ + "event-listener 4.0.0", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-std" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" +dependencies = [ + "async-attributes", + "async-channel 1.9.0", + "async-global-executor", + "async-io 1.13.0", + "async-lock 2.8.0", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite 1.13.0", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "async-task" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4eb2cdb97421e01129ccb49169d8279ed21e829929144f4a22a6e54ac549ca1" + +[[package]] +name = "async-trait" +version = "0.1.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "atomic-write-file" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edcdbedc2236483ab103a53415653d6b4442ea6141baf1ffa85df29635e88436" +dependencies = [ + "nix", + "rand", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "auto-future" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c1e7e457ea78e524f48639f551fd79703ac3f2237f5ecccdf4708f8a75ad373" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "axum" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "202651474fe73c62d9e0a56c6133f7a0ff1dc1c8cf7a5b03381af2a26553ac9d" +dependencies = [ + "async-trait", + "axum-core", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77cb22c689c44d4c07b0ab44ebc25d69d8ae601a2f28fb8d672d344178fa17aa" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-test" +version = "14.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ac99db40006a1a3fffeb381f2a78cb341dbc99d07b561e8bd119e22a2b1b0f" +dependencies = [ + "anyhow", + "async-trait", + "auto-future", + "axum", + "bytes", + "cookie", + "http", + "http-body-util", + "hyper", + "hyper-util", + "pretty_assertions", + "reserve-port", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "tokio", + "tower", + "url", +] + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "backtrace_printer" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8d28de81c708c843640982b66573df0f0168d87e42854b563971f326745aab7" +dependencies = [ + "btparse-stable", + "colored", + "regex", + "thiserror", +] + +[[package]] +name = "base64" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bb8" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98b4b0f25f18bcdc3ac72bdb486ed0acf7e185221fd4dc985bc15db5800b0ba2" +dependencies = [ + "async-trait", + "futures-channel", + "futures-util", + "parking_lot", + "tokio", +] + +[[package]] +name = "bigdecimal" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6773ddc0eafc0e509fb60e48dff7f450f8e674a0686ae8605e8d9901bd5eefa" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +dependencies = [ + "serde", +] + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blocking" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" +dependencies = [ + "async-channel 2.1.1", + "async-lock 3.2.0", + "async-task", + "fastrand 2.0.1", + "futures-io", + "futures-lite 2.1.0", + "piper", + "tracing", +] + +[[package]] +name = "borsh" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9897ef0f1bd2362169de6d7e436ea2237dc1085d7d1e4db75f4be34d86f309d1" +dependencies = [ + "borsh-derive", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478b41ff04256c5c8330f3dfdaaae2a5cc976a8e75088bafa4625b0d0208de8c" +dependencies = [ + "once_cell", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.41", + "syn_derive", +] + +[[package]] +name = "bstr" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "542f33a8835a0884b006a0c3df3dadd99c0c3f296ed26c2fdc8028e01ad6230c" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "btparse-stable" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d75b8252ed252f881d1dc4482ae3c3854df6ee8183c1906bac50ff358f4f89f" + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "byte-unit" +version = "4.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da78b32057b8fdfc352504708feeba7216dcd65a2c9ab02978cbd288d1279b6c" +dependencies = [ + "serde", + "utf8-width", +] + +[[package]] +name = "bytecheck" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6372023ac861f6e6dc89c8344a8f398fb42aaba2b5dbc649ca0c0e9dbcb627" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7ec4c6f261935ad534c0c22dbef2201b45918860eb1c574b972bd213a76af61" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "camino" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e34637b3140142bdf929fb439e8aa4ebad7651ebf7b1080b3930aa16ac1459ff" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "chrono" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets 0.48.5", +] + +[[package]] +name = "chrono-tz" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e23185c0e21df6ed832a12e2bda87c7d1def6842881fb634a8511ced741b0d76" +dependencies = [ + "chrono", + "chrono-tz-build", + "phf", +] + +[[package]] +name = "chrono-tz-build" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "433e39f13c9a060046954e0592a8d0a4bcb1040125cbf91cb8ee58964cfb350f" +dependencies = [ + "parse-zoneinfo", + "phf", + "phf_codegen", +] + +[[package]] +name = "chumsky" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eebd66744a15ded14960ab4ccdbfb51ad3b81f51f3f04a80adac98c985396c9" +dependencies = [ + "hashbrown 0.14.3", + "stacker", +] + +[[package]] +name = "clap" +version = "4.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "colored" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +dependencies = [ + "lazy_static", + "windows-sys 0.48.0", +] + +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "concurrent-queue" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "console" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "windows-sys 0.45.0", +] + +[[package]] +name = "const-oid" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" + +[[package]] +name = "cookie" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cd91cf61412820176e137621345ee43b3f4423e589e7ae4e50d601d93e35ef8" +dependencies = [ + "time", + "version_check", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cpufeatures" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "cron_clock" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a8699d8ed16e3db689f8ae04d8dc3c6666a4ba7e724e5a157884b7cc385d16b" +dependencies = [ + "chrono", + "nom", + "once_cell", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2fe95351b870527a5d09bf563ed3c97c0cffb87cf1c78a591bf48bb218d9aa" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9bcf5bdbfdd6030fb4a1c497b5d5fc5921aa2f60d359a17e249c0e6df3de153" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d96137f14f244c37f989d9fff8f95e6c18b918e71f36638f8c49112e4c78f" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossterm" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" +dependencies = [ + "bitflags 1.3.2", + "crossterm_winapi", + "libc", + "mio", + "parking_lot", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + +[[package]] +name = "cruet" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "113a9e83d8f614be76de8df1f25bf9d0ea6e85ea573710a3d3f3abe1438ae49c" +dependencies = [ + "once_cell", + "regex", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.3", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "deunicode" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae2a35373c5c74340b79ae6780b498b2b183915ec5dacf263aac5a099bf485a" + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "duct" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ae3fc31835f74c2a7ceda3aeede378b0ae2e74c8f1c36559fcc9ae2a4e7d3e" +dependencies = [ + "libc", + "once_cell", + "os_pipe", + "shared_child", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +dependencies = [ + "serde", +] + +[[package]] +name = "email-encoding" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbfb21b9878cf7a348dcb8559109aabc0ec40d69924bd706fa5149846c4fef75" +dependencies = [ + "base64", + "memchr", +] + +[[package]] +name = "email_address" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2153bd83ebc09db15bcbdc3e2194d901804952e3dc96967e1cd3b0c5c32d112" + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "770d968249b5d99410d61f5bf89057f3199a077a04d087092f58e7d10692baae" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" +dependencies = [ + "event-listener 4.0.0", + "pin-project-lite", +] + +[[package]] +name = "eyre" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6267a1fa6f59179ea4afc8e50fd8612a3cc60bc858f786ff877a4a8cb042799" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "finl_unicode" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" + +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "futures-core", + "futures-sink", + "spin 0.9.8", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs-err" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a41f105fe1d5b6b34b2055e3dc59bb79b46b48b2040b9e6c7b4b5de097aa41" +dependencies = [ + "autocfg", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" + +[[package]] +name = "futures-executor" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot", +] + +[[package]] +name = "futures-io" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" + +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand 1.9.0", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "futures-lite" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aeee267a1883f7ebef3700f262d2d54de95dfaf38189015a74fdc4e0c7ad8143" +dependencies = [ + "fastrand 2.0.1", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "futures-sink" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" + +[[package]] +name = "futures-task" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" + +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" + +[[package]] +name = "futures-util" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "gethostname" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "globset" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", +] + +[[package]] +name = "globwalk" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" +dependencies = [ + "bitflags 1.3.2", + "ignore", + "walkdir", +] + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "h2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d308f63daf4181410c242d34c11f928dcb3aa105852019e043c9d1f4e4368a" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.7", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash 0.8.6", + "allocator-api2", +] + +[[package]] +name = "hashlink" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +dependencies = [ + "hashbrown 0.14.3", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + +[[package]] +name = "http" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "http-range-header" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ce4ef31cda248bbdb6e6820603b82dfcd9e833db65a43e997a0ccec777d11fe" + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humansize" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" +dependencies = [ + "libm", +] + +[[package]] +name = "hyper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403f9214f3e703236b221f1a9cd88ec8b4adfa5296de01ab96216361f4692f56" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "tokio", + "want", +] + +[[package]] +name = "hyper-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ca339002caeb0d159cc6e023dff48e199f081e42fa039895c7c6f38b37f2e9d" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2 0.5.5", + "tokio", + "tower", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "if_chain" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" + +[[package]] +name = "ignore" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "747ad1b4ae841a78e8aba0d63adbfbeaea26b517b63705d47856b73015d27060" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata 0.4.3", + "same-file", + "walkdir", + "winapi-util", +] + +[[package]] +name = "include_dir" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18762faeff7122e89e0857b02f7ce6fcc0d101d5e9ad2ad7846cc01d61b7f19e" +dependencies = [ + "include_dir_macros", +] + +[[package]] +name = "include_dir_macros" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", +] + +[[package]] +name = "inherent" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce243b1bfa62ffc028f1cc3b6034ec63d649f3031bc8a4fbbb004e1ac17d1f68" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "insta" +version = "1.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d64600be34b2fcfc267740a243fa7744441bb4947a619ac4e5bb6507f35fbfc" +dependencies = [ + "console", + "lazy_static", + "linked-hash-map", + "pest", + "pest_derive", + "regex", + "serde", + "similar", + "yaml-rust", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.3", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "itertools" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "js-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "jsonwebtoken" +version = "9.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7ea04a7c5c055c175f189b6dc6ba036fd62306b58c66c9f6389036c503a3f4" +dependencies = [ + "base64", + "js-sys", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1", +] + +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin 0.5.2", +] + +[[package]] +name = "lettre" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a48c2e9831b370bc2d7233c2620298c45f3a158ed6b4b8d7416b2ada5a268fd8" +dependencies = [ + "async-trait", + "base64", + "chumsky", + "email-encoding", + "email_address", + "fastrand 2.0.1", + "futures-io", + "futures-util", + "hostname", + "httpdate", + "idna 0.5.0", + "mime", + "nom", + "once_cell", + "quoted_printable", + "rustls", + "rustls-pemfile", + "socket2 0.5.5", + "tokio", + "tokio-rustls", + "url", + "webpki-roots", +] + +[[package]] +name = "libc" +version = "0.2.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "libredox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.1", + "libc", + "redox_syscall", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "loco-rs" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af486e72ce8e7f6a8ae87bab477131db2b1b819f7f76ff4583f69207cc783f48" +dependencies = [ + "argon2", + "async-trait", + "axum", + "axum-test", + "backtrace_printer", + "bb8", + "byte-unit", + "cargo_metadata", + "cfg-if", + "chrono", + "clap", + "colored", + "duct", + "eyre", + "fs-err", + "include_dir", + "jsonwebtoken", + "lazy_static", + "lettre", + "rand", + "regex", + "requestty", + "rrgen", + "rusty-sidekiq", + "sea-orm", + "sea-orm-migration", + "serde", + "serde_json", + "serde_variant", + "serde_yaml", + "tera", + "thiserror", + "tokio", + "tower-http", + "tracing", + "tracing-subscriber", + "uuid", + "validator", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +dependencies = [ + "value-bag", +] + +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "migration" +version = "0.1.0" +dependencies = [ + "async-std", + "loco-rs", + "sea-orm-migration", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.4.1", + "cfg-if", + "libc", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.3", + "libc", +] + +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "ordered-float" +version = "3.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1e1c390732d15f1d48471625cd92d154e66db2c56645e29a9cd26f4699f72dc" +dependencies = [ + "num-traits", +] + +[[package]] +name = "os_pipe" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae859aa07428ca9a929b936690f8b12dc5f11dd8c6992a18ca93919f28bc177" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "ouroboros" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2ba07320d39dfea882faa70554b4bd342a5f273ed59ba7c1c6b4c840492c954" +dependencies = [ + "aliasable", + "ouroboros_macro", + "static_assertions", +] + +[[package]] +name = "ouroboros_macro" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec4c6225c69b4ca778c0aea097321a64c421cf4577b331c61b229267edabb6f8" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "parse-zoneinfo" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41" +dependencies = [ + "regex", +] + +[[package]] +name = "password-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core", + "subtle", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "pem" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8fcc794035347fb64beda2d3b462595dd2753e3f268d89c5aae77e8cf2c310" +dependencies = [ + "base64", + "serde", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pest" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81d78524685f5ef2a3b3bd1cafbc9fcabb036253d9b1463e726a91cd16e2dfc2" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68bd1206e71118b5356dae5ddc61c8b11e28b09ef6a31acbd15ea48a28e0c227" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "pest_meta" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c747191d4ad9e4a4ab9c8798f1e82a39affe7ef9648390b7e5548d18e099de6" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" +dependencies = [ + "atomic-waker", + "fastrand 2.0.1", + "futures-io", +] + +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "polling" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "concurrent-queue", + "libc", + "log", + "pin-project-lite", + "windows-sys 0.48.0", +] + +[[package]] +name = "polling" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf63fa624ab313c11656b4cda960bfc46c410187ad493c41f6ba2d8c1e991c9e" +dependencies = [ + "cfg-if", + "concurrent-queue", + "pin-project-lite", + "rustix 0.38.28", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "pretty_assertions" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" +dependencies = [ + "diff", + "yansi", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97dc5fea232fc28d2f597b37c4876b348a40e33f3b02cc975c8d006d78d94b1a" +dependencies = [ + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "psm" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +dependencies = [ + "cc", +] + +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "quoted_printable" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79ec282e887b434b68c18fe5c121d38e72a5cf35119b59e54ec5b992ea9c8eb0" + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redis" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa8455fa3621f6b41c514946de66ea0531f57ca017b2e6c7cc368035ea5b46df" +dependencies = [ + "async-trait", + "bytes", + "combine", + "futures-util", + "itoa", + "percent-encoding", + "pin-project-lite", + "ryu", + "sha1_smol", + "tokio", + "tokio-util", + "url", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_users" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "relative-path" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c707298afce11da2efef2f600116fa93ffa7a032b5d7b628aa17711ec81383ca" + +[[package]] +name = "rend" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2571463863a6bd50c32f94402933f03457a3fbaf697a707c5be741e459f08fd" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "requestty" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa883a1f3e288e65187f653e6ba2e84fdf810fe02f4c8074f9c723d1aa26e2ae" +dependencies = [ + "requestty-ui", + "shell-words", + "smallvec", + "tempfile", + "winsplit", +] + +[[package]] +name = "requestty-ui" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7549bab39cf982b629b68e7ec191a5574e85086e95c0ebe514c02d3b42ffe225" +dependencies = [ + "bitflags 1.3.2", + "crossterm", + "once_cell", + "textwrap", + "unicode-segmentation", +] + +[[package]] +name = "reserve-port" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3969e7fe15c6c1532ba1a761628298e870bbd18c252fd41a58445f6091c372a0" +dependencies = [ + "lazy_static", + "thiserror", +] + +[[package]] +name = "ring" +version = "0.17.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" +dependencies = [ + "cc", + "getrandom", + "libc", + "spin 0.9.8", + "untrusted", + "windows-sys 0.48.0", +] + +[[package]] +name = "rkyv" +version = "0.7.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0200c8230b013893c0b2d6213d6ec64ed2b9be2e0e016682b7224ff82cff5c58" +dependencies = [ + "bitvec", + "bytecheck", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2e06b915b5c230a17d7a736d1e2e63ee753c256a8614ef3f5147b13a4f5541d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rrgen" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e40013551787f9f535e7dbc8dafc164591d941aeae48881a385d8b0393dd45f6" +dependencies = [ + "cruet", + "fs-err", + "glob", + "heck", + "regex", + "serde", + "serde_json", + "serde_regex", + "serde_yaml", + "tera", + "thiserror", +] + +[[package]] +name = "rsa" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core", + "signature", + "spki", + "subtle", + "zeroize", +] + +[[package]] +name = "rstest" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97eeab2f3c0a199bc4be135c36c924b6590b88c377d416494288c14f2db30199" +dependencies = [ + "futures", + "futures-timer", + "rstest_macros", + "rustc_version", +] + +[[package]] +name = "rstest_macros" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d428f8247852f894ee1be110b375111b586d4fa431f6c46e64ba5a0dcccbe605" +dependencies = [ + "cfg-if", + "glob", + "proc-macro2", + "quote", + "regex", + "relative-path", + "rustc_version", + "syn 2.0.41", + "unicode-ident", +] + +[[package]] +name = "rust_decimal" +version = "1.33.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06676aec5ccb8fc1da723cc8c0f9a46549f21ebb8753d3915c6c41db1e7f1dc4" +dependencies = [ + "arrayvec", + "borsh", + "bytes", + "num-traits", + "rand", + "rkyv", + "serde", + "serde_json", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.37.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +dependencies = [ + "bitflags 2.4.1", + "errno", + "libc", + "linux-raw-sys 0.4.12", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.21.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "rusty-sidekiq" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a00db3916faeea070039864f98d4fd759d96fc07722571a4918d996fea5621" +dependencies = [ + "async-trait", + "bb8", + "chrono", + "cron_clock", + "gethostname", + "heck", + "hex", + "num_cpus", + "rand", + "redis", + "serde", + "serde_json", + "sha2", + "slog-term", + "thiserror", + "tokio", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "sea-bae" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bd3534a9978d0aa7edd2808dc1f8f31c4d0ecd31ddf71d997b3c98e9f3c9114" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "sea-orm" +version = "0.12.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5181eedee8ad0d002d2a288600140fe9937581c4668426a4ff1295c14c736cf" +dependencies = [ + "async-stream", + "async-trait", + "bigdecimal", + "chrono", + "futures", + "log", + "ouroboros", + "rust_decimal", + "sea-orm-macros", + "sea-query", + "sea-query-binder", + "serde", + "serde_json", + "sqlx", + "strum", + "thiserror", + "time", + "tracing", + "url", + "uuid", +] + +[[package]] +name = "sea-orm-cli" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507c2b99f83129f4d5d511012ab47c9f40d1c7923fd4698c19fae23f0bf62913" +dependencies = [ + "chrono", + "clap", + "dotenvy", + "glob", + "regex", + "sea-schema", + "tracing", + "tracing-subscriber", + "url", +] + +[[package]] +name = "sea-orm-macros" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "816183a751bf9c22087679b20b6142da0b5c6d8981835ebb7b99bf1bf924640a" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "sea-bae", + "syn 2.0.41", + "unicode-ident", +] + +[[package]] +name = "sea-orm-migration" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d45937e5d4869a0dcf0222bb264df564c077cbe9b312265f3717401d023a633" +dependencies = [ + "async-trait", + "clap", + "dotenvy", + "futures", + "sea-orm", + "sea-orm-cli", + "sea-schema", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "sea-query" +version = "0.30.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41558fa9bb5f4d73952dac0b9d9c2ce23966493fc9ee0008037b01d709838a68" +dependencies = [ + "bigdecimal", + "chrono", + "derivative", + "inherent", + "ordered-float", + "rust_decimal", + "sea-query-derive", + "serde_json", + "time", + "uuid", +] + +[[package]] +name = "sea-query-binder" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36bbb68df92e820e4d5aeb17b4acd5cc8b5d18b2c36a4dd6f4626aabfa7ab1b9" +dependencies = [ + "bigdecimal", + "chrono", + "rust_decimal", + "sea-query", + "serde_json", + "sqlx", + "time", + "uuid", +] + +[[package]] +name = "sea-query-derive" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a82fcb49253abcb45cdcb2adf92956060ec0928635eb21b4f7a6d8f25ab0bc" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.41", + "thiserror", +] + +[[package]] +name = "sea-schema" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cd9561232bd1b82ea748b581f15909d11de0db6563ddcf28c5d908aee8282f1" +dependencies = [ + "futures", + "sea-query", + "sea-schema-derive", +] + +[[package]] +name = "sea-schema-derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f686050f76bffc4f635cda8aea6df5548666b830b52387e8bc7de11056d11e" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "semver" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" +dependencies = [ + "serde", +] + +[[package]] +name = "serde" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335" +dependencies = [ + "itoa", + "serde", +] + +[[package]] +name = "serde_regex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8136f1a4ea815d7eac4101cfd0b16dc0cb5e1fe1b8609dfd728058656b7badf" +dependencies = [ + "regex", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_variant" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47a8ec0b2fd0506290348d9699c0e3eb2e3e8c0498b5a9a6158b3bd4d6970076" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.9.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "serial_test" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e56dd856803e253c8f298af3f4d7eb0ae5e23a737252cd90bb4f3b435033b2d" +dependencies = [ + "dashmap", + "futures", + "lazy_static", + "log", + "parking_lot", + "serial_test_derive", +] + +[[package]] +name = "serial_test_derive" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shared_child" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0d94659ad3c2137fef23ae75b03d5241d633f8acded53d672decfa0e6e0caef" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "simdutf8" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" + +[[package]] +name = "similar" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aeaf503862c419d66959f5d7ca015337d864e9c49485d771b732e2a20453597" + +[[package]] +name = "simple_asn1" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror", + "time", +] + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "slog" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8347046d4ebd943127157b94d63abb990fcf729dc4e9978927fdf4ac3c998d06" + +[[package]] +name = "slog-term" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87d29185c55b7b258b4f120eab00f48557d4d9bc814f41713f449d35b0f8977c" +dependencies = [ + "atty", + "slog", + "term", + "thread_local", + "time", +] + +[[package]] +name = "slug" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bd94acec9c8da640005f8e135a39fc0372e74535e6b368b7a04b875f784c8c4" +dependencies = [ + "deunicode", + "wasm-bindgen", +] + +[[package]] +name = "smallvec" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" + +[[package]] +name = "smawk" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" + +[[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "sqlformat" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce81b7bd7c4493975347ef60d8c7e8b742d4694f4c49f93e0a12ea263938176c" +dependencies = [ + "itertools", + "nom", + "unicode_categories", +] + +[[package]] +name = "sqlx" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dba03c279da73694ef99763320dea58b51095dfe87d001b1d4b5fe78ba8763cf" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", +] + +[[package]] +name = "sqlx-core" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d84b0a3c3739e220d94b3239fd69fb1f74bc36e16643423bd99de3b43c21bfbd" +dependencies = [ + "ahash 0.8.6", + "atoi", + "bigdecimal", + "byteorder", + "bytes", + "chrono", + "crc", + "crossbeam-queue", + "dotenvy", + "either", + "event-listener 2.5.3", + "futures-channel", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashlink", + "hex", + "indexmap", + "log", + "memchr", + "once_cell", + "paste", + "percent-encoding", + "rust_decimal", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlformat", + "thiserror", + "time", + "tokio", + "tokio-stream", + "tracing", + "url", + "uuid", + "webpki-roots", +] + +[[package]] +name = "sqlx-macros" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89961c00dc4d7dffb7aee214964b065072bff69e36ddb9e2c107541f75e4f2a5" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn 1.0.109", +] + +[[package]] +name = "sqlx-macros-core" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0bd4519486723648186a08785143599760f7cc81c52334a55d6a83ea1e20841" +dependencies = [ + "atomic-write-file", + "dotenvy", + "either", + "heck", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", + "syn 1.0.109", + "tempfile", + "tokio", + "url", +] + +[[package]] +name = "sqlx-mysql" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e37195395df71fd068f6e2082247891bc11e3289624bbc776a0cdfa1ca7f1ea4" +dependencies = [ + "atoi", + "base64", + "bigdecimal", + "bitflags 2.4.1", + "byteorder", + "bytes", + "chrono", + "crc", + "digest", + "dotenvy", + "either", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "generic-array", + "hex", + "hkdf", + "hmac", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "percent-encoding", + "rand", + "rsa", + "rust_decimal", + "serde", + "sha1", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "time", + "tracing", + "uuid", + "whoami", +] + +[[package]] +name = "sqlx-postgres" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6ac0ac3b7ccd10cc96c7ab29791a7dd236bd94021f31eec7ba3d46a74aa1c24" +dependencies = [ + "atoi", + "base64", + "bigdecimal", + "bitflags 2.4.1", + "byteorder", + "chrono", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "itoa", + "log", + "md-5", + "memchr", + "num-bigint", + "once_cell", + "rand", + "rust_decimal", + "serde", + "serde_json", + "sha1", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "time", + "tracing", + "uuid", + "whoami", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "210976b7d948c7ba9fced8ca835b11cbb2d677c59c79de41ac0d397e14547490" +dependencies = [ + "atoi", + "chrono", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "sqlx-core", + "time", + "tracing", + "url", + "urlencoding", + "uuid", +] + +[[package]] +name = "stacker" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "winapi", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "stringprep" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb41d74e231a107a1b4ee36bd1214b11285b77768d2e3824aedafa988fd36ee6" +dependencies = [ + "finl_unicode", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn_derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +dependencies = [ + "cfg-if", + "fastrand 2.0.1", + "redox_syscall", + "rustix 0.38.28", + "windows-sys 0.48.0", +] + +[[package]] +name = "tera" +version = "1.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "970dff17c11e884a4a09bc76e3a17ef71e01bb13447a11e85226e254fe6d10b8" +dependencies = [ + "chrono", + "chrono-tz", + "globwalk", + "humansize", + "lazy_static", + "percent-encoding", + "pest", + "pest_derive", + "rand", + "regex", + "serde", + "serde_json", + "slug", + "unic-segment", +] + +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + +[[package]] +name = "textwrap" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d" +dependencies = [ + "smawk", + "unicode-linebreak", + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" +dependencies = [ + "deranged", + "itoa", + "libc", + "num_threads", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +dependencies = [ + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "todolist" +version = "0.1.0" +dependencies = [ + "async-trait", + "axum", + "chrono", + "eyre", + "include_dir", + "insta", + "loco-rs", + "migration", + "rstest", + "sea-orm", + "serde", + "serde_json", + "serial_test", + "tokio", + "tracing", + "tracing-subscriber", + "uuid", + "validator", +] + +[[package]] +name = "tokio" +version = "1.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.5.5", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09e12e6351354851911bdf8c2b8f2ab15050c567d70a8b9a37ae7b8301a4080d" +dependencies = [ + "bitflags 2.4.1", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "http-range-header", + "httpdate", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-serde" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", + "tracing-serde", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-segment" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23" +dependencies = [ + "unic-ucd-segment", +] + +[[package]] +name = "unic-ucd-segment" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + +[[package]] +name = "unsafe-libyaml" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna 0.5.0", + "percent-encoding", +] + +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "utf8-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "uuid" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" +dependencies = [ + "getrandom", + "serde", +] + +[[package]] +name = "validator" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b92f40481c04ff1f4f61f304d61793c7b56ff76ac1469f1beb199b1445b253bd" +dependencies = [ + "idna 0.4.0", + "lazy_static", + "regex", + "serde", + "serde_derive", + "serde_json", + "url", + "validator_derive", +] + +[[package]] +name = "validator_derive" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc44ca3088bb3ba384d9aecf40c6a23a676ce23e09bdaca2073d99c207f864af" +dependencies = [ + "if_chain", + "lazy_static", + "proc-macro-error", + "proc-macro2", + "quote", + "regex", + "syn 1.0.109", + "validator_types", +] + +[[package]] +name = "validator_types" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "111abfe30072511849c5910134e8baf8dc05de4c0e5903d681cbd5c9c4d611e3" +dependencies = [ + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "value-bag" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a72e1902dde2bd6441347de2b70b7f5d59bf157c6c62f0c44572607a1d55bbe" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "waker-fn" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" + +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.41", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" + +[[package]] +name = "web-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" + +[[package]] +name = "whoami" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "winnow" +version = "0.5.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c830786f7720c2fd27a1a0e27a709dbd3c4d009b56d098fc742d4f4eab91fe2" +dependencies = [ + "memchr", +] + +[[package]] +name = "winsplit" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab703352da6a72f35c39a533526393725640575bb211f61987a2748323ad956" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + +[[package]] +name = "zerocopy" +version = "0.7.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c4061bedbb353041c12f413700357bec76df2c7e2ca8e4df8bac24c6bf68e3d" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/examples/loco_example/Cargo.toml b/examples/loco_example/Cargo.toml new file mode 100644 index 0000000000..ba860d7833 --- /dev/null +++ b/examples/loco_example/Cargo.toml @@ -0,0 +1,47 @@ +[workspace] + +[package] +name = "todolist" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +loco-rs = { version = "0.1.7" } +migration = { path = "migration" } + +serde = { version = "1", features = ["derive"] } +serde_json = "1" +eyre = "0.6" +tokio = { version = "1.33.0", default-features = false } +async-trait = "0.1.74" +tracing = "0.1.40" +chrono = "0.4" +validator = { version = "0.16" } +axum = "0.7.1" +include_dir = "0.7" +uuid = { version = "1.6.0", features = ["v4"] } +tracing-subscriber = { version = "0.3.17", features = ["env-filter", "json"] } + +[dependencies.sea-orm] +# path = "../../" # remove this line in your own project +version = "0.12.4" # sea-orm version +features = [ + "sqlx-sqlite", + "sqlx-postgres", + "runtime-tokio-rustls", + "macros", +] + +[[bin]] +name = "todolist-cli" +path = "src/bin/main.rs" +required-features = [] + +[dev-dependencies] +serial_test = "2.0.0" +rstest = "0.18.2" +loco-rs = { version = "0.1.7", features = ["testing"] } +insta = { version = "1.34.0", features = ["redactions", "yaml", "filters"] } diff --git a/examples/loco_example/README.md b/examples/loco_example/README.md new file mode 100644 index 0000000000..08cd9540a9 --- /dev/null +++ b/examples/loco_example/README.md @@ -0,0 +1,95 @@ +![screenshot](Screenshot.png) + +> Adapted from https://github.com/loco-rs/todo-list + +# Loco with SeaORM example todo list + +Build your own todo list website using Loco. Follow the step-by-step guide [here](<(https://loco.rs/blog/frontend-website/)>) to create it effortlessly. + +## Build Client + +Navigate to the `frontend` directory and build the client: + +```sh +$ cd frontend && yarn install && yarn build + +vite v5.0.8 building for production... +✓ 120 modules transformed. +dist/index.html 0.46 kB │ gzip: 0.30 kB +dist/assets/index-AbTMZIjW.css 1.26 kB │ gzip: 0.65 kB +dist/assets/index-MJFpQvzE.js 235.64 kB │ gzip: 75.58 kB +✓ built in 2.01s +``` + +## Run Locally + +You need: + +* A local postgres instance + +Check out your development [configuration](config/development.yaml). + +> To configure a database , please run a local postgres database with loco:loco and a db named [app name]_development.: +docker run -d -p 5432:5432 -e POSTGRES_USER=loco -e POSTGRES_DB=[app name]_development -e POSTGRES_PASSWORD="loco" postgres:15.3-alpine + +Execute the following command to run your todo list website locally, serving static assets from `frontend/dist`: + +```sh +$ cargo loco start + + Finished dev [unoptimized + debuginfo] target(s) in 0.53s + Running `target/debug/todolist-cli start` +2024-02-01T08:49:41.070430Z INFO loco_rs::db: auto migrating +2024-02-01T08:49:41.073698Z INFO sea_orm_migration::migrator: Applying all pending migrations +2024-02-01T08:49:41.078191Z INFO sea_orm_migration::migrator: No pending migrations +2024-02-01T08:49:41.100557Z INFO loco_rs::controller::app_routes: [GET] /api/_ping +2024-02-01T08:49:41.100617Z INFO loco_rs::controller::app_routes: [GET] /api/_health +2024-02-01T08:49:41.100667Z INFO loco_rs::controller::app_routes: [GET] /api/notes +2024-02-01T08:49:41.100702Z INFO loco_rs::controller::app_routes: [POST] /api/notes +2024-02-01T08:49:41.100738Z INFO loco_rs::controller::app_routes: [GET] /api/notes/:id +2024-02-01T08:49:41.100791Z INFO loco_rs::controller::app_routes: [DELETE] /api/notes/:id +2024-02-01T08:49:41.100817Z INFO loco_rs::controller::app_routes: [POST] /api/notes/:id +2024-02-01T08:49:41.100934Z INFO loco_rs::controller::app_routes: [Middleware] Adding limit payload data="5mb" +2024-02-01T08:49:41.101017Z INFO loco_rs::controller::app_routes: [Middleware] Adding log trace id +2024-02-01T08:49:41.101057Z INFO loco_rs::controller::app_routes: [Middleware] Adding timeout layer +2024-02-01T08:49:41.101192Z INFO loco_rs::controller::app_routes: [Middleware] Adding cors +2024-02-01T08:49:41.101241Z INFO loco_rs::controller::app_routes: [Middleware] Adding static + + ▄ ▀ + ▀ ▄ + ▄ ▀ ▄ ▄ ▄▀ + ▄ ▀▄▄ + ▄ ▀ ▀ ▀▄▀█▄ + ▀█▄ +▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄ ▀▀█ + ██████ █████ ███ █████ ███ █████ ███ ▀█ + ██████ █████ ███ █████ ▀▀▀ █████ ███ ▄█▄ + ██████ █████ ███ █████ █████ ███ ████▄ + ██████ █████ ███ █████ ▄▄▄ █████ ███ █████ + ██████ █████ ███ ████ ███ █████ ███ ████▀ + ▀▀▀██▄ ▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀ ██▀ + ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ + https://loco.rs + +environment: development + database: automigrate + logger: debug +compilation: debug + modes: server + +listening on port 3000 +``` + +## Development + +To develop the UI, run the following commands: + +```sh +$ cd frontend && yarn install && yarn dev +``` + +To run the server: + +```sh +$ cargo loco start +``` diff --git a/examples/loco_example/Screenshot.png b/examples/loco_example/Screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..3bb65b16329cb8e83c04249f64ff8982dce8dcf8 GIT binary patch literal 648485 zcmeFZcUTi!7e0!Df}#dRL8U}gR0N5L(n&-_L=+S}0!mRLBGN>92_vFZ1q)K7L_|as zL3$@DB1j8ON+_X+76J(*gd{U}qUW6Ndw%zM?!Eurf4(e}8TQPcnO)Y}?|Ro7BQKg5 zZr-?iqkw?GW}~yG%>)ECoEH#SUm~&|E^!vTJ}V%w@spFj{zW5w{XG}&x!-bfaS#wV z8~HLp_@cR$67QCE(D0~k$)%=wRHHf+y=385h1Vb4ZrWO6g|B6`WhRt zOLzZ@Qrf#W%JtI4xr#l}4+@keT;6jLJHO3?3<%;nPB!!A{V8dl^Qpe%6k!3Yw0+w$ zztIKWZcwe08=7BwKebcKZRg38>jYj4Uf-gw|9#U&y5X(sKHSQnvS`13_ximKcM~vB z-kUFDj(6AGe;^h^ak$$lh7O%F&OMv5L13os@(#sg2Id2iNB?@fJz{~UV3IP@t`LL2 zbv%)|LG*^)`E>%Oqj$eY3Ot>PNKLD|aC-FVfLMtbUFg(91Ch$NnVAeRbmV8b(a_$r z=8L-)fyfstrHP9jiP9!-;vAXBj`$(;!VbMGoPl&=pd(J%&- zi}S)Lh>l6FYc061PQ|nsZNo2EP2T!!e=Dheap6Vo1)Fh9(}uOElH(=^v|ZxayVBAN zl}r%NCeMi#CKfhtB~HA2`_Pzb6jX9N*vHIrT*-06UWt}_XU@GRryz{)NE)AdyUchr zH1FwO{Wj3FX{9N6oy#<-`R!5U^pK_}e5koRao42|l3x&lsENOuO*qtP^U64G_x@!{ zR)8^emsNU4aZ-b*W~tiQfUuv^#28$=%572EaZ8fM z>sNPaiQ3%HJ*C?wcw1n1$j~Xf>)X#i$o{OAgGjF3l(>QO^~Sku=-J-XKHK(a%53S9*S-pIoRsc;S$&}Xsmh(5;*a-* zJ&F9b-~P&V`q8??!{-Gjx4lECK4jmovv;x-%+c)@mU?pGNA3>^dlq&!pV})sx5r01 zyf%CI{N2{FlhVQokxjMKTABLP`sLBEyZD7;eiy2C42dE`B}Y9M>Q=BTYC$M)UGRfF zrfvH)wyoWu^X>3ApKn`FiH;n&5ph)S@}AvdsgXCn-yA(-zac-yFLEVu#pKMHGuM9T z9k4vJr%>B+avJCfEU5%(KBSBW%;k~ccrfA-f~pANwe?PEzfrat@XpQ`@sZ#Xd? zJ)SjgTCm+p)v83v)Ti1d$tA-jZ_ZisK}wpmxm&h%iFN*fQ+e{e47!j%&OctqFgP_R ziEG6Uq>pXfzRvj3$w$W@oqB}bXt5(~hey zsC+5xz2%2BFE3npCvnmAVr4e=T+}(Wb6d|TzHE$ZRaox4cpSGoIsRO~iMBzdG3S^m zPS#Tv80z--W&}`hm(%j*-ZfjaTI5>Hn}csoSu9>Qy4085@s@si$EQ0vyK^c|E#IG! z|JBa!Vw*6^84L>k6?Ad)71b-Em#%&Cv80BP$|}MtN-NId@T&&Pmo~o&OFmQZ!|jRM zldTt`-bKGV=~+7I-;&@Zuk|AB94g(nqrRxCD5z-v)g4y{uZok_lSW9Iq-Bz+SA^E4 zRH4*CEg3Cr#jA=n70zDH-pHA)OFILM0s@ySme4a&Q-(9{?d}~&wTo)krEW@f?bcD- zFIBNySv}`?753o?`<h>8M7~3^TH%T;=Uiopw z`s1;W+TAyuQM1e4E#2-e=UrCRgk9$Q&0GXsN_%~Jg9{5w#ruEtJ?Wh#)%PXzld%_f z={-L6V~6nL(8}6?@GIfO7@wG=m@H|?>zMaB53R}LRc+VhfRSg9LR(3#(v*#qV95&O zlqR`^V2dLbG0vH@s_HW8Y1dx%i@w{cn&h9tCJlbGnHw4gpG`DeZMwrc>F*af9_ST# zI3O%AJ^;Pk!92J0VR<*BWZ9NFI3K#W$mpB)jQ!L)S>W7@tzp(UOR&FzVZv7(a#hGgYh;)^FeA!{aM$6~zDMkjn zGo-fZ|Gf3{WW4betM05FTNO?6E?X*C`(s}&e)#m!_GX|1RJ6PLRb>VCN%yNK&g;96 z^<2pb?XJt4wahC-`|{e>(>5EbZ&QEg!aY&HH|r3E_paf>k@A^xse8ief$q62FIvPY zgZ-mL6T^rpq!7l-y?Cg?eU=qu3l^`9elYqV@6o_FXUtX5l0G(G^3Zj#pIUL6?aj{_Yp;7B?W+~4U5R4bRNU*ErPnFn>R~Ik zT3pT3vegZI;_+#^VsQC~REhfT;9_2JdVg-^;yYf;_K1tY=3k2)LH@ekI&F@y9FALyagqPkS4JKFDwM6#QIVeAzLOJdMVk``mzRl8aRc_^cQu z0?MiCa_we7ipaY+r29Y&v{rjiSonz2=}j}#z+re_L_lcGZh>|1-WvFe zUL*DI{+Tt01O$JVuN4pocM=f#`y3N^%l|xuzx+CXwt~;Y1cc$gw!vS&*K7Ydd&BwH zg8%HVFM*E<=$Y#q8Npj~>^%nuH_zMdUQL_7*uVuF?w+;s6cCV*=YQ82neAVIuixu* z+0x7M!g(#MyQ}hzo9^}w%6_hQ`PUK9@za8LT^+n`?D2DTar4yj)7|^~3@vz{UyRzj z=l3aI&boUoFI?QC?|#o=kDBsP<)eGi8~5zlqjT@(EiJRtXa24Z|E0V4wwKpkEfmVv z*H_tBRoVTXBT7Y6QxkRc80y%uBk&nVJpJ9gZulK>^W696BL7_Hw1X%1p3_|~CwI3! z{OjJZclY+v-Mg26qklhtp3}k4=|6XJ^ZdJ6a05~NBPbQ+qo{wc4Oi9Smug*f@^f&p zI_=~N_ZfT-w2HclhR*K_|F=W`x#fRVwfs-jW2zcQ|GVmc9r{13{^jX#Pv6}YzNr`b zKLhr6<^MkTcSRi(zwQ6k7k@hXcPZS{=#4t4e@6|yF{b254Lp$2PNz*T!&`Wo@jrss z;GaW(w($NsfqSR(%W4G#P6`;E*1PPtX0F=!vs=G6xmbQs@n?W>$y~#;eNs^xVsgTx z_o6PJy7b6n_t!F!?Cnl%YXY|G23)?h-8G=-(Cw3o56|o|=}>XIczKVJrjUJ8ddlTD z4e{eE={+xRRv+o6U89ut^v(v8O9{BvAqrc{yC==FMN`%j>`BDNlrt+h);0!mVr&2R z?|(h;|JnnJYuz_Qi23P>wOJ5KA1AE#$Ir5mHpU*y-4kyRZ^mWc%vXDlXPdrxBAVJr z!uV%9Vz>-Kx80)S@|w|`i@yElz553A-v_!^^Bik8l0!&96Q+^Sh-o5}R%4$$R>#T* zo~6XLB2zJ1fEPH$4MChtC6wSg7!qZEliQFi(?+9z4z;s07~!<}0pz6Z2zUv!`CF>b z7%`mZBcA=ZI5TwZAn}dtqmv>JPl{|35=jv{+mNuW#KUxS!%yt%RfnUE?K#w{od5fN zM=90_;+WLXb-|D)r)`>*rVIc3ITnPIHoJcg`Eurs@~!wKtAvD(=-{)mGt9mpxRuc6 zF!kP(*Y{nLR5f1BX)>NR!0bnOb6m#IT)kVhQZ9QwKYJgYB#-N!$Xt4Zd>&qbEAc>r zdkRN3tBLiV?W${jj-I8P1+*i6>JOs(%Mif-n)UA(|38ctN(yXN-54UQcYmGuqc3B7 zPPR=bUJ8oHbJ_eOj>GgPc-b+`5+)_Hy0U_2$TrHP4GUe408{;ZEe1-Sb)_MiI-GdA z-vpH2j>ewNnRWcpGwRMU<+!sA*%oX^wl6&8?NTw0!M*H2ZF4H|8Yi&8g;aLH8|-Zp zh@OTI{m};glsEjN9&VF{6Xa_q)x=U3>t@mfnSY8a<;b19QTB_nUHm!!MO^Mn`opFR zbkxKG_?L6ZPu(r71wSYHC+^jhL6o#N&uEBbx7{l zo%Si|7cgt~^=J3zKF-87nn?gr82BQ=SS#+0y6wP%FgC&?OpPb>*d@!Y@>o1m!Pttc z01hLqSD!d6hVL-venmc(`>~6(B!09YwSwAcNy<`kSj<`p1@2C($JZgRoL>|Mj)v`Y zyxnQ~rNL*WL=o$RFNmwdk0QXYndJWu55oRXhwL`o&R^+-g<@PktT#0FtGb<>a8$Na zU+T2cLMqF=blF_~o^59d*O-+~4s3ElaYit_8>h(<5kT;0(!D~q9_5H06LNH zVl6R$%>>3RG>ei$=Q48BoKSxr2AR))7|}HU5qB|OAp7s)DaCs35A^Sj>A4s98XIss z^bq4uYmfYH?K|4fyjXvCP3i=|CNG6g@9Q>wxhgBKW4mIr6eaiiMU{PL@Wi`WPbP7` zd}YlyjQd7}xBqlTjk{qy#({!2e79teeK3iy$S_UqN}qF2d-?j_89qkQx^|)jJn(MRP~n!nQ}Rv{#vw;h9NbfIL&3*+c2zv*7DrY!|6w5AEFo^h z)`p6?W-X?ezg4|4Qh{Zky~$xw@}`@;Jh~`430^sDh8D>7Yb^Dyt9tZd55x!Bs$|;i znr-Y=rWFaIPLo~8P4AtFA5BOv^K&0l=_c9>r7g_yT$?Q_tptv?~D&Z3iQj@vNIN^PuGTr==KXLaKWO_1D$>`*no zvIO5e^Xd{sQG%=S3C{h`Pc_V1wQwVNC_mU*e?#<;>Gwu3vmAEBGesK|E659+Lt+Mi#dseZ$vap2>K@S6Q4up48p)M8@ z!TnPSrtrPDn8It$$iY=HbV%&oyu3NwIYI&d(XM@9BljrnkAa`gZZ>bZe$`|1i1>;K zYqc@>Lc8$qd$gnQ0f!uA(%{9Z)^tfOXf1NY)~=5e7wk}3u+U#}nSnWJc~)A%F}0@K zZZ>v#Z1=Z=3tQ)xU-DkPLHxc)h8=qOVyl)8+YSuJPyN$EmIANQ$6=(@HG2~Hrw=|6 zx{Cmxy(?;Ds^3q~~-UmFh42`BotA}%@eiou0QTvZ>L#tRGVCRw|eOkay@ECyj{6a~JH-Oh(M z-_b*JG*)me4#p|{Aa=jaSi|>F*`5m=cUWj8veqXikiW%_6XXfd(02w7K}yg(x1Eih z?EfKz70UF;;8wyCcny*Jj83Dw+mEBkB(jv@vJ}>J(c<(K%qzdPeQs0?3IYrLPZd*k zuaTVa)NNSryRuFkay$6-1t2^C>`S9iRNp>%ZR{jf<#xTRI)3H^>R6F}Ag<)2I%g!- zbQ}nupubxl)pFu}S{YWX!*3Q{=&kP>rG*)Bh$X(_SXNC3*bhdV7D2fx-bfVemOF9*BW|!aMa#f;qSA%bsdl>>w{E6f7&%L|HfBRUws!ncJm8f015jGuE99Y z#M5$(OYlUgwQf1Y{xaK%#f9^hacC)HVC!`=p)*VDH{XU$lBx0ZdnhXB*0$9TePnEa z3n-+r!mKK)eD>f-S=1-_)u&^&1+g5QE=TH$y#&aHXY-0Vx0?O!Jq#*JxnnIT-R1;^ zraw++%4%=YSP8?9?fb8=1{D86+o7uQ{+YDGBig?K!!LFdd1B|mTe2#%JkXi3LuHmb z$mUq#35#_+cC5E?e>so5oQn#?^l)>K*Ptar6tiwe^Bc#f;4LLw)h>OH8? z`}yTm@^Sn=(et*eRC7tzLWA#lGUFyWUn7g>mn!V}s4DBBNefa}dMS1~L*|%KdKzy> zKh}@^_=^Hp-EK`R7qPv>@)p;Lw*!bGP%-t)7fkYrTk$|npu+7q;3e?vnryV>1m3x7 z*pbbWQ2dg?mD=7&h?H76MTvDqDnRHOd>pV%B>?&hc9~t>`qhuXDK!5=qW}JYFp=|y zfnl|N4i^e}?LcYS zNDa0Ro-Lk6;3L9GDu#-gE1V#NPh14^zgz^1C(?4B(21_DaXEvv+_ z0C-TZ<9-p(n!xiNrV{Cou;L{rPW@tIuSxymV~qql6K&#I9^hWm&cN(Kr2b@fCv+RD zY7VLQE$&Ikj1^Y65 zzc4TGLEwAV1Txt@4*I02RkP+L5Dyd_(1LP;?^g4YBwLz0(`(Z zZXdglL6Hz2L_K+yN>Fow-9gRm!7%D=QeE7f%?+$IIDe>#z?V|qTfP^WFv1-PcFbW7 z(T=#V(srIqt?HNl7G!QqBTnGw4<^(=fzq6qEPBY+;SfVaw18-g!DqYI%4&_c)U7RQ z<2HAvLZ-4@QKrbZTpY=;r-+vF+2P_>wVP5>^YRP}Sv#Jd4 z9^&&rPkz>sU7_orJ|~>?1lldEjX59_?@e}sTw&3D7(2Uiqxm*e&DMLYQVc6>J+BZ} z+6?aOB&e5i{7OYa95B>Oe1e3B5Ek{ZaFq;rqKex+9WZZ%+cpR@Nqp%MF$&tBa0s@v zBeqV;kHXJGM}vJpKTRT1hqg;JMi&SYfD$PP(qm-fKGA8+%45*e4f;z~^Q`~5aa1wF zZt0}R%3ayBJjG8EF5W)(6Sue{<%Mm|Nl17{1WHa?-(+6uaKCR6Q@hnJbT8sk#r=7E zWc7^K*9?Rit0?cn91vLkH2fWIB{gI09n{4wL_AQuh1Q?1MQ(Rn1;El(gWopE6>@@X zSP}HtBak9=7+x1_iSRV+4$5a(P>8MM*{es@o@Oq~C@{;n+C>PU4|a`hAC^=9l!Qjf`m z_}(x2k@LbKk(sSA*g_FGjz_R$gy;>sf}_^k8|F6zi^wijv^j;g_GLj%Bnng$`mV*w zH>WUYfSn;|Ch3#*ju*N(7Y>aI<}$eMHak39h!xJ>@v)WQH9&E#y%kpxl!Rfzo}<|L z94WzEbD{ z>rvcLl5VayAa@`ca;bqX0-c06}ywzV=LMCs)l*3KZa5xm( zmt}*V3#8d=JK*#8mEsAEYZc0`2+U!__}S%&iWW2gTGnVn^~FSu0wl;Lm*mFFdmICc zK(&-8On;3zcW~QHmN!!(*O*BjC6?qyVd}`{Y)2TGnDATHYxkN-X4=^wxD4k1ctp>G zCk72>hzTX!`_&=7WjWDou#`cGw?MTmfzDsQFdlIZ)pWw%zWCx-JHAb5B3NzUTX%2p z9&wJl=tn_}MsPk_zN#Feu!N^>(cWFAndv4$G92*hqMWcz0eBF}tR_%7!mI5B2z;P& zHjqn+=%X!Ov;hTyQ zGB>9-?A|_(v*vgzkvzFWSc`xw%v3ujzJ-B+yw{Dx%t>Jcfsb-U_&Bl-pM{4cfM`M; znTdxR%NM&*0nM+S0ning-2Ol1FL?U%3Q{o<&h<4IzjzAL#hHAfUPG?_4*W1*RSD_$ z!_nuMcQNlC)cZFT9v3<+mz?X{l0~YvPvsOl5R6biE}6 z>=YX_%$=R5wX%hGpW$D&ojeP#>s0WgHtKVK)Q><|6LJpxJq?mw)$w@995KcxZJ0Jy8jE#BR~ z)dJCP`kOqh--ldiZfg9DW~k1j0(g*};J7G8#w?^z7|ThWZmHnGd|FwS%#3xLOF{zw z78Rhdsh8Z^2RJ#7V#|*kYbc&;F4j+%2^*gC)3EH=(wF5q-`~DQK^-3+V}hYtlYEsr z(LQX|dqI8%m~qSDWNOT83eHbDkNJz8GpA~cnD4?fxwJcWn`oj?C5b?z;CYfwPyp!s z_%?6Ql?nn`kq zlE81RCqIb;_+V~Ghf;VT0BYyphk!wA$@!#|gK~7E?nW-Jl2DmRCIf^+6w@^Iu2q5( z7}8Vd2d?b&YK9h0@H|%W!A6*|?gWKjV~kQFo7&F;3GX9X<%R_{>&?kg_bo4(PzJ_A z%}pG86tY6cdZ+HsS;RcA`H8?shdr-zy>}Za|5mpCBbZIw#+iAAO{Y5b;m3-Dn`_G2 zga>=L#q25~UXzoP3=lC80a|fZy8B7@$;!tTSc4_QAVw*)dj3#4%;kmmI+&c2d9^tA zoa;%yW=Lej16l{p9fF9^VhV^heA$#FBb%ZPisNc*F2x&vke2j~! zhWu7oG%9A+>I1Ko*He7)Uwv2{okZ$XN{;Xl{=&3-_%q-LcuUjOEIbH*)<|mSgNd}X zC7voa!eq~|zD7H*Q9Tr~B?4=9N@IIMw5+U*4LUUlwt+bFGs1s`^XT@DAh~_vYpt+3 z{jfH*Gfe62<7k(eckVHUsq~9OSvc<-;fvSpI^LDlGK%I@W9Hn^@)9GE7Z(FRX8B|k zkc{H1^5mxR4d0FE59(Whu3{o4iqQW`5p=QSANuk5Z)%edXg+d111)aO2mRz2vO4Ir zWE!SU%$jemaFSj8$d(F!HTyRb=J%@=Ij%#2#rk)76l7U7CJO@5>dxwpZAL zXy3*@i1A+PE4`eDae4D(|L!-rDWb-l2CRN%{E~hkhi=N|e9qefFAVrJ9QEogz+l)( z7w{%;W5@J9d%0>>Y-CE_t6P#igGPi_SZX?*gsGTCwi<{Pr+v zeK8@zc5K4BZFVJzs86boC(fqNWk}Mk%+8tCf<<+f1*pA>5(pl*xSL@&b{fnw90%$M z;bdT`1Rmfl9)X+1>xt-v9cozEjyUqdMGBB{0j8_}NW#{9c=-QFG}-qh;6{Lgl7bIJ$P#9&zMF|yEA2!RBm;ROtCImHsRf-N`N3nasa|Bo;xSr{sTFSDGM z@Bqm2U){X~>eZ}cYv;lXI$u!1ge_YUk2kZHIW)-=&`q1qj_7F`M8ksfgF50rYITo~ zspB3eJ@;wyWJ;C@v%xjh+nBU5jyUDJbhdu|B!OA=4YQ82BW(Dnw?t(0$t({&^N#f+ zua#x<3r*4IR>uo*P8HzMbJv8-rH5h*yv)vx&#AWA+5^y zI$;7{rr>_)@oNJaev~}^^w07J=YP9qjuavr%=M5Io{ma9ZUjF4q{~(B)Nv}zeJDXBq zs?AI2RZp_)&65;fcEWoM{6cd}`y7W7dVv(Sojrch0l<}rZa;_zRxpnt%AZDsS8ue( z%O5!D3Gvrv#r<4L=|l={bK*c=$*KgugCotTO5;*X+7Pd)?_euHIF+Lc8J|Oor2>q zneCZq$wF4vTtAx1FeN$FYN9UmU`yQH@qtt)q|&b&n%**nP#FVri@a$0jiEHBO`Hpd zJ+wNZp1Q|yN+?*|`S;>Hx%bDTQKE!o6oHihwN{zq`E0}zTr?wEM;^`d$cKlv-uL_u zBV4GT6LQ&{Mh=3yz)EUy{bv`rpT5GJTI5m1EpQCzv95$_6k(R8$p^PhnC{}w5YLg_ zQvtIw@jylR)mM(6fvOMe(#26LBH7Uc$wpOJ(@eKPZX9r2nVqoG+M(HbtCEpLQ{yoD zs4POZuuY!On$eGEwGbwV=}p}V)EAPnk!Xi^V;8d!yH?9t`g_?^S;{cTt=jnjVgGCo zp_Dcn5{{n#bwTRk1yT{Mn%Om5t=9Eu3wTJ~N=nGwa3@ab6m? zyo_2|UEGn%E)XUz_(>g=^?2By&squW1AD;2`57wY-U^dye?*qf<%2)LwS3LzuwY~S zkIW8zzO}KG0EEZ(`v2t6=y^MS!BYp86XfehPEOeU5tJwBkQ3Ozvy;k|x1W_KW9G*4 z$a{3=S;AmpP_LuX<5>>|X8b9<6CKEC$qX2?m0D=62#Om;#0t@>N_m+m@5F@QH?aq% zLJzc+oef&h+NIMqop8I8J;#vsUv95AKpv-M>%Tyj_s?ZSL*i!wr*62+Hy7>5thEEKRlC;igRX6L z;#CJNmade_lRiO5Ap5##!^ON6;k9+pD1lRln}BBa{n+W={i|e961Z~uD)0%@V?0Y^ zvM@Ob6do^}Sh!MJ#~Fp9?sIN%>^Mk~BB&IqwB!GZBla+bXEHH zz2@+^e*L1?8%#y~9bei4 z-xT?%Z{^&gudZl;?CT+dD`or-lsKXM*|!85;;~E>1Ej)+5@3k@2PVVxnB;&w6vlE& z19Y)iNnjaNrnoyuEDEO2cC2L0jrAH{d9`djN2AKo*bx;B3yi$0^-R`Jiy;> zX4`$+bsc&sd8}p`!_oUrQY?)pEaPQTl9$9e z~0fiJjqnFI=vbm`qy(dV}x=c`n+71dm)N&EZ#RCfnpd)mz*lqwb46 z314AimoR~x)yC8Fl4M*emXfJy?3{A1?G zs2A*D^;M-a>?j1-rXUzhKDNNJ6DVk7#zx)`raw`6AGu^qCm0ObLVfQ`%5WX3MIpU6 z$ZT6S9z!03N#0G^#P2&Zu}t=4D3i^rPf7435qko?_91>&4t>I8;bwCDmn*??VacEy zOd{JEd&8eCGHiQ=98}4~ovKf;)hco139mky0wcS|!lez#bmLCbkliONJ=Wiv28LBl?L@IYIvZ zbd)%jiusbeat&6?5*S~XK=DwX#}}4k+P=ux>#9FlK+#KBfpl6qc0pPHLejs1sm4XH zeB#CUUvQdH+Syi-K?++CA2`6PADB>PqmEg($+PpY*W|nLr7d_wR$I%m1_7eSQum17 zaPJsGGt!@QXteTNnUe7dvCQk$=T(0+{5{Y*DPuAidUf!|&QnZcgsRcPh%qGPlMKk#+u zM@o3_KajPQ=NMg1#Yjo~nQy5O>@bR>zb{u+!%Y7Xs1DTx^iEr>jWrr51$%sX_8~Co zn#aOdLxX6OyY7xQWqG@=DU^@<+Xh498qrWwt^aAAETaLwvtJDi4XqpK0o`?ihDM2x z*TAG;J~*>p*SkLfMs@BznxAjH?w~<*s3RMb->2uU+)8sVQt9%Lk$>km18hq2V4|4*zxo5lx)tW%Es4s$6-oP@?GxZIfNfVfV1?=(N z0E$Zz2qz^1Nz7oUfKD3H2@ZW})f=m%J8Wx?3^)8KSl8#RLy|K_qFuXiQyS^57m{(t z%U4-l3dFd+e)4QveCurt)@SSM&{ePV^CPLug#KAX(rrW55Sq1($8tj2#6T&ymDirk z&1>!be{zfSHnHj_fk@nt#WS-}pu7O?JZ6CY4r24`TYx%nN%e^kk98;*L`nAQUJ(6d5}XG z0+^uhlU;N8+zq4e^W*3xWWwGau=2)w**dbMBKXP}zsgf5#zikL94C^$XC1TwqN8Sg ze|4ocmGg*Yem_?w#m{-@OhQZgELSA1x3UEt)HZ~6%^F2kF%=LPX39sMthrjqOiVmD zsX=(E%I|4Oc|#J4infM>cHuPxvJwjNqK4r~qLS51dO_o377?VkHmUKR)`6S>Q{L3W znriFd)5kfx#F_0ac2@(;I8%BA%Yg)5BnBJy+4~NdK*F!!lc6GD1L#`}NP&IiZoL)c z0A$Ov>nxq;$Mr#kL?uBEZ3%F3dF#g?kB~nTJy0R@=XWr&@ z>m}KF+;EA%DZW5M@VKNVtEe_s^+I3+T);f?EL6BdmSv8>b=DM+GWc`lsYN(Ft`p6A zS{2i;#xBpwwVkKcf+-kmTg_NIdctPTRJZYJD;r-xKH1|=3F%RSAp=t8Cc5ztUu@;Ey-6_8e~>MYS}FC;?4kBUrXWaoDY7u3&vjDGH+ zN6Meyxg`}fia5yJ8SdirkkW!YjPC!nc-Gu~QR`?PjlARL{tjzkxXN?j({C^y~ zxp2^UHNs{Fb_a3wGi-9$%fv^}j6Uq2wB+yt~RXD~a3G49>n7D*OM@xSlMXGF{GPQ@jQ{u$3NCYm5)7 zmz72C(bH1!PPHfI$<`0+%HQM^nQB+??4Jg1(45=n+gcM)tUj^p<9>2c>?RgvZXY7H zqP4}Y>ZylLT-#7hfLk)AAZs$u#IMLuh7QKzZcYbo%BUkkTkC}g{$W+Q(G73AUX?+KoRE--iuD+b-tx{6i zYk`hi zJAl2|$9-h(5qRZ=BSOn3AD1160dW*W1!mDhAb^JZ=}%wHb3IhahoP!S!`~V9h=NRS zo0LGPi~SJ}ujjSe)u^LcV6vat@ApPuQYHzB8cx7y?3VhQ zKYQV+!f6&AO(Phl6cpd*F`b4 zBV8I1uExtJ8$Q;j_*S8Y?b}J>)Tp z9_6P4w&>RI91X7LmRq^=K5=fqt8zbBn97hoI9Gf0+#Z8qf1BS&%n_5^BPf{2prn|je=l={X}qH=KI;6N3L-{Ojz+u zaz6m^3g!qo&m5PzErQ5;bj=4sT0b=M%!G|$f9on`0jrr#ovTVC$!uX<#}Mesz@y)7 zaqY`GqVkYfvI`1oZE&(kZT!&Bb`A|TO6xPUky)_9GhMTS(r=@`NZuK4y1q*r5z-;7 zPQgkEf5L9D$H*hB>dorsEM#r89v_n(56Z6jEG^>iMgRJY-f%Iz3!b7A+4_ZcY$q~k z&XhrJ$*W>M`)jCzITCV=z&wc0;6A^Rsa`)6JQ6_+Y9jJ9J?m8@8>p zMsud@xjd1TAZ34095W*}v8kks1=1LJdc&!rWiIL(z%O-J$;Ay>9K*bV|ZkPnX^7~xA95CE#96Un8J4@l$Hmn6*h zgwOu=+8CIRSY7-}z+I(wrTk6dh&hZWCt#l4o53=K^T1LLLCTO1^hI*dxt{Pve!2|9 zpcS*|6twFVc!9SKa)hdyxb6Pez~ax+KkhZ6d0E}$xcn^yuNj`=s!5AoUmu2=A^rxY z?sXS+9iZ3O**AhNNv@cM$MliMXgUXIhpT^z7fM6{gFhD4*?!jEm)9s3U~Br^dhIbg=8pi%D#PjUJ539ijsCAt}g=)JIi%h3E5-CRVoR7kHR8DfqvS{$T$f^>Uw{+HUs zBGaFNi1FJjGash_Fj9|j==j%nt%71flf$*?MS~Tn_b%Z;_jI_QUxVNDp7@3@fsr`Z zH{w~IN;5tq$~MBdxP`;Qc%LJteHK|kGkQHL9AifDk(mX(g*Qzq?}=tMOPy*eUR9*a z;m4LtgGnPHO2q_xEYq|FDMQM-T)M0aF5th}^*=VMjBCU78Y0x(3K6EvQp!fR5kl?` zoM&#XDSO852o^dXEh-04LG!EE`Hz*7rVSQAI`pFKKm#3!!T*`q+ueY z{w9g};2&QlHP0izGA>kqZqBKx;zteaZ&LD>m4?`!%j-tvyV#WsUkZ&uaqo`gWM^__ zUX0^&bZ?Kk_m?yB@&_0QXZCZhu(m4bWXx&aoC>eitFbLGQ1{lpMg54pM!)ELp6N+^ zWHSY`SL5M^!RJVnlpC#*@tW)4LzDEp_^xU3P zN#wCS<<;0eO1a+DGv9b|NN{Yv{sqa=`XaLySWN|=Le-jJp5iTD-|96FzX3SappW~0 znKg-f4<}^Eo!RNw^Q(YYRJ~F-iSlj1zzHUHVn@mS2{7z{F&eKP=hN?BT4wYKj;kjy zjM;GJ)a*>}kU~B>rW(#kyZVeok@=TpjRVRu29)YDe-lNszgjT2U_!rBknmd^={|gZ5r+#}b&+$;@d(lsp{N za}DH7z3d`Z2gsB%6^c#qK@In4Rq->$xS5&0eo7kpHmP(FP1Qd5Im4Y~2vVEmLHeEK zd+x|;(#f>I+oUXjltt@h7=pcAnmt}czmFVr;5P!1K*EFwoU}8>0XVc56Z}92fA;=% zTbn(dVKh-nbNn}-n0EsLi}~>5pg%MO=WG6JD}2C#py9tXfxJrcSUrZ`Zp=Wm9vzOt z>!rD8O98htpq=27OcT&93TG(^=rTA6Di-T**B{7fkk$rNXy)wFio~8G;!dP5*q{P< zeT24gcWIAZuWALdZDT_#R&pwTln`f|BLrs@Z5UY zJz7tHDGZ0)4uATWtSo0*`xY?MnMP;W@}^Rl+4@Xpycvgi=|d;G^BuN73+MN4?p|Qq z^tX#h>T_m!PYrIN)3h94YkhR)u1vEKA$v30eO;Wemc80Yrx8v&<<3&!vQ#N zfS-q+NT^hR0n96oJ`4)8*!pZrGC4f~AU6|Q4$iM~e8(G%Cc#>s&1fugcPfV`?DHDS zjse= z%V{*bW0zcXL)eycC>_f@h|!_n(U)1$O&GtAZ~A&9loZuWyfAABtneAe-W<4ihGGW&l~_8ve@t?k;dlx?tJKvYCZKvZzk zL=>bYA}Rs`3MwKc3L+vRD!n8TLBLH_DN;ib5L6;aF9`?|r1vhNmp}+Dy?qP!e!uVk zXU=)wb7o~6lF2AO>v`^Sm3uud?a(=Gw+MTH)?_90bxv@<2Yh`ke1+AYP}ZrpVvRPp zV5PxMcM{gS!#bS;;%FDNfGwdPK&85hp`g<`VZ2h<0`4nh}yd&v% z^;l2^-Ilg?oUG4gZ$81Hjx|3ImKMM0TJw({3@A+B^iA*SVlf*r0N+gYn>SPC;CSGvk-W&(zyOAtyuu8xf5OQ%p3 z)O2L={18q|Dz^^dpSDMDbYwY88hbJa!Ae|ptDXBsPD9LQm^$w7($I#Q@X;Fh^fH%s z#4GDbdlfj)7*7|&;|)L`(x?xuU=&w4J{K{X{|gNKPotcIV<+#j)LG>!k3r9&<5yYb zy1*}!kC=VfNOh;aar*4Z9koeOD~Z)ZUGDThsO!|9kzG7Df8Lt>Y0n;w~W*FxU!l~U(7K++7K~U(-f^T`4sT+dD%+>E zOQJrJpp(mtyNLl`)^7Xhfe_VQyfpa3Rq}Y~6M8;k6gV$K8D$o_@q@+A(&={R&=75v z0XN(J*b$@4(vEn2ps$j}pJTY^*LGk7CPVr$(AKdv-_DJbkyux>><-t+{Vc#LByAbK znIF1hy*kWJcm*VWX6%$jjgHJHcGK_HW*7Gx1-Ax9Ihrk_Ki2%9O_R$jYB1(OQIhei zIF=5(f`lUe;}H0+Qnq7eX)sL@ZwXYw}Jmb*;mweoC4Z9nHuY|DeVHD6vQ zFI1!TNHh;UFzu@;4ds|Nb0d*bN8rtW%R?2RNWDe-Rw6@4@#Qw=Nax8usM8o8?yT6s zi$4ssx%76F8dB zt2zoHdY=90d@9OxQt_FHiO`EGL1_m_Sdh-H(^OXG39wGkU1H|=n9IF-rK~;SSvPo6 zYNW-Hwx<|XZ#y9MecFR8szWdr#%FC(!0h37;k|)OIr@UsgJ0^p)_8McNkQIm>Ok(k zl?#2`CUo(i^5~LLYoF0@ahnLFAD}`2+WlG?JT#zdq=IKNvaKZI5+V9L=PYHxea&fA zT*xC1;46l#S5ff9-||1e;xd+e86b@zCwOW=2;aW`#zMAjRDe=W+n0>fgf9YbX5^5( z-hcJ?YDC5!!tt;-O7C?$38=oE;ETWfT$Cx?ta%K#X4GY0#T;yH1&5Ngc*>2(&%-=- z;X&vj0H8#_SNRN*dNTlZZy`K@*z1A4vjimW%bCoZ0nydlIguC)wja%hv(~ghqe0xo z*-@Z;@VO$LG#tM09??7})&29ftQVOQXtB#5=$pt@M^DgdMV)$gors9)^N}gNOZ3{> zRUXv3d@*tHWu;MUj67~*@ta41gYG%^a|##jwY{=D)iCy^%q&-k^l<7%sTr%)jCD>m zXbX66h1My>c|qtv&m2(fzr-$n*3Rt>dcWM{2jh+4*_BQAJy4x+=v;cm@$xKflwa3K zIJ`~_MGR!mSph|V&mYdbd8QcVH5x16P#(@p9fAy3a(@`>QFKkehHhKhS5eHiE%YEE z@y1`K01V*i$~A~OyLw0F<#x5`vK~B5_JIPq*y$B(FMv^%^QJ*@$iKzO#YMXYFOcDb z!`%#C2Wxw#0T}SnxXs>vb`pT9+Be8r_}IVE)3c5~+)5T1vdwX~>ng+N*&&DTrJX`Q zok+ybTgaT?#RH0kH2{-tNIooSl@X}gb-8&Xcd6c2nU@o*kz1~)?_}wSMmd7BpAaUn zn&}lY=Y&*Yl}63lj^Ub%0A;Fuh~z{*z>RSRQsK4UBF<}wli{I~bj=k*AQWe&t81%L zY&uQtih<#A&l(_wI(Jpac(nq+-TL;)i~V}VQD;26sfxkAe%4O=rp0Bqmy$R;Kq6w zpfqwd$}nY>k`_FqzIu2-5Duf(XlgOv^+%}kpBNctX{0&+Vik<1WLUsS=DE5oVeg>> zBakFd%$L^(tF!91)dMuydE~>!J#sLZIm`@q&J0! zAt6Pb$9eJjo#(op7);9rQ^Q%;@csr){7}Zm_?U-eNfgL3Lt6)Bf;Yi!;yR$UcH8x@ zE~$XgwSI4CkADA2@@W)38MCqIi!JZ5&^^Wr;sUhZUex`3J!WaX3c1H4wQC>uWUPJj ze630Y51_TgPz%Lq6x9R8g(|fIZl*h^<&=B+=bI0&{(?VTn7{&RXUGBG81J^^=hxdB zm9Y&A#H$h@32$E^?+x@`v=>*Z@v0e6!^hj8cOb8%`LFHyHti>Me^0o1b|oi(2i-8* z$OVMpWcZo@ZFlRo$0v&hu0#3BOu&?<7SCs_t08z(!(DYK7qV^z?_T7d6l#Gd_T^#) z*q*hi;J%fLQW;pLX|3YcGV$l4R;##aAv4+!P@>u`ZYUUP!C>iJ8O9#3J_8GoTZmw* zv#cI8OucY@JgmzIcVyry90(-P(cG(xt9i5njB)s0xoq)B9pn z9(KOboK-GX)9eF+TZiGVs*d$S7Z1XMZzq(Q)-{3M*ccz+HbF+5Hmzv@#)Cpo9S?AQ zH+gIi3VINxv~5NBMZHb4cXDHOg+()r0G*NAa?{xhdvo{MzeR1Aqtig27Jj^s;S$`S z@qk6{@ZIy=q_yuW=0nH6#;U6N z$r44_YgxEXywqO zC$}7s5WteC>6mi*l{WQJpU2!iV+LKy`KqKoaMqp+G;J2~1r4D7_#oa7p{6T2d$M`) zoRjig7Q@1ZUWIFd*8WB&M7Wd8Hs0X-9Z&$!d~gsIkz&h6yKMu@eLxX(om@&B%dXZyF zhR;h(UqbP+Eo#WN6Xo2gnJB~)F0p>+`vx_i=6D2`L~e`ECB(i?BvP2V4+u2bQA^PC z(tGdrf8WO*&v64Ls7!G_v14w^IM1`^p2duJZ7S%Hz9CZ)bUAM;m%aFv#m6 z{+yQgGmCW7?-EbTse;k2zchI4ahfQVxP#mZM7QU|nLL z+P466CWY|g5zP3iNW=mrdd1@~E3N4STkCPXBj7pQcF%rRucezdjSmVGPu*57ONU03 z00eR|{*)UJ&M5>Miwd@r{hkb<;~qneXtcswPPD?JK=U^7asIDJNeF%s3`jb2Pj>Yz9COdi4Q1@vp2s_ScWXxA8-Z2X?8sFF4@0<=* zBLO?Bef>wXru%cXiM7lwmiA@(VvcZck@t!&ULT@2YMoE5TDZ{6uwdc6ep;Zt#x})4 z05jcgvU8(W`-U26U+AdEQMux%?1 zygGgCc0~As*{NzW4LlrIN;^YZ=EeJ#k&)AP9A!4y!RCI7G)6dK*F)4j?O_ms!&yd?KK9?-LL~4lx^m1ljKan9g2;{>Jk&K#)zKOUQr&JD(66bGX9E zQ)V>(gN5$)=T?v0ItiMr1u0k0jM1Fvs?j2j)n+)v5*HAk6I%KTQKZjp(bl}5SG#Et z?gP~GmEn(vpv*VA4Kl!Qi&t zF0z@h1E^alMF0V5ACfuBdtUctVvs2o3rHD3g4>eLX>tJ8gIC3)K~}X$IN&eo0qEQw zAo~5jSmd+&M8I-@uop0W6)$p>9EzEtI9@7t3i@c513iwhvm}ld-#x1a-1;97S#}6> zOMTQ82&jZ5^T&2ZfFp%DBeB7HI8$%P4gTAKB$~+E-<6zEOlm3P~18D||IhE!cRvkcJw3I$1;UJZdOj5aM&A60~^mX4wVQ`g4c$_zFlJ_8F@U9_gMcmq&j z{We-crjHkA{?0LiKt?{z+971)xtrL>{hT9~QtQ_C zASBTF0XNuTR$9UHoHInG`7fE}R=~VKEihd(=}(7wQ?9Z~)uBSaCfan1bxx_k9(vk2 z-h=3Tu&E#wBJ%5)n?zs>Y)1?iav%#UZQ=H$f{^{X0JeNCrtXO|O^*f6T}0uZBfP4RepTGLIp%N< zGdyvF(?%(pb9_Rmu*0Z{AA^n(x(LNZ1uwIss>c)kB}#9P#%Y-ty}dNZ;fk~m{+ z46ZO5mBs;AVf5Z2oKE7Z?a=Td!kr>itsU=f6|}|@^ww1A!x@9%D40^pwJ6lD?N-bsz4LIz zB7y10&Vw}_JwQ}t(qFV-M5NKV)Pz1R7ujWm)A=Zm*OT)rwvZHuobUMIhz^SP#Eu(! zz>8qjcCcbQ-k>dkIS8X>ff0+FFm@x1O#!3mjUItK7J=7E4y4r(7@fe_R|c6xpe0t+ z&V9jHsZ#d|1hy5<2NGBl1HBcuZ{KmLQp^Rv+`Ka{N+=r-76?m78WT`UFw*>J&KauS z*D(SHD(fQfklC_F^htozCsQ(dxSqq-V<^a+aQN)>b>#X-2X68F<5hXkZJ95U)a2zd zuNPnb(G$D9-kr`X_ax>FUs>4}u+&ayY>TRr&e;IM3q*_Lh^*}NcX^$r-NZWHfw%5( z5^#H$zn*C+d>Z*L$=`O>9oQ6X6etjQaeHK2Y#`TRBa$4%8Y6I-WVNj?9*AX00()Ky zw;qV21;SA?ZE!Ag0mfsHgXT5R{$1kOwO2H?UuZi!63Vs+?ofoo*#(L5FCqImT&Ki5 z);kLalZQwGA4J@JajGVc!TI?-&^^35AYp{iW~kfbCu>R!WFdJMbP4FIti^kFA{5sy ze_fZ=FK!TRfE$W2^DpZH@H*vzd<&KVj3fGahegdx^D)8OA_yU*;<2y97vK_UzA6b6(HM7r=NTDcLWqK@QViei03GumN{IXHI?gCJU=r)LUy&i8n> zZ4{KsP(@;-lL`TTZ6h7zjhk6xfuTAnL2ab7+Jb&_=;NHl4`#oBf?zl}W&{n?O83?^S4-%&Oj-nVq(l1mk)vBb7aU<{)M4aF2*l)3`F+= zVs%IuExvU9y2xD6Ipnn_L(3%2nz=S{wl_2HUMhO8WuFn>zQMEV(O@)T?qCJgvYetyoRlJsqsw;v?aZ>CeWL;im+Qh)Ep3u zXy7L<1q1J){+gR9AS**GiVZf`4|vfd9#zExLkj;FugOd2U&J=j&+jhbj&20^k2wUA zh?F5XE?}r;!n=%ZYG)0h*Mo{C7@fWjbu-fBN+ySlq?w`?$n10!`=F7qii1JZ4F)Z}Mdpx?)B#eRlfj{E)JC^YD-|C;jOo26meG3s}5?4K;5Ll|Xkx zk$jQM706`|)pLL!h!WX0=PLnhL}b!;E$LqNdcW`vq!kOJCm~uAg)IBp;S~=Xo8> zv4YXwo~Fv@j00-i(NYUzU1q#ZVYrUd44T98OGlD*+Owj7cfhbxM za*538RpazxREF6dbIea|l<2t)TTyJ+3q{6`?bU+*@X%+U5R{pvLtkmqU1wg3XA^wp zT=S!$vsli@PW=)Qj$I1?>0j?sXY~yp4RD9sx8Ck#Xy2O6L*PU0N8ej#l>`jl$+V;e5MZ^n}gtLsNmU6AuG+{KcosA)8k z*%yxcvyA9r3jaN|_dkv&|9@Y2${*YaPYocj_Cd^4P#@=sp1^G&@Ls{`ui*B> zC}b7#Srr@E=%HV^z!EmWE=^A7$eWBQPUX*T=*H#-JT#Kwj;!kI5ii`b4N^PWbK+!v zR4xGr0BQFh(g4izs{Wd`k4X!|$|%EUt}=;A1t#K9d%`*S7xofedxF-6XJuYI&NrL< zxaWkoKVq0)m%$f}G@YHCX*}8jI<5C9#GF;Fv+;dcW?Y$c9dal;BX@~8CN{c~ zujdR$#IqPc>%#n~dy5!zFxd%^Rq`5)pURAPJMOUUlkrxl!U+6To4CfGHVL2vvEbbj z5lD(nZ@#i#93pimIH)s7&)Zp0BGnvwfV9X$`(dw>rod635(LCPA^hYcYYa_zSlfxdHUm zP$Uq?f2y~lN0`+t-DfIL~L#2<8q64C}wp3W%%2Tcm#I518wZVM>W7|bqlecpn+eNf{RJZf4} ztc9$j$e9_9#{Z$`XoJeM3oa$bj`nZ{2u>=+% z2kYB+{20u8;VQuJvDb`8*SHNPpv}8G!yNfQhl%|2hN8!q5=r$fN}0sop{)aC;Reqq*U=GBxTVkc4Da>#j z)eS|nM$TDt=d+h_4n-cyz-e0R2#3frIN^lk;nj*$rJEd~p#B~{2Inu%8eM-k0*RW| zI{r;qdURyW2y%wx2`C8B)zXdquMjZy21^_u3}>rk@fZZDXP;{u!wt?&SPn+&w16ym zC(8JZL1MEGvJXRQ(e-QB_#(=8MbQ>TZ<{XUDF-aQs~wUd5p!miA?T712`ahSmvHy)pv$FIiR3c(3O$rko1Ylp6aL7`vW;spWa1S>pp6)_Fz1Y0&-> zGJ*UbF3JD>+Bjb@(0-~|L)eob40Pj7tX&-$L1u06*h#$8C}$$f<^~(r_5&{wv@oS5 zgZzAt)$6@DQv=4qrfk7<*O19M2Yo6dT1c;(eS4S8F8ww0v8^nyr52U3th*^G&dld% zYm(edb&*3am1payt~smv8~w$f_TfvrnhbK!g`fJTk(;OImO@q*QV#&&C?^QUPl(S~m21@3;XuPQ zBdD$fCSZc0x~5hqj)l~^FTV7J-W^>r%4qX&1lX0z$#+Rq(DA+cXI!1khm9?n-Soq+ zbuQL`!+h=^r}ThdSb@MGyqKSf^VHDJ0j9 zHguoR-SaQrO5HNEOB@quSa_8|wk_hVx^Uakj5ifH4hc1j=6pl>P-@y>KZoE+I(G+R ztws2tanHXY{>Uf98~)C44i^OJve?*shwq@V_UMLzjVf5fIio|XByi`NbslmOgq$RT*}<@>yqYR%=9}}FDO(7^7O};2Q+YKW3n@JL0p9l1kOIpX`t!}YO$s{$i zaP@9bmbE@{jV0-?C|yCQJNLv{I@T!!4aIjz4HVx1BjSl6;?i*h!e?LbnwS&V_^OY2 zRJG;TK@84QF~44F}LMz9u8+yy|E$*sGzke}EV8+8hZ1T0M)sCX;&Al+Z0bH^83 zRkI;dDI<#LZ8$(80D`< zK$uawU~5Ze~s z0H8gWuqXVgg6II|XVim*T#~!WlZtdIX###M@Jx)MSs-{c6c0tgaDKuVY9%c&&XNUu zePKRZ#P6oaB*vIC84hC?UajyC`#Xo!4=6FFZglDY+s0|~)%nR^ZSZ(zJ&SjT*sVD=IKrI~X=QPNt??4ZERMCW3uGK#@!);IQ9iZdc2MEUOfL2Lx-dw*RE-hQ zNH(M*TCZiy;hxA(G{G7d1@w~6IH)zsuCj!~{a^={n`=;)p$8^N*+WxW` znp$6;_f~z4T0SS2WmZ}NH0-Az5m`j9K;9VMpi zeeF*c(~=-?73Z0{ZI7XH#2nVF!oPM=sB;(8LW}AE>lkaw8`+vAAQ`v$)k3llTH9`_ z95?k=ii}#DfpKS0TsXZEMTgI!r&%2Ywi!#wa5C2@6Za>j7dCz3@b3px0pX zaQjd$%jjj0#&>wYv@RIS%bcA~6~Xe>oN{slIKOsdMyCv@wr$5Dgz)Zta$RozEo8Gr z!^A*V0gtzm4tnwHW-`@c_pYVr&;$IT4*bf3p$XYu4Vj-5RsRyUXG3kA?N@3(AGOb*q3r~58>%AeA%y+B%E zSd5MX8pfRg>(*;d-LaJ?i$BkuT<#0Bfk1 z;J)=|uhy*U_K4>n#Wzk`FXhyxtoNzy-4L;wfAB@G^~>!ClpK$F3$x_Yb8F9L@DO&p zM%RZ|d&I*ce+X>_30_SkGP;Hp&hTHZ7djlQBHw-Rik0nZ_bvImf>AF-PyPOw@4tBC zS0($k@#A&Z`WJk^rYn`Ki>_{pD?jwbom-kU2`z)XrOf&2ckRJw(BCGm@3zra#N?OO7d(dLn+y5UE;=@@gxoN|r^tv73NZ$Bt4pveO{cz2qcfr)E%3UwK1{aPUXv)`SN#5ZdjncL(zY)$C=}k>!LyKq66KDFB{BNK)lh~5! zXd6n~xxqPT`B|y^hC^OiOEGWvZ|&bf9DwcJA?7#S2FkCKIe-+2+L8BJPG5r|GDu$x zzM}RuS#)E{RQTS7*bO5i8TQTavp(ssZ2Y^$avZ#nOY1kG?0As$%c9S*4%}RB%1jIl z*czEwl$h-G6igOf&s({Cd5UhR_B10z4% z?+##_U?cpL7N^kXYrn@}9zQku5bJDaAuNazO&tD&J*TiGXa3hY75WvtavgKe%`f}> zP3k&t4O(NG~=^+$w-`=cT_4(Up+qnUrY^(BDYOkh8_Cy3rJ zun)u!7MuXtfuCJ?U);Fs-XWul8nxnYavW{mG4)p3_)wwat){Yp7o`p|uXQlJeLj!= zz+JZ}ISJOV92}DP5%NYD_IPD3r|hXEbe^dZT6m(>FsSd6*%|$3XXQHN`7htSX8mIE z?wm|bP_n5{%JYGDF(#-TKVm^ScaFCN+-={_m@$e9YWd_baaug|WKglvEo7lZ>|X`6 z9;n?TsB_0@3^F*r>LaS{C&s^{n^ZpbWLN;K7dQAt3>6g*EY>%X_mzF-`pf5`(f>rA z;1fsJ`ui-j)NN@dxj0#0Owq3||0XB_>zTXN%hv;x|Q~(x2ZO6%xhPbzez*ZJ-S6dArFSf1PF17Hg+;p9T>DVI74c< z^w7SnR%EM6jpJN6)-beL$E2mhptW(2;J|^F0J=`4qju%vrjKwhM^N#tv>jHpTZOjs zMbDIISjl!(N(Upr7K(y13D+bL)oPC&kA_1=F_{ ziJnu2R|}G00R!Sjmng|Utv5UlK}uIioXQkf|w%tUYk)4T0p&oyj!scOw%?T z7Y-@>$T7M<6!NFHGj3RPqa`WX@4-N<^C7Pz!;8k00j*+ZPK^ZstAX%pEl3oj4ZeTH zC{lFswGiE>AcKAbapLcQ{ZNx%ZNWA#B|3Lg;@^cR;8dPp7JAy)TUqR6tL0-wN~?4( z?9mE%ed-`Ps%(f$w65(U#~rSI3hLb{unOYG33To<6rag7oD6*!A2>MsLgDDqcjr#b z%E{Dhd<@%~z5m4^YNPmsICTG)w&b&vo3VC_2jGJWXmshCm>w zW=YJ;DK_Up{6e91=l7p~aWu2r71FlSl@TtRI{p49D6?Ais>^lZjX$A@RWXm7mZwh7 zMDEiKyvT90P`w!7xFy_?5P@EB&zzU_h`9LUjt^boDQxXvgQ4F3 z$7^sPus;v2ZdSM#9VbxGr6yfGofKykbbxL1sfa1UyX+#AWEXN<5Vh{oU7$7nx`$Dm z)dx*vT>Z=1eJeKtWf)}hZOfH-=Tb__nYVmz&+k0EFKY3g&{MYH+w)CH*tI|IGBRhH zkI&sC=fbMitffh>?|sDFesIr1_yykFydyVS&l$nUR9gO~y6hGUdntUqR_2^WHgl^B zcw)i>>V|vxRYh~(kz{)0-|w)|sCxRd8A~)Y8(OMeYr69@90%3cbun+OlYUyy*T5WppJ##Y z6t)t{Ynl=(**K%z-7|3$y=N+*%KavbAkfe2M{RfZaW^W<3X6ut|7&p-R z;OpxvWiiew;!vQZqO2Vk*AQr^04&zmJm`zHYw#QIj$H9SG}u&teM~=l=|<_vpM}Q# z%C`HIc6c4$lDbpot+=nX8oDzsKIHHhr{tmGn^s_c$!xpXonF-a0}5fmU{47{l`D4& z3zz|li*3x}FO-*RRV@YQf{xFg|0?gyDSoWU6~7ZHaK1S+8vW`yMq0Y=Ni9Q(XpDGh!NH!OulXhQb0W#RpyB8KJSwE# z$m@nC`m4x9uzMOW$-%WJ(TcaQCE+Jof5xPJp2|8U-|mc{v_vW9Gp|dX6`8Y4qn{d1 z2suo$iA06iQY|FU$s^YX+T3dJM84@ln5D26MSb=JB!JyEPPCzv;Xx$ zE7vn)J$T+1?eU)fPg9FanHmoGiZ0~4K0c6{u>eT;kx=u&wxoOs+OKq(TEej(VLQJ4 zvrJxp^1CxY_W7Fgj{hm{caua9>v%n!))>!Fj7(BCHg!C66m&JlG*`3kExA4-n8$|> zkxXn-d+j{vWa!1(@3wy9z38(GYX&WRMjz6C%G)b@KA{R}+*ce>X9}HN*TH&3diOZ+7E?t>WzMdl$dKNS_sdL!v z!}zz$YjdffsRj|X)Q#PWL0_jpldY0%X`pnXm^?&3v;_NTa0nl6AhR>uPkT9eyrD7tv%dw;|m-4E6f)bk@p$iwcj z%CCKgC)i_>y?gd7R49zA|7(SG1cM*cC1MOyJT3oYO+P5Ka_Z5IBd6}3uYfkrr@*UD zPw151ZJ2LY*heE+hI&qc6TG;QBtZJ0$Zm>LmO8YXu1w3w!_-?8cV zdU4St!MdQYZWhuXK6l-rd{f7Yn5ypbnY-oi*cmZ6ngj*@6sl_Vwn2L%5Oa5%Puz=+ zIh=kSnu}(S=>g9OJ<1Pec%7dca40($!A>4gGT-rSNMJhisQ#|KkLlTJo1zLY9BtZ6 zV@kq^kBTaa71XkspKn4W1E$aBC@m`~Xk1Xv4oWjvxPZYXOSIzK&29@(zKWV=NF<*+ zNN!%~#LVj6rN?xx1VoD!PrIfOl5?p_No^$OHFzuT*@<%+P?Jx42y@P+y zrdqR_;lL<#vci#=WmoE+{2JW~s!jE2VEY5DL$1N@)r6g3nH1`T2d~sVT`kycu*kTA z!_C`jTEX9$U3!$dK7-F+K%)Qwj(y}mQRT7V)%V1auAzJl{(tR;|FZ9WY%16sBb{2l zyr&~;;pb?XTc788@*{am-^Tv<2vK8WyM4W1!wA85!)N$-hI$FTVGed8D|Goav#+8` zJ=N{uuywl0?Z}GkpsiyWe?4A2>cNo`Uc8Evt2m9@sT#q{# z87rgX?Y;WkOfTg2^EM;xQiYwcjW^Z{ih;NEe>{Y&+I+B9jW0Y2bhzR6fh%-N-s);l zV}FBuc&-&b)>t80@?F?taic%cmp=#o;Av(@y;HNVQROhJ{Fg>u=JNu@Rbz5|Yo)4( zGPgz>!}G27LEePzU2KdFNX_v16694`Y<;Kv5u%zJ8`-;c*cVp&`2nfr$66gIUmC9c zXSBBCMWWV6k1bKsrFV5oA9EchlhvaLX4X4J7h}YGH!D=HM9L7;xGB%5F+iA4)S0go z1M2rwWZUg3NB`=DBHg~3Sbg?U%bwN1)%2Kvf%R1V&M;xgUKJe6EIcj`xbJDyQ8$bo zs{sVLs&wmday+PEc`^jt$+gg@+(^;#aL^&wZuW{ANxkXv?$lnWy&uydQ@Tg8IZEQ4 z&!}tuEnU0Phu2fiy50Jusr0+#0diH*)%O8?sT1)NUd$y4D%#%rCUc|7G?)^61#+)^{fi5F{J8?Pf$YATIvgsC^ z!B}^*JCRvNk3K=yb0L8jy@%VhBu6)2?M#)^@k+MnSe9LJPJ)$ri!8P3gYL(g`E<-G zL6os8rUf*)L)R@nzn%&n9J>*2qu*3AZv;F>VLFG|8lq-N4K-tgf_xkg;vF;}3?ydg zza(K-P2TG`C9N;DAFgSRwoH@iy$-b$ffk-Q*{OK50~$Eey7f*W!`K=+GVF09;g~xw zVE$Q7uZ=>K`8%JevFPV+3EZu_gvq0EUzBy)4iD;lLK@xF3Ddh2t~CGoiUlvgA%NLjMN;`y@Jvk0@TIGV zSZx^jY(B>HUx66y(3^t#E}7;S3P8CRNqnY;IVUcqucCwmFApu3nE(z{K; z+ie0IggX)P;&@>s6_)%P6cv?c3NPy>)}*Y2cn+lW5uL;E33x`kF#Q!=ev%+KDV35Gza* z*JsQ%Rv)027(TT`vJ*@-o*m>D{A9*hzv&P(VtND4KE!Sb{&;A)BqJyy0sJj&i>}_v z{7cyT(00DUJRD;rTrR)$@CvA>p}%%(E+b&W%AGnF9HF%90{=n%W5;D;R+_T$M*cr< zQV))e3fxtJFC`yuwSJrR@sim_A$FxOC#^lAv&n&T>XQ+p&DzrjB8_xLOjKM}>&Lkf z`wK!UnGb;jSWJ?6PF)r2(+*roVLofLIOQ*EN?9$F>v|UdenzV7kLARLJ7Vpfkr4l* z2y$`$og*owx(og4OB|G#^)6~K|4n~Cl{+mfIe+C{c$Ym@7#h$q8odNVG3=`8qcL*F zNgLJ0t~4$42=WWXoyLXF6K*^?FuY^$OQE)Ln}Jtj`Q4B0D&#t2(p*|cB07p0Ah(tV+?bt{-UP{t#N62E8F%zRjH zx$7nk$K$Z?e0Oe3%*C;4aAS@WfQBK`wfBQDgMkBtQXLh>Kggm>Q5u~s$5J=DV6d_H z+3ZMFlbmV&#y!*awJ3m*Q1h-xh5vQxrpzJ;MmMPDGM7znR@~l}k8}!}3BmQWwlmk5 zOz=B%os(woPsssW(^>eeCw0UnUjZF-`j_8Kv@pXX+A4>aIhJyS^!n_*Lj8~0&Ho;a z>-HAe-dgqx_Y=&Ia2Nr<@gRLShH=+t(Y7P#%RY}1C^n78*QGnMTB{B!AgYqU`d4Dj-5yN0bRB^aJr6CJ z27Uds&uvbH(i%dzwad8TRx8LzRoX7#Y@70Q>;v|?(xvdrc)ma0KBzzHAX^yIna$+4 zd)6KoRXU_4w*FVNbLS_SwFDf~#bPf5=xq*7dpWFJk-#uh609g|3Kaq0&C&F4x1_2h zP*IY38w2XUUY+u8?wsj`tyUO~s+tei><)Y-dsF}3WswrFTH>C?Klf&urDb0IVB+c? zmxpE6=RE7X;b{zWk*P3b4%;^8A`uin*N1&tEny!a<3-oj&K*7&h@Cn)IN;MqJS*92 zUG)APGRVr)qh-pOqTvS0H4PJW*5mtnho2dkxaF|d%WZo@XkCw; z`V)W2otE~WeKWG;1YzCgeyuALdIEX5x5g-qf+#Xc7_kHk}In8^8XK#G|50#M@L@%UaP28ov@*W%)BGnHb48gmn^CJZi?X@D8)0MYF82@>pBZE08(YN5MIR763d$wpN>lIIPQ9}F0XC7Ktd(gx}+Sy zOwH6k70;#weGR&9xj397!V6caP;~B%6*(_%{w+zx0-egZQtdl6{dr_m&5C*?9?I%` z?b=)ECk#PcsuQl_+iiWtDD~0fqepLg9fS-H$bZ12;ptHC5v!$Dqsmtv%g*$PkUl6m zJ$`2|{ExCb#SS;49^K4NAHTliNPJ=I7_+eA`c0u*C%>e9`%{1I*NAJ6AzNnRY;H1Y z63b}!%?l3MITuo7SFC66{R36OJc8Y&OG-2M>*)94D{*9OZ1uIFx{HQ8{)m>;Qsv-L zY$Hv=GraWD%RQrRVu1Hs$ySVYVYdZc*8C9jMzm33pOw@K%)m|as zP`on%TZ`=0T5*Yr%op8dPkvP=9ebL5s_YLD_tX4iJNS^~muHTT>~}3ZX8Mx{&{O>- zhVz$s219Z`D&qP>3shb>{nOn3cfEh6q-=M4mxuQ_hdQRVjf##w>8t7i+J4Net*G#( z3A9IWGUAj-$I-LJ;>A07gTTIQ4Z(RGuPQA4b_?-AOFB#@{f?0 znfR;aR%@>3$y))`I~qyI>4wniK?pbE0p<3*ff1y=^^0d%A>u;zmxK}<(HE`FUP8op z(6N1@=lD(Z)!h~wW-Np^G`2DwlRZCSiJ3<6R(02EK3hmZXK!@e9;{alRPIvBjwdDr z&NW<)+W3~2LA0_89PFLxN-@~R93cMW=plFE7bAVrPu?^0Wlif`r|IL>J0i{gNWOO8 z5p1-4(5Ohgz)|wb4R)jamcOu$ob?Z3E4Wba4UO4855mdxb>f>IP=)OuJrUK{Kl_#; z^JH!=JAKkTc>jaR!r5|bWWQI{|6%Mc!=haOwP6JWR8mxw4ke_!2axXW7y$uk=^8+! zK}x!%o1sey>23xjBnN3>U~m}1=VrP7``PcY-@VrJQTTwzHP`&^^LN(GWpaEGvh(}u zXveBPu%^!9v8b%&M-F} zIv4+aB0e2XOh2_dTR#2yJF-f58SoQ8i-5h%`oa8-Z_8`sTu<@FnVk?LevPWPoh|DL zI~%z1yzx4(_b6*?a-U1px1!dg3ROjZesT3Tp}u}#N{9l0K7*rz0R@!K9%|olb=II~ z;0J_96bQvm)jw`aa{qfhU!BI0fkA8>0AHy5JlZ+K*05!g0I|S?jX1j|` zPL+&!KDyv*M9TDyaWNfA3cxrwc8ut_qTNBwFEKx-lsFEKWXZ2^Ou_^-^*Vz~g(bf5 z{qwfz#)FlPJgHGFykb{eJuIZO6#7nhKX3(3|m*+owbYggTYZ`xp^kl!k=wQep4Dl9FRG@39aza(6&nCHSyK&f!W_ z?RSUJyUa5!F}50V4b?U#W1BC=!*b9}cEnN~Q(Wb$rT5)o8%iTt&fVJW+{NW2MFdYk zye4k*o4n6JC@9S>JoPwkK4zG@_bF4?gW_a4_&_HS=#eD@^IAQxqt*Gv@2q$$R@;?P z-gw?&$MTW4C#btc3j7v#o2d9jkqbuB2 zSi=hsZpa8l9KYA&^~;~OIedZ&p2yLs9r<)CZ%6*R!>#x1=$_G*x{gx{U88xQwS^(+ z)$*?-xv6!z3|9;FhPTs2R#$-+L;0~PzK3m2hjz=Y4Lgj#k!`w8DP3#OSRb7QyPT^< zzfd_UADZQ}u#5%D6~oo1)|v6&k3t1rPv0b+PE$HA3quw=12K8LBYpxQpY!n@@NXp8 z&ZBLL@a33+bygDcCTKE6AhNH1!b}Qh)9nt$S3*VNX`^dGS&@N+5SmJp;ha*36bG5q zbTMC#o0!pLo*D6&AyYYd^#y<)zptM$`TPbjLQ<*#W9717_kj-hH+oJWin$}3R%_ec zgwDG`cf$^RVc2>yCSt^6U=7G0jI`Cyr)C=Il$=j*2C@R@>`*GFPEgnVf2PSKnxsD& z)>n2=1ZY#P z|JC@1Hw>!OBe2EeucqVU8q{T4urRSsD(YD|IriS(+CFY|Kgd&5a(;!HD8JjLmLJlf zuhb(=3t4Evexjt7?QvfrotC!*1rVK{!S~#p2t!QM+z)GB= zpmZ$yqM$VL*IZS40(*;x;{{o{?19TdbNz)8gZ+>OTfFlJpSHZo=oz5IlyaiBij4TP z{#sCo4y1Bjd~c|Ddf&#iEZfA+78uY_-wmUw@c4YhTDJG@pxXxiw1BVrfaV6{{!ACh zC2Z1WATF29Eo1RZ^;^l-tsi>!V@vE;?`~sqM_|94@%y!$a(%FJT`7|dV8zj$XeWpT z%-L1bTT2UgqqC0bk z`kDv>Bf74g523-&dsN7CS1XLdMIJpM2m1KA70`hZ-)7e3#L<%r-cM?zoO6cgsCx9Do#ZkISn1eArrVHJW~D3$=(5VwijX2|4E3&Xa&Xf+ODn&zvUL zk!8=7EcTYok6*Ts;jIp3x;-}+T$nP@XX;^-{IT6h4~bd@a>_tJsq_`FS0w~kk1{B` zKc1?x_-B*M_LINHcdObv*A+Z|< zJgaV<%*1mcgowGGcl31r5&OAAnWSe&QmYZ#poLf&+?x3bo8-0;NtIbWN^=AHnXnX1 zSidTJGgpO9j`99;!y7NrnWfrpSb9ZGIf$>oy01^-NM>R>2f;&+QYc2F8jKQ~m+#V5 z+b2Wt_@Gwq61xLo1~|#8&ZxVW51w64(rB)7ib7Cmw1dRQz-) z1IVu|1Yk)zZr#0UYd^mMO%!eAn)uHu(y0&ksmcZ2YM`TN%wHdx+F9_!jZ)*pSA0EN ze;tZ9?iAo$&nCJZ<%S77tmDR&)4N7p;GZ}4Mi}LLEW6Luvpec+B>5I@_m#i_!R01T zP{`bYJ{X{n9oZyb=6oI&#oo#kO~&_vO~0DvLSg(eeeMfGf``5oO)FxE#Q?UMkaPL^ zfh*eYr!V;ZmucM{)~<&?ishCQ?>O)76z7Z`5RmaQt&H`C&i!<*(p01Xf}zyMDcSb^ zi;Lfr8C22}!TGegGx_`tj*uxI{RPsUJK$kupdp9DFDC`4q}y210aY=PJ-%R!pF5RHuYdP$)wy# z%?Q>rE4Mh@9vm~P)!0`Y-W+j}ic`0JrG>(u%briv>9y-yf#F|6IlBeSk~_>x)j(%M z-RZfa6*@*jJ>a`qqeX&<>SQ7*%HA?655%@;WchfvX-$uPL*i(Iz|&}Y+jh}Jf)6Iq zJoFgGgjiVwBdw=%0$~GEGZKnNxA8^lPEon#II4bJ<-he|>qmO{kbzv|nh`sQw%oNa zNu68uPyLj<%w0@!5FWgTLBr35cK)c1o}|cpa@Hxe6`d+IT@{3*dsjS(2g z+1R#VDP-OXL?ky}P#D?0Z^<%E?$i>c@)^Fsv7g{Ux!Cdk1+x4lp(twUzUw@{gl=Aq z`pc{L@-pEN^QYt=P^XHq;|58l7ixr7?!Fxq$4P@W6U|;oy@HBmf}{i;7OhCX!_wN4 zFEP+5qn}G=g@yb2NSwt-lPi?eX=!!t4IDQYxWco8V`ZxQ-0^o##&kDfWAv;318uF} zTcf3(KM_q)DsP<26CJD9JwE+zuXrA4m0*3LCr9b7y&g)x39NbYUxW$Q9y#5ltxsAT z$dwI0o4pX}Phlf28WaD0>9(C4wzXI!jnJSt*`Ca|3#P`E5?Fz_+W1eV8>KgAl|97O z+PzW&qczt(iZyjB4U_ooC!Bf<3*qQLRH&t*F06y9+UW~+;=)IOYK=Wl@>)n~cG&4t`<_>+X<_c3ZpWZ{U;zIhFIwjrpiyRvs-`zTlB^8}K{^L<&L03Zcs#2K5-%Tg zV3C8+>Ir-vF1ES^=0xz9<78kfmroWn3n|yTNybAgU@Pe_;s9HVt^~sd$?IE6C{)_F)ve6|+4c7xzb^HPizC5Y7w_qdTr%B62-GjR5_^8Zq zpNBLUcgXGD%>-%VTe)zuaJ1L<*J3-i^u>S;a>QOz?GD7mG@R}~8n2)j!EMJwe$wwP z0iT9L_{vqiVIu2EkhDNfi@pzv+N4)Liwbgu2N&A0j~U0F<-ek~#L$*yVFAzhVL#_7 zT1Taw#%0;9IH;%SI_T$IqeFjtYuFn-^bFYRIC^ z&-;Z_H`Xk7pKT)?rHm$!#>{4{G_n zP4m8O;O|y|A^W-Fsy6>Qn|F_J=_6-Diem#Or=4^Xm@69cgS6la=0u#DnixbMOn#z7aa&A%Of{+UgqD2ZXQ-QfG&?4HXL ztR}Q*#$L-z2B%KmS_AQRI&P-l**7-RZn>07Vgr%pSX%bzy}z~+B4%xoLt z^6s51!c=F;Z0>bTtBJR5eJ6&ll|PS_`I|obaWt!kaPEFcr>TBLR)wQ^)d?D}TwVxP zQ;7R0?&ETDEY5STuQ!OvXM2lZp@seAl{FMXbax;u1~Nm+C?h;;*_R?o;VbRGO$;<` znB+a#O~`L=cVWQ}!1fuf8QF7*tr~s#-by|#tWQYW`7#rHyeKf;7kVB(8Q`CToXg4K{@`7QSh(R4CeJt}89mf}fEI-yKY#qKu?* zyFa7_d{etaJ) zit=*+U@)BJoJ@=^xYWq0g}Kt8uJ!R091M@YB*HJ zEo&wDrh{@MHyNC1Ku^qcVp@kWI)c$>O&wW)=$O1UmX$FE@DGMi!}=6XBryG|&+du9 z7nUyPEn*9SAVk3TOrgEuxk-n~8cu#iULrGFi_5~zNS3M+%T?L(xsGe3_sW7D4%fC$ zSi>haRvMF?Mz(M@Fsh4m>S(RIi6~xEA@xB~tSCYrJyQ&7=xz4N2Zms{7cc}L14FQI z#w@=hWw7I^D}YArWGlJ*i`Pv~j6Nd7PEl9;W#x^V*??D;t;-_+kIMbu2I2q5miuY? zuSa=?-Ir`$9ke^IL8RdgpWyX;We7Kjvw z7>8ykXX!7(l*hffDq#vfkfZy*ejpCv4crueYD=-@)^N z4Vm-}K`V;WH|Yp8KknvRL=%t${z3zAj_7U_pZ|V@8HZ-V<;$cHF%YU&mkq9`2Ef)H zdAGjW?A?J_G*MCGj~Dq>*NW{q)RfRoa&8)lh?H*UjrfVRo*G#4(|Ls5kGJ=GL(6=(8(l~UMMnpXUMbrb9c;%L29V*fH&3bGp3Gp+US zUB!LmEF>@Dm-{|+JLBBEYsTtb%>u65Z7hHSTZ#$G1k{CwL*WeG*=_M{TYQhH_jU;@ z+E*NbsIUX;jbCs6c4Pc+_Y~mMsIi38Iqy6G0Kg6AJoux3I@ttMl1ODfl$XTK**;-z z+s2+hDL&INxiVG8JPkLG&||khl2%-QWGDD+9x!g74+Jp*CbdCz@sXY~E7lW3Ul-c4 zapN;-*7F%0e(XH5`S@CpFS3*j#Vk-GpQgkIyhS_Gn!MwMGkLe^rYaBMRHDR?tF+M$ zWAz)xlpn8Xpc1Te13=}$Z5g}U-Bh_ZW?c^FnH1=Lnkrd$#pUZc$=^G#tMauT=hHS6A?CBUL z0}ej7?c&SqH2q>#q`utRFDvq!zWw7MzK`Ouz&B|Y__@-S)*bOR{j431CU8rN(rh_X z0_$j-omsl`R|{_U(iQ_iP(W;3OlSN4T->U2{ya0z{@aYc_Kf&`!z3*COn6FTCrYHK7u_@@tGZ)KaK?;Ya2Cjdtvs^<)O{NL*JDAV8|iL7?M zu`t%)^23fyOE?Ik(D*u!&vq+E6hwsv1GD&q6-wob(L=f4htN~ZICZ_C%SiCnk73WQR<;P1t3UO!HF zB{yE-1%Y>0WIu|fq|7r-J!ZSmQ-!dwMk*SAqvqPQ)cvx{VtV#=uw6K6zk{%ACJuGl zF}%WwJX~fhB0S_{CLqSje)04!m3C;3U(&-~<6K!C8~MzK;#(+;$z0D0SNoW~r1s;Z zjh)*5?5^BpglF*wh2KX-&cq$1{)KNfveCyKhJJjy2aHYn#{=2Y9}o8QMio+wMVgk| zh?|FPHl_}pzLoZ#e?mL#psYxyw_f||T})MozwM97 ziBknNAZ!~Ov*Q=@n)z&9Yx_+vis)m78m-#b7QjV6vi*u0oz7lTcb?dpji&RJ2K>H0 zw<;QKTdySU(mpQ}LSBRle4)*9?tAKe5k!4SV-j$MkDjQ9fgc_bt(lbpX z5Idb%z}M6R`?5s z*YVL-^cOni(QL8L9xQrMubcpkxb22@dQo%FwJvc$b87bD5&#z}D_4h}mWS0XHv+6a zW`=_O--GmjAB@0nCRB)S7V>Bc(y51C~CxF)sNMr zJp?(ni7IQ7JOml{BS^I*Ja)Qf-aqW60vM+>Nj9tX!NP3)l!6!Eb`&xnt!s-XH+Z>O zirNJmaE;)Nb$v)Yb1|uLtlhipDGfNU_ttfFAYjFKgS$%9dHWgM64q^&%n@y2v7I{` zl$*J&q>*`x$+e`tJt7r(q}VxGjw9w~K6_sX`D#LGf~9G!+jONOF2A1L&tOB19hr;X zjn$v+FFJAdU*{WYHY!%lBKz60mxTJ%!7_ZiJFF;*AR{yM?7-+elnH9_K;gw62EjFT zaR^aYCFd8kBc?0p6+aeP(sC`I-E@oRxIB;PYSHJj|A+yHHro=WPxVX3-v($vvH@WCEX_?sFe zb9{*-5IyRY{{A%x=f`|(z~#odyIa|}?InX?X7uJGpG*HP7VL4L4iSiO6@e3eU;q4_ z&7hnbw}{n_#UxDQ;UOgw4PicQaY5yfq{*{SC1QTvzwi3KWjJzgbE>Nk%t0) zK$Nf$8(62v1mC`D-Oh_bl;b+DHm2E37g>(P3T>sHHa4}fkxXnK1FcG)QRKkO6X>AW zJ$;qNO-n9H_kAlr{Uwmy!$3-xzy>sJ3%-+HrCDb)yM~S2OlPS`C)(1xT1L0}|Bh?? zF+crJ8@+^p#HX~J6_4Hxeqb=a=WJjdJ1$#iJ(3~vqKu0n8o=C6o8*a#?II)A3vO6n zjjV|H8g_xO0LZAUFMvgAHU)o06N(du(otI>*+5b2oB6IK{=u7Gr?@`w zS5QH?1(c@~s=md(I;VR_^ZeByuH~PRLW=SO~zYDu7eZ<@Xy+L04y4Q&0P2@=7y144*&vVas+eG5swR-T!``({X@tFR2Q3 z_5Sp?Kgum?{><^r`xm>-rE|V3S5^mz)F*w=(@63#vO!RbP6eL2`!wf#`SNttvaYU- z#oy*HyNaiT@MV&m({ZZZT$KXLeLDQHfadc)-T>1*fndt3vW;m+-$Rq(^ZrFg1;X#* zb&Wqu?qFrrSoCk5av+Dk036ehQNhgKLDNchgyz2IckdsNjR!r|qk}2}%ZY>X8Ffwy zE5J?l@dDZwGH%4*r7;uF<)n5%ABGtQg`WcA1HGhdLU?uQRi=>Z1L*Z)b>K><*7~FO zH*C_kl2!m^s(VXBWcZvsTsCnPb?lEGbS6npyt)}qMg87tz8}N?4$b1KzZ?z zw}(vlj>jdu9KFx8Ot;0&-uSI4Q-?ZWw_F^eozqe=*t*%Y8kN{E5VkG4`Km;3x4J(D zicT{1PcLK)7Q3(UT<-ucGCl4UpOUbp*_}jvQ15O35t0z~)b1 z>E|;RgRVGy3)b$&F!L( zPPzJ}{27lRMz7in>p>4Af9q9A5hJbzgQ^a-qP>8&Uygn|x1YyW_s;i_sN*HuwAvq^ zPQQ7oC@1utK{q3X<7nQ*JVv}|=i#}_Y{#-vqGPIe$dkZuv_C5%2C~*c&k$xSdWtV) zE@p)O;hD)GdJv8?R$wc z{ao~4KD{@Rk)`6ueS_GdmEVVR$X(rEIFt~5bUAa_ti94>4pqJ1Zqw!8Z9@t4GhK$5 zcPM{)$&DF5$TVc>W%(W>yjxNaI8qk&R)N*4=`L~tuFuc;yqDVD zU6n@ipWU(Y&uRziQk0ZZ99z=}YG(jn-&e>63{cr@g$B+67XfX@%P4o|88|<) zWBNzY^UZ_^^77`a`)kO6FWA)&!_`*D?5-8pJUAQ)?V_=Iqrs=Z9(FOidz|TIdUKH* z@i7>)+%(&>VvJVEh~>2|slFb#P_o?&)BKB)ai=bAF}m;M|8s!;cbck|6l3)Ivavj? z{SPmicMiC)u?6|DDM+z3#iP`MvI$T2!cNf>#4-2j#9^pT=D;T*RN!w7ePMh&?T~n@Lb&2(mzf%^v$6w7gq&G8Z?dNZPZnaG+MJSkT5Z+Hs%W*y%goq=_ zX#X~ZM!n$h$J(<203d2rA~B-X$p_NvRbSsdD`(;0Tp_k z6>e~n@sEu*E?YGs1+iBLs2M2Kv@C^17f~ne=AdP2ty0)TVAeKhNUCTE|2v1XuGzYx~*P z_H###Jts#-SnK-P&sgaC9R~ok4|?dUGOK$8eJEs`c(lTuy*JOUN&U5*j6|6keCGC` zTHW>?ULIix5oKK4`fQBdldU{U(K<1%K~U+}BL(LS!|U51L#fL+CB1Jz8-bg%z63uRx$odX@;AiLz_VKyB9niZm} zQS>>_PJ~_d@IuvPA^mmdvgO?JG*^FCDoysxa5AnIQ6a-YyPojogkHgI%##dGS3&5T z&v4HU1+@|X3Q*Rs|9pIncQO82gfn>l9~a?NPJc*wP91;KP~%|VkM?oV#rY66AkAQr zXMv(OCkR*SVnpHA$xGC7(T_mh;v1A0Cn$k>?8Xg)GJDqh)&0In=_E`w)z>lLc(`{E>r5`wMT8mv*$gp(SSN7EDbr zVTl&f31rygk|7m;$T7=FwJzmo8C3tQ;t}aEg%&LMU4d!!Fe&w2LL%{5N$3|tfUkb` zc*TWsu|g^~D|4hw4TP1oKEDymQ6X~P!@W!qTpG9>%_*~C-J_xv@Oc=;YctR(_=bXG z9`rRT@~)!xQczFCIYu{5X_YmHQM^C*#Wy#2LiCla&RxfgIi6VZq%mQCH#ibv37$0i zNfxNI!Z4eW@p9Y_oSZ9MugU zHzOl6fiZb2Pw-SdC&02>JNC2rd27B_3(u z^S9OXQ?9q9gL7-CA>ms7RySQ0=ZR6RtyIb#FOvWl5Y^7$*Y=TopR#;0A)*}v5;+3+ z9H04&9wp?-DPx=Y%cQG`a>n(6;u9MvdfUA{qd41R;S5sEXR$S+?#UtKwOQ@9i{jcT zaL%8OJ9x(w^Wx*f;>}GiDLKPh4?i^WwV4X2N3t&3ml;TY-E=^|<2XX-V+LQ0PAUsRbMqAuDC$4Bv{KRT}SnV5#-!MW0?$-k&~cVgsU2h79g0IUR?1_=h43l)D@+7gBlPB$ zfxu!b(hjxY{ubv?UR@$Z?$dH`DSH67z?*+on}DRigFs5zXhlqHy!m7{H~oVo7QLeL z`(&S=6ds0_>gi>?KcW2|HlW2x{RCK?U=JWN#^$3qmk24s2#oav6g?g7T5&UaY5zEd z!#5OB_c;Dt!)}SwE{ph=hqDou4s0SNY#}-FYVNBw7r+98XzYcwJZ!4GeqX5l6nTezJffx99JQ2V$vwb^a17t^(mo;zP zf|SCx`st$f@(T_}o%I{-tY7Rrjr7AV#$>=( z_Ep}_>5JJX@f6-5Botr zF*B~#Qjv=$UbxI#kyRJY?t@pOA5^fYt#*zrJ_QkC2;C!pPen^5!XpgVBk1K$@n~j8 zm{oyOjGw?Ko|c5fK}Wi?1LM^AN2hpL#Or(#auD2m5ISq(^152x>{`atAxY|k$Il%+ z-{Wcr36~HksKRhZxxEJ7(Nf0;xxL!?RtKZ?=0qt9*2~KYV_y5j%}4ag%Rl6K4ZPwos-c`Gr$oJ6;Kyf})QpL{uQWhIyOjP8*Yyuwar)8C0 zEhO5;>q_)_BI5sY!Qofi6hVG}!9f#s92yC{p=v4}unU*zd}YWM5E%8@Uprl@8)xdN z`&-)zWRG|Uq#F=5&CLHo=r+lH3V?(PTb*t@2E@P!I6c3AsTT&3J4$n#yOgBC?ghpiHDRTSPN10FB@kCTrTYfPIz52uf7w{WjUp;O8}(~WEfcr zskW8v?*Do4x6%VQ&b_~Q>)%{H%b0=38-0Z!KWUq7DzOgjl)X8q+W1)7iKZi;uVm^v zpEhF;QL&Wh<7Rq1XSLP5aN}UA0BMS+^2$Sj1o7fNZbOWY_ga#aYW6J|iz=XoBW2?s z#2fUZx?g7Aq!`bS=2}MzTWc*WO!`;?O6p1&8i@9HRrtaI2WaJG8EM(s-N8oPL@69* z>IcG<@A>fQZH;jN(OK$KWNL87?kWonK&8;X{dQko49m(5x2|1dAmkH$jNxvEQNhDl z75Zep4o$fZgdW@M^I}`hVGdw}C~Vx_7-!Rr)T7+TJ%Mf{K`_6ia^AU@oE-|>92v5mYp1L5i2v%nm|06gkpW+_>E# zh4Bh(|ax<5u?6H|uw5nEA) z7B&2c6Q($u0@JKQbFD&P-RD5R!e~A7vHf&N|Do6n>+)iM`vCG#>2RSSt zjzG-V;(4&WWBFUZg8a_&?VOdWzp9wY@^11yAteWo+QV!l+4G~1CFqaqS#DSL8eB?sfiss9O-n_jJzihxvqE`XQg2xAH;=|?@SQF?C--HrkzLzJX|eX%W?mFZh+$HjU|gmoecz=m*5xn1z&GM zg`|(!YTZVj}C5)=>3lkH4gk(~^-#`o*WNTYbeOgOAP9<(WI7+3#v2Re` zemOr}Y#i2lkeBBWyYx_lpNWQd4)ZJ3*Qy0%n8pVJMor@D=S-Wlk41cDKUk_@Rj%g=R5SkrX0oCr0Eh799qj;vgC(uz^nX1m9W3OBb<>GgzLNxeZ}GW2I>=O7D|}!$9j^k zFMVz4cUq<<26@eK@fA9n*X2N9|NNb?aI@7?z2kw|ZP~j8Xj)mq7=sq~S60~m7ztu| z6eE_Ir@LWKxj*jqb6*U{wm@i5dx2U}e0k`Fa>6YbtfwS&K;4p+AG@Xu+FD4xw+HFg z{`6`m{^pSAsmlp1|30L5H;0rpaR^HaXNjGby-s)J`sYl^lfW_QR}-HkZ8pP+vkYo? z8XmVFy)0JKYm%cIcFP5hwSdMrKRW3f^wX31`lOVFq{gZ(WsVquuFY!Lb99fJwhgPGT5`@};U=^&&dHBDvyAy51+hfoz zyYX_=X*~j8t0oQPKqjCZ!5Q*2Cfu~Gm=6uS;_9u~k>n8oO7I}WZg{ZusA^cYSEzXd zBVnHL{H&4zwHWAu{ZMpt;3pENP_l(`e$bH8hg}v@4}w7q z)+W`N8JK11yPn$v#h5RO&wS!2u+-gOokd!hPzg4kuhKnD;)mbHgC}GON_!9UIdA5- zhs&Xe%QFqbUIgK%-OVN7H!oHCVug&M)KEQ0n@qYEPJy^T-i;#^1LTGT9C8;a2IbxW zou7ILom* z4*iM4Om_k0tj7`3%I}rvKsa`HduUI$fk8z8}B34zD{1ngS6Dj-1Da+?ElClbm zSbW;5<-BKrlM>6ti*IOz%4~F1-B7E^7_cJcllXDs**!NzgY%lec zsab_0Hu3|(Eet7C<_6+3-(o4Q%Iw@w+sd07D)xpJdVb6Nh3Rl*Jmk|3;KS}T+ljzc zUa~39_F~N${QEp4ifL||=sXp(95jMEp6qd~F6On{{Y$&k2Z{6#F7N)+1)v19LE6~b z4Qgh7iQz;psDC9kJKC_bx?&<x;TzT~V1VdH%}alQ?7FV%ZGCX4Y=^C6g_YD&P?eS>KH#WwwTQh4Eo(sKR#T><-eB?i*!<&>7KV z1XA#7ZfRLy1uVd317`lZci=t1OErM$)F)EbRsf*+SEPu*jkpC zML>9eKSC#3-zpcBN0DCGUFDjm)lmjiLsW_(s|3vM4X}#g-!#olXP1os9e&7)z9pax z1gGU9t`UnG=UH?Ac7p(}d3g;9F*+e@N7y{PiD;rrxmF>ZsN)vXred*Rdg<2%Lsjd9 zKQYDa-%9G|)60rSrV#RUoh{7`_KHE`$Vp-)f{07) z*r!>$Z~AiF+p-`tdfB6JJwJF%oFU#G|6P@Oy_s-=BB@#Gitn*Lh0xu-)geaCaO-;$QL@UP|sObZ2i>7T;AD;4<+W`evm^W~#&-Ik~&uDqINV__Ra1hxy`S#Dpa^xcO z*KkHvo0WPxO+IhMen8G|FCPWS7sDwWPKr`MypvA(>m9Ssy@?9GOM+Wwva4YdW#)vW zGSCy2D=8C_UYp0B!D0>1b_3g7N=bKFV$eO@C4|4RKf5IP=laJ}iNiZ%s^t>%GY91; zcY^QuG;?NxJrJL)*_0r;WfXzcjl*j53%$AM^Qqih{;xdOs=LakG zXLZ%)cX9z`)WwuUKpDShFqm1r`t%{zrEq&T;+Y@8pN(xk8Q1M%4$&L%FpeLg2mZg4 z`>#H)|0w{wk^c3>4<&f|@dh6~H{8-KeXf*Jgxt$5m@4YGbV}8(x%QZPb1#Zrml+EWyx_F(yI{; zI*pV9=g9eK)&c2rbAD1N`c>XSPqmdilx(C7Dx~c|!Usz(;SObp1M=sLap7N6+8`DN zMtkbpM{HMunFXf_1YqJw?btLYg3iaZ%*lK-4Gf-FPiJy=ZDB2Ge2HObLl#E&S#lv_ zTQfa&cwbQiq(Sd@+29AllX~UUNN_lh)!2v1UR<{TEXZf=Ujd<@z&}994gc?_S@l1< zLjF3sZF?;A*+B+&f3z0hbXATk5!RxStJJTdV~Zj$1bR0KOT8&?zJZSnz~q>YxLtT^ zcg*nRg2gTgMz^a6$4vcEw{+8RC@F{u=)Q;V?)wR(<$`<^vC+0?m=IyX9^^UcU-jO@t7=(5{>DbuMg z7So14E}U@>w7;<=s~~%T!v;|h7fzH@2L`Fy&1!ONcJ_S&`DQ7#&+GG)K%ZD;=}DKW zTN1Y@xF<##v=Kpl3JIv!H=|ViSM&II{0H2R72B`$OdW zJCY^9aqI*7*PHbu&>=+P_w^M?{))$9VLKPPhvgz2xd1Y4Y6A7O0U)ERUhVw@lBB~0Nz#g#H9=*j4f<+sV(PotO2xC z!Odj*4FEP*D@+i@wvJPZ&EpYj{+h)uq~#7lBt5ox!3cQQQct<}b0GAYO;mWNlq6-S z*C~c3KVM1OmNp^vd8y5<_fMX2%1~ga@012HJq8D7WFKh-Y$ld4v}!^rA~pO@!yRMk zppLbgYV)%Kd;VO49q4XZlTcf8i%Kzf{e%QQDR&^d|3J+TF-$dV_`N)claJ{<3G&K( zXA8HJiHgU^O+Y+kQQ&XLJ%J=TV$(ADnbu7I&m0tB`)bFf02p;ge_&_%vc6Ui@UE+F z;TyEqcs#}rn`!51MS&(%gD<4bwA*WO+mD&-d$>0Hl_{lt)`?ClhA&a!q*8vc%}?!s zy6LKQR0wK|i)xd0f5al<`)$HTjS{UsroZJ{+U}!?pCyS5N!wj5=HrLq`piPbwLIpR zNT1JVn)vYL$xiMO&A-VsCc^-rI$6!gr{UP5c$aZ94SNNqt{fj+pEHc70rn%$pX?g& zIHP7RfO_(;%6Qig&T7k{4{+@9kP(#+Z66d<%+K8S>1B?+Oe5Y+ND$jBE59LD0Pnd( z*VF;=Q8}vhivINW-^=fP`o9p=+Tqf_u5?cV4}&Ct5}E*#tZw8x#%|V02d$|HIePFJ zuCE~7{@Qyc_GE)0v358OL7>u0(K@UtE#|TyLc7`iFwyN2j#_P2aJ?QYPmrLwqL{C2 z8W^-bH$V*_;gPOfP^=3POFW@3TCQNaJH4u=?*wu<7p zq&yCtsTeVfnEP!R0ZxTj4lY~|7w(PN)i70|M~q^8o8{TFL6Gj}7pqEsSQ)m%VC&fr zl;Y+y7OZw{6DjDpY|})-tYC^wco8$)vYi1)mT>`^1Im^QW0~Z(WAb_^^Zy-26I{5c zSqNoNdELJO%FbcJKwu$+n%bc2ZCKV%mpJc?W2)j{Sl?mC#WV{I4$itg7u4p(i6GQr zQ!RKtIwXl_2cJ^*L&T9&f+_$SYEy||Hl^y6(6uo|Xfj?(l=SjxSz%qGV2OY8>}9xy zDO{7_@{v>}ckynnRK2yVc8Y=U=(pSkju)`>${A*%YeNfbwG&GXH@wKM;D-G5U7z;@!silLOust{$N|GzD3{|zJr zp3%oSr7kpc^|GAn$~<;VeSbl#`5a{envWsDHO8FG+rj??6JuqRyWp_PFvbE zsCK6NgIltkAUBaP+9eM5U^t8dwaOA7EeOMd2VQDzCE(v8l9)35#H8j?;;$h#J4ICo z^TAz6FcJ_b<>&=A{)T#8>Y@D9x25Na$J?d75^5AeKyNED4NgzqT&?Q|+oewyqHevJ zJVYUPwEzyl5jT}}b~!us;!k@-ATXpAw3e^#wg&v$(3ywf0*`qTGev1eFQy~W)j&?3 zo+~1SyOb0UAWspq6lE1GzlNB}ogdiK2{TjIL>7bW?ANO}N%V43+Ab}=(BP;?Wxrz@q4OwIC$HvEa2W&9`Ky9PdI#3aJ zDYZ~lo1d7(;cF&xq1l=(HrWWGl2l_A=CG@A{MpbzQXK7dGC3Y>ob(-v0~+%q9_aPyBFyQ>Jkegg9r6l7lyEB^{T=7{ zOutA68k2cN0R#ScJ1UMuBaF(-foj*45S8A)KveWNm{TV%VuT!vM!}&0o_tcY!b^tr zUV;c0L-Li1%xYOV^pHkP=FbmkaIpeui2nZV|MFk>F}_=g)V9 zT})a*&8Ra0nLoLWSlj8fHKs?0jDvRgE zOcQ&44Z77sTTRr}ivw01({k*o^4+6KO+zoh9<@=tI(?S^9b_C!uE3j=I`f|2g5(2W z>V)*%W*1Pl?~vT@Bf_ahrRO)0jQ2*t`ckRE>orZs^RyJSJ!XuApe<|OZ1hOO#_$6A zV~XKtv4j?M|q^ok)W??-abUuX3{PUFAMX_bI>f?io{02a3x@>Dlu~vozu!Jx|tWa~T=WiBA^Eg(2{zxFAEAm80Jl z%(|+cYosj&rmD7O{ymb*{QdHEI9Gtk-1n`gBa2(uL*EP7-VZ*BY#22U;>3E3^7{5} z6!U?z18Y0mlZsE$jU%B{T`|Y%R#<1&!$w_jbpMw;; z#|~Gcg<1NpO|Mz^9l#D$_7xE#KT3gj6TNrLa&HAP^ChhGlCOA)S5a*$!;kJ_NdA2Q zvLt~-#j+>v3VQ$f$^UkBIDK$=gG!a zMS>D~?+}nK5F))9klq3z6z`9<_Wt(0=iI&5`Od!*9vX8$8eW%vD&*u8L3X4Sy8#b+mjtD08`=pxLFW$)1m{RF-vJjxeQ z1M*H8kI<$vdJke1ATP}wxz@n;OW+?@rg;zaLN?6O=Sa4p!1v;RxJ7@(jcEV~2R&M+ z4zbJJINt4F&}wver}Ks(gEkcXH6$)r_MS#Cb#QEyEWG);%AOkCwwB9lKa@azyWyLw za-iK*utMWv8+LrC?CHpW9D)fy;!rM%kCEv0fuq5(xGqO?_&&PMxz#IrjQF z6y{HESqGiXev&W!Yzf}--fMS$KHm9%Y2dVVIeS4CBB#vkyCT;%d%4o*}) ztcEZ&2jkk)nqY?6k^G&bBmBCwMUFaNz2~F!7OxOQ?>je~YOUQD@4j!B+y1oydil=s z2Ug8>b*XB1JZ}4-bs)V(@WW*fdI+vs`kF~V8UFgpiUUgXhgr6DcUxCjc5qe&q0ljK zhkO4VbvtwLq%+qszuFHWSUaiR?Sp21N!}~DRK7*SLBs5!fcK4lEZw_k)s*okwVFG^ zmenN_1(vJDO-P@7ud#Kk@R)oUf#qL!?C;O#uQc5}9@-2(-3s3Tj5=14zuB;XJoV4<%(?h<=FtS)<=dZcF9ep<-LTEu9r0Z5gAPQE#2ru zgB;HE&>K6;n67bzy?Kq$a(BzKTZ)Xw1#w*N&VilM`U+A7aWOTJMRC_u?BsNKjVT;V zLU+ExQNnjC-EqH|(@mGhI+=ETQ(p@YORK0HvugM2@l8ia!^h7-)??mRUAML1!TQ>c zlZcM6)${>Nyqg|Ja8~U(U6;*VT}r>mxz;ff_SWa~MEo60M*ZGPmN+gn9bC1SR|aNV zE?LP8IpH8OIGmJShCW~DrZS4pk+v!?n0UhA`)IxR?X86WlZ^j;n*MJ;d3}!jGrMa& zy6)7csp!dPXvrRxQ2--YTg^=~rR`u=bwo~6bmkR(R>WN)EzWo(OXTVjw0PQ7L*x=q z@3viE#4{Oeee?(>wy%cJ_mWFtUh4`6)9TzXUf>;di>3nX$d;e-V6C1YH4S+^QH`q< z4eaYn>p@0x3Q~yjJy2ukwNWZoicfmg%!YHn zj2ilUPOO!2@tW3y_)pw=|D)pmUr#VkBF3ww%f0?gD#c4Eo`lzMy#5qFaoqkvY=Er>{YLC7uw+=U5(-l+H5nA z#$w_sAf((S)h@Tuvss=dhW@!5v|KZ2qEdR~9s*~fWQx2)@{kD6vqy(e9a8aVT^%sg7p zzuaxP`1JzviacTK5y-1|NIT>CLdl14q3>7Y_|H2go7lQ|#DkR@BM4gwuEAMyr$pfE zMJ~^M!r7$KA{vzEL3Swh8E0TW&Y+$UnPd=R#W84XGZ zC$?(DW!M7KFARg4N_ttfd>Y|Zgt|e$G%dKDL3%X>G+uztdqu5ZJ{N#YB6dDQ z>dfMvtHo8;RX>?5M$k8QG|vF#f4gJ<|EKiNk2Rw})jzQobu*T5Jxl}u7Sg}%)ckBc zS9O6l(vlJHrc@#~X8mw9n`O;Sgb>i3x%?`~?v`H+^rH)V9B0r;$wGMig~rQ- zKb(ufwfm(Me(MuMvd+tyi6L@)$USnpR}hfrYvh+mm0lHVx!WtOF?RgF?>kb{z?)sj zG(uY~nL02}$<07pDnLet;2%4|XEEc)SQ!)XQOh@}B^KGHV?=s87evH%M?T;`{Z+EHU3 zoj)G){@eM^lcUujYNzehi4?pV9~%SpR$;sk5a~4br~TE3i>s zr}1GS_-3%ECCGLzjHL)z30T8Lh%-ZQ=nA<(zC}BiX=!Hh#qTaB-hfF#XpK04z=40? zyCaN^?%%HPL9C6M9uLlG2EvPOetz!bj2w%OfE}6Tt)FP43)?BS%H !!vECxpV!l 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