diff options
author | Ekaitz Zarraga <ekaitz@elenq.tech> | 2024-08-01 22:33:27 +0200 |
---|---|---|
committer | Ekaitz Zarraga <ekaitz@elenq.tech> | 2024-08-01 22:45:22 +0200 |
commit | 3cf6abc4db67436200f60843fee8f81e052d0880 (patch) | |
tree | 8ffebdb191b316427ecbb47a2f661d323e572903 /src/duckdb | |
parent | a86a078a9707bdc3b0bd7ce9c8d72a89c8c942b4 (diff) |
Simple querying passing!
Diffstat (limited to 'src/duckdb')
-rw-r--r-- | src/duckdb/Result.zig | 114 | ||||
-rw-r--r-- | src/duckdb/db.zig | 26 |
2 files changed, 110 insertions, 30 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){ + // } + // } }; } diff --git a/src/duckdb/db.zig b/src/duckdb/db.zig index 134a1f4..63826e4 100644 --- a/src/duckdb/db.zig +++ b/src/duckdb/db.zig @@ -55,22 +55,36 @@ test "Open and connect" { defer connection.deinit(); } -test "Query size" { +test "Simple querying" { var database = try Database.init(":memory:"); defer database.deinit(); var connection = try database.connect(); defer connection.deinit(); const s : type = comptime struct { - primer: u8, - segund: u8 + primer: i32, // This is safe because the first column is NOT NULL + segund: ?i32 }; - _ = try connection.query("CREATE TABLE integers (i INTEGER, j INTEGER);", void); - _ = try connection.query("INSERT INTO integers VALUES (3, 4), (5, 6), (7, NULL);", void); + var x = try connection.query("CREATE TABLE integers (i INTEGER NOT NULL, j INTEGER);", void); + x.deinit(); + var y = try connection.query("INSERT INTO integers VALUES (3, 4), (5, 6), (7, NULL);", void); + y.deinit(); var result = try connection.query("SELECT * FROM integers;", s); - _ = result.next(); defer result.deinit(); + try std.testing.expect(2 == result.getColumnCount()); + + var z = try result.next(); + try std.testing.expect(z.primer == 3); + try std.testing.expect(z.segund.? == 4); + + z = try result.next(); + try std.testing.expect(z.primer == 5); + try std.testing.expect(z.segund.? == 6); + + z = try result.next(); + try std.testing.expect(z.primer == 7); + try std.testing.expect(z.segund == null); } |