Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Casting columns as a different data type on select, insert and update #1304

Merged
merged 10 commits into from
Jan 12, 2023

Conversation

billy1624
Copy link
Member

@billy1624 billy1624 commented Dec 14, 2022

PR Info

New Features

  • Added ColumnTrait::select_as() and ColumnTrait::select_enum_as() methods: casting select expression of a column
  • Added ColumnTrait::save_as() and ColumnTrait::save_enum_as() methods: casting value of a column in insert and update statement
  • Added select_as and save_as macro attribute to DeriveEntityModel

@billy1624 billy1624 self-assigned this Dec 14, 2022
@billy1624 billy1624 marked this pull request as ready for review December 14, 2022 11:26
Copy link
Contributor

@karatakis karatakis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks fine by me

@@ -338,6 +340,42 @@ pub trait ColumnTrait: IdenStatic + Iterable + FromStr {
fn into_expr(self) -> Expr {
Expr::expr(self.into_simple_expr())
}

/// Cast column expression used in select statement.
/// By default it only cast database enum as text.
Copy link
Member

@tyt2y3 tyt2y3 Dec 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I don't understand is, under what circumstance is not default?
How do I customize the behaviour?
This seems to be part of the public API, and is it intended? Can we hide it instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's intended to be part of the public API. For example, user can override it and cast citext column type as text during select, SELECT CAST("citext_column" AS text), ....

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add a self-explanatory test case now

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added 6295fcf

Copy link
Member

@tyt2y3 tyt2y3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah okay, the only remaining question now is naming.

For example cast_value_enum only do any casting only if it is an enum and so I think it will be better named as may_be_cast_value_enum

But then, cast_value is also a bit confusing. I understand now it's the counterpart to cast_select, but of course cast_insert_or_update is too long as a name. So may be we can use the word, save.

And so, the entire naming could be: select_as and save_as, cast_save_as etc?

@billy1624
Copy link
Member Author

I think select_as and save_as are short and concise. How about?

  • select_as: casting columns on select
  • select_enum_as: casting enum columns on select
  • save_as: casting columns on insert and update
  • save_enum_as: casting enum columns on insert and update

@tyt2y3
Copy link
Member

tyt2y3 commented Jan 11, 2023

sounds good!

@billy1624
Copy link
Member Author

Nice! I'll work on that :)

@billy1624 billy1624 requested a review from tyt2y3 January 11, 2023 13:32
@tyt2y3 tyt2y3 merged commit e9df3aa into master Jan 12, 2023
@tyt2y3 tyt2y3 deleted the cast-cols branch January 12, 2023 06:56
denwong47 pushed a commit to denwong47/sea-orm that referenced this pull request Jan 20, 2023
…SeaQL#1304)

* Cast select and value

* Refactoring

* Test casting Postgres citext

* Fixup

* Revert

* Add test cases

* Fixup

* Rename methods
@praveenperera
Copy link

praveenperera commented Jan 26, 2023

@billy1624 Hey this does not seem to work, I tried the sea-orm on master:

sea-orm = {git = "https://github.com/SeaQL/sea-orm", features = ["runtime-tokio-native-tls", "sqlx-postgres", "macros"]}

Tried inserting:

    let user_params = UserActiveModel::from_json(user_json)?.set_timestamps();
    let user = user_params.insert(&state.database_connection).await?;

Entity:

//! `SeaORM` Entity. Generated by sea-orm-codegen 0.10.7
use sea_orm::{entity::prelude::*, Set};
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
#[sea_orm(table_name = "users")]
pub struct Model {
    #[sea_orm(primary_key, auto_increment = false)]
    #[serde(skip_deserializing)] // Skip deserializing
    pub id: Uuid,
    pub first_name: String,
    pub last_name: String,
    #[sea_orm(unique)]
    pub nickname: String,
    pub password: String,
    #[sea_orm(
        column_type = r#"Custom("citext".into())"#,
        save_as = "citext"
        select_as = "text",
    )]
    pub email: String,
    pub country: String,
    #[serde(skip_deserializing)]
    pub created_at: DateTimeUtc,
    #[serde(skip_deserializing)]
    pub updated_at: DateTimeUtc,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}

impl ActiveModelBehavior for ActiveModel {}

impl ActiveModel {
    pub fn set_timestamps(mut self) -> Self {
        if self.created_at.is_not_set() {
            self.created_at = Set(chrono::Utc::now())
        }
        self.updated_at = Set(chrono::Utc::now());
        self
    }
}

Error

Query Error: error occurred while decoding column \"email\": mismatched types; Rust type `core::option::Option<alloc::string::String>` (as SQL type `TEXT`) is not compatible with SQL type `citext

@billy1624
Copy link
Member Author

Hey @praveenperera, thanks for asking!! Notice the select_as is text.

    #[sea_orm(
        column_type = r#"Custom("citext".into())"#,
        save_as = "citext"
-       select_as = "citext",
+       select_as = "text",
    )]
    pub email: String,

@praveenperera
Copy link

Hey @billy1624, sorry that's what I had before and I was changing stuff to try stuff out. I tried select_as ="text" again and same error. I invited you to the repo to check it out. On the citext branch: https://github.com/praveenperera/rest-template-rust/tree/citext

@billy1624
Copy link
Member Author

billy1624 commented Jan 27, 2023

Okay. I know what have happened... "a missing ," I was surprised that Rust compiler didn't thrown any errors loll

    #[sea_orm(
        column_type = r#"Custom("citext".into())"#,
-       save_as = "citext"
+       save_as = "citext",
        select_as = "text",
    )]
    pub email: String,

@praveenperera
Copy link

Hmm interesting that actually fixed it. Thanks!

@billy1624
Copy link
Member Author

Nice!

@billy1624 billy1624 added this to the 0.11.x milestone Jan 31, 2023
@praveenperera
Copy link

praveenperera commented Feb 1, 2023

@billy1624 Would this be possible to use when using the --expanded-format?

Got it, maybe would be a good thing to add to docs?

impl ColumnTrait for Column {
    type EntityName = Entity;
    fn def(&self) -> ColumnDef {
        match self {
            Self::Id => ColumnType::Uuid.def(),
            Self::FirstName => ColumnType::String(Some(255u32)).def(),
            Self::LastName => ColumnType::String(Some(255u32)).def(),
            Self::Nickname => ColumnType::String(Some(255u32)).def().unique(),
            Self::HashedPassword => ColumnType::String(Some(255u32)).def(),
            Self::Email => ColumnType::Custom("citext".to_owned()).def().unique(),
            Self::Country => ColumnType::String(Some(255u32)).def(),
            Self::CreatedAt => ColumnType::TimestampWithTimeZone.def(),
            Self::UpdatedAt => ColumnType::TimestampWithTimeZone.def(),
        }
    }

    fn select_as(&self, expr: Expr) -> SimpleExpr {
        match self {
            Self::Email => SimpleExpr::cast_as(Into::<SimpleExpr>::into(expr), Alias::new("text")),
            _ => self.select_enum_as(expr),
        }
    }
}

tyt2y3 added a commit to SeaQL/seaql.github.io that referenced this pull request Feb 3, 2023
* Update 02-writing-migration.md

* Update SeaORM/docs/03-migration/02-writing-migration.md

* Support various UUID formats that are available in `uuid::fmt` module (SeaQL/sea-orm#1325)

* Casting columns as a different data type on select, insert and update (SeaQL/sea-orm#1304)

* Methods of `ActiveModelBehavior` receive db connection as a parameter (SeaQL/sea-orm#1145, SeaQL/sea-orm#1328)

* Added `execute_unprepared` method to `DatabaseConnection` and `DatabaseTransaction` (SeaQL/sea-orm#1327)

* Added `Select::into_tuple` to select rows as tuples (instead of defining a custom Model) (SeaQL/sea-orm#1311)

* Generate `#[serde(skip)]` for hidden columns (SeaQL/sea-orm#1171, SeaQL/sea-orm#1320)

* Generate entity with extra derives and attributes for model struct (SeaQL/sea-orm#1124, SeaQL/sea-orm#1321)

* Generate entity with extra derives and attributes for model struct (SeaQL/sea-orm#1124, SeaQL/sea-orm#1321)

* async_trait

* Migrations are now performed inside a transaction for Postgres (SeaQL/sea-orm#1379)

* `MockDatabase::append_exec_results()`, `MockDatabase::append_query_results()`, `MockDatabase::append_exec_errors()` and `MockDatabase::append_query_errors()` take any types implemented `IntoIterator` trait (SeaQL/sea-orm#1367)

* Cleanup the use of `vec!` macros

* Added `DatabaseConnection::close` (SeaQL/sea-orm#1236)

* Added `ActiveValue::reset` to convert `Unchanged` into `Set` (SeaQL/sea-orm#1177)

* Added `QueryTrait::apply_if` to optionally apply a filter (SeaQL/sea-orm#1415)

* Added the `sea-orm-internal` feature flag to expose some SQLx types (SeaQL/sea-orm#1297, SeaQL/sea-orm#1434)

* Add `QuerySelect::columns` method - select multiple columns (SeaQL/sea-orm#1264)

* Edit

* Update SeaORM/docs/02-install-and-config/02-connection.md

Co-authored-by: Chris Tsang <chris.2y3@outlook.com>

* Update SeaORM/docs/05-basic-crud/03-insert.md

Co-authored-by: Chris Tsang <chris.2y3@outlook.com>

* fmt

* Edit

---------

Co-authored-by: Chris Tsang <chris.2y3@outlook.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

None yet

4 participants