aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.zig9
-rw-r--r--src/db/root.zig7
-rw-r--r--src/db/sqlite.h1
-rw-r--r--src/db/sqlite.zig192
4 files changed, 205 insertions, 4 deletions
diff --git a/build.zig b/build.zig
index c84375a..d18745c 100644
--- a/build.zig
+++ b/build.zig
@@ -4,10 +4,17 @@ pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
- const db_mod = b.addModule("db", .{
+ const sqlite_c = b.addTranslateC(.{
+ .root_source_file = b.path("src/db/sqlite.h"),
+ .target = target,
+ .optimize = optimize,
+ });
+
+ var db_mod = b.addModule("db", .{
.root_source_file = b.path("src/db/root.zig"),
.target = target,
});
+ db_mod.addImport("sqlite", sqlite_c.createModule());
const api_mod = b.addModule("api", .{
.root_source_file = b.path("src/api/root.zig"),
diff --git a/src/db/root.zig b/src/db/root.zig
index 049a63b..4ca9847 100644
--- a/src/db/root.zig
+++ b/src/db/root.zig
@@ -1,9 +1,10 @@
-//! Implements the layer between the sqlite
-//! database and the interface for the api endpoints or
-//! the cli.
+//! Implements the layer between the sqlite database and the interface for the
+//! API endpoints or the CLI.
const std = @import("std");
+pub const sqlite = @import("sqlite.zig");
+
test {
_ = std.testing.refAllDecls(@This());
}
diff --git a/src/db/sqlite.h b/src/db/sqlite.h
new file mode 100644
index 0000000..f52e1f0
--- /dev/null
+++ b/src/db/sqlite.h
@@ -0,0 +1 @@
+#include <sqlite3.h>
diff --git a/src/db/sqlite.zig b/src/db/sqlite.zig
new file mode 100644
index 0000000..ad76992
--- /dev/null
+++ b/src/db/sqlite.zig
@@ -0,0 +1,192 @@
+const std = @import("std");
+const mem = std.mem;
+const sqlite = @import("sqlite");
+
+pub const Sqlite3Database = struct {
+ handle: *sqlite.sqlite3,
+
+ // FIXME: this is shit, generate these enums at comptime where
+ // primary and extended codes are given as tuples
+ // zig fmt: off
+ pub const PrimaryResultCode = enum(u8) {
+ /// Successful result.
+ 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,
+ };
+
+ // zig fmt: off
+ pub const ExtendedResultCode = enum(i32) {
+ err_missing_collseq = (@as(i32, @intFromEnum(PrimaryResultCode.@"error" )) | (1 << 8)),
+ err_retry = (@as(i32, @intFromEnum(PrimaryResultCode.@"error" )) | (2 << 8)),
+ err_snapshot = (@as(i32, @intFromEnum(PrimaryResultCode.@"error" )) | (3 << 8)),
+ err_reservesize = (@as(i32, @intFromEnum(PrimaryResultCode.@"error" )) | (4 << 8)),
+ err_key = (@as(i32, @intFromEnum(PrimaryResultCode.@"error" )) | (5 << 8)),
+ err_unable = (@as(i32, @intFromEnum(PrimaryResultCode.@"error" )) | (6 << 8)),
+ ioerr_read = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (1 << 8)),
+ ioerr_short_read = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (2 << 8)),
+ ioerr_write = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (3 << 8)),
+ ioerr_fsync = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (4 << 8)),
+ ioerr_dir_fsync = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (5 << 8)),
+ ioerr_truncate = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (6 << 8)),
+ ioerr_fstat = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (7 << 8)),
+ ioerr_unlock = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (8 << 8)),
+ ioerr_rdlock = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (9 << 8)),
+ ioerr_delete = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (10 << 8)),
+ ioerr_blocked = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (11 << 8)),
+ ioerr_nomem = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (12 << 8)),
+ ioerr_access = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (13 << 8)),
+ ioerr_checkreservedlock = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (14 << 8)),
+ ioerr_lock = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (15 << 8)),
+ ioerr_close = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (16 << 8)),
+ ioerr_dir_close = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (17 << 8)),
+ ioerr_shmopen = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (18 << 8)),
+ ioerr_shmsize = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (19 << 8)),
+ ioerr_shmlock = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (20 << 8)),
+ ioerr_shmmap = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (21 << 8)),
+ ioerr_seek = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (22 << 8)),
+ ioerr_delete_noent = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (23 << 8)),
+ ioerr_mmap = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (24 << 8)),
+ ioerr_gettemppath = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (25 << 8)),
+ ioerr_convpath = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (26 << 8)),
+ ioerr_vnode = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (27 << 8)),
+ ioerr_auth = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (28 << 8)),
+ ioerr_begin_atomic = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (29 << 8)),
+ ioerr_commit_atomic = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (30 << 8)),
+ ioerr_rollback_atomic = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (31 << 8)),
+ ioerr_data = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (32 << 8)),
+ ioerr_corruptfs = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (33 << 8)),
+ ioerr_in_page = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (34 << 8)),
+ ioerr_badkey = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (35 << 8)),
+ ioerr_codec = (@as(i32, @intFromEnum(PrimaryResultCode.ioerr )) | (36 << 8)),
+ locked_sharedcache = (@as(i32, @intFromEnum(PrimaryResultCode.locked )) | (1 << 8)),
+ locked_vtab = (@as(i32, @intFromEnum(PrimaryResultCode.locked )) | (2 << 8)),
+ busy_recovery = (@as(i32, @intFromEnum(PrimaryResultCode.busy )) | (1 << 8)),
+ busy_snapshot = (@as(i32, @intFromEnum(PrimaryResultCode.busy )) | (2 << 8)),
+ busy_timeout = (@as(i32, @intFromEnum(PrimaryResultCode.busy )) | (3 << 8)),
+ cantopen_notempdir = (@as(i32, @intFromEnum(PrimaryResultCode.cantopen )) | (1 << 8)),
+ cantopen_isdir = (@as(i32, @intFromEnum(PrimaryResultCode.cantopen )) | (2 << 8)),
+ cantopen_fullpath = (@as(i32, @intFromEnum(PrimaryResultCode.cantopen )) | (3 << 8)),
+ cantopen_convpath = (@as(i32, @intFromEnum(PrimaryResultCode.cantopen )) | (4 << 8)),
+ /// Not used.
+ cantopen_dirtywal = (@as(i32, @intFromEnum(PrimaryResultCode.cantopen )) | (5 << 8)),
+ cantopen_symlink = (@as(i32, @intFromEnum(PrimaryResultCode.cantopen )) | (6 << 8)),
+ corrupt_vtab = (@as(i32, @intFromEnum(PrimaryResultCode.corrupt )) | (1 << 8)),
+ corrupt_sequence = (@as(i32, @intFromEnum(PrimaryResultCode.corrupt )) | (2 << 8)),
+ corrupt_index = (@as(i32, @intFromEnum(PrimaryResultCode.corrupt )) | (3 << 8)),
+ readonly_recovery = (@as(i32, @intFromEnum(PrimaryResultCode.readonly )) | (1 << 8)),
+ readonly_cantlock = (@as(i32, @intFromEnum(PrimaryResultCode.readonly )) | (2 << 8)),
+ readonly_rollback = (@as(i32, @intFromEnum(PrimaryResultCode.readonly )) | (3 << 8)),
+ readonly_dbmoved = (@as(i32, @intFromEnum(PrimaryResultCode.readonly )) | (4 << 8)),
+ readonly_cantinit = (@as(i32, @intFromEnum(PrimaryResultCode.readonly )) | (5 << 8)),
+ readonly_directory = (@as(i32, @intFromEnum(PrimaryResultCode.readonly )) | (6 << 8)),
+ abort_rollback = (@as(i32, @intFromEnum(PrimaryResultCode.abort )) | (2 << 8)),
+ constraint_check = (@as(i32, @intFromEnum(PrimaryResultCode.constraint )) | (1 << 8)),
+ constraint_commithook = (@as(i32, @intFromEnum(PrimaryResultCode.constraint )) | (2 << 8)),
+ constraint_foreignkey = (@as(i32, @intFromEnum(PrimaryResultCode.constraint )) | (3 << 8)),
+ constraint_function = (@as(i32, @intFromEnum(PrimaryResultCode.constraint )) | (4 << 8)),
+ constraint_notnull = (@as(i32, @intFromEnum(PrimaryResultCode.constraint )) | (5 << 8)),
+ constraint_primarykey = (@as(i32, @intFromEnum(PrimaryResultCode.constraint )) | (6 << 8)),
+ constraint_trigger = (@as(i32, @intFromEnum(PrimaryResultCode.constraint )) | (7 << 8)),
+ constraint_unique = (@as(i32, @intFromEnum(PrimaryResultCode.constraint )) | (8 << 8)),
+ constraint_vtab = (@as(i32, @intFromEnum(PrimaryResultCode.constraint )) | (9 << 8)),
+ constraint_rowid = (@as(i32, @intFromEnum(PrimaryResultCode.constraint )) | (10 << 8)),
+ constraint_pinned = (@as(i32, @intFromEnum(PrimaryResultCode.constraint )) | (11 << 8)),
+ constraint_datatype = (@as(i32, @intFromEnum(PrimaryResultCode.constraint )) | (12 << 8)),
+ notice_recover_wal = (@as(i32, @intFromEnum(PrimaryResultCode.notice )) | (1 << 8)),
+ notice_recover_rollback = (@as(i32, @intFromEnum(PrimaryResultCode.notice )) | (2 << 8)),
+ notice_rbu = (@as(i32, @intFromEnum(PrimaryResultCode.notice )) | (3 << 8)),
+ warning_autoindex = (@as(i32, @intFromEnum(PrimaryResultCode.warning )) | (1 << 8)),
+ auth_user = (@as(i32, @intFromEnum(PrimaryResultCode.auth )) | (1 << 8)),
+ ok_load_permanently = (@as(i32, @intFromEnum(PrimaryResultCode.ok )) | (1 << 8)),
+ /// Internal only.
+ ok_symlink = (@as(i32, @intFromEnum(PrimaryResultCode.ok )) | (2 << 8)),
+ };
+
+ pub const Error = error{ExtendedResultCode};
+
+ pub const Self = @This();
+
+ pub fn init(path: []const u8) Error!Self {
+ var handle: ?*sqlite.sqlite3 = null;
+
+ const result = sqlite.sqlite3_open(path.ptr, &handle);
+ const primary_code = @as(PrimaryResultCode, @enumFromInt(result));
+
+ switch (primary_code) {
+ .ok => .{},
+ else => {
+ const code = sqlite.sqlite3_extended_errcode(handle);
+ return error.@as(ExtendedResultCode, @enumFromInt(code));
+ },
+ }
+
+ return .{ .handle = handle };
+ }
+};
+
+test {
+ const db: Sqlite3Database = try .init(":memory:");
+
+ _ = db;
+
+ std.debug.print("asdf", .{});
+}