diff options
Diffstat (limited to 'sqlite.zig')
| -rw-r--r-- | sqlite.zig | 100 |
1 files changed, 73 insertions, 27 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); |
| @@ -248,10 +252,10 @@ pub fn Iterator(comptime Type: type) type { | |||
| 248 | // If the array is too small for the data an error will be returned. | 252 | // If the array is too small for the data an error will be returned. |
| 249 | fn readArray(self: *Self, comptime ArrayType: type, _i: usize) error{ArrayTooSmall}!ArrayType { | 253 | fn readArray(self: *Self, comptime ArrayType: type, _i: usize) error{ArrayTooSmall}!ArrayType { |
| 250 | const i = @intCast(c_int, _i); | 254 | const i = @intCast(c_int, _i); |
| 251 | const array_type_info = @typeInfo(ArrayType); | 255 | const type_info = @typeInfo(ArrayType); |
| 252 | 256 | ||
| 253 | var ret: ArrayType = undefined; | 257 | var ret: ArrayType = undefined; |
| 254 | switch (array_type_info) { | 258 | switch (type_info) { |
| 255 | .Array => |arr| { | 259 | .Array => |arr| { |
| 256 | comptime if (arr.sentinel == null) { | 260 | comptime if (arr.sentinel == null) { |
| 257 | @compileError("cannot populate array of " ++ @typeName(arr.child) ++ ", arrays must have a sentinel"); | 261 | @compileError("cannot populate array of " ++ @typeName(arr.child) ++ ", arrays must have a sentinel"); |
| @@ -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,48 +335,70 @@ 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); | 379 | }, |
| 351 | break :blk tmp; | 380 | } |
| 381 | } | ||
| 382 | |||
| 383 | fn readPointer(self: *Self, comptime PointerType: type, i: usize, options: anytype) !PointerType { | ||
| 384 | const type_info = @typeInfo(PointerType); | ||
| 385 | |||
| 386 | var ret: PointerType = undefined; | ||
| 387 | switch (type_info) { | ||
| 388 | .Pointer => |ptr| { | ||
| 389 | switch (ptr.size) { | ||
| 390 | .One => unreachable, | ||
| 391 | .Slice => switch (ptr.child) { | ||
| 392 | u8 => ret = try self.readBytes(PointerType, i, .Text, options), | ||
| 393 | else => @compileError("cannot read pointer of type " ++ @typeName(PointerType)), | ||
| 352 | }, | 394 | }, |
| 353 | else => @compileError("cannot read text into type " ++ @typeName(BytesType)), | 395 | else => @compileError("cannot read pointer of type " ++ @typeName(PointerType)), |
| 354 | }; | 396 | } |
| 355 | }, | 397 | }, |
| 398 | else => @compileError("cannot read pointer of type " ++ @typeName(PointerType)), | ||
| 356 | } | 399 | } |
| 400 | |||
| 401 | return ret; | ||
| 357 | } | 402 | } |
| 358 | 403 | ||
| 359 | // readStruct reads an entire sqlite row into a struct. | 404 | // readStruct reads an entire sqlite row into a struct. |
| @@ -385,7 +430,6 @@ pub fn Iterator(comptime Type: type) type { | |||
| 385 | const field_type_info = @typeInfo(field.field_type); | 430 | const field_type_info = @typeInfo(field.field_type); |
| 386 | 431 | ||
| 387 | const ret = switch (field.field_type) { | 432 | const ret = switch (field.field_type) { |
| 388 | []const u8, []u8 => try self.readBytes(field.field_type, i, .Blob, options), | ||
| 389 | Blob => try self.readBytes(Blob, i, .Blob, options), | 433 | Blob => try self.readBytes(Blob, i, .Blob, options), |
| 390 | Text => try self.readBytes(Text, i, .Text, options), | 434 | Text => try self.readBytes(Text, i, .Text, options), |
| 391 | else => switch (field_type_info) { | 435 | else => switch (field_type_info) { |
| @@ -394,6 +438,7 @@ pub fn Iterator(comptime Type: type) type { | |||
| 394 | .Bool => try self.readBool(i, options), | 438 | .Bool => try self.readBool(i, options), |
| 395 | .Void => {}, | 439 | .Void => {}, |
| 396 | .Array => try self.readArray(field.field_type, i), | 440 | .Array => try self.readArray(field.field_type, i), |
| 441 | .Pointer => try self.readPointer(field.field_type, i, options), | ||
| 397 | else => @compileError("cannot populate field " ++ field.name ++ " of type " ++ @typeName(field.field_type)), | 442 | else => @compileError("cannot populate field " ++ field.name ++ " of type " ++ @typeName(field.field_type)), |
| 398 | }, | 443 | }, |
| 399 | }; | 444 | }; |
| @@ -905,16 +950,17 @@ test "sqlite: read a single text value" { | |||
| 905 | try db.init(testing.allocator, .{ .mode = dbMode() }); | 950 | try db.init(testing.allocator, .{ .mode = dbMode() }); |
| 906 | try addTestData(&db); | 951 | try addTestData(&db); |
| 907 | 952 | ||
| 908 | // TODO(vincent): implement the following | ||
| 909 | // [:0]const u8 | ||
| 910 | // [:0]u8 | ||
| 911 | |||
| 912 | const types = &[_]type{ | 953 | const types = &[_]type{ |
| 913 | // Slices | 954 | // Slices |
| 914 | []const u8, | 955 | []const u8, |
| 915 | []u8, | 956 | []u8, |
| 957 | [:0]const u8, | ||
| 958 | [:0]u8, | ||
| 959 | [:0xAD]const u8, | ||
| 960 | [:0xAD]u8, | ||
| 916 | // Array | 961 | // Array |
| 917 | [8:0]u8, | 962 | [8:0]u8, |
| 963 | [8:0xAD]u8, | ||
| 918 | // Specific text or blob | 964 | // Specific text or blob |
| 919 | Text, | 965 | Text, |
| 920 | Blob, | 966 | Blob, |