summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Vincent Rischmann2021-01-06 01:29:01 +0100
committerGravatar GitHub2021-01-06 01:29:01 +0100
commite35220eb184e15219af0ba279423c529299e2b61 (patch)
tree11d871c9372976bb9dc29bebbd49c658f80b1f28
parentci: aarch64 builds are super slow, don't do them twice (diff)
parentreadme: mention that pointers are allowed (diff)
downloadzig-sqlite-e35220eb184e15219af0ba279423c529299e2b61.tar.gz
zig-sqlite-e35220eb184e15219af0ba279423c529299e2b61.tar.xz
zig-sqlite-e35220eb184e15219af0ba279423c529299e2b61.zip
Merge pull request #12 from vrischmann/read-pointers
Read pointers
-rw-r--r--README.md2
-rw-r--r--sqlite.zig111
2 files changed, 80 insertions, 33 deletions
diff --git a/README.md b/README.md
index 0bd450c..ff070eb 100644
--- a/README.md
+++ b/README.md
@@ -138,6 +138,8 @@ The type represents a "row", it can be:
138* a struct where each field maps to the corresponding column in the resultset (so field 0 must map to column 1 and so on). 138* a struct where each field maps to the corresponding column in the resultset (so field 0 must map to column 1 and so on).
139* a single type, in that case the resultset must only return one column. 139* a single type, in that case the resultset must only return one column.
140 140
141The type can be a pointer but only when using the methods taking an allocator.
142
141Not all types are allowed, see the section "Bind parameters and resultset rows" for more information on the types mapping rules. 143Not all types are allowed, see the section "Bind parameters and resultset rows" for more information on the types mapping rules.
142 144
143### Non allocating 145### Non allocating
diff --git a/sqlite.zig b/sqlite.zig
index 20ccc6d..f68d13f 100644
--- a/sqlite.zig
+++ b/sqlite.zig
@@ -529,7 +529,12 @@ pub fn Iterator(comptime Type: type) type {
529 switch (type_info) { 529 switch (type_info) {
530 .Pointer => |ptr| { 530 .Pointer => |ptr| {
531 switch (ptr.size) { 531 switch (ptr.size) {
532 .One => unreachable, 532 .One => {
533 ret = try allocator.create(ptr.child);
534 errdefer allocator.destroy(ret);
535
536 ret.* = try self.readField(ptr.child, i, .{ .allocator = allocator });
537 },
533 .Slice => switch (ptr.child) { 538 .Slice => switch (ptr.child) {
534 u8 => ret = try self.readBytes(PointerType, allocator, i, .Text), 539 u8 => ret = try self.readBytes(PointerType, allocator, i, .Text),
535 else => @compileError("cannot read pointer of type " ++ @typeName(PointerType)), 540 else => @compileError("cannot read pointer of type " ++ @typeName(PointerType)),
@@ -569,27 +574,32 @@ pub fn Iterator(comptime Type: type) type {
569 574
570 inline for (@typeInfo(Type).Struct.fields) |field, _i| { 575 inline for (@typeInfo(Type).Struct.fields) |field, _i| {
571 const i = @as(usize, _i); 576 const i = @as(usize, _i);
572 const field_type_info = @typeInfo(field.field_type); 577
573 578 const ret = try self.readField(field.field_type, i, options);
574 const ret = switch (field.field_type) {
575 Blob => try self.readBytes(Blob, options.allocator, i, .Blob),
576 Text => try self.readBytes(Text, options.allocator, i, .Text),
577 else => switch (field_type_info) {
578 .Int => try self.readInt(field.field_type, i),
579 .Float => try self.readFloat(field.field_type, i),
580 .Bool => try self.readBool(i),
581 .Void => {},
582 .Array => try self.readArray(field.field_type, i),
583 .Pointer => try self.readPointer(field.field_type, options.allocator, i),
584 else => @compileError("cannot populate field " ++ field.name ++ " of type " ++ @typeName(field.field_type)),
585 },
586 };
587 579
588 @field(value, field.name) = ret; 580 @field(value, field.name) = ret;
589 } 581 }
590 582
591 return value; 583 return value;
592 } 584 }
585
586 fn readField(self: *Self, comptime FieldType: type, i: usize, options: anytype) !FieldType {
587 const field_type_info = @typeInfo(FieldType);
588
589 return switch (FieldType) {
590 Blob => try self.readBytes(Blob, options.allocator, i, .Blob),
591 Text => try self.readBytes(Text, options.allocator, i, .Text),
592 else => switch (field_type_info) {
593 .Int => try self.readInt(FieldType, i),
594 .Float => try self.readFloat(FieldType, i),
595 .Bool => try self.readBool(i),
596 .Void => {},
597 .Array => try self.readArray(FieldType, i),
598 .Pointer => try self.readPointer(FieldType, options.allocator, i),
599 else => @compileError("cannot populate field " ++ field.name ++ " of type " ++ @typeName(FieldType)),
600 },
601 };
602 }
593 }; 603 };
594} 604}
595 605
@@ -889,16 +899,16 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: ParsedQuery) t
889} 899}
890 900
891const TestUser = struct { 901const TestUser = struct {
892 id: usize,
893 name: []const u8, 902 name: []const u8,
903 id: usize,
894 age: usize, 904 age: usize,
895 weight: f32, 905 weight: f32,
896}; 906};
897 907
898const test_users = &[_]TestUser{ 908const test_users = &[_]TestUser{
899 .{ .id = 20, .name = "Vincent", .age = 33, .weight = 85.4 }, 909 .{ .name = "Vincent", .id = 20, .age = 33, .weight = 85.4 },
900 .{ .id = 40, .name = "Julien", .age = 35, .weight = 100.3 }, 910 .{ .name = "Julien", .id = 40, .age = 35, .weight = 100.3 },
901 .{ .id = 60, .name = "José", .age = 40, .weight = 240.2 }, 911 .{ .name = "José", .id = 60, .age = 40, .weight = 240.2 },
902}; 912};
903 913
904fn addTestData(db: *Db) !void { 914fn addTestData(db: *Db) !void {
@@ -925,7 +935,7 @@ fn addTestData(db: *Db) !void {
925 } 935 }
926 936
927 for (test_users) |user| { 937 for (test_users) |user| {
928 try db.exec("INSERT INTO user(id, name, age, weight) VALUES(?{usize}, ?{[]const u8}, ?{usize}, ?{f32})", user); 938 try db.exec("INSERT INTO user(name, id, age, weight) VALUES(?{[]const u8}, ?{usize}, ?{usize}, ?{f32})", user);
929 939
930 const rows_inserted = db.rowsAffected(); 940 const rows_inserted = db.rowsAffected();
931 testing.expectEqual(@as(usize, 1), rows_inserted); 941 testing.expectEqual(@as(usize, 1), rows_inserted);
@@ -1010,7 +1020,7 @@ test "sqlite: read a single user into a struct" {
1010 try db.init(initOptions()); 1020 try db.init(initOptions());
1011 try addTestData(&db); 1021 try addTestData(&db);
1012 1022
1013 var stmt = try db.prepare("SELECT id, name, age, weight FROM user WHERE id = ?{usize}"); 1023 var stmt = try db.prepare("SELECT name, id, age, weight FROM user WHERE id = ?{usize}");
1014 defer stmt.deinit(); 1024 defer stmt.deinit();
1015 1025
1016 var rows = try stmt.all(TestUser, &arena.allocator, .{}, .{ 1026 var rows = try stmt.all(TestUser, &arena.allocator, .{}, .{
@@ -1026,11 +1036,11 @@ test "sqlite: read a single user into a struct" {
1026 { 1036 {
1027 var row = try db.one( 1037 var row = try db.one(
1028 struct { 1038 struct {
1029 id: usize,
1030 name: [128:0]u8, 1039 name: [128:0]u8,
1040 id: usize,
1031 age: usize, 1041 age: usize,
1032 }, 1042 },
1033 "SELECT id, name, age FROM user WHERE id = ?{usize}", 1043 "SELECT name, id, age FROM user WHERE id = ?{usize}",
1034 .{}, 1044 .{},
1035 .{@as(usize, 20)}, 1045 .{@as(usize, 20)},
1036 ); 1046 );
@@ -1046,12 +1056,12 @@ test "sqlite: read a single user into a struct" {
1046 { 1056 {
1047 var row = try db.oneAlloc( 1057 var row = try db.oneAlloc(
1048 struct { 1058 struct {
1049 id: usize,
1050 name: Text, 1059 name: Text,
1060 id: usize,
1051 age: usize, 1061 age: usize,
1052 }, 1062 },
1053 &arena.allocator, 1063 &arena.allocator,
1054 "SELECT id, name, age FROM user WHERE id = ?{usize}", 1064 "SELECT name, id, age FROM user WHERE id = ?{usize}",
1055 .{}, 1065 .{},
1056 .{@as(usize, 20)}, 1066 .{@as(usize, 20)},
1057 ); 1067 );
@@ -1072,7 +1082,7 @@ test "sqlite: read all users into a struct" {
1072 try db.init(initOptions()); 1082 try db.init(initOptions());
1073 try addTestData(&db); 1083 try addTestData(&db);
1074 1084
1075 var stmt = try db.prepare("SELECT id, name, age, weight FROM user"); 1085 var stmt = try db.prepare("SELECT name, id, age, weight FROM user");
1076 defer stmt.deinit(); 1086 defer stmt.deinit();
1077 1087
1078 var rows = try stmt.all(TestUser, &arena.allocator, .{}, .{}); 1088 var rows = try stmt.all(TestUser, &arena.allocator, .{}, .{});
@@ -1094,13 +1104,13 @@ test "sqlite: read in an anonymous struct" {
1094 try db.init(initOptions()); 1104 try db.init(initOptions());
1095 try addTestData(&db); 1105 try addTestData(&db);
1096 1106
1097 var stmt = try db.prepare("SELECT id, name, name, age, id, weight FROM user WHERE id = ?{usize}"); 1107 var stmt = try db.prepare("SELECT name, id, name, age, id, weight FROM user WHERE id = ?{usize}");
1098 defer stmt.deinit(); 1108 defer stmt.deinit();
1099 1109
1100 var row = try stmt.oneAlloc( 1110 var row = try stmt.oneAlloc(
1101 struct { 1111 struct {
1102 id: usize,
1103 name: []const u8, 1112 name: []const u8,
1113 id: usize,
1104 name_2: [200:0xAD]u8, 1114 name_2: [200:0xAD]u8,
1105 age: usize, 1115 age: usize,
1106 is_id: bool, 1116 is_id: bool,
@@ -1129,13 +1139,13 @@ test "sqlite: read in a Text struct" {
1129 try db.init(initOptions()); 1139 try db.init(initOptions());
1130 try addTestData(&db); 1140 try addTestData(&db);
1131 1141
1132 var stmt = try db.prepare("SELECT id, name, age FROM user WHERE id = ?{usize}"); 1142 var stmt = try db.prepare("SELECT name, id, age FROM user WHERE id = ?{usize}");
1133 defer stmt.deinit(); 1143 defer stmt.deinit();
1134 1144
1135 var row = try stmt.oneAlloc( 1145 var row = try stmt.oneAlloc(
1136 struct { 1146 struct {
1137 id: usize,
1138 name: Text, 1147 name: Text,
1148 id: usize,
1139 age: usize, 1149 age: usize,
1140 }, 1150 },
1141 &arena.allocator, 1151 &arena.allocator,
@@ -1335,6 +1345,41 @@ test "sqlite: bind pointer" {
1335 } 1345 }
1336} 1346}
1337 1347
1348test "sqlite: read pointers" {
1349 var arena = std.heap.ArenaAllocator.init(testing.allocator);
1350 defer arena.deinit();
1351
1352 var db: Db = undefined;
1353 try db.init(initOptions());
1354 try addTestData(&db);
1355
1356 const query = "SELECT id, name, age, weight FROM user";
1357
1358 var stmt = try db.prepare(query);
1359 defer stmt.deinit();
1360
1361 const rows = try stmt.all(
1362 struct {
1363 id: *usize,
1364 name: *[]const u8,
1365 age: *u32,
1366 weight: *f32,
1367 },
1368 &arena.allocator,
1369 .{},
1370 .{},
1371 );
1372
1373 testing.expectEqual(@as(usize, 3), rows.len);
1374 for (rows) |row, i| {
1375 const exp = test_users[i];
1376 testing.expectEqual(exp.id, row.id.*);
1377 testing.expectEqualStrings(exp.name, row.name.*);
1378 testing.expectEqual(exp.age, row.age.*);
1379 testing.expectEqual(exp.weight, row.weight.*);
1380 }
1381}
1382
1338test "sqlite: statement reset" { 1383test "sqlite: statement reset" {
1339 var db: Db = undefined; 1384 var db: Db = undefined;
1340 try db.init(initOptions()); 1385 try db.init(initOptions());
@@ -1342,7 +1387,7 @@ test "sqlite: statement reset" {
1342 1387
1343 // Add data 1388 // Add data
1344 1389
1345 var stmt = try db.prepare("INSERT INTO user(id, name, age, weight) VALUES(?{usize}, ?{[]const u8}, ?{usize}, ?{f32})"); 1390 var stmt = try db.prepare("INSERT INTO user(name, id, age, weight) VALUES(?{[]const u8}, ?{usize}, ?{usize}, ?{f32})");
1346 defer stmt.deinit(); 1391 defer stmt.deinit();
1347 1392
1348 const users = &[_]TestUser{ 1393 const users = &[_]TestUser{
@@ -1373,7 +1418,7 @@ test "sqlite: statement iterator" {
1373 try db.exec("DELETE FROM user", .{}); 1418 try db.exec("DELETE FROM user", .{});
1374 1419
1375 // Add data 1420 // Add data
1376 var stmt = try db.prepare("INSERT INTO user(id, name, age, weight) VALUES(?{usize}, ?{[]const u8}, ?{usize}, ?{f32})"); 1421 var stmt = try db.prepare("INSERT INTO user(name, id, age, weight) VALUES(?{[]const u8}, ?{usize}, ?{usize}, ?{f32})");
1377 defer stmt.deinit(); 1422 defer stmt.deinit();
1378 1423
1379 var expected_rows = std.ArrayList(TestUser).init(allocator); 1424 var expected_rows = std.ArrayList(TestUser).init(allocator);