diff options
| author | 2021-01-06 01:29:01 +0100 | |
|---|---|---|
| committer | 2021-01-06 01:29:01 +0100 | |
| commit | e35220eb184e15219af0ba279423c529299e2b61 (patch) | |
| tree | 11d871c9372976bb9dc29bebbd49c658f80b1f28 | |
| parent | ci: aarch64 builds are super slow, don't do them twice (diff) | |
| parent | readme: mention that pointers are allowed (diff) | |
| download | zig-sqlite-e35220eb184e15219af0ba279423c529299e2b61.tar.gz zig-sqlite-e35220eb184e15219af0ba279423c529299e2b61.tar.xz zig-sqlite-e35220eb184e15219af0ba279423c529299e2b61.zip | |
Merge pull request #12 from vrischmann/read-pointers
Read pointers
Diffstat (limited to '')
| -rw-r--r-- | README.md | 2 | ||||
| -rw-r--r-- | sqlite.zig | 111 |
2 files changed, 80 insertions, 33 deletions
| @@ -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 | ||
| 141 | The type can be a pointer but only when using the methods taking an allocator. | ||
| 142 | |||
| 141 | Not all types are allowed, see the section "Bind parameters and resultset rows" for more information on the types mapping rules. | 143 | Not 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 |
| @@ -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 | ||
| 891 | const TestUser = struct { | 901 | const 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 | ||
| 898 | const test_users = &[_]TestUser{ | 908 | const 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 | ||
| 904 | fn addTestData(db: *Db) !void { | 914 | fn 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 | ||
| 1348 | test "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 | |||
| 1338 | test "sqlite: statement reset" { | 1383 | test "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); |