Repository: SeaQL/sea-orm (rust)

Issue:
Support for BYTEA Postgres primary keys
Currently any column marked as a primary key needs to satisfy `TryFromU64`.

A BYTEA (`Vec<u8>`) does not satisfy this (and, I don't think ever could).

Would it be possible to add support for this type of primary key?  We use it a lot in our application

Pull request description:
Support `Vec<u8>` primary key
- Resolve #286 

Patch:
diff --git a/src/executor/query.rs b/src/executor/query.rs
index 96455945..3c1a9184 100644
--- a/src/executor/query.rs
+++ b/src/executor/query.rs
@@ -539,5 +539,7 @@ macro_rules! try_from_u64_string {
 
 try_from_u64_string!(String);
 
+try_from_u64_err!(Vec<u8>);
+
 #[cfg(feature = "with-uuid")]
 try_from_u64_err!(uuid::Uuid);


Test patch:
diff --git a/tests/byte_primary_key_tests.rs b/tests/byte_primary_key_tests.rs
new file mode 100644
index 00000000..173efc93
--- /dev/null
+++ b/tests/byte_primary_key_tests.rs
@@ -0,0 +1,74 @@
+pub mod common;
+
+pub use common::{features::*, setup::*, TestContext};
+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"
+))]
+async fn main() -> Result<(), DbErr> {
+    let ctx = TestContext::new("byte_primary_key_tests").await;
+    create_tables(&ctx.db).await?;
+    create_and_update(&ctx.db).await?;
+    ctx.delete().await;
+
+    Ok(())
+}
+
+pub async fn create_and_update(db: &DatabaseConnection) -> Result<(), DbErr> {
+    use common::features::byte_primary_key::*;
+
+    let model = Model {
+        id: vec![1, 2, 3],
+        value: "First Row".to_owned(),
+    };
+
+    let res = Entity::insert(model.clone().into_active_model())
+        .exec(db)
+        .await?;
+
+    assert_eq!(Entity::find().one(db).await?, Some(model.clone()));
+
+    assert_eq!(res.last_insert_id, model.id);
+
+    let updated_active_model = ActiveModel {
+        value: Set("First Row (Updated)".to_owned()),
+        ..model.clone().into_active_model()
+    };
+
+    let update_res = Entity::update(updated_active_model.clone())
+        .filter(Column::Id.eq(vec![1, 2, 4]))
+        .exec(db)
+        .await;
+
+    assert_eq!(
+        update_res,
+        Err(DbErr::RecordNotFound(
+            "None of the database rows are affected".to_owned()
+        ))
+    );
+
+    let update_res = Entity::update(updated_active_model.clone())
+        .filter(Column::Id.eq(vec![1, 2, 3]))
+        .exec(db)
+        .await?;
+
+    assert_eq!(update_res, updated_active_model);
+
+    assert_eq!(
+        Entity::find()
+            .filter(Column::Id.eq(vec![1, 2, 3]))
+            .one(db)
+            .await?,
+        Some(Model {
+            id: vec![1, 2, 3],
+            value: "First Row (Updated)".to_owned(),
+        })
+    );
+
+    Ok(())
+}
diff --git a/tests/common/features/byte_primary_key.rs b/tests/common/features/byte_primary_key.rs
new file mode 100644
index 00000000..7578e3c8
--- /dev/null
+++ b/tests/common/features/byte_primary_key.rs
@@ -0,0 +1,14 @@
+use sea_orm::entity::prelude::*;
+
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
+#[sea_orm(table_name = "byte_primary_key")]
+pub struct Model {
+    #[sea_orm(primary_key, auto_increment = false)]
+    pub id: Vec<u8>,
+    pub value: String,
+}
+
+#[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 1e709edf..186256ae 100644
--- a/tests/common/features/mod.rs
+++ b/tests/common/features/mod.rs
@@ -1,10 +1,12 @@
 pub mod applog;
+pub mod byte_primary_key;
 pub mod metadata;
 pub mod repository;
 pub mod schema;
 pub mod self_join;
 
 pub use applog::Entity as Applog;
+pub use byte_primary_key::Entity as BytePrimaryKey;
 pub use metadata::Entity as Metadata;
 pub use repository::Entity as Repository;
 pub use schema::*;
diff --git a/tests/common/features/schema.rs b/tests/common/features/schema.rs
index 7e6f6424..fe1c7ea3 100644
--- a/tests/common/features/schema.rs
+++ b/tests/common/features/schema.rs
@@ -1,8 +1,10 @@
 pub use super::super::bakery_chain::*;
 
 use super::*;
-use crate::common::setup::create_table;
-use sea_orm::{error::*, sea_query, DatabaseConnection, DbConn, ExecResult};
+use crate::common::setup::{create_table, create_table_without_asserts};
+use sea_orm::{
+    error::*, sea_query, ConnectionTrait, DatabaseConnection, DbBackend, DbConn, ExecResult,
+};
 use sea_query::{ColumnDef, ForeignKeyCreateStatement};
 
 pub async fn create_tables(db: &DatabaseConnection) -> Result<(), DbErr> {
@@ -10,6 +12,7 @@ pub async fn create_tables(db: &DatabaseConnection) -> Result<(), DbErr> {
     create_metadata_table(db).await?;
     create_repository_table(db).await?;
     create_self_join_table(db).await?;
+    create_byte_primary_key_table(db).await?;
 
     Ok(())
 }
@@ -100,3 +103,23 @@ pub async fn create_self_join_table(db: &DbConn) -> Result<ExecResult, DbErr> {
 
     create_table(db, &stmt, SelfJoin).await
 }
+
+pub async fn create_byte_primary_key_table(db: &DbConn) -> Result<ExecResult, DbErr> {
+    let mut primary_key_col = ColumnDef::new(byte_primary_key::Column::Id);
+    match db.get_database_backend() {
+        DbBackend::MySql => primary_key_col.binary_len(3),
+        DbBackend::Sqlite | DbBackend::Postgres => primary_key_col.binary(),
+    };
+
+    let stmt = sea_query::Table::create()
+        .table(byte_primary_key::Entity)
+        .col(primary_key_col.not_null().primary_key())
+        .col(
+            ColumnDef::new(byte_primary_key::Column::Value)
+                .string()
+                .not_null(),
+        )
+        .to_owned();
+
+    create_table_without_asserts(db, &stmt).await
+}
diff --git a/tests/common/setup/mod.rs b/tests/common/setup/mod.rs
index bbe8baa7..dfa1e29c 100644
--- a/tests/common/setup/mod.rs
+++ b/tests/common/setup/mod.rs
@@ -82,6 +82,19 @@ pub async fn create_table<E>(
 where
     E: EntityTrait,
 {
+    let builder = db.get_database_backend();
+    assert_eq!(
+        builder.build(&Schema::create_table_from_entity(entity)),
+        builder.build(create)
+    );
+
+    create_table_without_asserts(db, create).await
+}
+
+pub async fn create_table_without_asserts(
+    db: &DbConn,
+    create: &TableCreateStatement,
+) -> Result<ExecResult, DbErr> {
     let builder = db.get_database_backend();
     if builder != DbBackend::Sqlite {
         let stmt = builder.build(
@@ -92,11 +105,5 @@ where
         );
         db.execute(stmt).await?;
     }
-
-    let stmt = builder.build(create);
-    assert_eq!(
-        builder.build(&Schema::create_table_from_entity(entity)),
-        stmt
-    );
-    db.execute(stmt).await
+    db.execute(builder.build(create)).await
 }