diff options
Diffstat (limited to 'src/db/sqlite.zig')
| -rw-r--r-- | src/db/sqlite.zig | 118 |
1 files changed, 72 insertions, 46 deletions
diff --git a/src/db/sqlite.zig b/src/db/sqlite.zig index 1e45537..8b2c42c 100644 --- a/src/db/sqlite.zig +++ b/src/db/sqlite.zig @@ -23,6 +23,20 @@ pub const Database = struct { path: []const u8, }; + pub const Query = struct { + handle: *sqlite.sqlite3_stmt, + + // https://sqlite.org/c3ref/finalize.html + pub fn deinit(self: *Query) void { + const result: Result = .from(sqlite.sqlite3_finalize(self.handle)); + + // TODO: maybe don't panic? + if (!result.isOk()) @panic("Last error was " ++ @errorName(result.asError())); + + self.* = undefined; + } + }; + // Source: https://sqlite.org/c3ref/open.html pub fn init(kind: Kind) Error!Database { const name = switch (kind) { @@ -34,82 +48,68 @@ pub const Database = struct { const result: Result = .from(sqlite.sqlite3_open(name.ptr, &handle)); - switch (result.primary()) { - .ok => {}, - inline else => |code| return @field(anyerror, @tagName(code)), - } + return if (result.isOk()) .{ .handle = handle.? } else result.asError(handle); + } + + // https://sqlite.org/c3ref/close.html + pub fn deinit(self: *Database) void { + const result: Result = .from(sqlite.sqlite3_close(self.handle)); + + // TODO: check if we can loop over the unfinalised objects and commit them + if (!result.isOk()) @panic("There are still unfinalised objects"); - return .{ .handle = handle.? }; + self.* = undefined; + } + + // https://sqlite.org/c3ref/prepare.html + pub fn query(self: *Database, statement: []const u8) Error!Query { + var handle: ?*sqlite.sqlite3_stmt = null; + var unused: [*c]const u8 = null; + + const length: c_int = @truncate(@as(isize, @intCast(statement.len))); + + const result: Result = .from(sqlite.sqlite3_prepare_v2(self.handle, statement.ptr, length, &handle, &unused)); + + return if (result.isOk()) .{ .handle = handle.? } else result.asError(self.handle); } }; // Source: https://sqlite.org/rescode.html -pub const Result = enum(i32) { +const Result = enum(i32) { _, // zig fmt: off - pub const Primary = enum(u8) { - /// Successful result. + const Primary = enum(u8) { ok = 0, - /// Generic error. @"error" = 1, - /// Internal logic error in SQLite. internal = 2, - /// Access permission denied. perm = 3, - /// Callback routine requested an abort. abort = 4, - /// The database file is locked. busy = 5, - /// A table in the database is locked. locked = 6, - /// A malloc() failed. nomem = 7, - /// Attempt to write a readonly database. readonly = 8, - /// Operation terminated by sqlite3_interrupt(). interrupt = 9, - /// Some kind of disk I/O error occurred. ioerr = 10, - /// The database disk image is malformed. corrupt = 11, - /// Unknown opcode in sqlite3_file_control(). notfound = 12, - /// Insertion failed because database is full. full = 13, - /// Unable to open the database file. cantopen = 14, - /// Database lock protocol error. protocol = 15, - /// Internal use only. empty = 16, - /// The database schema changed. schema = 17, - /// String or BLOB exceeds size limit. toobig = 18, - /// Abort due to constraint violation. constraint = 19, - /// Data type mismatch. mismatch = 20, - /// Library used incorrectly. misuse = 21, - /// Uses OS features not supported on host. nolfs = 22, - /// Authorization denied. auth = 23, - /// Not used. format = 24, - /// 2nd parameter to sqlite3_bind out of range. range = 25, - /// File opened that is not a database file. notadb = 26, - /// Notifications from sqlite3_log(). notice = 27, - /// Warnings from sqlite3_log(). warning = 28, - /// sqlite3_step() has another row ready. row = 100, - /// sqlite3_step() has finished executing. done = 101, fn as(self: Primary, comptime T: type) T { @@ -118,7 +118,7 @@ pub const Result = enum(i32) { }; // zig fmt: off - pub const Extended = enum(i32) { + const Extended = enum(i32) { error_missing_collseq = (Primary.@"error".as(i32) | ( 1 << 8)), error_retry = (Primary.@"error".as(i32) | ( 2 << 8)), error_snapshot = (Primary.@"error".as(i32) | ( 3 << 8)), @@ -201,21 +201,47 @@ pub const Result = enum(i32) { ok_load_permanently = (Primary.ok.as(i32) | ( 1 << 8)), }; - pub fn from(value: anytype) Result { + fn from(value: anytype) Result { return @enumFromInt(value); } - pub fn primary(self: Result) Primary { - return @enumFromInt(0x000000FF & @intFromEnum(self)); + fn isOk(self: Result) bool { + return if (self.asPrimary() == .ok) true else false; + } + + fn asError(self: Result, handle: ?*sqlite.sqlite3) Database.Error { + const result: Result = .from(sqlite.sqlite3_extended_errcode(handle)); + const primary = self.asPrimary(); + + if (self == result) switch (primary) { + inline else => |code| return @field(Database.Error, @tagName(code)), + }; + + const extended: Extended = @enumFromInt(@intFromEnum(result)); + + return switch (extended) { + inline else => |code| @field(Database.Error, @tagName(code)), + }; } - pub fn extended(self: Result) Extended { - return @enumFromInt(@intFromEnum(self)); + fn asPrimary(self: Result) Primary { + return @enumFromInt(0x000000FF & @intFromEnum(self)); } }; test { - const db: Database = try .init(.temp_in_memory); + var db: Database = try .init(.temp_in_memory); + // defer db.deinit(); + + const query = db.query( + \\ CREATE TABLE contacts ( + \\ contact_id INTEGER PRIMARY KEY, + \\ first_name TEXT NOT NULL, + \\ last_name TEXT NOT NULL, + \\ email TEXT NOT NULL UNIQUE, + \\ phone TEXT NOT NULL UNIQUE + \\ ); + ); - _ = db; + std.debug.print("{any}\n", .{ query }); } |