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); }