diff options
| author | 2020-12-21 02:35:40 +0100 | |
|---|---|---|
| committer | 2020-12-21 02:35:40 +0100 | |
| commit | 9221d3056be82bb18d601df790aebfae30d850a7 (patch) | |
| tree | 1c4cf1c71bd34ad172fcbd43d06036bf0ad2f94f /sqlite.zig | |
| parent | add the readPointer method (diff) | |
| download | zig-sqlite-9221d3056be82bb18d601df790aebfae30d850a7.tar.gz zig-sqlite-9221d3056be82bb18d601df790aebfae30d850a7.tar.xz zig-sqlite-9221d3056be82bb18d601df790aebfae30d850a7.zip | |
implement reading of sentineled slices
Diffstat (limited to 'sqlite.zig')
| -rw-r--r-- | sqlite.zig | 75 |
1 files changed, 50 insertions, 25 deletions
| @@ -232,6 +232,10 @@ pub fn Iterator(comptime Type: type) type { | |||
| 232 | debug.assert(columns == 1); | 232 | debug.assert(columns == 1); |
| 233 | return try self.readArray(Type, 0); | 233 | return try self.readArray(Type, 0); |
| 234 | }, | 234 | }, |
| 235 | .Pointer => { | ||
| 236 | debug.assert(columns == 1); | ||
| 237 | return try self.readPointer(Type, 0, options); | ||
| 238 | }, | ||
| 235 | .Struct => { | 239 | .Struct => { |
| 236 | std.debug.assert(columns == TypeInfo.Struct.fields.len); | 240 | std.debug.assert(columns == TypeInfo.Struct.fields.len); |
| 237 | return try self.readStruct(options); | 241 | return try self.readStruct(options); |
| @@ -300,6 +304,25 @@ pub fn Iterator(comptime Type: type) type { | |||
| 300 | Text, | 304 | Text, |
| 301 | }; | 305 | }; |
| 302 | 306 | ||
| 307 | // dupeWithSentinel is like dupe/dupeZ but allows for any sentinel value. | ||
| 308 | fn dupeWithSentinel(comptime SliceType: type, allocator: *mem.Allocator, data: []const u8) !SliceType { | ||
| 309 | const type_info = @typeInfo(SliceType); | ||
| 310 | switch (type_info) { | ||
| 311 | .Pointer => |ptr_info| { | ||
| 312 | if (ptr_info.sentinel) |sentinel| { | ||
| 313 | const slice = try allocator.alloc(u8, data.len + 1); | ||
| 314 | mem.copy(u8, slice, data); | ||
| 315 | slice[data.len] = sentinel; | ||
| 316 | |||
| 317 | return slice[0..data.len :sentinel]; | ||
| 318 | } else { | ||
| 319 | return try allocator.dupe(u8, data); | ||
| 320 | } | ||
| 321 | }, | ||
| 322 | else => @compileError("cannot dupe type " ++ @typeName(SliceType)), | ||
| 323 | } | ||
| 324 | } | ||
| 325 | |||
| 303 | // readBytes reads a sqlite BLOB or TEXT column. | 326 | // readBytes reads a sqlite BLOB or TEXT column. |
| 304 | // | 327 | // |
| 305 | // The mode controls which sqlite function is used to retrieve the data: | 328 | // The mode controls which sqlite function is used to retrieve the data: |
| @@ -312,46 +335,47 @@ pub fn Iterator(comptime Type: type) type { | |||
| 312 | // The options must contain an `allocator` field which will be used to create a copy of the data. | 335 | // The options must contain an `allocator` field which will be used to create a copy of the data. |
| 313 | fn readBytes(self: *Self, comptime BytesType: type, _i: usize, comptime mode: ReadBytesMode, options: anytype) !BytesType { | 336 | fn readBytes(self: *Self, comptime BytesType: type, _i: usize, comptime mode: ReadBytesMode, options: anytype) !BytesType { |
| 314 | const i = @intCast(c_int, _i); | 337 | const i = @intCast(c_int, _i); |
| 338 | const type_info = @typeInfo(BytesType); | ||
| 315 | 339 | ||
| 316 | var ret: BytesType = switch (BytesType) { | 340 | var ret: BytesType = switch (BytesType) { |
| 317 | Text, Blob => .{ .data = "" }, | 341 | Text, Blob => .{ .data = "" }, |
| 318 | else => "", // TODO(vincent): I think with a []u8 this will crash if the caller attempts to modify it... | 342 | else => try dupeWithSentinel(BytesType, options.allocator, ""), |
| 319 | }; | 343 | }; |
| 320 | 344 | ||
| 321 | switch (mode) { | 345 | switch (mode) { |
| 322 | .Blob => { | 346 | .Blob => { |
| 323 | const data = c.sqlite3_column_blob(self.stmt, i); | 347 | const data = c.sqlite3_column_blob(self.stmt, i); |
| 324 | if (data == null) return ret; | 348 | if (data == null) { |
| 349 | return switch (BytesType) { | ||
| 350 | Text, Blob => .{ .data = try options.allocator.dupe(u8, "") }, | ||
| 351 | else => try dupeWithSentinel(BytesType, options.allocator, ""), | ||
| 352 | }; | ||
| 353 | } | ||
| 325 | 354 | ||
| 326 | const size = @intCast(usize, c.sqlite3_column_bytes(self.stmt, i)); | 355 | const size = @intCast(usize, c.sqlite3_column_bytes(self.stmt, i)); |
| 327 | const ptr = @ptrCast([*c]const u8, data)[0..size]; | 356 | const ptr = @ptrCast([*c]const u8, data)[0..size]; |
| 328 | 357 | ||
| 329 | return switch (BytesType) { | 358 | if (BytesType == Blob) { |
| 330 | []const u8, []u8 => try options.allocator.dupe(u8, ptr), | 359 | return Blob{ .data = try options.allocator.dupe(u8, ptr) }; |
| 331 | Blob => blk: { | 360 | } |
| 332 | var tmp: Blob = undefined; | 361 | return try dupeWithSentinel(BytesType, options.allocator, ptr); |
| 333 | tmp.data = try options.allocator.dupe(u8, ptr); | ||
| 334 | break :blk tmp; | ||
| 335 | }, | ||
| 336 | else => @compileError("cannot read blob into type " ++ @typeName(BytesType)), | ||
| 337 | }; | ||
| 338 | }, | 362 | }, |
| 339 | .Text => { | 363 | .Text => { |
| 340 | const data = c.sqlite3_column_text(self.stmt, i); | 364 | const data = c.sqlite3_column_text(self.stmt, i); |
| 341 | if (data == null) return ret; | 365 | if (data == null) { |
| 366 | return switch (BytesType) { | ||
| 367 | Text, Blob => .{ .data = try options.allocator.dupe(u8, "") }, | ||
| 368 | else => try dupeWithSentinel(BytesType, options.allocator, ""), | ||
| 369 | }; | ||
| 370 | } | ||
| 342 | 371 | ||
| 343 | const size = @intCast(usize, c.sqlite3_column_bytes(self.stmt, i)); | 372 | const size = @intCast(usize, c.sqlite3_column_bytes(self.stmt, i)); |
| 344 | const ptr = @ptrCast([*c]const u8, data)[0..size]; | 373 | const ptr = @ptrCast([*c]const u8, data)[0..size]; |
| 345 | 374 | ||
| 346 | return switch (BytesType) { | 375 | if (BytesType == Text) { |
| 347 | []const u8, []u8 => try options.allocator.dupe(u8, ptr), | 376 | return Text{ .data = try options.allocator.dupe(u8, ptr) }; |
| 348 | Text => blk: { | 377 | } |
| 349 | var tmp: Text = undefined; | 378 | return try dupeWithSentinel(BytesType, options.allocator, ptr); |
| 350 | tmp.data = try options.allocator.dupe(u8, ptr); | ||
| 351 | break :blk tmp; | ||
| 352 | }, | ||
| 353 | else => @compileError("cannot read text into type " ++ @typeName(BytesType)), | ||
| 354 | }; | ||
| 355 | }, | 379 | }, |
| 356 | } | 380 | } |
| 357 | } | 381 | } |
| @@ -926,16 +950,17 @@ test "sqlite: read a single text value" { | |||
| 926 | try db.init(testing.allocator, .{ .mode = dbMode() }); | 950 | try db.init(testing.allocator, .{ .mode = dbMode() }); |
| 927 | try addTestData(&db); | 951 | try addTestData(&db); |
| 928 | 952 | ||
| 929 | // TODO(vincent): implement the following | ||
| 930 | // [:0]const u8 | ||
| 931 | // [:0]u8 | ||
| 932 | |||
| 933 | const types = &[_]type{ | 953 | const types = &[_]type{ |
| 934 | // Slices | 954 | // Slices |
| 935 | []const u8, | 955 | []const u8, |
| 936 | []u8, | 956 | []u8, |
| 957 | [:0]const u8, | ||
| 958 | [:0]u8, | ||
| 959 | [:0xAD]const u8, | ||
| 960 | [:0xAD]u8, | ||
| 937 | // Array | 961 | // Array |
| 938 | [8:0]u8, | 962 | [8:0]u8, |
| 963 | [8:0xAD]u8, | ||
| 939 | // Specific text or blob | 964 | // Specific text or blob |
| 940 | Text, | 965 | Text, |
| 941 | Blob, | 966 | Blob, |