const std = @import("std"); const zqlite = @import("zqlite"); pub fn init(allocator: std.mem.Allocator, path: [*:0]const u8) !zqlite.Conn { const flags = zqlite.OpenFlags.Create | zqlite.OpenFlags.EXResCode; const conn = try zqlite.open(path, flags); try conn.execNoArgs("PRAGMA foreign_keys = ON"); try conn.execNoArgs("PRAGMA journal_mode = WAL"); try migrate(allocator, conn, .{@embedFile("migrations/01.sql")}); return conn; } fn migrate(allocator: std.mem.Allocator, conn: zqlite.Conn, migrations: [1][]const u8) !void { var version: i64 = 0; if (try conn.row("PRAGMA user_version", .{})) |row| { defer row.deinit(); version = row.int(0); } // Start transaction try conn.transaction(); errdefer conn.rollback(); for (migrations, 1..) |migration, i| { const v: i64 = @intCast(i); if (version < v) { std.debug.print("Applying migration: {d}\n", .{i}); var it = std.mem.splitScalar(u8, migration, ';'); while (it.next()) |statement| { if (!is_statement_empty(statement)) { try conn.exec(statement, .{}); } } try set_version(allocator, conn, @intCast(i)); } } // Commit transaction try conn.commit(); } fn is_statement_empty(str: []const u8) bool { for (str) |c| { if (c == ' ') continue; if (c == '\n') continue; if (c == '\t') continue; return false; } return true; } fn set_version(allocator: std.mem.Allocator, conn: zqlite.Conn, version: i64) !void { const string = try std.fmt.allocPrint( allocator, "PRAGMA user_version = {d}", .{version}, ); defer allocator.free(string); try conn.exec(string, .{}); }