diff options
-rw-r--r-- | src/duckdb/Result.zig | 13 | ||||
-rw-r--r-- | src/duckdb/Types.zig | 66 | ||||
-rw-r--r-- | src/duckdb/db.zig | 28 |
3 files changed, 103 insertions, 4 deletions
diff --git a/src/duckdb/Result.zig b/src/duckdb/Result.zig index f399fb5..75f74fd 100644 --- a/src/duckdb/Result.zig +++ b/src/duckdb/Result.zig @@ -1,4 +1,5 @@ -const std = @import("std"); +const std = @import("std"); +const types = @import("Types.zig"); const c = @cImport({ @cInclude("duckdb.h"); }); @@ -102,7 +103,7 @@ pub fn Result(comptime T: type) type{ // TODO: check compatibility between the column type and // the struct provided as result container // - // const column_type = c.duckdb_column_type(&self._res, i); + const column_type = c.duckdb_column_type(&self._res, i); // std.debug.print("tipo => {any}\n", .{column_type}); // std.debug.print("{any}\n", .{self._data}); @@ -123,8 +124,12 @@ pub fn Result(comptime T: type) type{ .Optional => |t| t.child, else => field.type }; - const col : [*]t = @alignCast(@ptrCast(self._data[i])); - @field(result, field.name) = col[self._current_row]; + // Unpack the C array of duckdb_type + const col: [*]types.unpack_type(t) = @alignCast(@ptrCast(self._data[i])); + std.debug.assert(types.valid_unpack(column_type, t)); + + // Convert to Zig data type + @field(result, field.name) = try types.cast(&col[self._current_row], t); } else { // Got a NULL from the DB if (@typeInfo(field.type) != .Optional){ diff --git a/src/duckdb/Types.zig b/src/duckdb/Types.zig new file mode 100644 index 0000000..2a12cd2 --- /dev/null +++ b/src/duckdb/Types.zig @@ -0,0 +1,66 @@ +const std = @import("std"); +const c = @cImport({ + @cInclude("duckdb.h"); +}); + +pub fn unpack_type(T: type) type { + return switch(@typeInfo(T)) { + .Bool, .Int, .Float => T, + .Pointer => |p| switch (p.size) { + .Slice => if (p.child == u8) c.duckdb_string_t else + @compileError("Invalid type for output data"), + else => @compileError("Invalid type for output data") + }, + // .Array => + // .Struct => + // .Enum => + // .Union => + else => @compileError("Invalid type for output data") + }; +} + +pub fn valid_unpack(column_type: c.DUCKDB_TYPE, T: type) bool { + switch (column_type) { + c.DUCKDB_TYPE_BOOLEAN => if(T == bool) return true else return false, + c.DUCKDB_TYPE_TINYINT => if(T == i8 ) return true else return false, + c.DUCKDB_TYPE_SMALLINT => if(T == i16 ) return true else return false, + c.DUCKDB_TYPE_INTEGER => if(T == i32 ) return true else return false, + c.DUCKDB_TYPE_BIGINT => if(T == i64 ) return true else return false, + c.DUCKDB_TYPE_UTINYINT => if(T == u8 ) return true else return false, + c.DUCKDB_TYPE_USMALLINT => if(T == u16 ) return true else return false, + c.DUCKDB_TYPE_UINTEGER => if(T == u32 ) return true else return false, + c.DUCKDB_TYPE_UBIGINT => if(T == u64 ) return true else return false, + c.DUCKDB_TYPE_FLOAT => if(T == f32 ) return true else return false, + c.DUCKDB_TYPE_DOUBLE => if(T == f64 ) return true else return false, + c.DUCKDB_TYPE_VARCHAR => if(T == []const u8) return true else return false, + c.DUCKDB_TYPE_BLOB => if(T == []const u8) return true else return false, + else => return false, + } +} + +/// Receives a pointer of the element (!) +pub fn cast(el: anytype, T: type) !T { + return switch (@typeInfo(T)) { + .Bool, .Int, .Float => el.*, + .Pointer => |p| switch (p.size) { + .Slice => blk: { + if ( p.child == u8 ) { + var result: T = undefined; + if (c.duckdb_string_is_inlined(el.*)){ + result = &el.value.inlined.inlined; + result.len = el.value.inlined.length; + break :blk result; + } else { + result.len = el.value.pointer.length; + result.ptr = el.value.pointer.ptr; + break :blk result; + } + } else { + @compileError("Invalid type for output data"); + } + }, + else => @compileError("Invalid type for output data") + }, + else => @compileError("Invalid type for output data") + }; +} diff --git a/src/duckdb/db.zig b/src/duckdb/db.zig index 19feffe..f662ee0 100644 --- a/src/duckdb/db.zig +++ b/src/duckdb/db.zig @@ -109,3 +109,31 @@ test "Checks if all fields are captured" { try std.testing.expectError(error.QueryColumnCountCapture, connection.query("SELECT * FROM integers;", s)); } + +test "String queries" { + var database = try Database.init(":memory:"); + defer database.deinit(); + var connection = try database.connect(); + defer connection.deinit(); + + const s : type = comptime struct { + primer: []const u8 + }; + + var x = try connection.query("CREATE TABLE text (i VARCHAR NOT NULL );", void); + x.deinit(); + var y = try connection.query("INSERT INTO text VALUES ('Inlined');", void); + y.deinit(); + var z = try connection.query("INSERT INTO text VALUES ('A very long string that is not inlined');", void); + z.deinit(); + var result = try connection.query("SELECT * FROM text;", s); + defer result.deinit(); + + + try std.testing.expect(1 == result.getColumnCount()); + + var w = try result.next(); + try std.testing.expect(std.mem.eql(u8, w.primer, "Inlined")); + w = try result.next(); + try std.testing.expect(std.mem.eql(u8, w.primer, "A very long string that is not inlined")); +} |