From 1d6f7bfa08b1b7739ea94fb1db61d2e255439088 Mon Sep 17 00:00:00 2001 From: jaina heartles Date: Sat, 16 Jul 2022 12:00:33 -0700 Subject: [PATCH] Allow querying by other fields --- build.zig | 7 ++----- src/main/db.zig | 52 +++++++++++++++++++++++++++++++++++++++---------- src/sql/lib.zig | 17 ++++++++++++++++ 3 files changed, 61 insertions(+), 15 deletions(-) diff --git a/build.zig b/build.zig index 4f02e7c..8b7984a 100644 --- a/build.zig +++ b/build.zig @@ -26,18 +26,15 @@ pub fn build(b: *std.build.Builder) void { exe.setTarget(target); exe.setBuildMode(mode); - const sql = std.build.Pkg{ - .name = "sql", - .source = std.build.FileSource.relative("src/sql/lib.zig"), - }; const util = std.build.Pkg{ .name = "util", .source = std.build.FileSource.relative("src/util/lib.zig"), }; + const sql = std.build.Pkg{ .name = "sql", .source = std.build.FileSource.relative("src/sql/lib.zig"), .dependencies = &.{util} }; const http = std.build.Pkg{ .name = "http", .source = std.build.FileSource.relative("src/http/lib.zig"), - .dependencies = &[_]std.build.Pkg{util}, + .dependencies = &.{util}, }; exe.addPackage(sql); exe.addPackage(util); diff --git a/src/main/db.zig b/src/main/db.zig index 871b1cd..e303c54 100644 --- a/src/main/db.zig +++ b/src/main/db.zig @@ -123,7 +123,7 @@ pub const Database = struct { self.db.close(); } - pub fn getById(self: *Database, comptime T: type, id: Uuid, alloc: std.mem.Allocator) !?T { + pub fn getByIdOld(self: *Database, comptime T: type, id: Uuid, alloc: std.mem.Allocator) !?T { const fields = comptime fieldsExcept(T, &.{"id"}); const q = comptime (Query{ .select = fields, @@ -153,6 +153,46 @@ pub const Database = struct { return val; } + pub fn getById(self: *Database, comptime T: type, id: Uuid, alloc: std.mem.Allocator) !?T { + return self.getBy(T, .id, id, alloc); + } + + pub fn getBy( + self: *Database, + comptime T: type, + comptime field: std.meta.FieldEnum(T), + val: std.meta.fieldInfo(T, field).field_type, + alloc: std.mem.Allocator, + ) !?T { + const field_name = std.meta.fieldInfo(T, field).name; + const fields = comptime fieldsExcept(T, &.{field_name}); + const q = comptime (Query{ + .select = fields, + .from = tableName(T), + .where = field_name ++ " = ?", + .limit = 1, + }).str(); + + var stmt = try self.db.prepare(q); + defer stmt.finalize(); + + try stmt.bind(1, val); + + const row = (try stmt.step()) orelse return null; + var result: T = undefined; + @field(result, field_name) = val; + + inline for (fields) |f, i| { + @field(result, f) = switch (@TypeOf(@field(result, f))) { + // TODO: Handle allocation failures gracefully + []const u8 => row.getTextAlloc(i, alloc) catch unreachable, + else => @compileError("unknown type"), + }; + } + + return result; + } + pub fn getNoteById(self: *Database, id: Uuid, alloc: std.mem.Allocator) !?models.Note { return self.getById(models.Note, id, alloc); } @@ -169,15 +209,7 @@ pub const Database = struct { defer stmt.finalize(); inline for (fields) |f, i| { - const arg_idx = i + 1; - switch (@TypeOf(@field(val, f))) { - []const u8 => try stmt.bindText(arg_idx, @field(val, f)), - Uuid => { - const str = @field(val, f).toCharArray(); - try stmt.bindText(arg_idx, &str); - }, - else => @compileError("unknown type"), - } + try stmt.bind(i + 1, @field(val, f)); } if ((try stmt.step()) != null) return error.UnknownError; diff --git a/src/sql/lib.zig b/src/sql/lib.zig index 7153060..3f9b444 100644 --- a/src/sql/lib.zig +++ b/src/sql/lib.zig @@ -3,6 +3,8 @@ const c = @cImport({ @cInclude("sqlite3.h"); }); +const Uuid = @import("util").Uuid; + pub const Sqlite = struct { db: *c.sqlite3, @@ -71,6 +73,11 @@ pub const PreparedStmt = struct { }; } + pub fn bindUuid(self: *PreparedStmt, idx: u15, id: Uuid) !void { + const str = id.toCharArray(); + return self.bindText(idx, &str); + } + pub fn bindText(self: *PreparedStmt, idx: u15, str: []const u8) !void { return switch (c.sqlite3_bind_text(self.stmt, idx, str.ptr, @intCast(c_int, str.len), c.SQLITE_TRANSIENT)) { c.SQLITE_OK => {}, @@ -85,6 +92,16 @@ pub const PreparedStmt = struct { }; } + pub fn bind(self: *PreparedStmt, idx: u15, val: anytype) !void { + return switch (@TypeOf(val)) { + []const u8 => self.bindText(idx, val), + i64 => self.bindI64(idx, val), + Uuid => self.bindUuid(idx, val), + @Type(.Null) => self.bindNull(idx), + else => @compileError("Unknown Type"), + }; + } + pub fn step(self: *PreparedStmt) !?Row { return switch (c.sqlite3_step(self.stmt)) { c.SQLITE_ROW => Row{ .stmt = self.stmt, .db = self.db },