sea-orm icon indicating copy to clipboard operation
sea-orm copied to clipboard

Handle tables with no primary keys

Open Sytten opened this issue 3 years ago • 9 comments

It it quite common to have tables with no primary key, here are a few use cases:

  • Time series data (specially with TimescaleDB)
  • One-to-many where individual elements are never references

It would be nice it was supported by sea-orm. At first I would be totally fine by just rendering the Column so we can use it with sea-query and later down the line do a better integration. Currently it is a bit annoying because we cannot ignore it in the CLI so effectively either you remove the entity after each run or you add a primary key.

Sytten avatar Jan 23 '22 01:01 Sytten

Currently it is a bit annoying because we cannot ignore it in the CLI so effectively either you remove the entity after each run or you add a primary key.

Hey @Sytten, do you mean generating entities of table without primary key will cause errors?

billy1624 avatar Jan 24 '22 16:01 billy1624

Yeah currently if you generate entities for tables without a PK it won't compile

Sytten avatar Jan 24 '22 16:01 Sytten

That might be a bit tricky. I always have an assumption that for a table without primary key, the DB engine implicitly adds one and hides it from you anyway. A table without primary key breaks many assumptions about the concept of "Entity", and hence methods like find_by_id is not possible. If we need to support it within SeaORM, we might have to invent a new concept, Datum I guess? (just some random name popping up in my mind)

tyt2y3 avatar Feb 02 '22 02:02 tyt2y3

You are partially right. Databases in fact have a row ID no matter if you have a primary key or not.

Yeah it is probably hard to support in an ORM. I think my wish would mostly be to be able to still generate some entities for it (at least the columns and models) so they are usually with sea-query and dont break the build.

This would fit with my other issue on providing something in between the raw query and the orm.

Sytten avatar Feb 02 '22 05:02 Sytten

Hey @Sytten, you can select the Datum like below.

#[derive(Debug, Clone, FromQueryResult)]
struct JoinedResult {
    ...
}

let builder = self.db.get_database_backend();

let mut stmt = sea_query::Query::select();

stmt.columns([stuff::Column::Name, stuff::Column::CreatedAt])
    .column(status::Column::Status)
    .from(stuff::Entity)
    .join(JoinType::InnerJoin, status::Entity, Cond::any());

JoinedResult::find_by_statement(builder.build(&stmt))
    .all(&self.db)
    .await

Adapted from a Discord message by jochen#5064

billy1624 avatar Feb 10 '22 10:02 billy1624

It would still fail to build when compiling the codegen code directly without any manual modification. Maybe we could add settings in the codegen CLI to either ignore the tables or treat them differently (like only build the columns enum).

Sytten avatar Feb 10 '22 14:02 Sytten

Good idea! We could simply generate a Model struct and Column enum.

billy1624 avatar Feb 10 '22 14:02 billy1624

That might be a bit tricky. I always have an assumption that for a table without primary key, the DB engine implicitly adds one and hides it from you anyway.

In my case it was very easy to create a Sqlite table without a pk using sqlite-utils and then it would still have a ROWID. It's also easily fixed, but there's nothing in sqlite that requires you to explicitly have a primary key.

alper avatar Aug 29 '22 15:08 alper

I'm trying to produce a clearer error message at compile time.

  • https://github.com/SeaQL/sea-orm/pull/1020

billy1624 avatar Sep 08 '22 09:09 billy1624

It would still fail to build when compiling the codegen code directly without any manual modification. Maybe we could add settings in the codegen CLI to either ignore the tables or treat them differently (like only build the columns enum).

+1 for this solution, as I ran into the same issue today.

Should I open a new issue to track the cli changes or can this be reopened? It is unclear to me why this was closed by #1020.

joshpostel avatar Nov 01 '22 19:11 joshpostel

what was the solution here? i would like to avoid creating another index if possible, is it possible to tell the entity that it should treat some other index as a "primary" key?

adding this to the generated entity struct makes it stop complaining, but i'm not sure of its implications, and of course as mentioned above this would need to be manually done after every post migration entity regeneration

#[sea_orm(primary_key, auto_increment = false, column_type = "Boolean")]
_fake_primary_key: bool,

databasedav avatar Apr 23 '23 11:04 databasedav

Hey @databasedav, every table is assumed to have a primary key. For table without a primary key, I would suggest doing one of the following:

  • Adding a autoincrement primary key column (if it's possible to alter the schema)
  • Or, let all column in the table be a composite primary key, i.e.
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "cake_filling")]
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,
}

billy1624 avatar Apr 24 '23 03:04 billy1624

Hey @databasedav, every table is assumed to have a primary key. For table without a primary key, I would suggest doing one of the following:

  • Adding a autoincrement primary key column (if it's possible to alter the schema)
  • Or, let all column in the table be a composite primary key, i.e.
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "cake_filling")]
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,
}

Although this works, what about for users who have hundreds of Models. Is there scope to add this into sea-orm-cli

vablings avatar Jan 18 '24 16:01 vablings

let all column in the table be a composite primary key

@vablings thank you for the suggestion! actually it seemed to be a very smart way, and the only drawback probably is the max. number of column is 12

tyt2y3 avatar Jan 19 '24 16:01 tyt2y3

I am trying to make use of https://github.com/pgpartman/pg_partman/blob/master/doc/pg_partman_howto.md#simple-time-based-1-partition-per-day which wants me to create a parent table without having a primary key.

But in that case I am getting a lot of

 the trait `sea_orm::IdenStatic` is not implemented for `entities::transfer::PrimaryKey`

errors even for other entities that have primary keys.

The above mentioned workarounds do not help me.

gitmalong avatar Mar 03 '24 19:03 gitmalong