diff options
| author | 2020-12-18 02:46:53 +0100 | |
|---|---|---|
| committer | 2020-12-21 00:18:06 +0100 | |
| commit | 0db9b6ac7bcab8dd641058c3f7795e0e638916d4 (patch) | |
| tree | 0cf85b02e4fb5d076c31a998dbda11473b958851 /sqlite.zig | |
| parent | ci: add the build manifest for alpine/edge amd64 (diff) | |
| download | zig-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.
Diffstat (limited to '')
| -rw-r--r-- | sqlite.zig | 65 |
1 files changed, 52 insertions, 13 deletions
| @@ -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 | } |