aboutsummaryrefslogtreecommitdiff
path: root/src/sqlite.zig
blob: e0668cc84108dfc2b2cce719d1879adef6706f3e (plain)
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
101
const std = @import("std");
const sqlite = @cImport({
    @cInclude("sqlite3.h");
});
const Db = @import("main.zig");
const OpenError = Db.OpenError;
const PrepareError = Db.PrepareError;
const StepError = Db.StepError;
const ColumnError = Db.ColumnError;
const log = @import("log.zig").scoped_log_t(.sqlite);

//// Sqlite implementation
pub const Sqlite = @This();

allocator: std.mem.Allocator,
c_db: *sqlite.sqlite3,

pub fn open(allocator: std.mem.Allocator, filename: [:0]const u8) OpenError!Db {
    var db: ?*sqlite.sqlite3 = null;
    const oo = sqlite.SQLITE_OPEN_CREATE | sqlite.SQLITE_OPEN_READWRITE | sqlite.SQLITE_OPEN_FULLMUTEX;
    if (sqlite.sqlite3_open_v2(filename.ptr, &db, oo, null) != sqlite.SQLITE_OK) {
        log.err("Sqlite#open: sqlite3_open_v2 error {s}", .{sqlite.sqlite3_errmsg(db)});
        return OpenError.Failed;
    }
    var sqlite_db = try allocator.create(Sqlite);
    sqlite_db.allocator = allocator;
    sqlite_db.c_db = db.?;
    return Db{
        .ptr = sqlite_db,
        .vtable = .{
            .prepare = prepare,
            .step = step,
            .column_i64 = column_i64,
            .column_slice_const_u8 = column_slice_const_u8,
            .close_stmt = close_stmt,
            .close_db = close_db,
        },
    };
}

fn prepare(db: *anyopaque, query: [:0]const u8) PrepareError!*anyopaque {
    var self: *Sqlite = @alignCast(@ptrCast(db));
    var sstmt: ?*sqlite.sqlite3_stmt = null;
    if (sqlite.sqlite3_prepare_v2(self.c_db, query.ptr, @intCast(query.len), &sstmt, null) != sqlite.SQLITE_OK) {
        log.err("Sqlite#prepare: sqlite3_prepare_v2: {s}", .{sqlite.sqlite3_errmsg(self.c_db)});
        return PrepareError.Failed;
    }
    return sstmt.?;
}

fn step(db: *anyopaque, stmt: *anyopaque) StepError!bool {
    var self: *Sqlite = @alignCast(@ptrCast(db));
    var sstmt: *sqlite.sqlite3_stmt = @alignCast(@ptrCast(stmt));
    const res = sqlite.sqlite3_step(sstmt);
    if (res == sqlite.SQLITE_ROW) {
        return true;
    } else if (res == sqlite.SQLITE_DONE) {
        return false;
    } else {
        log.err("Sqlite#step: sqlite3_step: {s}", .{sqlite.sqlite3_errmsg(self.c_db)});
        return StepError.Failed;
    }
}

fn column_i64(db: *anyopaque, stmt: *anyopaque, idx: u31) ColumnError!?i64 {
    _ = db;
    var sstmt: *sqlite.sqlite3_stmt = @alignCast(@ptrCast(stmt));
    const ct = sqlite.sqlite3_column_type(sstmt, idx);
    if (ct == sqlite.SQLITE_NULL) {
        return null;
    } else if (ct == sqlite.SQLITE_INTEGER) {
        return sqlite.sqlite3_column_int64(sstmt, idx);
    } else {
        return ColumnError.WrongType;
    }
}
fn column_slice_const_u8(db: *anyopaque, stmt: *anyopaque, idx: u31) ColumnError!?[:0]const u8 {
    _ = db;
    var sstmt: *sqlite.sqlite3_stmt = @alignCast(@ptrCast(stmt));
    const ct = sqlite.sqlite3_column_type(sstmt, idx);
    if (ct == sqlite.SQLITE_NULL) {
        return null;
    } else if (ct == sqlite.SQLITE_TEXT) {
        const value_c = sqlite.sqlite3_column_text(sstmt, idx);
        return std.mem.sliceTo(value_c, 0);
    } else {
        return ColumnError.WrongType;
    }
}

fn close_stmt(db: *anyopaque, stmt: *anyopaque) void {
    _ = db;
    var sstmt: *sqlite.sqlite3_stmt = @alignCast(@ptrCast(stmt));
    _ = sqlite.sqlite3_finalize(sstmt);
}

fn close_db(db: *anyopaque) void {
    var self: *Sqlite = @alignCast(@ptrCast(db));
    _ = sqlite.sqlite3_close_v2(self.c_db);
    self.allocator.destroy(self);
}