aboutsummaryrefslogtreecommitdiff
path: root/backend/src/repos/repos.zig
diff options
context:
space:
mode:
Diffstat (limited to 'backend/src/repos/repos.zig')
-rw-r--r--backend/src/repos/repos.zig62
1 files changed, 62 insertions, 0 deletions
diff --git a/backend/src/repos/repos.zig b/backend/src/repos/repos.zig
new file mode 100644
index 0000000..860811d
--- /dev/null
+++ b/backend/src/repos/repos.zig
@@ -0,0 +1,62 @@
+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, .{});
+}