Storage and ORM
OxL has three levels of storage (§12.8): the built-in replicated storage of the cluster nodes (Raft, shards), the
transactional KV std.kv, and ORM tables std.orm over SQLite or PostgreSQL. The data of a node
lives in the --data-dir directory.
Tables#
A data with @table("name") is a table; @pk is the primary key, @auto means the database assigns the value. Queries are
written in the language itself: from n in db.Note order by n.id select n.id (§5.9); the compiler turns them into SQL
and checks the types.
use std.orm { Db }
@table("notes")
data Note {
@pk @auto id: i64
text: str
}
fn main() io, db {
let db = Db.open("sqlite::memory:")!
db.insert(Note{ id: 0, text: "first" })
let a = db.lastInsertId()
db.insert(Note{ id: 0, text: "second" })
let b = db.lastInsertId()
print("{a} {b}")
print("{db.find(Note, 2)!.text} {db.count(Note)}")
let ids = from n in db.Note order by n.id select n.id
print("{ids}")
}1 2 second 2 [1, 2]
The schema changes through migrations: oxl db keeps oxl.db.lock and the files migrations/NNNN_name.up.sql /
.down.sql (§12.8). atomic(db) { … } groups several writes into one transaction (§11.2); a conflict
restarts the body, and emit events are published only after the commit.
Key-value#
Kv.open("name") opens a named key space; values are bytes, every key has a monotonic version, so compare-and-set (putIf),
prefix scans (scan) and subscriptions (watch) are available.
use std.kv { Kv }
use std.core { Utf8 }
fn main() io, db {
let kv = Kv.open("tour")!
kv.put("a/1", Utf8.encode("one"))!
kv.put("a/2", Utf8.encode("two"))!
kv.put("b/1", Utf8.encode("other"))!
let page = kv.scan("a/", 0)!
var keys: List<str> = []
for e in page { keys.push(e.key) }
print("{keys}")
let v = kv.version("a/1")!
print("{kv.putIf("a/1", Utf8.encode("uno"), v + 100)!} {kv.putIf("a/1", Utf8.encode("uno"), v)!}")
print("{kv.putIf("fresh", Utf8.encode("x"), 0)!} {kv.putIf("fresh", Utf8.encode("y"), 0)!}")
kv.delete("a/2")!
print("{kv.scan("a/", 0)!.len()}")
kv.close()
}["a/1", "a/2"] false true true false 1
Db.open(url) understands sqlite:…, postgres://… and builtin://name, a table in the built-in storage of the cluster: every
replica of a shard has its own SQLite database and Raft replicates the row changes (§12.8). The same code works with all
URLs; the URL usually comes from the configuration.