mysql

    Typed async MySQL pool for native MoonBit using Connector/C workers

    Download zip
    Author
    Version
    0.3.0
    License
    Apache-2.0
    Last updated
    yesterday
    Downloads
    34

    #Hosi121/mysql

    Native MySQL adapter for the shared SQL contract, using one MariaDB Connector/C worker per physical connection. It imports Hosi121/sql_session and async, with no dependency on PostgreSQL, WebSocket, an application or Node.

    Version 0.3.0 uses Hosi121/sql_session for lifetime management. After registry publication: moon add Hosi121/mysql. Import "Hosi121/mysql" @mysql and, when naming the returned database type, "Hosi121/sql_session" @sql in moon.pkg. Apache-2.0; Connector/C retains its own license.

    The consumer executable needs Connector/C development files and:

    options(link: { "native": { "cc-link-flags": "-lmariadb -lpthread" } })

    The pinned moon does not propagate executable link flags from a library.

    #API and values

    let pool = @mysql.Pool::new(
    host="127.0.0.1", user="app", password="password", database="app", size=4,
    )
    let db = pool.database()
    defer db.close()
    let rows = db.query("SELECT name FROM users WHERE id=?", params=[Integer(42L)])
    for row in rows {
    if row.get("name") is Some(Text(name)) { println(name) }
    }

    pool.database() returns @sql.Database[Value, Row, Command, TransactionOptions]. Use db.with_transaction(@mysql.TransactionOptions::new(), callback) for an interactive transaction. Options expose typed isolation and optional read-only mode. SQL remains MySQL SQL, including DDL's implicit-commit behavior.

    • Value: Null, Text(String), Integer(Int64), Unsigned(UInt64), Float(Double), Decimal(String), Blob(Bytes). Native integer binds preserve all 64 bits. Decimal parameters bind decimal text; date/time results are text.
    • Row.columns and Row.values are parallel, ordered arrays. get_at(index) returns an optional value. get(name) returns None for a missing column and raises @sql.AmbiguousColumn for duplicates; SQL NULL is Some(Null).
    • Command retains has_rows, affected_rows: UInt64, insert_id: UInt64. The default found_rows=true means UPDATE reports matched rows; set false for changed-row semantics. These are not database-neutral insert/count semantics.
    • For binary expressions use a binary column or CAST(? AS BINARY); types follow server metadata. Null and embedded NUL bytes in values are preserved.
    • Legacy Pool.query and Pool.transaction(Array[Statement]) remain conveniences over the shared API, returning QueryResult; the latter returns the last statement's result. Their old row-map/Closed-error behavior is not preserved.

    #Ownership and limits

    Workers touch only copied malloc memory and Connector/C handles. Completion is observed through a pipe; no MoonBit-managed memory crosses into foreign threads. The application uses one async event loop and closes each database it creates.

    Each lease is reset with mysql_reset_connection before reuse, then its configured charset/timezone are reapplied. Temporary tables, user variables and unmanaged transactions cannot leak between borrowers. A worker keeps its physical connection through the whole callback, including interactive transactions. A SQL error closes that physical connection; the failed shared session rejects further operations and never silently reconnects inside a transaction.

    Cancellation drains submitted work; it does not interrupt SQL. Timeout return can therefore be delayed. Callback cancellation rolls back before returning the connection. Closing rejects queued/new requests, while active leases retain their workers until cleanup. db.close_and_wait() observes completion.

    Defaults: 10 connections, 128 waiting requests, 5-second checkout and Connector/C connect/read/write timeouts, 10,000 result rows and 16 MiB value payload. Payload limits exclude metadata and allocation overhead. Allocation failure in the C stub currently aborts. There are no automatic retries of uncertain writes.

    charset supports utf8mb4 (default), utf8mb3 or utf8, matching the Unicode text codec. time_zone defaults to +00:00 and accepts MySQL timezone names/offsets. found_rows defaults to true. These are explicit MySQL settings, not policies in the shared SQL module. Multi-statements and LOCAL INFILE remain disabled.

    Driver errors retain InvalidConfig, InvalidParameter, ServerError(Int), CompletionLost, WorkerUnavailable and ResultTooLarge. The shared module supplies admission/lifetime/cleanup errors and retains underlying causes. No HTTP status or automatic retry policy is assigned here.

    Connection setup is lazy. A nonempty ssl_ca requires TLS and certificate verification; otherwise Connector/C's default TLS policy applies. plugin_dir configures authentication plugins. No environment variables are loaded by the library, and local tests do not exercise TLS.

    DatabaseError

    pub(all) suberror DatabaseError {
    InvalidConfig(String)
    InvalidParameter
    ServerError(Int)
    CompletionLost
    WorkerUnavailable
    ResultTooLarge
    } derive(
    Debug
    )

    Command

    pub struct Command {
    has_rows : Bool
    affected_rows : UInt64
    insert_id : UInt64
    }

    Isolation

    pub(all) enum Isolation {
    ReadUncommitted
    ReadCommitted
    RepeatableRead
    Serializable
    }

    Pool

    pub struct Pool {
    // private fields
    }

    Pool::close

    fn Pool::close(self : Pool) -> Unit

    Pool::new

    async fn Pool::new(host~ : String, user~ : String, password~ : String, database~ : String, port? : Int, size? : Int, ssl_ca? : String, plugin_dir? : String, timeout_seconds? : Int, max_rows? : Int, max_bytes? : Int, max_waiters? : Int, checkout_timeout_ms? : Int, charset? : String, time_zone? : String, found_rows? : Bool) -> Pool

    Physical connections are established on foreign workers. charset, time_zone and CLIENT_FOUND_ROWS are explicit MySQL policies, reapplied after recycling.

    Pool::query

    async fn Pool::query(self : Pool, sql : String, params? : Array[Value]) -> QueryResult

    Compatibility helper. New consumers can use database().query/execute/run.

    Pool::transaction

    async fn Pool::transaction(self : Pool, statements : Array[Statement]) -> QueryResult

    QueryResult

    pub struct QueryResult {
    rows : Array[Row]
    has_rows : Bool
    affected_rows : UInt64
    insert_id : UInt64
    }

    Row

    pub struct Row {
    columns : Array[String]
    values : Array[Value]
    }

    Row::get

    fn Row::get(self : Row, name : String) -> Value? raise

    Row::get_at

    fn Row::get_at(self : Row, index : Int) -> Value?

    Statement

    pub(all) struct Statement {
    sql : String
    params : Array[Value]
    }

    TransactionOptions

    pub(all) struct TransactionOptions {
    isolation : Isolation?
    read_only : Bool?
    }

    TransactionOptions::new

    fn TransactionOptions::new(isolation? : Isolation, read_only? : Bool) -> TransactionOptions

    Value

    pub(all) enum Value {
    Null
    Text(String)
    Integer(Int64)
    Unsigned(UInt64)
    Float(Double)
    Decimal(String)
    Blob(Bytes)
    } derive(Eq,
    Debug
    )

    Integers, decimals and binary data never round-trip through Double/JSON.

    Value::equal

    fn Value::equal(Value, Value) -> Bool

    Value::not_equal

    fn Value::not_equal(x : Value, y : Value) -> Bool

    Value::to_repr

    statement

    fn statement(sql : String, params? : Array[Value]) -> Statement

    Powered by MoonBit

    Site sourceReport issuePackagesBuild queueSkillsStatistics

    © 2026 mooncakes.io