aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Rolley <arolley35@gmail.com>2026-05-21 15:30:07 +0200
committerAlexander Rolley <arolley35@gmail.com>2026-05-21 15:30:07 +0200
commita9b6b2495de6c97fd17b37885dd99d8557a9ea0e (patch)
treeee84978802a281b572f7baf5f534aa0816e404cd
parent1cb3ae4156de430628abbc2ca19f51384857c6ba (diff)
- Add associated function on `Result` type that returns the error
type, as not every primary error code has an extended error code. - Implement `Database.deinit` and `Database.query` functions.
-rw-r--r--src/db/sqlite.zig118
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 });
}