summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEkaitz Zarraga <ekaitz@elenq.tech>2024-08-02 13:23:21 +0200
committerEkaitz Zarraga <ekaitz@elenq.tech>2024-08-03 13:44:49 +0200
commitdabbb107d719cb753673e394fbd4da090f74d458 (patch)
tree1beba6ec67922882db0cf6606d712195dcfb62d1
parentf225c34d82cb6ea4133af64c34f977be00769afc (diff)
WIP String queries and proper conversion:
Still have to make the type checking better, not just an assert. Rethink.
-rw-r--r--src/duckdb/Result.zig13
-rw-r--r--src/duckdb/Types.zig66
-rw-r--r--src/duckdb/db.zig28
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"));
+}