Allow querying by other fields
This commit is contained in:
parent
90d2dcd4c1
commit
1d6f7bfa08
3 changed files with 61 additions and 15 deletions
|
@ -26,18 +26,15 @@ pub fn build(b: *std.build.Builder) void {
|
||||||
exe.setTarget(target);
|
exe.setTarget(target);
|
||||||
exe.setBuildMode(mode);
|
exe.setBuildMode(mode);
|
||||||
|
|
||||||
const sql = std.build.Pkg{
|
|
||||||
.name = "sql",
|
|
||||||
.source = std.build.FileSource.relative("src/sql/lib.zig"),
|
|
||||||
};
|
|
||||||
const util = std.build.Pkg{
|
const util = std.build.Pkg{
|
||||||
.name = "util",
|
.name = "util",
|
||||||
.source = std.build.FileSource.relative("src/util/lib.zig"),
|
.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{
|
const http = std.build.Pkg{
|
||||||
.name = "http",
|
.name = "http",
|
||||||
.source = std.build.FileSource.relative("src/http/lib.zig"),
|
.source = std.build.FileSource.relative("src/http/lib.zig"),
|
||||||
.dependencies = &[_]std.build.Pkg{util},
|
.dependencies = &.{util},
|
||||||
};
|
};
|
||||||
exe.addPackage(sql);
|
exe.addPackage(sql);
|
||||||
exe.addPackage(util);
|
exe.addPackage(util);
|
||||||
|
|
|
@ -123,7 +123,7 @@ pub const Database = struct {
|
||||||
self.db.close();
|
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 fields = comptime fieldsExcept(T, &.{"id"});
|
||||||
const q = comptime (Query{
|
const q = comptime (Query{
|
||||||
.select = fields,
|
.select = fields,
|
||||||
|
@ -153,6 +153,46 @@ pub const Database = struct {
|
||||||
return val;
|
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 {
|
pub fn getNoteById(self: *Database, id: Uuid, alloc: std.mem.Allocator) !?models.Note {
|
||||||
return self.getById(models.Note, id, alloc);
|
return self.getById(models.Note, id, alloc);
|
||||||
}
|
}
|
||||||
|
@ -169,15 +209,7 @@ pub const Database = struct {
|
||||||
defer stmt.finalize();
|
defer stmt.finalize();
|
||||||
|
|
||||||
inline for (fields) |f, i| {
|
inline for (fields) |f, i| {
|
||||||
const arg_idx = i + 1;
|
try stmt.bind(i + 1, @field(val, f));
|
||||||
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"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((try stmt.step()) != null) return error.UnknownError;
|
if ((try stmt.step()) != null) return error.UnknownError;
|
||||||
|
|
|
@ -3,6 +3,8 @@ const c = @cImport({
|
||||||
@cInclude("sqlite3.h");
|
@cInclude("sqlite3.h");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const Uuid = @import("util").Uuid;
|
||||||
|
|
||||||
pub const Sqlite = struct {
|
pub const Sqlite = struct {
|
||||||
db: *c.sqlite3,
|
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 {
|
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)) {
|
return switch (c.sqlite3_bind_text(self.stmt, idx, str.ptr, @intCast(c_int, str.len), c.SQLITE_TRANSIENT)) {
|
||||||
c.SQLITE_OK => {},
|
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 {
|
pub fn step(self: *PreparedStmt) !?Row {
|
||||||
return switch (c.sqlite3_step(self.stmt)) {
|
return switch (c.sqlite3_step(self.stmt)) {
|
||||||
c.SQLITE_ROW => Row{ .stmt = self.stmt, .db = self.db },
|
c.SQLITE_ROW => Row{ .stmt = self.stmt, .db = self.db },
|
||||||
|
|
Loading…
Reference in a new issue