ORM API¶
Driver Interface (Multi-Database)¶
Zypher supports multiple database backends through a vtable-by-value interface. All relational operations use RelationalDb and AnyStmt regardless of the underlying driver.
Interface Types (orm.driver.interface / orm.*)¶
RelationalDb— vtable wrapper withprepare,exec,close,lastInsertId,dialectAnyStmt— vtable wrapper withbind,step,column,columnType,reset,finalizeValueunion:int: i64,float: f64,text: []const u8,null: voidColumnTypeenum:integer,float,text,blob,nullDialect— dialect metadata:name,placeholder,pk_type,float_type,bool_type,text_type,timestamp_nowDbErrorerror set:OpenFailed,ExecFailed,PrepareFailed,StepFailed,BindFailed,ColumnFailed,ConstraintViolation,UnexpectedResult
SQLite Driver (orm.driver.sqlite)¶
SqliteDb.open(gpa, path) SqliteDb— open a SQLite databaseSqliteDb.asRelationalDb() RelationalDb— wrap in vtable
PostgreSQL Driver (orm.driver.postgres) [optional, -Ddb_postgres=true]¶
PostgresDb.open(gpa, connstr) PostgresDb— open via libpqPostgresDb.asRelationalDb() RelationalDb— wrap in vtable
MySQL Driver (orm.driver.mysql) [optional, -Ddb_mysql=true]¶
MysqlDb.open(gpa, config) MysqlDb— open via libmysqlclientMysqlDb.asRelationalDb() RelationalDb— wrap in vtable
Database Configuration API (orm.config / orm.*)¶
SqliteConfig,PostgresConfig,MysqlConfig,MongoConfig,RedisConfigDatabaseConfigtagged union:{ .sqlite = ... },{ .postgres = ... }, etc.openDatabase(gpa, cfg) OpenResult— open any backend from a config unionOpenResultcontainsopen_db: OpenDb(cleanup handle) + typed wrappersDriverNotEnablederror returned for disabled backends
Convenience re-exports at orm.*:
| Type | Path |
|---|---|
orm.RelationalDb |
orm.driver.interface.RelationalDb |
orm.DocumentStore |
orm.document.interface.DocumentStore |
orm.KVStore |
orm.kv.interface.KVStore |
orm.Value |
orm.driver.interface.Value |
orm.Dialect |
orm.driver.interface.Dialect |
orm.DatabaseConfig |
orm.config.DatabaseConfig |
orm.openDatabase |
orm.config.openDatabase |
Schema¶
Comptime model and field definition.
FieldKind¶
Enum: integer, float, text, boolean
FieldOptions¶
primary: bool = false— primary key fieldrequired: bool = false— NOT NULLunique: bool = false— UNIQUE constraintforeign: ?[:0]const u8 = null— FOREIGN KEY reference (e.g."users(id)")default: ?DefaultValue = null— DEFAULT value
Field(name, kind, opts) FieldDef¶
Comptime field constructor. Returns a FieldDef:
- name: [:0]const u8
- kind: FieldKind
- primary: bool
- required: bool
- unique: bool
- foreign: ?[:0]const u8
- default: ?DefaultValue
Model(table, Fields) type¶
Define a model type from a table name and a struct of FieldDef values.
Generated comptime constants:
- model.table_name — table name
- model.fields_len — number of fields
- model.insert_field_count — number of non-primary-key fields (for INSERT)
- model.fieldAt(i) FieldDef — get field definition by index
- model.create_table_sql — CREATE TABLE IF NOT EXISTS ... SQL
- model.drop_table_sql — DROP TABLE IF EXISTS ... SQL
- model.insert_sql — INSERT INTO ... (cols) VALUES (?, ...) SQL
- model.select_all_sql — SELECT col1, col2, ... FROM table SQL
- model.select_by_id_sql — SELECT ... FROM table WHERE id = ? SQL
- model.update_by_id_sql — UPDATE table SET col1 = ?, ... WHERE id = ? SQL
- model.delete_by_id_sql — DELETE FROM table WHERE id = ? SQL
Query¶
Runtime query execution using comptime-generated SQL.
RowType(M) type¶
Returns a tuple type matching the model's fields: i64 for integer, f64 for float, []const u8 for text, bool for boolean.
freeRow(M, gpa, row) void¶
Free owned (text) memory in a row tuple.
CRUD Operations¶
create(M, db, values) QueryError!i64— INSERT a record; returns rowidgetById(M, db, gpa, id) QueryError!RowType(M)— SELECT by primary key; returnserror.NotFoundif not foundall(M, db, gpa) QueryError!ArrayList(RowType(M))— SELECT all rowsupdateById(M, db, id, values) QueryError!void— UPDATE by primary keydeleteById(M, db, id) QueryError!void— DELETE by primary keysave(M, db, gpa, row) QueryError!i64— INSERT or UPDATE; ifrow[0](id) is 0, inserts and updatesrow[0]; otherwise updates
Query Operations¶
filter(M, db, gpa, where, values) QueryError!ArrayList(RowType(M))— SELECT with WHERE clause; values are bound as parameters (SQL injection safe)filterLimitOffset(M, db, gpa, where, values, limit, offset) QueryError!ArrayList(RowType(M))— paginated filtered queryfilterOrderLimitOffset(M, db, gpa, where, values, order_by, limit, offset) QueryError!ArrayList(RowType(M))— ordered paginated filtered queryorder(M, db, gpa, order_by) QueryError!ArrayList(RowType(M))— ORDER BY (standalone)first(M, db, gpa, where, values) QueryError!?RowType(M)— first matching row or nullcount(M, db) QueryError!u64— COUNT all rows
QuerySet Builder¶
Chainable builder for complex queries:
var qs = QuerySet(Model).init(&db, gpa);
defer qs.deinit();
var results = try qs.filterBy("name LIKE ?", &.{.{ .text = "%foo%" }})
.orderBy("name ASC")
.limit(10)
.offset(0)
.exec();
Methods:
- qs.init(db, gpa) Self — create a QuerySet
- qs.deinit() — free where values
- qs.filterBy(where, values) *Self — set WHERE clause and values
- qs.orderBy(order_by) *Self — set ORDER BY clause
- qs.limit(n) *Self — set LIMIT
- qs.offset(n) *Self — set OFFSET
- qs.exec() QueryError!ArrayList(RowType(M)) — execute the built query
QueryError¶
Error set: NotFound, NoRows, BindFailed, ExecFailed, PrepareFailed, StepFailed, ColumnFailed, AllocatorFailed
Migration¶
Database schema migration runner.
Migration¶
id: i64— unique migration IDname: [:0]const u8— migration nameup_sql: [:0]const u8— forward SQLdown_sql: [:0]const u8— rollback SQL
MigrationStatus¶
id: i64— migration IDname: [:0]const u8— migration nameapplied: bool— whether migration has been applied
MigrationRunner¶
MigrationRunner.init(db) MigrationRunner— create a runnerrunner.ensureHistoryTable() MigrationError!void— createzypher_migrationstracking tablerunner.countApplied() MigrationError!u64— count applied migrationsrunner.migrate(migrations, io) MigrationError!void— apply all pending migrations in orderrunner.status(gpa, migrations) MigrationError![]MigrationStatus— return migration application statusrunner.rollback(migrations, n) MigrationError!void— roll back the most recentnapplied migrations (in reverse order) that havedown_sql
MigrationError¶
Error set: PrepareFailed, StepFailed, ExecFailed, BindFailed, ColumnFailed, AllocatorFailed, NoMigrationsToRollback
Full Example¶
SQLite (via config API)¶
const std = @import("std");
const zypher = @import("zypher");
const UserModel = zypher.orm.schema.Model("users", .{
zypher.orm.schema.Field("id", .integer, .{ .primary = true }),
zypher.orm.schema.Field("name", .text, .{ .required = true }),
zypher.orm.schema.Field("age", .integer, .{}),
zypher.orm.schema.Field("active", .boolean, .{ .default = .{ .boolean = true } }),
});
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const alloc = gpa.allocator();
// Open via config API (supports sqlite, postgres, mysql)
var result = try zypher.orm.openDatabase(alloc, .{
.sqlite = .{ .path = "app.db" },
});
defer result.open_db.close(alloc);
const db = result.relational orelse return error.NoDatabase;
try db.exec(UserModel.create_table_sql);
// Insert a record
const id = try zypher.orm.query.create(UserModel, db, &.{
.{ .text = "Alice" },
.{ .int = 30 },
.{ .int = 1 },
});
// Fetch by ID
const row = try zypher.orm.query.getById(UserModel, db, alloc, id);
defer zypher.orm.query.freeRow(UserModel, alloc, &row);
// row[0] == id, row[1] == "Alice", row[2] == 30, row[3] == true
}
PostgreSQL¶
var result = try zypher.orm.openDatabase(alloc, .{
.postgres = .{ .connstr = "postgresql://user:pass@localhost/mydb" },
});
defer result.open_db.close(alloc);
const db = result.relational orelse return error.NoDatabase;