summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Vincent Rischmann2020-12-18 02:46:53 +0100
committerGravatar Vincent Rischmann2020-12-21 00:18:06 +0100
commit0db9b6ac7bcab8dd641058c3f7795e0e638916d4 (patch)
tree0cf85b02e4fb5d076c31a998dbda11473b958851
parentci: add the build manifest for alpine/edge amd64 (diff)
downloadzig-sqlite-0db9b6ac7bcab8dd641058c3f7795e0e638916d4.tar.gz
zig-sqlite-0db9b6ac7bcab8dd641058c3f7795e0e638916d4.tar.xz
zig-sqlite-0db9b6ac7bcab8dd641058c3f7795e0e638916d4.zip
add reading a field into an array
We require having a sentineled array because otherwise we have no way of communicating to the caller the actual length of the data we put into the array.
-rw-r--r--sqlite.zig65
1 files changed, 52 insertions, 13 deletions
diff --git a/sqlite.zig b/sqlite.zig
index c479e5c..556188f 100644
--- a/sqlite.zig
+++ b/sqlite.zig
@@ -227,6 +227,12 @@ pub fn Iterator(comptime Type: type) type {
227 .Void => { 227 .Void => {
228 debug.assert(columns == 1); 228 debug.assert(columns == 1);
229 }, 229 },
230 .Array => {
231 debug.assert(columns == 1);
232 var ret: Type = undefined;
233 try self.readArray(Type, &ret);
234 return ret;
235 },
230 .Struct => { 236 .Struct => {
231 std.debug.assert(columns == TypeInfo.Struct.fields.len); 237 std.debug.assert(columns == TypeInfo.Struct.fields.len);
232 return try self.readStruct(options); 238 return try self.readStruct(options);
@@ -235,6 +241,32 @@ pub fn Iterator(comptime Type: type) type {
235 } 241 }
236 } 242 }
237 243
244 fn readArray(self: *Self, comptime ArrayType: type, array: anytype) !void {
245 const array_type_info = @typeInfo(ArrayType);
246
247 switch (array_type_info) {
248 .Array => |arr| {
249 comptime if (arr.sentinel == null) {
250 @compileError("cannot populate array of " ++ @typeName(arr.child) ++ ", arrays must have a sentinel");
251 };
252
253 switch (arr.child) {
254 u8 => {
255 const data = c.sqlite3_column_blob(self.stmt, 0);
256 const size = @intCast(usize, c.sqlite3_column_bytes(self.stmt, 0));
257
258 if (size >= @as(usize, arr.len)) return error.ArrayTooSmall;
259
260 mem.copy(u8, array[0..], @ptrCast([*c]const u8, data)[0..size]);
261 array[size] = arr.sentinel.?;
262 },
263 else => @compileError("cannot populate field " ++ field.name ++ " of type array of " ++ @typeName(arr.child)),
264 }
265 },
266 else => @compileError("cannot populate field " ++ field.name ++ " of type array of " ++ @typeName(arr.child)),
267 }
268 }
269
238 fn readInt(self: *Self, options: anytype) !Type { 270 fn readInt(self: *Self, options: anytype) !Type {
239 const n = c.sqlite3_column_int64(self.stmt, 0); 271 const n = c.sqlite3_column_int64(self.stmt, 0);
240 return @intCast(Type, n); 272 return @intCast(Type, n);
@@ -307,18 +339,8 @@ pub fn Iterator(comptime Type: type) type {
307 .Void => { 339 .Void => {
308 @field(value, field.name) = {}; 340 @field(value, field.name) = {};
309 }, 341 },
310 .Array => |arr| { 342 .Array => {
311 switch (arr.child) { 343 try self.readArray(field.fieldtype, &@field(value, field.name), .{});
312 u8 => {
313 const data = c.sqlite3_column_blob(self.stmt, i);
314 const size = @intCast(usize, c.sqlite3_column_bytes(self.stmt, i));
315
316 if (size > @as(usize, arr.len)) return error.ArrayTooSmall;
317
318 mem.copy(u8, @field(value, field.name)[0..], @ptrCast([*c]const u8, data)[0..size]);
319 },
320 else => @compileError("cannot populate field " ++ field.name ++ " of type array of " ++ @typeName(arr.child)),
321 }
322 }, 344 },
323 else => @compileError("cannot populate field " ++ field.name ++ " of type " ++ @typeName(field.field_type)), 345 else => @compileError("cannot populate field " ++ field.name ++ " of type " ++ @typeName(field.field_type)),
324 }, 346 },
@@ -819,9 +841,17 @@ test "sqlite: read a single text value" {
819 try db.init(testing.allocator, .{ .mode = dbMode() }); 841 try db.init(testing.allocator, .{ .mode = dbMode() });
820 try addTestData(&db); 842 try addTestData(&db);
821 843
844 // TODO(vincent): implement the following
845 // [:0]const u8
846 // [:0]u8
847
822 const types = &[_]type{ 848 const types = &[_]type{
849 // Slices
823 []const u8, 850 []const u8,
824 []u8, 851 []u8,
852 // Array
853 [8:0]u8,
854 // Specific text or blob
825 Text, 855 Text,
826 Blob, 856 Blob,
827 }; 857 };
@@ -843,7 +873,16 @@ test "sqlite: read a single text value" {
843 testing.expectEqualStrings("Vincent", name.?.data); 873 testing.expectEqualStrings("Vincent", name.?.data);
844 }, 874 },
845 else => { 875 else => {
846 testing.expectEqualStrings("Vincent", name.?); 876 const span = blk: {
877 const type_info = @typeInfo(typ);
878 break :blk switch (type_info) {
879 .Pointer => name.?,
880 .Array => mem.spanZ(&(name.?)),
881 else => @compileError("invalid type " ++ @typeName(typ)),
882 };
883 };
884
885 testing.expectEqualStrings("Vincent", span);
847 }, 886 },
848 } 887 }
849 } 888 }