1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
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,
}
}
|