diff options
| -rw-r--r-- | build.zig | 9 | ||||
| -rw-r--r-- | src/db/root.zig | 7 | ||||
| -rw-r--r-- | src/db/sqlite.h | 1 | ||||
| -rw-r--r-- | src/db/sqlite.zig | 192 |
4 files changed, 205 insertions, 4 deletions
@@ -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", .{}); +} |