sqlite

libsqlite3 bindings for MoonBit

native
sqlite
js
moon add mizchi/sqlite@0.3.1
Download zip
Author
Version
0.3.1
License
MIT
Last updated
3 months ago
Downloads
8K

Dependencies

README

#sqlite.mbt

SQLite database bindings for MoonBit - supports both native (C FFI) and JavaScript (Node.js) targets.

#Features

  • Database management (open, close)
  • SQL execution
  • Type-safe prepared statements with SqlValue enum
  • Array-based parameter binding with bind_all()
  • Iterator-based query results with iter()
  • Transaction support (BEGIN, COMMIT, ROLLBACK, SAVEPOINT)
  • UTF-8 text encoding
  • Cross-target compatibility (native and js)

#Requirements

#Native target

  • libsqlite3
    • Ubuntu: apt install libsqlite3-dev
    • macOS: Pre-installed

#JavaScript target

  • Node.js 22.5.0+ (requires node:sqlite built-in module)

#Installation

moon add mizchi/sqlite

Add to your moon.pkg.json:

{ "import": ["mizchi/sqlite"], "link": { "native": { "cc-link-flags": "-lsqlite3" } } }

#Usage

let db = match @sqlite.Database::open(":memory:") {
Some(d) => d
None => {
println("Failed to open database")
return
}
}

// Create table
db.exec("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)")

// Insert data with bind_all()
match db.prepare("INSERT INTO users (name, age) VALUES (?, ?)") {
Some(stmt) => {
stmt.bind_all([Text(string_to_bytes("Alice")), Int(30)]) |> ignore
stmt.execute() |> ignore
stmt.finalize()
}
None => println("Failed to prepare statement")
}

// Query with iterator
match db.query("SELECT id, name, age FROM users") {
Some(stmt) => {
for row in stmt.iter() {
let id = row.column_int(0)
let age = row.column_int(2)
println("id=\{id}, age=\{age}")
}
stmt.finalize()
}
None => println("Failed to prepare statement")
}

db.close()

#Build & Test

# Native target moon build --target native moon test --target native # JavaScript target moon build --target js moon test --target js

#API

#High-level API

Database operations:
  • Database::open(path: String) -> Database? - Open database
  • Database::close(self) - Close database
  • Database::exec(self, sql: String) -> Bool - Execute SQL
  • Database::prepare(self, sql: String) -> Statement? - Prepare statement
  • Database::query(self, sql: String) -> Statement? - Prepare SELECT statement

Transaction API:
  • Database::begin(self) -> Bool - Begin transaction
  • Database::begin_immediate(self) -> Bool - Begin immediate transaction
  • Database::begin_exclusive(self) -> Bool - Begin exclusive transaction
  • Database::commit(self) -> Bool - Commit transaction
  • Database::rollback(self) -> Bool - Rollback transaction
  • Database::savepoint(self, name: String) -> Bool - Create savepoint
  • Database::release(self, name: String) -> Bool - Release savepoint
  • Database::rollback_to(self, name: String) -> Bool - Rollback to savepoint

Statement operations:
  • Statement::bind(idx: Int, value: SqlValue) -> Bool - Bind single parameter
  • Statement::bind_all(values: Array[SqlValue]) -> Bool - Bind all parameters at once
  • Statement::execute() -> Bool - Execute INSERT/UPDATE/DELETE
  • Statement::step() -> Bool - Step to next row (for SELECT)
  • Statement::column(col: Int) -> SqlValue - Get column value as SqlValue
  • Statement::column_int(col: Int) -> Int - Get column as Int
  • Statement::column_text(col: Int) -> Bytes - Get column as Bytes
  • Statement::column_count() -> Int - Get number of columns
  • Statement::iter() -> Iter[Statement] - Create iterator for query results
  • Statement::reset() - Reset statement
  • Statement::finalize() - Finalize statement

SqlValue enum:
pub enum SqlValue {
Null
Int(Int)
Int64(Int64)
Double(Double)
Text(Bytes)
Blob(Bytes)
}

#Target Compatibility

APINativeJSNotes
Database::open
Database::close
Database::exec
Database::prepare
Database::query
Database::begin
Database::commit
Database::rollback
Database::savepoint
Database::changesJS uses SELECT changes()
Database::last_insert_rowidJS uses SELECT last_insert_rowid()
Database::total_changesJS uses SELECT total_changes()
Database::errcode⚠️JS always returns 0
Database::errmsg⚠️JS always returns empty
Database::extended_errcode⚠️JS always returns 0
Database::busy_timeout⚠️JS always returns false
Database::get_autocommit⚠️JS always returns true
Statement::bind
Statement::bind_all
Statement::execute
Statement::step
Statement::columnInteger type differs (see below)
Statement::column_int
Statement::column_text
Statement::column_count
Statement::iter
Statement::reset
Statement::finalize

#Known Differences

  1. Integer column type: Native returns Int64, JS returns Int for integer columns via column().

  2. Int64 binding: Node.js SQLite doesn't support BigInt as bind parameter. Int64 values are converted to Number (may lose precision for very large integers).

  3. Error handling: JS target has limited error information. Use try-catch for JS error handling.

  4. Low-level API: Native target exposes low-level C FFI functions (e.g., sqlite_*). These are not available on JS target.

#License

MIT License

#
Database

pub struct Database {
// private fields
}

#
Database::begin

fn Database::begin(self : Database) -> Bool

#
Database::begin_exclusive

fn Database::begin_exclusive(self : Database) -> Bool

#
Database::begin_immediate

fn Database::begin_immediate(self : Database) -> Bool

#
Database::busy_timeout

fn Database::busy_timeout(self : Database, ms : Int) -> Bool

#
Database::changes

fn Database::changes(self : Database) -> Int

#
Database::close

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

#
Database::commit

fn Database::commit(self : Database) -> Bool

#
Database::errcode

fn Database::errcode(self : Database) -> Int

#
Database::errmsg

fn Database::errmsg(self : Database) -> Bytes

#
Database::exec

fn Database::exec(self : Database, sql : String) -> Bool

#
Database::extended_errcode

fn Database::extended_errcode(self : Database) -> Int

#
Database::get_autocommit

fn Database::get_autocommit(self : Database) -> Bool

#
Database::last_insert_rowid

fn Database::last_insert_rowid(self : Database) -> Int64

#
Database::open

fn Database::open(path : String) -> Database?

#
Database::prepare

fn Database::prepare(self : Database, sql : String) -> Statement?

#
Database::query

fn Database::query(self : Database, sql : String) -> Statement?

#
Database::release

fn Database::release(self : Database, name : String) -> Bool

#
Database::rollback

fn Database::rollback(self : Database) -> Bool

#
Database::rollback_to

fn Database::rollback_to(self : Database, name : String) -> Bool

#
Database::savepoint

fn Database::savepoint(self : Database, name : String) -> Bool

#
Database::total_changes

fn Database::total_changes(self : Database) -> Int

#
SqlValue

pub(all) enum SqlValue {
Null
Int(Int)
Int64(Int64)
Double(Double)
Text(Bytes)
Blob(Bytes)
}

#
Sqlite3

type Sqlite3

#
Sqlite3Stmt

type Sqlite3Stmt

#
Statement

pub struct Statement {
// private fields
}

#
Statement::bind

fn Statement::bind(self : Statement, idx : Int, value : SqlValue) -> Bool

#
Statement::bind_all

fn Statement::bind_all(self : Statement, values : Array[SqlValue]) -> Bool

#
Statement::column

fn Statement::column(self : Statement, col : Int) -> SqlValue

#
Statement::column_count

fn Statement::column_count(self : Statement) -> Int

#
Statement::column_int

fn Statement::column_int(self : Statement, col : Int) -> Int

#
Statement::column_name

fn Statement::column_name(self : Statement, col : Int) -> String

Return the declared name of the col-th column in the result set. Returns "" when col is out of range. Mirrors sqlite3_column_name.

#
Statement::column_text

fn Statement::column_text(self : Statement, col : Int) -> Bytes

#
Statement::execute

fn Statement::execute(self : Statement) -> Bool

#
Statement::finalize

fn Statement::finalize(self : Statement) -> Unit

#
Statement::iter

fn Statement::iter(self : Statement) -> Iter[Statement]

#
Statement::reset

fn Statement::reset(self : Statement) -> Unit

#
Statement::step

fn Statement::step(self : Statement) -> Bool

#
SQLITE_BLOB

let SQLITE_BLOB : Int

#
SQLITE_DONE

let SQLITE_DONE : Int

#
SQLITE_FLOAT

let SQLITE_FLOAT : Int

#
SQLITE_INTEGER

let SQLITE_INTEGER : Int

#
SQLITE_LIMIT_ATTACHED

let SQLITE_LIMIT_ATTACHED : Int

#
SQLITE_LIMIT_COLUMN

let SQLITE_LIMIT_COLUMN : Int

#
SQLITE_LIMIT_COMPOUND_SELECT

let SQLITE_LIMIT_COMPOUND_SELECT : Int

#
SQLITE_LIMIT_EXPR_DEPTH

let SQLITE_LIMIT_EXPR_DEPTH : Int

#
SQLITE_LIMIT_FUNCTION_ARG

let SQLITE_LIMIT_FUNCTION_ARG : Int

#
SQLITE_LIMIT_LENGTH

let SQLITE_LIMIT_LENGTH : Int

#
SQLITE_LIMIT_LIKE_PATTERN_LENGTH

let SQLITE_LIMIT_LIKE_PATTERN_LENGTH : Int

#
SQLITE_LIMIT_SQL_LENGTH

let SQLITE_LIMIT_SQL_LENGTH : Int

#
SQLITE_LIMIT_TRIGGER_DEPTH

let SQLITE_LIMIT_TRIGGER_DEPTH : Int

#
SQLITE_LIMIT_VARIABLE_NUMBER

let SQLITE_LIMIT_VARIABLE_NUMBER : Int

#
SQLITE_LIMIT_VDBE_OP

let SQLITE_LIMIT_VDBE_OP : Int

#
SQLITE_NULL

let SQLITE_NULL : Int

#
SQLITE_OPEN_CREATE

let SQLITE_OPEN_CREATE : Int

#
SQLITE_OPEN_FULLMUTEX

let SQLITE_OPEN_FULLMUTEX : Int

#
SQLITE_OPEN_MEMORY

let SQLITE_OPEN_MEMORY : Int

#
SQLITE_OPEN_NOMUTEX

let SQLITE_OPEN_NOMUTEX : Int

#
SQLITE_OPEN_READONLY

let SQLITE_OPEN_READONLY : Int

#
SQLITE_OPEN_READWRITE

let SQLITE_OPEN_READWRITE : Int

#
SQLITE_ROW

let SQLITE_ROW : Int

#
SQLITE_TEXT

let SQLITE_TEXT : Int

#
sqlite_bind_blob

fn sqlite_bind_blob(stmt : Sqlite3Stmt, idx : Int, blob : Bytes) -> Int

#
sqlite_bind_double

fn sqlite_bind_double(stmt : Sqlite3Stmt, idx : Int, value : Double) -> Int

#
sqlite_bind_int

fn sqlite_bind_int(stmt : Sqlite3Stmt, idx : Int, value : Int) -> Int

#
sqlite_bind_int64

fn sqlite_bind_int64(stmt : Sqlite3Stmt, idx : Int, value : Int64) -> Int

#
sqlite_bind_null

fn sqlite_bind_null(stmt : Sqlite3Stmt, idx : Int) -> Int

#
sqlite_bind_parameter_count

fn sqlite_bind_parameter_count(stmt : Sqlite3Stmt) -> Int

#
sqlite_bind_parameter_index

fn sqlite_bind_parameter_index(stmt : Sqlite3Stmt, name : Bytes) -> Int

#
sqlite_bind_parameter_name

fn sqlite_bind_parameter_name(stmt : Sqlite3Stmt, idx : Int) -> Bytes

#
sqlite_bind_text

fn sqlite_bind_text(stmt : Sqlite3Stmt, idx : Int, text : Bytes) -> Int

#
sqlite_busy_timeout

fn sqlite_busy_timeout(db : Sqlite3, ms : Int) -> Int

#
sqlite_changes

fn sqlite_changes(db : Sqlite3) -> Int

#
sqlite_clear_bindings

fn sqlite_clear_bindings(stmt : Sqlite3Stmt) -> Int

#
sqlite_column_blob

fn sqlite_column_blob(stmt : Sqlite3Stmt, col : Int) -> Bytes

#
sqlite_column_bytes

fn sqlite_column_bytes(stmt : Sqlite3Stmt, col : Int) -> Int

#
sqlite_column_count

fn sqlite_column_count(stmt : Sqlite3Stmt) -> Int

#
sqlite_column_double

fn sqlite_column_double(stmt : Sqlite3Stmt, col : Int) -> Double

#
sqlite_column_int

fn sqlite_column_int(stmt : Sqlite3Stmt, col : Int) -> Int

#
sqlite_column_int64

fn sqlite_column_int64(stmt : Sqlite3Stmt, col : Int) -> Int64

#
sqlite_column_name

fn sqlite_column_name(stmt : Sqlite3Stmt, col : Int) -> Bytes

#
sqlite_column_text

fn sqlite_column_text(stmt : Sqlite3Stmt, col : Int) -> Bytes

#
sqlite_column_type

fn sqlite_column_type(stmt : Sqlite3Stmt, col : Int) -> Int

#
sqlite_db_filename

fn sqlite_db_filename(db : Sqlite3, dbname : Bytes) -> Bytes

#
sqlite_db_readonly

fn sqlite_db_readonly(db : Sqlite3, dbname : Bytes) -> Int

#
sqlite_errcode

fn sqlite_errcode(db : Sqlite3) -> Int

#
sqlite_errmsg

fn sqlite_errmsg(db : Sqlite3) -> Bytes

#
sqlite_errstr

fn sqlite_errstr(errcode : Int) -> Bytes

#
sqlite_expanded_sql

fn sqlite_expanded_sql(stmt : Sqlite3Stmt) -> Bytes

#
sqlite_extended_errcode

fn sqlite_extended_errcode(db : Sqlite3) -> Int

#
sqlite_finalize

fn sqlite_finalize(stmt : Sqlite3Stmt) -> Unit

#
sqlite_get_autocommit

fn sqlite_get_autocommit(db : Sqlite3) -> Int

#
sqlite_interrupt

fn sqlite_interrupt(db : Sqlite3) -> Unit

#
sqlite_last_insert_rowid

fn sqlite_last_insert_rowid(db : Sqlite3) -> Int64

#
sqlite_limit

fn sqlite_limit(db : Sqlite3, id : Int, newVal : Int) -> Int

#
sqlite_open_v2

fn sqlite_open_v2(filename : Bytes, flags : Int, vfs : Bytes) -> Sqlite3

#
sqlite_prepare

fn sqlite_prepare(db : Sqlite3, sql : Bytes) -> Sqlite3Stmt

#
sqlite_sql

fn sqlite_sql(stmt : Sqlite3Stmt) -> Bytes

#
sqlite_step

fn sqlite_step(stmt : Sqlite3Stmt) -> Int

#
sqlite_stmt_is_null

fn sqlite_stmt_is_null(stmt : Sqlite3Stmt) -> Int

#
sqlite_stmt_readonly

fn sqlite_stmt_readonly(stmt : Sqlite3Stmt) -> Int

#
sqlite_total_changes

fn sqlite_total_changes(db : Sqlite3) -> Int