summaryrefslogtreecommitdiff
path: root/src/DB.zig
blob: 6510e7cbf573cd774674cf3ac324ebfb120ddb8f (plain) (blame)
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,
    }
}