SeaQL / SeaQL/sea-orm

[PIP] Schema compatibility check

Open
#1,122 5 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
9.9k
Forks
734
Avg merge
6h 36m
Merged PRs (30d)
8

Description

No pun intended, PIP stands for "Proposal & Implementation Plan".

Motivation

To allow an application to check that all Entities are compatible with the current database schema, on CI and/or on application startup.

Architecture

We need to introduce a standardized way to define all entities, and I imagine:

trait EntityCollection: Iterable {
    fn entity_schema(&self, db: DbBackend) -> EntitySchema {
        // this will call `create_table_from_entity` etc to construct EntitySchema
    }
}

struct EntitySchema {
    name: DynIden,
    table: TableCreateStatement,
    enums: Vec<TypeCreateStatement>,
    indices: Vec<IndexCreateStatement>,
}

// user space

#[derive(EnumIter, DeriveEntityCollection)]
enum Entities {
    Cake,
    Fruit,
}

And as such, we can have the following API:

impl Schema {
    async fn check_compatibility<C: EntityCollection>(db: &DbConn, entities: &C) -> Result<(), SchemaErrors> {
        let mut errors = Vec::new();
        // discover schema
        for entity in entities::iter() {
            check(entity.entity_schema(db), db_schema);
        }
        if errors.is_empty() {
            Ok(())
        } else {
            Err(SchemaErrors { errors })
        }
    }
}

struct SchemaErrors {
    errors: Vec<SchemaError>,
}

enum SchemaError {
    TableMissing(..),
    ColumnMissing(..),
    ColumnTypeIncompatible(..),
    ..
}

Mode of operation

Under the hood, it will discover the entire schema from the database (just like codegen) and compare it against EntitySchema.

The tricky part is that these two schema will not be exactly the same, so we should be somewhat tolerant to permutations.

The live schema can be a superset of Entity schema, i.e. there can be more tables and columns. So we should start from EntitySchema and check that 1) column exist and 2) the types are inter-operable (i.e. DateTime can work with time::DateTime or chrono::DateTime, to complicate things a bit, numeric can also work with both Decimal and BigDecimal)

The MVP don't have to check absolutely everything, but just some sanity checks in CI would be better than nothing.

Since this depends on SeaSchema, we should place this implementation in sea-orm-migration.

And we need the support of the DeriveEntityCollection macro as well as codegen to generate the Entities enum in the first place.

A bonus would be that we can return all errors instead of failing immediately.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading the proposed placement in sea-orm-migration, then trace the existing SeaSchema and codegen concepts named in the proposal. Define the MVP around discovering the live schema, comparing required entity columns and interoperable types, and reporting all found errors rather than stopping at the first.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
databases
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
20/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.