Sign in

    morm

    Download zip
    Author
    Version
    0.3.9
    License
    Apache-2.0
    Last updated
    6 months ago
    Downloads
    660

    #oboard/morm

    morm is a lightweight ORM toolkit for MoonBit.

    It is built around a simple split:

    • entities describe schema
    • mormgen generates plain MoonBit code
    • query builders describe SQL intent
    • engines render and execute real database behavior

    The project deliberately avoids runtime reflection and hidden ORM state.

    #What It Covers

    morm provides:

    • entity-to-table metadata generation with #morm.entity
    • mapper generation from annotated traits
    • typed query builders for select, insert, upsert, update, and delete
    • page-based pagination helpers with sortable Pageable (paginate / paginate_raw)
    • multi-engine support through a shared Engine contract
    • local time-type support for PlainDate, PlainTime, PlainDateTime, and ZonedDateTime
    • direct MoonBit enum support in generated ToParam / FromParam impls and schema metadata
    • generated auto timestamp handling for created_at / updated_at and explicit timestamp annotations
    • transient entity fields via #morm.transient (kept in model, excluded from physical columns and from(entity) writes)
    • PostgreSQL schema ownership and grant support via #morm.postgres.schema, #morm.postgres.table, and #morm.postgres.grant

    #Install

    Add the package to your application's moon.mod.json:

    { "bin-deps": { "oboard/morm": "latest" } }

    #Generate Code

    A typical package uses pre-build to generate .g.mbt files:

    options(
    "pre-build": [
    {
    "command": "$mod_dir/.mooncakes/oboard/morm/morm-gen $input -o $output && moonfmt -w $output",
    "input": "entities.mbt",
    "output": "entities.g.mbt",
    },
    {
    "command": "$mod_dir/.mooncakes/oboard/morm/morm-gen $input -o $output && moonfmt -w $output",
    "input": "mapper.mbt",
    "output": "mapper.g.mbt",
    },
    ],
    )

    #Entity Example

    ///|
    using @time {type PlainDateTime}

    ///|
    #morm.entity
    pub(all) struct Class {
    #morm.id
    #morm.default(autoincrement())
    id : Int64

    #morm.varchar(length="255")
    name : String

    created_at : PlainDateTime
    updated_at : PlainDateTime
    } derive(ToJson, FromJson)

    Payload-free MoonBit enums can also be used directly as entity fields. mormgen will generate enum codecs and emit native enum DDL for engines that support it (currently MySQL and PostgreSQL).

    #Mapper Example

    ///|
    #morm.mapper(table="class")
    pub trait ClassMapper {
    async save(Self, entity : Class) -> Class
    }

    ///|
    #morm.mapper(table="student")
    pub trait StudentMapper {
    async find_student_by_id(Self, id : Int) -> Student?
    async find_student_by_name(Self, name : String) -> Student?
    async find_students_by_age(Self, age : Int) -> FixedArray[Student]
    }

    Generated save methods can assign:

    • created_at
    • updated_at
    • fields marked with #morm.auto_create_time
    • fields marked with #morm.auto_update_time

    PlainDateTime fields use @morm.current_plain_date_time_utc(), and ZonedDateTime fields use @morm.current_timestamp_utc().

    #Query Builder Example

    let q = @morm.select_from("class")
    .where_eq("id", 1)
    .order_by(@morm.desc("id"))
    .limit(1)

    Execute through an engine:

    let res = engine.exec(q)

    #Pagination Example

    morm pagination is 1-based (page=1 is the first page):

    let pageable = @morm.pageable_with_sort(1, 20, @morm.desc("id"))

    let q = @morm.select_from("student")
    .where_gte("age", 18)

    let page = @morm.paginate(
    engine,
    q,
    pageable,
    )

    For raw SQL:

    let page = @morm.paginate_raw(
    engine,
    "SELECT id, name, age FROM student WHERE age >= ?",
    [18],
    @morm.pageable(2, 10),
    )

    #Migration Example

    @morm.auto_migrate(engine, [Class::table()])

    Migration execution stays engine-specific by design.

    #Documentation

    See the VitePress docs in docs/:

    Column

    using @oboard/morm/table { type Column }

    ColumnEngineOption

    ColumnType

    DbResult

    type DbResult[T] = Result[T, DbError]

    ForeignKey

    Index

    using @oboard/morm/table { type Index }

    IndexType

    OrderBy

    Set

    using @oboard/morm/engine { type Set }

    Table

    using @oboard/morm/table { type Table }

    TableEngineOption

    Where

    using @oboard/morm/engine { type Where }

    WhereType

    Driver

    pub(open) trait Driver {
    open(Self, dsn : String) -> Result[Self, DbError]
    close(Self) -> Result[Self, DbError]
    ping(Self) -> Result[Unit, DbError]
    exec(Self, sql : String, args : FixedArray[
    Param
    ]) -> Result[Unit, DbError]
    query(Self, sql : String, args : FixedArray[
    Param
    ]) -> Result[FixedArray[Json], DbError]
    }

    Entity

    Association

    pub(all) struct Association {
    name : String
    kind : AssociationKind
    owner_key : String
    target_table : String
    target_key : String
    join_table : String?
    join_owner_key : String?
    join_target_key : String?
    } derive(Eq, ToJson,
    FromJson
    )

    impl Show for Association

    AssociationKind

    pub(all) enum AssociationKind {
    BelongsTo
    HasOne
    HasMany
    ManyToMany
    } derive(Eq, ToJson,
    FromJson
    )

    ColumnDiff

    pub(all) struct ColumnDiff {
    added : FixedArray[
    Column
    ]
    removed : FixedArray[
    Column
    ]
    changed : FixedArray[(String,
    Column
    ,
    Column
    )]
    } derive(Eq, ToJson,
    FromJson
    )

    impl Show for ColumnDiff

    DbError

    pub(all) struct DbError {
    message : String
    } derive(ToJson)

    impl Show for DbError

    DeleteQuery

    impl Show for DeleteQuery

    DeleteQuery::from

    DeleteQuery::where_eq

    fn[V :
    ToParam
    ] DeleteQuery::where_eq(self : DeleteQuery, col : String, value : V) -> DeleteQuery

    DeleteQuery::where_gt

    fn[V :
    ToParam
    ] DeleteQuery::where_gt(self : DeleteQuery, col : String, value : V) -> DeleteQuery

    DeleteQuery::where_gte

    fn[V :
    ToParam
    ] DeleteQuery::where_gte(self : DeleteQuery, col : String, value : V) -> DeleteQuery

    DeleteQuery::where_like

    fn[V :
    ToParam
    ] DeleteQuery::where_like(self : DeleteQuery, col : String, pattern : V) -> DeleteQuery

    DeleteQuery::where_lt

    fn[V :
    ToParam
    ] DeleteQuery::where_lt(self : DeleteQuery, col : String, value : V) -> DeleteQuery

    DeleteQuery::where_lte

    fn[V :
    ToParam
    ] DeleteQuery::where_lte(self : DeleteQuery, col : String, value : V) -> DeleteQuery

    DeleteQuery::where_ne

    fn[V :
    ToParam
    ] DeleteQuery::where_ne(self : DeleteQuery, col : String, value : V) -> DeleteQuery

    ForeignKeyDiff

    IndexDiff

    pub(all) struct IndexDiff {
    added : FixedArray[
    Index
    ]
    removed : FixedArray[
    Index
    ]
    changed : FixedArray[(String,
    Index
    ,
    Index
    )]
    } derive(Eq, ToJson,
    FromJson
    )

    impl Show for IndexDiff

    InsertQuery

    impl Show for InsertQuery

    InsertQuery::columns

    fn InsertQuery::columns(self : InsertQuery, columns : FixedArray[String]) -> InsertQuery

    InsertQuery::from

    InsertQuery::from_many

    fn[E : Entity + ToJson +
    FromParam
    ] InsertQuery::from_many(self : InsertQuery, entities : FixedArray[E]) -> InsertQuery

    InsertQuery::values

    fn InsertQuery::values(self : InsertQuery, values : FixedArray[&
    ToParam
    ]) -> InsertQuery

    InsertQuery::values_many

    fn InsertQuery::values_many(self : InsertQuery, rows : FixedArray[FixedArray[&
    ToParam
    ]]) -> InsertQuery

    PreloadResult

    pub(all) enum PreloadResult[T] {
    One(Map[String, T])
    Many(Map[String, Array[T]])
    }

    Query

    impl Show for Query

    Query::join

    fn Query::join(self : Query, join_sql : String) -> Query

    Query::limit

    fn Query::limit(self : Query, n : Int) -> Query

    Query::offset

    fn Query::offset(self : Query, n : Int) -> Query

    Query::order_by

    fn Query::order_by(self : Query, order_by :
    OrderBy
    ) -> Query

    Query::to_count_sql

    fn Query::to_count_sql(self : Query) -> String

    Query::where_eq

    fn[V :
    ToParam
    ] Query::where_eq(self : Query, col : String, value : V) -> Query

    Query::where_gt

    fn[V :
    ToParam
    ] Query::where_gt(self : Query, col : String, value : V) -> Query

    Query::where_gte

    fn[V :
    ToParam
    ] Query::where_gte(self : Query, col : String, value : V) -> Query

    Query::where_like

    fn[V :
    ToParam
    ] Query::where_like(self : Query, col : String, pattern : V) -> Query

    Query::where_lt

    fn[V :
    ToParam
    ] Query::where_lt(self : Query, col : String, value : V) -> Query

    Query::where_lte

    fn[V :
    ToParam
    ] Query::where_lte(self : Query, col : String, value : V) -> Query

    Query::where_ne

    fn[V :
    ToParam
    ] Query::where_ne(self : Query, col : String, value : V) -> Query

    TableDiff

    pub(all) struct TableDiff {
    columns : ColumnDiff
    indexes : IndexDiff
    foreign_keys : ForeignKeyDiff
    } derive(Eq, ToJson,
    FromJson
    )

    impl Show for TableDiff

    TxBatch

    pub struct TxBatch[E] {
    engine : E
    }

    TxBatch::rollback_to_savepoint

    async fn[E :
    Engine
    ] TxBatch::rollback_to_savepoint(self : TxBatch[E], name : String) ->
    QueryResult

    TxBatch::savepoint

    TxQuery

    impl Show for TxQuery

    UpdateQuery

    impl Show for UpdateQuery

    UpdateQuery::from

    UpdateQuery::set

    fn[V :
    ToParam
    ] UpdateQuery::set(self : UpdateQuery, col : String, value : V) -> UpdateQuery

    UpdateQuery::where_eq

    fn[V :
    ToParam
    ] UpdateQuery::where_eq(self : UpdateQuery, col : String, value : V) -> UpdateQuery

    UpdateQuery::where_gt

    fn[V :
    ToParam
    ] UpdateQuery::where_gt(self : UpdateQuery, col : String, value : V) -> UpdateQuery

    UpdateQuery::where_gte

    fn[V :
    ToParam
    ] UpdateQuery::where_gte(self : UpdateQuery, col : String, value : V) -> UpdateQuery

    UpdateQuery::where_like

    fn[V :
    ToParam
    ] UpdateQuery::where_like(self : UpdateQuery, col : String, pattern : V) -> UpdateQuery

    UpdateQuery::where_lt

    fn[V :
    ToParam
    ] UpdateQuery::where_lt(self : UpdateQuery, col : String, value : V) -> UpdateQuery

    UpdateQuery::where_lte

    fn[V :
    ToParam
    ] UpdateQuery::where_lte(self : UpdateQuery, col : String, value : V) -> UpdateQuery

    UpdateQuery::where_ne

    fn[V :
    ToParam
    ] UpdateQuery::where_ne(self : UpdateQuery, col : String, value : V) -> UpdateQuery

    UpsertQuery

    impl Show for UpsertQuery

    UpsertQuery::do_update_set

    fn UpsertQuery::do_update_set(self : UpsertQuery, col : String, value :
    Param
    ) -> UpsertQuery

    UpsertQuery::from

    UpsertQuery::on_conflict

    fn UpsertQuery::on_conflict(self : UpsertQuery, columns : FixedArray[String]) -> UpsertQuery

    UpsertQuery::set

    fn UpsertQuery::set(self : UpsertQuery, col : String, value :
    Param
    ) -> UpsertQuery

    add_column_engine_option

    fn add_column_engine_option(col :
    Column
    , key : String, value : String) ->
    Column

    add_table_engine_option

    fn add_table_engine_option(table :
    Table
    , key : String, value : String) ->
    Table

    after_create

    fn[T] after_create(entity : T, callback : (T) -> T) -> T

    after_delete

    fn[T] after_delete(entity : T, callback : (T) -> T) -> T

    after_find

    fn[T] after_find(entity : T, callback : (T) -> T) -> T

    after_update

    fn[T] after_update(entity : T, callback : (T) -> T) -> T

    asc

    fn asc(property : String) ->
    Sort

    auto_migrate

    async fn[E :
    Engine
    ] auto_migrate(engine : E, tables : Array[
    Table
    ]) -> Unit

    before_create

    fn[T] before_create(entity : T, callback : (T) -> T) -> T

    Hook callbacks helpers.

    before_delete

    fn[T] before_delete(entity : T, callback : (T) -> T) -> T

    before_update

    fn[T] before_update(entity : T, callback : (T) -> T) -> T

    belongs_to

    fn belongs_to(name : String, owner_key : String, target_table : String, target_key : String) -> Association

    current_plain_date_time_utc

    fn current_plain_date_time_utc() ->
    PlainDateTime

    current_timestamp_utc

    fn current_timestamp_utc() ->
    ZonedDateTime

    delete_from

    fn delete_from(table : String) -> DeleteQuery

    desc

    fn desc(property : String) ->
    Sort

    diff_table

    fn diff_table(old_table :
    Table
    , new_table :
    Table
    ) -> TableDiff

    has_many

    fn has_many(name : String, owner_key : String, target_table : String, target_key : String) -> Association

    has_one

    fn has_one(name : String, owner_key : String, target_table : String, target_key : String) -> Association

    infer_column_type

    fn infer_column_type(value : Json) ->
    ColumnType

    insert_into

    fn insert_into(table : String) -> InsertQuery

    many_to_many

    fn many_to_many(name : String, owner_key : String, target_table : String, target_key : String, join_table : String, join_owner_key : String, join_target_key : String) -> Association

    mysql_table_compression

    fn mysql_table_compression(table :
    Table
    , compression : String) ->
    Table

    mysql_table_key_block_size

    fn mysql_table_key_block_size(table :
    Table
    , key_block_size : Int) ->
    Table

    mysql_table_row_format

    fn mysql_table_row_format(table :
    Table
    , row_format : String) ->
    Table

    new_column

    fn new_column(name : String, column_type :
    ColumnType
    ) ->
    Column

    new_foreign_key

    fn new_foreign_key(name : String, column : String, referenced_table : String, referenced_column : String) ->
    ForeignKey

    new_index

    fn new_index(name : String, columns : FixedArray[String]) ->
    Index

    new_primary_index

    fn new_primary_index(columns : FixedArray[String]) ->
    Index

    new_table

    fn new_table(name : String, columns? : FixedArray[
    Column
    ], engine? : String) ->
    Table

    new_unique_index

    fn new_unique_index(name : String, columns : FixedArray[String]) ->
    Index

    oracle_table_on_commit

    fn oracle_table_on_commit(table :
    Table
    , on_commit : String) ->
    Table

    oracle_table_organization

    fn oracle_table_organization(table :
    Table
    , organization : String) ->
    Table

    page

    fn[T] page(content : FixedArray[T], total_elements : Int, number : Int, size : Int) ->
    Page
    [T]

    pageable

    fn pageable(page : Int, size : Int) ->
    Pageable

    pageable_with_sort

    fn pageable_with_sort(page : Int, size : Int, sort :
    Sort
    ) ->
    Pageable

    paginate_raw

    pg_default_sequence_grant

    fn pg_default_sequence_grant(table :
    Table
    , role : String, privileges : String, with_grant_option : Bool, for_role : String?) ->
    Table

    pg_default_table_grant

    fn pg_default_table_grant(table :
    Table
    , role : String, privileges : String, with_grant_option : Bool, for_role : String?) ->
    Table

    pg_schema_authorization

    fn pg_schema_authorization(table :
    Table
    , role : String) ->
    Table

    pg_schema_grant

    fn pg_schema_grant(table :
    Table
    , role : String, privileges : String, with_grant_option : Bool) ->
    Table

    pg_schema_sequence_grant

    fn pg_schema_sequence_grant(table :
    Table
    , role : String, privileges : String, with_grant_option : Bool) ->
    Table

    pg_sequence_grant

    fn pg_sequence_grant(table :
    Table
    , role : String, privileges : String, with_grant_option : Bool) ->
    Table

    pg_table_grant

    fn pg_table_grant(table :
    Table
    , role : String, privileges : String, with_grant_option : Bool) ->
    Table

    pg_table_on_commit

    fn pg_table_on_commit(table :
    Table
    , on_commit : String) ->
    Table

    pg_table_owner

    fn pg_table_owner(table :
    Table
    , role : String) ->
    Table

    pg_table_unlogged

    pg_table_with

    fn pg_table_with(table :
    Table
    , with_clause : String) ->
    Table

    preload

    async fn[E :
    Engine
    , O : ToJson, T :
    FromParam
    ] preload(engine : E, owners : FixedArray[O], association : Association) -> PreloadResult[T]

    preload_belongs_to

    async fn[E :
    Engine
    , O : ToJson, T :
    FromParam
    ] preload_belongs_to(engine : E, owners : FixedArray[O], owner_key : String, target_table : String, target_key : String) -> Map[String, T]

    preload_has_many

    async fn[E :
    Engine
    , O : ToJson, T :
    FromParam
    ] preload_has_many(engine : E, owners : FixedArray[O], owner_key : String, target_table : String, target_key : String) -> Map[String, Array[T]]

    preload_has_one

    async fn[E :
    Engine
    , O : ToJson, T :
    FromParam
    ] preload_has_one(engine : E, owners : FixedArray[O], owner_key : String, target_table : String, target_key : String) -> Map[String, T]

    preload_many_to_many

    async fn[E :
    Engine
    , O : ToJson, T :
    FromParam
    ] preload_many_to_many(engine : E, owners : FixedArray[O], owner_key : String, target_table : String, target_key : String, join_table : String, join_owner_key : String, join_target_key : String) -> Map[String, Array[T]]

    register_driver_scheme

    fn register_driver_scheme(scheme : String, driver_name : String) -> Unit

    resolve_driver_name

    fn resolve_driver_name(dsn : String) -> String?

    restore_by_id

    fn[V :
    ToParam
    ] restore_by_id(table : String, id_col : String, id : V, deleted_col? : String) -> UpdateQuery

    select_from

    fn select_from(table : String) -> Query

    select_from_scoped

    fn select_from_scoped(table : String, deleted_col? : String) -> Query

    Soft delete default scope (boolean convention): deleted = false

    select_raw

    fn select_raw(table : String, cols_expr : String) -> Query

    soft_delete_by_id

    fn[V :
    ToParam
    ] soft_delete_by_id(table : String, id_col : String, id : V, deleted_col? : String) -> UpdateQuery

    sqlite_table_strict

    sqlite_table_without_rowid

    fn sqlite_table_without_rowid(table :
    Table
    ) ->
    Table

    sqlserver_table_durability

    fn sqlserver_table_durability(table :
    Table
    , durability : String) ->
    Table

    sqlserver_table_filegroup

    fn sqlserver_table_filegroup(table :
    Table
    , filegroup : String) ->
    Table

    sqlserver_table_memory_optimized

    fn sqlserver_table_memory_optimized(table :
    Table
    ) ->
    Table

    sqlserver_table_textimage_on

    fn sqlserver_table_textimage_on(table :
    Table
    , filegroup : String) ->
    Table

    table_catalog

    fn table_catalog(table :
    Table
    , catalog : String) ->
    Table

    table_comment

    fn table_comment(table :
    Table
    , text : String) ->
    Table

    table_create_suffix

    fn table_create_suffix(table :
    Table
    , suffix : String) ->
    Table

    table_engine

    fn table_engine(table :
    Table
    , engine : String) ->
    Table

    table_has_primary_key

    fn table_has_primary_key(table :
    Table
    ) -> Bool

    table_if_not_exists

    fn table_if_not_exists(table :
    Table
    , if_not_exists : Bool) ->
    Table

    table_primary_key

    fn table_primary_key(table :
    Table
    ) -> String?

    table_schema

    fn table_schema(table :
    Table
    , schema : String) ->
    Table

    table_tablespace

    fn table_tablespace(table :
    Table
    , tablespace : String) ->
    Table

    table_temporary

    fn table_temporary(table :
    Table
    , temporary : Bool) ->
    Table

    tx_begin

    fn tx_begin() -> TxQuery

    tx_block

    async fn[E :
    Engine
    ] tx_block(engine : E) -> TxBatch[E]

    tx_commit

    fn tx_commit() -> TxQuery

    tx_rollback

    fn tx_rollback() -> TxQuery

    tx_rollback_to_savepoint

    fn tx_rollback_to_savepoint(name : String) -> TxQuery

    tx_savepoint

    fn tx_savepoint(name : String) -> TxQuery

    unregister_driver_scheme

    fn unregister_driver_scheme(scheme : String) -> Unit

    update

    fn update(table : String) -> UpdateQuery

    upsert_into

    fn upsert_into(table : String) -> UpsertQuery