aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.zig8
-rw-r--r--build.zig.zon1
-rw-r--r--src/db/root.zig7
-rw-r--r--src/db/sqlite.h1
-rw-r--r--src/db/sqlite.zig221
5 files changed, 235 insertions, 3 deletions
diff --git a/build.zig b/build.zig
index c84375a..81eadfb 100644
--- a/build.zig
+++ b/build.zig
@@ -4,10 +4,18 @@ pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
+ const sqlite_c = b.addTranslateC(.{
+ .root_source_file = b.path("src/db/sqlite.h"),
+ .target = target,
+ .optimize = optimize,
+ });
+ sqlite_c.linkSystemLibrary("sqlite3", .{});
+
const 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/build.zig.zon b/build.zig.zon
index 69dae0f..afb2159 100644
--- a/build.zig.zon
+++ b/build.zig.zon
@@ -1,6 +1,7 @@
.{
.name = .memora,
.version = "0.0.0",
+ .fingerprint = 0xf901ac636a172b52,
.minimum_zig_version = "0.16.0",
.fingerprint = 0xf901ac6387a8536b,
.dependencies = .{
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..1e45537
--- /dev/null
+++ b/src/db/sqlite.zig
@@ -0,0 +1,221 @@
+const std = @import("std");
+const sqlite = @import("sqlite");
+
+pub const Database = struct {
+ handle: *sqlite.sqlite3,
+
+ pub const Error = set: {
+ var set: type = error{};
+
+ for (std.meta.fieldNames(Result.Primary)) |name| {
+ set = set || @TypeOf(@field(anyerror, name));
+ }
+ for (std.meta.fieldNames(Result.Extended)) |name| {
+ set = set || @TypeOf(@field(anyerror, name));
+ }
+
+ break :set set;
+ };
+
+ pub const Kind = union(enum) {
+ temp_in_memory,
+ temp_on_disk,
+ path: []const u8,
+ };
+
+ // Source: https://sqlite.org/c3ref/open.html
+ pub fn init(kind: Kind) Error!Database {
+ const name = switch (kind) {
+ .temp_in_memory => ":memory:",
+ .temp_on_disk => "",
+ .path => |path| path,
+ };
+ var handle: ?*sqlite.sqlite3 = null;
+
+ const result: Result = .from(sqlite.sqlite3_open(name.ptr, &handle));
+
+ switch (result.primary()) {
+ .ok => {},
+ inline else => |code| return @field(anyerror, @tagName(code)),
+ }
+
+ return .{ .handle = handle.? };
+ }
+};
+
+// Source: https://sqlite.org/rescode.html
+pub const Result = enum(i32) {
+ _,
+
+ // zig fmt: off
+ pub const Primary = 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,
+
+ fn as(self: Primary, comptime T: type) T {
+ return @as(T, @intFromEnum(self));
+ }
+ };
+
+ // zig fmt: off
+ pub 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)),
+ error_reservesize = (Primary.@"error".as(i32) | ( 4 << 8)),
+ error_key = (Primary.@"error".as(i32) | ( 5 << 8)),
+ error_unable = (Primary.@"error".as(i32) | ( 6 << 8)),
+ ioerr_read = (Primary.ioerr.as(i32) | ( 1 << 8)),
+ ioerr_short_read = (Primary.ioerr.as(i32) | ( 2 << 8)),
+ ioerr_write = (Primary.ioerr.as(i32) | ( 3 << 8)),
+ ioerr_fsync = (Primary.ioerr.as(i32) | ( 4 << 8)),
+ ioerr_dir_fsync = (Primary.ioerr.as(i32) | ( 5 << 8)),
+ ioerr_truncate = (Primary.ioerr.as(i32) | ( 6 << 8)),
+ ioerr_fstat = (Primary.ioerr.as(i32) | ( 7 << 8)),
+ ioerr_unlock = (Primary.ioerr.as(i32) | ( 8 << 8)),
+ ioerr_rdlock = (Primary.ioerr.as(i32) | ( 9 << 8)),
+ ioerr_delete = (Primary.ioerr.as(i32) | (10 << 8)),
+ ioerr_blocked = (Primary.ioerr.as(i32) | (11 << 8)),
+ ioerr_nomem = (Primary.ioerr.as(i32) | (12 << 8)),
+ ioerr_access = (Primary.ioerr.as(i32) | (13 << 8)),
+ ioerr_checkreservedlock = (Primary.ioerr.as(i32) | (14 << 8)),
+ ioerr_lock = (Primary.ioerr.as(i32) | (15 << 8)),
+ ioerr_close = (Primary.ioerr.as(i32) | (16 << 8)),
+ ioerr_dir_close = (Primary.ioerr.as(i32) | (17 << 8)),
+ ioerr_shmopen = (Primary.ioerr.as(i32) | (18 << 8)),
+ ioerr_shmsize = (Primary.ioerr.as(i32) | (19 << 8)),
+ ioerr_shmlock = (Primary.ioerr.as(i32) | (20 << 8)),
+ ioerr_shmmap = (Primary.ioerr.as(i32) | (21 << 8)),
+ ioerr_seek = (Primary.ioerr.as(i32) | (22 << 8)),
+ ioerr_delete_noent = (Primary.ioerr.as(i32) | (23 << 8)),
+ ioerr_mmap = (Primary.ioerr.as(i32) | (24 << 8)),
+ ioerr_gettemppath = (Primary.ioerr.as(i32) | (25 << 8)),
+ ioerr_convpath = (Primary.ioerr.as(i32) | (26 << 8)),
+ ioerr_vnode = (Primary.ioerr.as(i32) | (27 << 8)),
+ ioerr_auth = (Primary.ioerr.as(i32) | (28 << 8)),
+ ioerr_begin_atomic = (Primary.ioerr.as(i32) | (29 << 8)),
+ ioerr_commit_atomic = (Primary.ioerr.as(i32) | (30 << 8)),
+ ioerr_rollback_atomic = (Primary.ioerr.as(i32) | (31 << 8)),
+ ioerr_data = (Primary.ioerr.as(i32) | (32 << 8)),
+ ioerr_corruptfs = (Primary.ioerr.as(i32) | (33 << 8)),
+ ioerr_in_page = (Primary.ioerr.as(i32) | (34 << 8)),
+ ioerr_badkey = (Primary.ioerr.as(i32) | (35 << 8)),
+ ioerr_codec = (Primary.ioerr.as(i32) | (36 << 8)),
+ locked_sharedcache = (Primary.locked.as(i32) | ( 1 << 8)),
+ locked_vtab = (Primary.locked.as(i32) | ( 2 << 8)),
+ busy_recovery = (Primary.busy.as(i32) | ( 1 << 8)),
+ busy_snapshot = (Primary.busy.as(i32) | ( 2 << 8)),
+ busy_timeout = (Primary.busy.as(i32) | ( 3 << 8)),
+ cantopen_notempdir = (Primary.cantopen.as(i32) | ( 1 << 8)),
+ cantopen_isdir = (Primary.cantopen.as(i32) | ( 2 << 8)),
+ cantopen_fullpath = (Primary.cantopen.as(i32) | ( 3 << 8)),
+ cantopen_convpath = (Primary.cantopen.as(i32) | ( 4 << 8)),
+ cantopen_symlink = (Primary.cantopen.as(i32) | ( 6 << 8)),
+ corrupt_vtab = (Primary.corrupt.as(i32) | ( 1 << 8)),
+ corrupt_sequence = (Primary.corrupt.as(i32) | ( 2 << 8)),
+ corrupt_index = (Primary.corrupt.as(i32) | ( 3 << 8)),
+ readonly_recovery = (Primary.readonly.as(i32) | ( 1 << 8)),
+ readonly_cantlock = (Primary.readonly.as(i32) | ( 2 << 8)),
+ readonly_rollback = (Primary.readonly.as(i32) | ( 3 << 8)),
+ readonly_dbmoved = (Primary.readonly.as(i32) | ( 4 << 8)),
+ readonly_cantinit = (Primary.readonly.as(i32) | ( 5 << 8)),
+ readonly_directory = (Primary.readonly.as(i32) | ( 6 << 8)),
+ abort_rollback = (Primary.abort.as(i32) | ( 2 << 8)),
+ constraint_check = (Primary.constraint.as(i32) | ( 1 << 8)),
+ constraint_commithook = (Primary.constraint.as(i32) | ( 2 << 8)),
+ constraint_foreignkey = (Primary.constraint.as(i32) | ( 3 << 8)),
+ constraint_function = (Primary.constraint.as(i32) | ( 4 << 8)),
+ constraint_notnull = (Primary.constraint.as(i32) | ( 5 << 8)),
+ constraint_primarykey = (Primary.constraint.as(i32) | ( 6 << 8)),
+ constraint_trigger = (Primary.constraint.as(i32) | ( 7 << 8)),
+ constraint_unique = (Primary.constraint.as(i32) | ( 8 << 8)),
+ constraint_vtab = (Primary.constraint.as(i32) | ( 9 << 8)),
+ constraint_rowid = (Primary.constraint.as(i32) | (10 << 8)),
+ constraint_pinned = (Primary.constraint.as(i32) | (11 << 8)),
+ constraint_datatype = (Primary.constraint.as(i32) | (12 << 8)),
+ notice_recover_wal = (Primary.notice.as(i32) | ( 1 << 8)),
+ notice_recover_rollback = (Primary.notice.as(i32) | ( 2 << 8)),
+ notice_rbu = (Primary.notice.as(i32) | ( 3 << 8)),
+ warning_autoindex = (Primary.warning.as(i32) | ( 1 << 8)),
+ auth_user = (Primary.auth.as(i32) | ( 1 << 8)),
+ ok_load_permanently = (Primary.ok.as(i32) | ( 1 << 8)),
+ };
+
+ pub fn from(value: anytype) Result {
+ return @enumFromInt(value);
+ }
+
+ pub fn primary(self: Result) Primary {
+ return @enumFromInt(0x000000FF & @intFromEnum(self));
+ }
+
+ pub fn extended(self: Result) Extended {
+ return @enumFromInt(@intFromEnum(self));
+ }
+};
+
+test {
+ const db: Database = try .init(.temp_in_memory);
+
+ _ = db;
+}