diff --git a/src/sql/lib.zig b/src/sql/lib.zig index ad509fd..90486b9 100644 --- a/src/sql/lib.zig +++ b/src/sql/lib.zig @@ -7,6 +7,40 @@ const c = @cImport({ const Uuid = util.Uuid; const DateTime = util.DateTime; +pub fn ByteArray(comptime n: usize) type { + return struct { + const Self = @This(); + + data: [n]u8, + + pub fn bindToSqlite(self: Self, stmt: *PreparedStmt, idx: u15) !void { + return stmt.bindBlob(idx, &self.data); + } + + pub fn getFromSqlite(row: *Row, idx: u15, _: std.mem.Alloc) !Self { + var self: Self = undefined; + _ = try row.getBlob(idx, &self.data); + return self; + } + }; +} + +pub const ByteSlice = struct { + const Self = @This(); + + data: []const u8, + + pub fn bindToSqlite(self: Self, stmt: *PreparedStmt, idx: u15) !void { + return stmt.bindBlob(idx, self.data); + } + + pub fn getFromSqlite(row: *Row, idx: u15, alloc: std.mem.Alloc) !void { + return Self{ + .data = try row.getBlobAlloc(idx, alloc), + }; + } +}; + pub const Sqlite = struct { db: *c.sqlite3, @@ -63,6 +97,24 @@ pub const Row = struct { return self.getText(idx, buf); } + pub fn getBlob(self: Row, idx: u15, buf: []u8) ![]u8 { + const ptr = c.sqlite3_column_blob(self.stmt, idx); + + const size = @intCast(usize, c.sqlite3_column_bytes(self.stmt, idx)); + if (size > buf.len) return error.StreamTooLong; + for (ptr[0..size]) |ch, i| buf[i] = ch; + + return buf[0..size]; + } + + pub fn getBlobAlloc(self: Row, idx: u15, alloc: std.mem.Allocator) ![]u8 { + const size = c.sqlite3_column_bytes(self.stmt, idx); + var buf = try alloc.alloc(u8, @intCast(usize, size)); + errdefer alloc.free(buf); + + return self.getBlob(idx, buf); + } + pub fn getUuid(self: Row, idx: u15) !Uuid { var buf: [Uuid.string_len + 1]u8 = undefined; _ = try self.getText(idx, &buf); @@ -87,6 +139,7 @@ pub const Row = struct { c.SQLITE_NULL => return null, else => return try self.getAlloc(std.meta.Child(T), idx, alloc), }, + .Struct, .Union, .Enum, .Opaque => if (@hasDecl(T, "getFromSqlite")) T.getFromSqlite(self, idx, alloc), else => @compileError("unknown type " ++ @typeName(T)), } }, @@ -117,6 +170,13 @@ pub const PreparedStmt = struct { }; } + pub fn bindBlob(self: *PreparedStmt, idx: u15, blob: []const u8) !void { + return switch (c.sqlite3_bind_blob64(self.stmt, idx, blob.ptr, blob.len, c.SQLITE_TRANSIENT)) { + c.SQLITE_OK => {}, + else => error.UnknownError, + }; + } + pub fn bindI64(self: *PreparedStmt, idx: u15, val: i64) !void { return switch (c.sqlite3_bind_int64(self.stmt, idx, val)) { c.SQLITE_OK => {}, @@ -137,6 +197,7 @@ pub const PreparedStmt = struct { @TypeOf(null) => self.bindNull(idx), else => |T| switch (@typeInfo(T)) { .Optional => if (val) |v| self.bind(idx, v) else self.bindNull(idx), + .Struct, .Union, .Enum, .Opaque => if (@hasDecl(T, "bindToSqlite")) val.bindToSqlite(self, idx), else => @compileError("Unknown Type" ++ @typeName(T)), }, };