const sqlite = @import("sqlite"); const std = @import("std"); const DB = @This(); const target_version = 1; sql: sqlite.Db, pub const InlineBotType = enum(u32) { blacklisted = 0, whitelisted = 1, }; pub fn init(db_path: [:0]const u8) !DB { const sql = try sqlite.Db.init(.{ .mode = .{ .File = db_path }, .open_flags = .{ .write = true, .create = true, }, .threading_mode = .MultiThread, }); return DB{ .sql = sql, }; } pub fn deinit(self: *DB) void { self.sql.deinit(); } pub fn getInlineBotType(self: *DB, id: i64) !?InlineBotType { const row = try self.sql.one(u32, "SELECT type FROM inline_bots WHERE id = ?", .{}, .{ .id = id }); if (row) |r| { return @enumFromInt(r); } return null; } pub fn setInlineBotType(self: *DB, id: i64, ty: InlineBotType) !void { try self.sql.exec("INSERT OR REPLACE INTO inline_bots (id, type) VALUES (?, ?)", .{}, .{ id, @intFromEnum(ty) }); } pub fn upgrade(self: *DB) !void { try self.sql.exec("CREATE TABLE IF NOT EXISTS version(id INTEGER PRIMARY KEY, version INTEGER)", .{}, .{}); const row = try self.sql.one(struct { version: u32 }, "SELECT version FROM version WHERE id = 0", .{}, .{}); var current_ver: u32 = if (row) |r| r.version else 0; if (current_ver == target_version) { std.log.info("Database is up to date", .{}); return; } else if (current_ver > target_version) { std.log.err("Database has a higher version than supported?", .{}); return error.CorruptedDatabase; } std.log.info("Updating database from version {} to {}", .{ current_ver, target_version }); var setVerStmt = try self.sql.prepare("INSERT OR REPLACE INTO version(id, version) VALUES (0, ?)"); defer setVerStmt.deinit(); while (current_ver < target_version) : (current_ver += 1) { std.log.info("Updating database step from {}", .{current_ver}); try self.upgradeStep(current_ver + 1); setVerStmt.reset(); try setVerStmt.exec(.{}, .{ current_ver + 1 }); } } fn upgradeStep(self: *DB, new_version: u32) !void { switch (new_version) { 1 => { try self.sql.exec("DROP TABLE IF EXISTS inline_bots_enum", .{}, .{}); try self.sql.exec( \\CREATE TABLE inline_bots_enum ( \\ id INTEGER PRIMARY KEY, \\ value TEXT UNIQUE \\) , .{}, .{}); try self.sql.exec( \\INSERT INTO inline_bots_enum(id, value) \\VALUES (?, 'blacklisted'), (?, 'whitelisted') , .{}, .{ .blacklisted = @intFromEnum(InlineBotType.blacklisted), .whitelisted = @intFromEnum(InlineBotType.whitelisted), }); try self.sql.exec("DROP TABLE IF EXISTS inline_bots", .{}, .{}); try self.sql.exec( \\CREATE TABLE inline_bots ( \\ id INTEGER PRIMARY KEY, \\ type INTEGER REFERENCES inline_bots_enum(id) \\) , .{}, .{}); }, else => unreachable, } }