summaryrefslogtreecommitdiff
path: root/src/duckdb/Result.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/duckdb/Result.zig')
-rw-r--r--src/duckdb/Result.zig114
1 files changed, 90 insertions, 24 deletions
diff --git a/src/duckdb/Result.zig b/src/duckdb/Result.zig
index 2b5b992..53ed351 100644
--- a/src/duckdb/Result.zig
+++ b/src/duckdb/Result.zig
@@ -5,49 +5,52 @@ const c = @cImport({
pub fn Result(comptime T: type) type{
+ const column_count = switch (@typeInfo(T)) {
+ .Struct => |v| v.fields.len,
+ .Void => 0,
+ else => @compileError("Expecting struct or void in query result type"),
+ };
return struct {
- _res: c.duckdb_result,
- _chunk: c.duckdb_data_chunk,
+ _res: c.duckdb_result,
+ _chunk: c.duckdb_data_chunk,
+ _columns: [column_count]c.duckdb_vector,
+ _validities: [column_count]?[*]u64,
+ _data: [column_count]?*anyopaque,
+
+ _current_row: usize,
const Self = @This();
pub fn init(conn : c.duckdb_connection, query: [:0]const u8) !Self {
var self : Self = .{
- ._res = undefined,
- ._chunk = null
+ ._res = undefined,
+ ._chunk = null,
+ ._columns = undefined,
+ ._validities = undefined,
+ ._data = undefined,
+ ._current_row = 0,
};
const state = c.duckdb_query(conn, query, &self._res);
if ( state == c.DuckDBError){
return error.DuckDBError;
}
- self.fetchDataChunk();
-
- // Get column vectors
- switch (@typeInfo(T)) {
- .Struct => |v| {
- const column_count = v.fields.len;
- var columns : [column_count]c.duckdb_vector = undefined;
- for (columns, 0..) |_, i| {
- columns[i] = c.duckdb_data_chunk_get_vector(self._chunk, i);
- }
- std.debug.print("{any}", .{columns});
- },
- .Void => {},
- else => @compileError("Expecting struct or void in query result type"),
- }
+ self.fetchDataChunk();
return self;
}
pub fn deinit(self: *Self) void {
c.duckdb_destroy_result(&self._res);
- c.duckdb_destroy_data_chunk(&self._chunk);
+ if (self._chunk != null){
+ c.duckdb_destroy_data_chunk(&self._chunk);
+ self._chunk = null;
+ }
}
/// There's not way to know how many total elements we have, but we can
/// know how many we have in the current chunk.
fn getCurrentChunkSize(self: Self) usize {
- if (self._chunk != null) {
+ if (self._chunk == null) {
return 0;
}
return c.duckdb_data_chunk_get_size(self._chunk);
@@ -63,8 +66,18 @@ pub fn Result(comptime T: type) type{
fn fetchDataChunk(self: *Self) void{
if (self._chunk != null){
c.duckdb_destroy_data_chunk(&self._chunk);
+ self._chunk = null;
+ self._current_row = 0;
}
self._chunk = c.duckdb_fetch_chunk(self._res);
+ for (self._columns, 0..) |_, i| {
+ const col = c.duckdb_data_chunk_get_vector(self._chunk, i);
+ self._columns[i] = col;
+ self._validities[i] = c.duckdb_vector_get_validity(col);
+ self._data[i] = c.duckdb_vector_get_data(col);
+ }
+ self._current_row = 0;
+
}
pub fn exausted(self: Self) bool{
@@ -73,10 +86,63 @@ pub fn Result(comptime T: type) type{
/// We need some comptime magic to create the output structures from
/// the T.
- pub fn next(self: *Self) T{
- const result: T = undefined;
- _ = self;
+ pub fn next(self: *Self) !T{
+ var result: T = undefined;
+ const fields = comptime switch (@typeInfo(T)) {
+ .Void => .{},
+ else => |f| f.Struct.fields,
+ };
+
+ if (self._current_row == self.getCurrentChunkSize()){
+ self.fetchDataChunk();
+ }
+
+ inline for (fields, 0..) |field, i| {
+ // TODO: check compatibility between the column type and
+ // the struct provided as result container
+ //
+ // 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});
+
+ // Check validity
+ const entry_idx :usize = self._current_row / 64;
+ const idx_in_entry :u6 = @intCast(@mod(self._current_row, 64));
+ const one :u64 = 1;
+ var is_valid :bool = false;
+ if (self._validities[i]) |v| {
+ const num = v[entry_idx] & @shlExact(one, idx_in_entry);
+ is_valid = num != 0;
+ }
+
+ // Store the column in current row
+ if (is_valid){
+ // Unwrap the Optional
+ const t = switch (@typeInfo(field.type)){
+ .Optional => |t| t.child,
+ else => field.type
+ };
+ const col : [*]t = @alignCast(@ptrCast(self._data[i]));
+ @field(result, field.name) = col[self._current_row];
+ } else {
+ // Got a NULL from the DB
+ if (@typeInfo(field.type) != .Optional){
+ // Cannot return it because it's not optional
+ return error.NullInNotOptional;
+ }
+ @field(result, field.name) = null;
+ }
+ }
+ self._current_row += 1;
return result;
}
+
+
+
+ // pub fn giveMeAll(){
+ // // TODO: know the chunk length and use it
+ // while (self._current_row < self._chunk_size) : (self._current_row += 1){
+ // }
+ // }
};
}