///|
/// The declarative `Model` as the CRUD unit: `create_table_sql`, `column_names`,
/// `table_descriptor`, `insert_of` (record -> bound `Insert`), and `Model::select`
/// (every column projected in a fixed order `from_row` expects). Then a full round trip
/// through a `Session` over `@moondb.MockDriver` — `create_table`, `insert_record`,
/// `all`, `fetch_as` — decoding stored rows back into records via the model's mapping.
///
/// moon run examples/11-model-crud
struct Hero {
id : Int32
name : String
power : Int32
}
///|
let heroes : @moonorm.Model[Hero] = @moonorm.Model::new(
"heroes",
[
@moonorm.column("id", IntType, primary_key=true),
@moonorm.column("name", TextType, nullable=false),
@moonorm.column("power", IntType),
],
r => { id: r.int(0), name: r.text(1), power: r.int(2), },
h => {
[
("id", @moondb.Int(h.id)),
("name", @moondb.Text(h.name)),
("power", @moondb.Int(h.power)),
]
},
)
///|
fn render(v : @moondb.Value) -> String {
match v {
Null => "Null"
Bool(b) => "Bool(\{b})"
Int(n) => "Int(\{n})"
Int64(n) => "Int64(\{n})"
Double(d) => "Double(\{d})"
Text(s) => "Text(\"\{s}\")"
Blob(b) => "Blob(\{b.length()} bytes)"
Time(s) => "Time(\"\{s}\")"
Numeric(s) => "Numeric(\{s})"
Uuid(b) => "Uuid(\{b.length()} bytes)"
Json(s) => "Json(\{s})"
Array(items) => "Array(\{items.length()} items)"
}
}
///|
fn params_str(vals : Array[@moondb.Value]) -> String {
"[" + vals.map(render).join(", ") + "]"
}
///|
#coverage.skip
fn main {
println("-- create_table_sql")
println(heroes.create_table_sql(if_not_exists=true))
println("-- column_names: " + heroes.column_names().join(", "))
let td = heroes.table_descriptor()
println("-- table_descriptor: \{td.name} (" + td.columns.join(", ") + ")")
// insert_of turns a record into an Insert; values bind as placeholders.
let (isql, iparams) = heroes
.insert_of({ id: 1, name: "lfanke", power: 233, })
.build()
println("")
println("-- insert_of")
println(isql)
println("params: " + params_str(iparams))
// Model::select projects every declared column, refinable like any Select.
let (ssql, sparams) = heroes
.select()
.where_("power", ">", @moondb.Int(100))
.build()
println("")
println("-- Model::select refined")
println(ssql)
println("params: " + params_str(sparams))
// create_table issues the DDL; over MockDriver it returns a one-row ExecResult.
try {
let drv = @moondb.MockDriver::new()
let sess = @moonorm.Session::new(drv)
let ddl = sess.create_table(heroes, if_not_exists=true)
println("")
println("-- Session.create_table rows_affected=\{ddl.rows_affected}")
sess.close()
} catch {
e => println("db error: " + e.to_string())
}
// A clean session for the round trip (MockDriver echoes every stored row, so keep
// the DDL echo out of the read-back store).
try {
let drv = @moondb.MockDriver::new()
let sess = @moonorm.Session::new(drv)
let roster : Array[Hero] = [
{ id: 1, name: "lfanke", power: 233, },
{ id: 233, name: "heke1228", power: 23333, },
{ id: 66666, name: "qwq", power: 66666, },
]
for h in roster {
sess.insert_record(heroes, h) |> ignore
}
println("")
println("-- Session.all (decoded records)")
for h in sess.all(heroes) {
println(" #\{h.id} \{h.name} power=\{h.power}")
}
let some = sess.fetch_as(heroes, heroes.select())
println("-- fetch_as returned \{some.length()} records")
// fetch returns raw rows (undecoded) for the same Select.
let raw = sess.fetch(heroes.select())
println(
"-- fetch returned \{raw.length()} raw rows, first width=\{raw[0].width()}",
)
sess.close()
} catch {
e => println("db error: " + e.to_string())
}
}