summaryrefslogtreecommitdiff
path: root/sqlite.zig
diff options
context:
space:
mode:
authorGravatar Vincent Rischmann2020-11-11 13:51:00 +0100
committerGravatar Vincent Rischmann2020-11-11 13:51:00 +0100
commitad4092a87a1f81b217d63ffaa36fb2f1e4a77711 (patch)
treee41d127fdc0b7fc6317fc2a8ba7d310ca6937c0f /sqlite.zig
parentadd types to bind markers and check them at comptime (diff)
downloadzig-sqlite-ad4092a87a1f81b217d63ffaa36fb2f1e4a77711.tar.gz
zig-sqlite-ad4092a87a1f81b217d63ffaa36fb2f1e4a77711.tar.xz
zig-sqlite-ad4092a87a1f81b217d63ffaa36fb2f1e4a77711.zip
add readBytes and allow reading into a Text or Blob struct
Diffstat (limited to 'sqlite.zig')
-rw-r--r--sqlite.zig94
1 files changed, 80 insertions, 14 deletions
diff --git a/sqlite.zig b/sqlite.zig
index a30da70..4e92e6b 100644
--- a/sqlite.zig
+++ b/sqlite.zig
@@ -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
563fn dbMode() Db.Mode { 629fn dbMode() Db.Mode {