diff options
| author | 2020-11-11 13:51:00 +0100 | |
|---|---|---|
| committer | 2020-11-11 13:51:00 +0100 | |
| commit | ad4092a87a1f81b217d63ffaa36fb2f1e4a77711 (patch) | |
| tree | e41d127fdc0b7fc6317fc2a8ba7d310ca6937c0f | |
| parent | add types to bind markers and check them at comptime (diff) | |
| download | zig-sqlite-ad4092a87a1f81b217d63ffaa36fb2f1e4a77711.tar.gz zig-sqlite-ad4092a87a1f81b217d63ffaa36fb2f1e4a77711.tar.xz zig-sqlite-ad4092a87a1f81b217d63ffaa36fb2f1e4a77711.zip | |
add readBytes and allow reading into a Text or Blob struct
| -rw-r--r-- | sqlite.zig | 94 |
1 files changed, 80 insertions, 14 deletions
| @@ -109,6 +109,7 @@ pub const Db = struct { | |||
| 109 | /// This is done because we type check the bind parameters when executing the statement later. | 109 | /// This is done because we type check the bind parameters when executing the statement later. |
| 110 | /// | 110 | /// |
| 111 | pub fn prepare(self: *Self, comptime query: []const u8) !Statement(.{}, ParsedQuery.from(query)) { | 111 | pub fn prepare(self: *Self, comptime query: []const u8) !Statement(.{}, ParsedQuery.from(query)) { |
| 112 | @setEvalBranchQuota(3000); | ||
| 112 | const parsed_query = ParsedQuery.from(query); | 113 | const parsed_query = ParsedQuery.from(query); |
| 113 | return Statement(.{}, comptime parsed_query).prepare(self, 0); | 114 | return Statement(.{}, comptime parsed_query).prepare(self, 0); |
| 114 | } | 115 | } |
| @@ -371,6 +372,39 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: ParsedQuery) t | |||
| 371 | return @intCast(Type, n); | 372 | return @intCast(Type, n); |
| 372 | } | 373 | } |
| 373 | 374 | ||
| 375 | const ReadBytesMode = enum { | ||
| 376 | Blob, | ||
| 377 | Text, | ||
| 378 | }; | ||
| 379 | |||
| 380 | fn readBytes(self: *Self, allocator: *mem.Allocator, mode: ReadBytesMode, _i: usize) !?[]const u8 { | ||
| 381 | const i = @intCast(c_int, _i); | ||
| 382 | switch (mode) { | ||
| 383 | .Blob => { | ||
| 384 | const data = c.sqlite3_column_blob(self.stmt, i); | ||
| 385 | if (data == null) return null; | ||
| 386 | |||
| 387 | const size = @intCast(usize, c.sqlite3_column_bytes(self.stmt, i)); | ||
| 388 | |||
| 389 | var tmp = try allocator.alloc(u8, size); | ||
| 390 | mem.copy(u8, tmp, @ptrCast([*c]const u8, data)[0..size]); | ||
| 391 | |||
| 392 | return tmp; | ||
| 393 | }, | ||
| 394 | .Text => { | ||
| 395 | const data = c.sqlite3_column_text(self.stmt, i); | ||
| 396 | if (data == null) return null; | ||
| 397 | |||
| 398 | const size = @intCast(usize, c.sqlite3_column_bytes(self.stmt, i)); | ||
| 399 | |||
| 400 | var tmp = try allocator.alloc(u8, size); | ||
| 401 | mem.copy(u8, tmp, @ptrCast([*c]const u8, data)[0..size]); | ||
| 402 | |||
| 403 | return tmp; | ||
| 404 | }, | ||
| 405 | } | ||
| 406 | } | ||
| 407 | |||
| 374 | fn readStruct(self: *Self, comptime Type: type, options: anytype) !Type { | 408 | fn readStruct(self: *Self, comptime Type: type, options: anytype) !Type { |
| 375 | var value: Type = undefined; | 409 | var value: Type = undefined; |
| 376 | 410 | ||
| @@ -379,18 +413,20 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: ParsedQuery) t | |||
| 379 | const field_type_info = @typeInfo(field.field_type); | 413 | const field_type_info = @typeInfo(field.field_type); |
| 380 | 414 | ||
| 381 | switch (field.field_type) { | 415 | switch (field.field_type) { |
| 382 | []const u8, []u8 => { | 416 | []const u8, []u8 => if (try self.readBytes(options.allocator, .Blob, i)) |tmp| { |
| 383 | const data = c.sqlite3_column_blob(self.stmt, i); | 417 | @field(value, field.name) = tmp; |
| 384 | if (data == null) { | 418 | } else { |
| 385 | @field(value, field.name) = ""; | 419 | @field(value, field.name) = ""; |
| 386 | } else { | 420 | }, |
| 387 | const size = @intCast(usize, c.sqlite3_column_bytes(self.stmt, i)); | 421 | Blob => if (try self.readBytes(options.allocator, .Blob, i)) |tmp| { |
| 388 | 422 | @field(value, field.name).data = tmp; | |
| 389 | var tmp = try options.allocator.alloc(u8, size); | 423 | } else { |
| 390 | mem.copy(u8, tmp, @ptrCast([*c]const u8, data)[0..size]); | 424 | @field(value, field.name).data = ""; |
| 391 | 425 | }, | |
| 392 | @field(value, field.name) = tmp; | 426 | Text => if (try self.readBytes(options.allocator, .Text, i)) |tmp| { |
| 393 | } | 427 | @field(value, field.name).data = tmp; |
| 428 | } else { | ||
| 429 | @field(value, field.name).data = ""; | ||
| 394 | }, | 430 | }, |
| 395 | else => switch (field_type_info) { | 431 | else => switch (field_type_info) { |
| 396 | .Int => { | 432 | .Int => { |
| @@ -549,8 +585,7 @@ test "sqlite: statement exec" { | |||
| 549 | testing.expectEqual(@as(usize, 33), age.?); | 585 | testing.expectEqual(@as(usize, 33), age.?); |
| 550 | } | 586 | } |
| 551 | 587 | ||
| 552 | // Test with a Bytes struct | 588 | // Test with a Blob struct |
| 553 | |||
| 554 | { | 589 | { |
| 555 | try db.exec("INSERT INTO user(id, name, age) VALUES(?{usize}, ?{blob}, ?{u32})", .{ | 590 | try db.exec("INSERT INTO user(id, name, age) VALUES(?{usize}, ?{blob}, ?{u32})", .{ |
| 556 | .id = @as(usize, 200), | 591 | .id = @as(usize, 200), |
| @@ -558,6 +593,37 @@ test "sqlite: statement exec" { | |||
| 558 | .age = @as(u32, 20), | 593 | .age = @as(u32, 20), |
| 559 | }); | 594 | }); |
| 560 | } | 595 | } |
| 596 | |||
| 597 | // Test with a Text struct | ||
| 598 | { | ||
| 599 | try db.exec("INSERT INTO user(id, name, age) VALUES(?{usize}, ?{text}, ?{u32})", .{ | ||
| 600 | .id = @as(usize, 201), | ||
| 601 | .name = Text{ .data = "hello" }, | ||
| 602 | .age = @as(u32, 20), | ||
| 603 | }); | ||
| 604 | } | ||
| 605 | |||
| 606 | // Read in a Text struct | ||
| 607 | { | ||
| 608 | var stmt = try db.prepare("SELECT id, name, age FROM user WHERE id = ?{usize}"); | ||
| 609 | defer stmt.deinit(); | ||
| 610 | |||
| 611 | var row = try stmt.one( | ||
| 612 | struct { | ||
| 613 | id: usize, | ||
| 614 | name: Text, | ||
| 615 | age: usize, | ||
| 616 | }, | ||
| 617 | .{ .allocator = allocator }, | ||
| 618 | .{@as(usize, 20)}, | ||
| 619 | ); | ||
| 620 | testing.expect(row != null); | ||
| 621 | |||
| 622 | const exp = users[0]; | ||
| 623 | testing.expectEqual(exp.id, row.?.id); | ||
| 624 | testing.expectEqualStrings(exp.name, row.?.name.data); | ||
| 625 | testing.expectEqual(exp.age, row.?.age); | ||
| 626 | } | ||
| 561 | } | 627 | } |
| 562 | 628 | ||
| 563 | fn dbMode() Db.Mode { | 629 | fn dbMode() Db.Mode { |