summaryrefslogtreecommitdiff
path: root/sqlite.zig
diff options
context:
space:
mode:
authorGravatar Vincent Rischmann2020-12-31 15:28:26 +0100
committerGravatar GitHub2020-12-31 15:28:26 +0100
commit70ed4b3ae4c78adf5fbc5aada642158bf33d7c3e (patch)
tree78d32ef16cc107bcb3eaadc1c168b385b0cd164c /sqlite.zig
parentdocument OpenFlags (diff)
parentfix readme (diff)
downloadzig-sqlite-70ed4b3ae4c78adf5fbc5aada642158bf33d7c3e.tar.gz
zig-sqlite-70ed4b3ae4c78adf5fbc5aada642158bf33d7c3e.tar.xz
zig-sqlite-70ed4b3ae4c78adf5fbc5aada642158bf33d7c3e.zip
Merge pull request #9 from vrischmann/split-alloc
Split allocating from non-allocating methods
Diffstat (limited to '')
-rw-r--r--sqlite.zig318
1 files changed, 228 insertions, 90 deletions
diff --git a/sqlite.zig b/sqlite.zig
index e72c2d5..6abedc4 100644
--- a/sqlite.zig
+++ b/sqlite.zig
@@ -159,6 +159,30 @@ pub const Db = struct {
159 return getLastDetailedErrorFromDb(self.db); 159 return getLastDetailedErrorFromDb(self.db);
160 } 160 }
161 161
162 fn getPragmaQuery(comptime buf: []u8, comptime name: []const u8, comptime arg: anytype) []const u8 {
163 return if (arg.len == 1) blk: {
164 break :blk try std.fmt.bufPrint(buf, "PRAGMA {} = {}", .{ name, arg[0] });
165 } else blk: {
166 break :blk try std.fmt.bufPrint(buf, "PRAGMA {}", .{name});
167 };
168 }
169
170 /// pragmaAlloc is like `pragma` but can allocate memory.
171 ///
172 /// Useful when the pragma command returns text, for example:
173 ///
174 /// const journal_mode = try db.pragma([]const u8, allocator, .{}, "journal_mode", .{});
175 ///
176 pub fn pragmaAlloc(self: *Self, comptime Type: type, allocator: *mem.Allocator, options: anytype, comptime name: []const u8, comptime arg: anytype) !?Type {
177 comptime var buf: [1024]u8 = undefined;
178 comptime var query = getPragmaQuery(&buf, name, arg);
179
180 var stmt = try self.prepare(query);
181 defer stmt.deinit();
182
183 return try stmt.oneAlloc(Type, allocator, options, .{});
184 }
185
162 /// pragma is a convenience function to use the PRAGMA statement. 186 /// pragma is a convenience function to use the PRAGMA statement.
163 /// 187 ///
164 /// Here is how to set a pragma value: 188 /// Here is how to set a pragma value:
@@ -167,21 +191,14 @@ pub const Db = struct {
167 /// 191 ///
168 /// Here is how to query a pragama value: 192 /// Here is how to query a pragama value:
169 /// 193 ///
170 /// const journal_mode = try db.pragma( 194 /// const journal_mode = try db.pragma([128:0]const u8, .{}, "journal_mode", .{});
171 /// []const u8,
172 /// "journal_mode",
173 /// .{ .allocator = allocator },
174 /// .{},
175 /// );
176 /// 195 ///
177 /// The pragma name must be known at comptime. 196 /// The pragma name must be known at comptime.
178 pub fn pragma(self: *Self, comptime Type: type, comptime name: []const u8, options: anytype, arg: anytype) !?Type { 197 ///
198 /// This cannot allocate memory. If your pragma command returns text you must use an array or call `pragmaAlloc`.
199 pub fn pragma(self: *Self, comptime Type: type, options: anytype, comptime name: []const u8, arg: anytype) !?Type {
179 comptime var buf: [1024]u8 = undefined; 200 comptime var buf: [1024]u8 = undefined;
180 comptime var query = if (arg.len == 1) blk: { 201 comptime var query = getPragmaQuery(&buf, name, arg);
181 break :blk try std.fmt.bufPrint(&buf, "PRAGMA {} = {}", .{ name, arg[0] });
182 } else blk: {
183 break :blk try std.fmt.bufPrint(&buf, "PRAGMA {}", .{name});
184 };
185 202
186 var stmt = try self.prepare(query); 203 var stmt = try self.prepare(query);
187 defer stmt.deinit(); 204 defer stmt.deinit();
@@ -203,6 +220,13 @@ pub const Db = struct {
203 return try stmt.one(Type, options, values); 220 return try stmt.one(Type, options, values);
204 } 221 }
205 222
223 /// oneAlloc is like `one` but can allocate memory.
224 pub fn oneAlloc(self: *Self, comptime Type: type, allocator: *mem.Allocator, comptime query: []const u8, options: anytype, values: anytype) !?Type {
225 var stmt = try self.prepare(query);
226 defer stmt.deinit();
227 return try stmt.oneAlloc(Type, allocator, options, values);
228 }
229
206 /// prepare prepares a statement for the `query` provided. 230 /// prepare prepares a statement for the `query` provided.
207 /// 231 ///
208 /// The query is analysed at comptime to search for bind markers. 232 /// The query is analysed at comptime to search for bind markers.
@@ -259,16 +283,55 @@ pub fn Iterator(comptime Type: type) type {
259 stmt: *c.sqlite3_stmt, 283 stmt: *c.sqlite3_stmt,
260 284
261 // next scans the next row using the prepared statement. 285 // next scans the next row using the prepared statement.
262 //
263 // If it returns null iterating is done. 286 // If it returns null iterating is done.
287 //
288 // This cannot allocate memory. If you need to read TEXT or BLOB columns you need to use arrays or alternatively call nextAlloc.
264 pub fn next(self: *Self, options: anytype) !?Type { 289 pub fn next(self: *Self, options: anytype) !?Type {
265 var result = c.sqlite3_step(self.stmt); 290 var result = c.sqlite3_step(self.stmt);
266 if (result == c.SQLITE_DONE) { 291 if (result == c.SQLITE_DONE) {
267 return null; 292 return null;
268 } 293 }
294 if (result != c.SQLITE_ROW) {
295 return error.SQLiteStepError;
296 }
297
298 const columns = c.sqlite3_column_count(self.stmt);
269 299
300 switch (TypeInfo) {
301 .Int => {
302 debug.assert(columns == 1);
303 return try self.readInt(Type, 0);
304 },
305 .Float => {
306 debug.assert(columns == 1);
307 return try self.readFloat(Type, 0);
308 },
309 .Bool => {
310 debug.assert(columns == 1);
311 return try self.readBool(0);
312 },
313 .Void => {
314 debug.assert(columns == 1);
315 },
316 .Array => {
317 debug.assert(columns == 1);
318 return try self.readArray(Type, 0);
319 },
320 .Struct => {
321 std.debug.assert(columns == TypeInfo.Struct.fields.len);
322 return try self.readStruct(.{});
323 },
324 else => @compileError("cannot read into type " ++ @typeName(Type) ++ " ; if dynamic memory allocation is required use nextAlloc"),
325 }
326 }
327
328 // nextAlloc is like `next` but can allocate memory.
329 pub fn nextAlloc(self: *Self, allocator: *mem.Allocator, options: anytype) !?Type {
330 var result = c.sqlite3_step(self.stmt);
331 if (result == c.SQLITE_DONE) {
332 return null;
333 }
270 if (result != c.SQLITE_ROW) { 334 if (result != c.SQLITE_ROW) {
271 logger.err("unable to iterate, result: {}", .{result});
272 return error.SQLiteStepError; 335 return error.SQLiteStepError;
273 } 336 }
274 337
@@ -277,15 +340,15 @@ pub fn Iterator(comptime Type: type) type {
277 switch (Type) { 340 switch (Type) {
278 []const u8, []u8 => { 341 []const u8, []u8 => {
279 debug.assert(columns == 1); 342 debug.assert(columns == 1);
280 return try self.readBytes(Type, options.allocator, 0, .Text); 343 return try self.readBytes(Type, allocator, 0, .Text);
281 }, 344 },
282 Blob => { 345 Blob => {
283 debug.assert(columns == 1); 346 debug.assert(columns == 1);
284 return try self.readBytes(Blob, options.allocator, 0, .Blob); 347 return try self.readBytes(Blob, allocator, 0, .Blob);
285 }, 348 },
286 Text => { 349 Text => {
287 debug.assert(columns == 1); 350 debug.assert(columns == 1);
288 return try self.readBytes(Text, options.allocator, 0, .Text); 351 return try self.readBytes(Text, allocator, 0, .Text);
289 }, 352 },
290 else => {}, 353 else => {},
291 } 354 }
@@ -312,11 +375,13 @@ pub fn Iterator(comptime Type: type) type {
312 }, 375 },
313 .Pointer => { 376 .Pointer => {
314 debug.assert(columns == 1); 377 debug.assert(columns == 1);
315 return try self.readPointer(Type, 0, options); 378 return try self.readPointer(Type, allocator, 0);
316 }, 379 },
317 .Struct => { 380 .Struct => {
318 std.debug.assert(columns == TypeInfo.Struct.fields.len); 381 std.debug.assert(columns == TypeInfo.Struct.fields.len);
319 return try self.readStruct(options); 382 return try self.readStruct(.{
383 .allocator = allocator,
384 });
320 }, 385 },
321 else => @compileError("cannot read into type " ++ @typeName(Type)), 386 else => @compileError("cannot read into type " ++ @typeName(Type)),
322 } 387 }
@@ -458,7 +523,7 @@ pub fn Iterator(comptime Type: type) type {
458 } 523 }
459 } 524 }
460 525
461 fn readPointer(self: *Self, comptime PointerType: type, i: usize, options: anytype) !PointerType { 526 fn readPointer(self: *Self, comptime PointerType: type, allocator: *mem.Allocator, i: usize) !PointerType {
462 const type_info = @typeInfo(PointerType); 527 const type_info = @typeInfo(PointerType);
463 528
464 var ret: PointerType = undefined; 529 var ret: PointerType = undefined;
@@ -467,7 +532,7 @@ pub fn Iterator(comptime Type: type) type {
467 switch (ptr.size) { 532 switch (ptr.size) {
468 .One => unreachable, 533 .One => unreachable,
469 .Slice => switch (ptr.child) { 534 .Slice => switch (ptr.child) {
470 u8 => ret = try self.readBytes(PointerType, options.allocator, i, .Text), 535 u8 => ret = try self.readBytes(PointerType, allocator, i, .Text),
471 else => @compileError("cannot read pointer of type " ++ @typeName(PointerType)), 536 else => @compileError("cannot read pointer of type " ++ @typeName(PointerType)),
472 }, 537 },
473 else => @compileError("cannot read pointer of type " ++ @typeName(PointerType)), 538 else => @compileError("cannot read pointer of type " ++ @typeName(PointerType)),
@@ -516,7 +581,7 @@ pub fn Iterator(comptime Type: type) type {
516 .Bool => try self.readBool(i), 581 .Bool => try self.readBool(i),
517 .Void => {}, 582 .Void => {},
518 .Array => try self.readArray(field.field_type, i), 583 .Array => try self.readArray(field.field_type, i),
519 .Pointer => try self.readPointer(field.field_type, i, options), 584 .Pointer => try self.readPointer(field.field_type, options.allocator, i),
520 else => @compileError("cannot populate field " ++ field.name ++ " of type " ++ @typeName(field.field_type)), 585 else => @compileError("cannot populate field " ++ field.name ++ " of type " ++ @typeName(field.field_type)),
521 }, 586 },
522 }; 587 };
@@ -734,10 +799,10 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: ParsedQuery) t
734 /// const row = try stmt.one( 799 /// const row = try stmt.one(
735 /// struct { 800 /// struct {
736 /// id: usize, 801 /// id: usize,
737 /// name: []const u8, 802 /// name: [400]u8,
738 /// age: usize, 803 /// age: usize,
739 /// }, 804 /// },
740 /// .{ .allocator = allocator }, 805 /// .{},
741 /// .{ .foo = "bar", .age = 500 }, 806 /// .{ .foo = "bar", .age = 500 },
742 /// ); 807 /// );
743 /// 808 ///
@@ -747,6 +812,7 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: ParsedQuery) t
747 /// The `values` tuple is used for the bind parameters. It must have as many fields as there are bind markers 812 /// The `values` tuple is used for the bind parameters. It must have as many fields as there are bind markers
748 /// in the input query string. 813 /// in the input query string.
749 /// 814 ///
815 /// This cannot allocate memory. If you need to read TEXT or BLOB columns you need to use arrays or alternatively call `oneAlloc`.
750 pub fn one(self: *Self, comptime Type: type, options: anytype, values: anytype) !?Type { 816 pub fn one(self: *Self, comptime Type: type, options: anytype, values: anytype) !?Type {
751 if (!comptime std.meta.trait.is(.Struct)(@TypeOf(options))) { 817 if (!comptime std.meta.trait.is(.Struct)(@TypeOf(options))) {
752 @compileError("options passed to iterator must be a struct"); 818 @compileError("options passed to iterator must be a struct");
@@ -758,6 +824,18 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: ParsedQuery) t
758 return row; 824 return row;
759 } 825 }
760 826
827 /// oneAlloc is like `one` but can allocate memory.
828 pub fn oneAlloc(self: *Self, comptime Type: type, allocator: *mem.Allocator, options: anytype, values: anytype) !?Type {
829 if (!comptime std.meta.trait.is(.Struct)(@TypeOf(options))) {
830 @compileError("options passed to iterator must be a struct");
831 }
832
833 var iter = try self.iterator(Type, values);
834
835 const row = (try iter.nextAlloc(allocator, options)) orelse return null;
836 return row;
837 }
838
761 /// all reads all rows from the result set of this statement. 839 /// all reads all rows from the result set of this statement.
762 /// 840 ///
763 /// The data in each row is used to populate a value of the type `Type`. 841 /// The data in each row is used to populate a value of the type `Type`.
@@ -773,7 +851,8 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: ParsedQuery) t
773 /// name: []const u8, 851 /// name: []const u8,
774 /// age: usize, 852 /// age: usize,
775 /// }, 853 /// },
776 /// .{ .allocator = allocator }, 854 /// allocator,
855 /// .{},
777 /// .{ .foo = "bar", .age = 500 }, 856 /// .{ .foo = "bar", .age = 500 },
778 /// ); 857 /// );
779 /// 858 ///
@@ -783,18 +862,16 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: ParsedQuery) t
783 /// The `values` tuple is used for the bind parameters. It must have as many fields as there are bind markers 862 /// The `values` tuple is used for the bind parameters. It must have as many fields as there are bind markers
784 /// in the input query string. 863 /// in the input query string.
785 /// 864 ///
786 /// Note that this allocates all rows into a single slice: if you read a lot of data this can 865 /// Note that this allocates all rows into a single slice: if you read a lot of data this can use a lot of memory.
787 /// use a lot of memory. 866 pub fn all(self: *Self, comptime Type: type, allocator: *mem.Allocator, options: anytype, values: anytype) ![]Type {
788 ///
789 pub fn all(self: *Self, comptime Type: type, options: anytype, values: anytype) ![]Type {
790 if (!comptime std.meta.trait.is(.Struct)(@TypeOf(options))) { 867 if (!comptime std.meta.trait.is(.Struct)(@TypeOf(options))) {
791 @compileError("options passed to iterator must be a struct"); 868 @compileError("options passed to iterator must be a struct");
792 } 869 }
793 var iter = try self.iterator(Type, values); 870 var iter = try self.iterator(Type, values);
794 871
795 var rows = std.ArrayList(Type).init(options.allocator); 872 var rows = std.ArrayList(Type).init(allocator);
796 while (true) { 873 while (true) {
797 const row = (try iter.next(options)) orelse break; 874 const row = (try iter.nextAlloc(allocator, options)) orelse break;
798 try rows.append(row); 875 try rows.append(row);
799 } 876 }
800 877
@@ -860,28 +937,36 @@ test "sqlite: db pragma" {
860 var db: Db = undefined; 937 var db: Db = undefined;
861 try db.init(initOptions()); 938 try db.init(initOptions());
862 939
863 const foreign_keys = try db.pragma(usize, "foreign_keys", .{}, .{}); 940 const foreign_keys = try db.pragma(usize, .{}, "foreign_keys", .{});
864 testing.expect(foreign_keys != null); 941 testing.expect(foreign_keys != null);
865 testing.expectEqual(@as(usize, 0), foreign_keys.?); 942 testing.expectEqual(@as(usize, 0), foreign_keys.?);
866 943
944 const arg = .{"wal"};
945
867 if (build_options.in_memory) { 946 if (build_options.in_memory) {
868 const journal_mode = try db.pragma( 947 {
869 []const u8, 948 const journal_mode = try db.pragma([128:0]u8, .{}, "journal_mode", arg);
870 "journal_mode", 949 testing.expect(journal_mode != null);
871 .{ .allocator = &arena.allocator }, 950 testing.expectEqualStrings("memory", mem.spanZ(&journal_mode.?));
872 .{"wal"}, 951 }
873 ); 952
874 testing.expect(journal_mode != null); 953 {
875 testing.expectEqualStrings("memory", journal_mode.?); 954 const journal_mode = try db.pragmaAlloc([]const u8, &arena.allocator, .{}, "journal_mode", arg);
955 testing.expect(journal_mode != null);
956 testing.expectEqualStrings("memory", journal_mode.?);
957 }
876 } else { 958 } else {
877 const journal_mode = try db.pragma( 959 {
878 []const u8, 960 const journal_mode = try db.pragma([128:0]u8, .{}, "journal_mode", arg);
879 "journal_mode", 961 testing.expect(journal_mode != null);
880 .{ .allocator = &arena.allocator }, 962 testing.expectEqualStrings("wal", mem.spanZ(&journal_mode.?));
881 .{"wal"}, 963 }
882 ); 964
883 testing.expect(journal_mode != null); 965 {
884 testing.expectEqualStrings("wal", journal_mode.?); 966 const journal_mode = try db.pragmaAlloc([]const u8, &arena.allocator, .{}, "journal_mode", arg);
967 testing.expect(journal_mode != null);
968 testing.expectEqualStrings("wal", journal_mode.?);
969 }
885 } 970 }
886} 971}
887 972
@@ -920,11 +1005,9 @@ test "sqlite: read a single user into a struct" {
920 var stmt = try db.prepare("SELECT id, name, age, weight FROM user WHERE id = ?{usize}"); 1005 var stmt = try db.prepare("SELECT id, name, age, weight FROM user WHERE id = ?{usize}");
921 defer stmt.deinit(); 1006 defer stmt.deinit();
922 1007
923 var rows = try stmt.all( 1008 var rows = try stmt.all(TestUser, &arena.allocator, .{}, .{
924 TestUser, 1009 .id = @as(usize, 20),
925 .{ .allocator = &arena.allocator }, 1010 });
926 .{ .id = @as(usize, 20) },
927 );
928 for (rows) |row| { 1011 for (rows) |row| {
929 testing.expectEqual(test_users[0].id, row.id); 1012 testing.expectEqual(test_users[0].id, row.id);
930 testing.expectEqualStrings(test_users[0].name, row.name); 1013 testing.expectEqualStrings(test_users[0].name, row.name);
@@ -936,11 +1019,32 @@ test "sqlite: read a single user into a struct" {
936 var row = try db.one( 1019 var row = try db.one(
937 struct { 1020 struct {
938 id: usize, 1021 id: usize,
1022 name: [128:0]u8,
1023 age: usize,
1024 },
1025 "SELECT id, name, age FROM user WHERE id = ?{usize}",
1026 .{},
1027 .{@as(usize, 20)},
1028 );
1029 testing.expect(row != null);
1030
1031 const exp = test_users[0];
1032 testing.expectEqual(exp.id, row.?.id);
1033 testing.expectEqualStrings(exp.name, mem.spanZ(&row.?.name));
1034 testing.expectEqual(exp.age, row.?.age);
1035 }
1036
1037 // Read a row with db.oneAlloc()
1038 {
1039 var row = try db.oneAlloc(
1040 struct {
1041 id: usize,
939 name: Text, 1042 name: Text,
940 age: usize, 1043 age: usize,
941 }, 1044 },
1045 &arena.allocator,
942 "SELECT id, name, age FROM user WHERE id = ?{usize}", 1046 "SELECT id, name, age FROM user WHERE id = ?{usize}",
943 .{ .allocator = &arena.allocator }, 1047 .{},
944 .{@as(usize, 20)}, 1048 .{@as(usize, 20)},
945 ); 1049 );
946 testing.expect(row != null); 1050 testing.expect(row != null);
@@ -963,11 +1067,7 @@ test "sqlite: read all users into a struct" {
963 var stmt = try db.prepare("SELECT id, name, age, weight FROM user"); 1067 var stmt = try db.prepare("SELECT id, name, age, weight FROM user");
964 defer stmt.deinit(); 1068 defer stmt.deinit();
965 1069
966 var rows = try stmt.all( 1070 var rows = try stmt.all(TestUser, &arena.allocator, .{}, .{});
967 TestUser,
968 .{ .allocator = &arena.allocator },
969 .{},
970 );
971 testing.expectEqual(@as(usize, 3), rows.len); 1071 testing.expectEqual(@as(usize, 3), rows.len);
972 for (rows) |row, i| { 1072 for (rows) |row, i| {
973 const exp = test_users[i]; 1073 const exp = test_users[i];
@@ -988,7 +1088,7 @@ test "sqlite: read in an anonymous struct" {
988 var stmt = try db.prepare("SELECT id, name, name, age, id, weight FROM user WHERE id = ?{usize}"); 1088 var stmt = try db.prepare("SELECT id, name, name, age, id, weight FROM user WHERE id = ?{usize}");
989 defer stmt.deinit(); 1089 defer stmt.deinit();
990 1090
991 var row = try stmt.one( 1091 var row = try stmt.oneAlloc(
992 struct { 1092 struct {
993 id: usize, 1093 id: usize,
994 name: []const u8, 1094 name: []const u8,
@@ -997,7 +1097,8 @@ test "sqlite: read in an anonymous struct" {
997 is_id: bool, 1097 is_id: bool,
998 weight: f64, 1098 weight: f64,
999 }, 1099 },
1000 .{ .allocator = &arena.allocator }, 1100 &arena.allocator,
1101 .{},
1001 .{ .id = @as(usize, 20) }, 1102 .{ .id = @as(usize, 20) },
1002 ); 1103 );
1003 testing.expect(row != null); 1104 testing.expect(row != null);
@@ -1022,13 +1123,14 @@ test "sqlite: read in a Text struct" {
1022 var stmt = try db.prepare("SELECT id, name, age FROM user WHERE id = ?{usize}"); 1123 var stmt = try db.prepare("SELECT id, name, age FROM user WHERE id = ?{usize}");
1023 defer stmt.deinit(); 1124 defer stmt.deinit();
1024 1125
1025 var row = try stmt.one( 1126 var row = try stmt.oneAlloc(
1026 struct { 1127 struct {
1027 id: usize, 1128 id: usize,
1028 name: Text, 1129 name: Text,
1029 age: usize, 1130 age: usize,
1030 }, 1131 },
1031 .{ .allocator = &arena.allocator }, 1132 &arena.allocator,
1133 .{},
1032 .{@as(usize, 20)}, 1134 .{@as(usize, 20)},
1033 ); 1135 );
1034 testing.expect(row != null); 1136 testing.expect(row != null);
@@ -1069,11 +1171,9 @@ test "sqlite: read a single text value" {
1069 var stmt: Statement(.{}, ParsedQuery.from(query)) = try db.prepare(query); 1171 var stmt: Statement(.{}, ParsedQuery.from(query)) = try db.prepare(query);
1070 defer stmt.deinit(); 1172 defer stmt.deinit();
1071 1173
1072 const name = try stmt.one( 1174 const name = try stmt.oneAlloc(typ, &arena.allocator, .{}, .{
1073 typ, 1175 .id = @as(usize, 20),
1074 .{ .allocator = &arena.allocator }, 1176 });
1075 .{ .id = @as(usize, 20) },
1076 );
1077 testing.expect(name != null); 1177 testing.expect(name != null);
1078 switch (typ) { 1178 switch (typ) {
1079 Text, Blob => { 1179 Text, Blob => {
@@ -1120,7 +1220,9 @@ test "sqlite: read a single integer value" {
1120 var stmt: Statement(.{}, ParsedQuery.from(query)) = try db.prepare(query); 1220 var stmt: Statement(.{}, ParsedQuery.from(query)) = try db.prepare(query);
1121 defer stmt.deinit(); 1221 defer stmt.deinit();
1122 1222
1123 var age = try stmt.one(typ, .{}, .{ .id = @as(usize, 20) }); 1223 var age = try stmt.one(typ, .{}, .{
1224 .id = @as(usize, 20),
1225 });
1124 testing.expect(age != null); 1226 testing.expect(age != null);
1125 1227
1126 testing.expectEqual(@as(typ, 33), age.?); 1228 testing.expectEqual(@as(typ, 33), age.?);
@@ -1137,7 +1239,9 @@ test "sqlite: read a single value into void" {
1137 var stmt: Statement(.{}, ParsedQuery.from(query)) = try db.prepare(query); 1239 var stmt: Statement(.{}, ParsedQuery.from(query)) = try db.prepare(query);
1138 defer stmt.deinit(); 1240 defer stmt.deinit();
1139 1241
1140 _ = try stmt.one(void, .{}, .{ .id = @as(usize, 20) }); 1242 _ = try stmt.one(void, .{}, .{
1243 .id = @as(usize, 20),
1244 });
1141} 1245}
1142 1246
1143test "sqlite: read a single value into bool" { 1247test "sqlite: read a single value into bool" {
@@ -1150,7 +1254,9 @@ test "sqlite: read a single value into bool" {
1150 var stmt: Statement(.{}, ParsedQuery.from(query)) = try db.prepare(query); 1254 var stmt: Statement(.{}, ParsedQuery.from(query)) = try db.prepare(query);
1151 defer stmt.deinit(); 1255 defer stmt.deinit();
1152 1256
1153 const b = try stmt.one(bool, .{}, .{ .id = @as(usize, 20) }); 1257 const b = try stmt.one(bool, .{}, .{
1258 .id = @as(usize, 20),
1259 });
1154 testing.expect(b != null); 1260 testing.expect(b != null);
1155 testing.expect(b.?); 1261 testing.expect(b.?);
1156} 1262}
@@ -1171,7 +1277,9 @@ test "sqlite: insert bool and bind bool" {
1171 var stmt: Statement(.{}, ParsedQuery.from(query)) = try db.prepare(query); 1277 var stmt: Statement(.{}, ParsedQuery.from(query)) = try db.prepare(query);
1172 defer stmt.deinit(); 1278 defer stmt.deinit();
1173 1279
1174 const b = try stmt.one(bool, .{}, .{ .is_published = true }); 1280 const b = try stmt.one(bool, .{}, .{
1281 .is_published = true,
1282 });
1175 testing.expect(b != null); 1283 testing.expect(b != null);
1176 testing.expect(b.?); 1284 testing.expect(b.?);
1177} 1285}
@@ -1232,30 +1340,60 @@ test "sqlite: statement iterator" {
1232 testing.expectEqual(@as(usize, 1), rows_inserted); 1340 testing.expectEqual(@as(usize, 1), rows_inserted);
1233 } 1341 }
1234 1342
1235 // Get the data with an iterator 1343 // Get data with a non-allocating iterator.
1236 var stmt2 = try db.prepare("SELECT name, age FROM user"); 1344 {
1237 defer stmt2.deinit(); 1345 var stmt2 = try db.prepare("SELECT name, age FROM user");
1346 defer stmt2.deinit();
1238 1347
1239 const Type = struct { 1348 const RowType = struct {
1240 name: Text, 1349 name: [128:0]u8,
1241 age: usize, 1350 age: usize,
1242 }; 1351 };
1352
1353 var iter = try stmt2.iterator(RowType, .{});
1354
1355 var rows = std.ArrayList(RowType).init(allocator);
1356 while (true) {
1357 const row = (try iter.next(.{})) orelse break;
1358 try rows.append(row);
1359 }
1243 1360
1244 var iter = try stmt2.iterator(Type, .{}); 1361 // Check the data
1362 testing.expectEqual(expected_rows.items.len, rows.items.len);
1245 1363
1246 var rows = std.ArrayList(Type).init(allocator); 1364 for (rows.items) |row, j| {
1247 while (true) { 1365 const exp_row = expected_rows.items[j];
1248 const row = (try iter.next(.{ .allocator = allocator })) orelse break; 1366 testing.expectEqualStrings(exp_row.name, mem.spanZ(&row.name));
1249 try rows.append(row); 1367 testing.expectEqual(exp_row.age, row.age);
1368 }
1250 } 1369 }
1251 1370
1252 // Check the data 1371 // Get data with an iterator
1253 testing.expectEqual(expected_rows.items.len, rows.items.len); 1372 {
1373 var stmt2 = try db.prepare("SELECT name, age FROM user");
1374 defer stmt2.deinit();
1254 1375
1255 for (rows.items) |row, j| { 1376 const RowType = struct {
1256 const exp_row = expected_rows.items[j]; 1377 name: Text,
1257 testing.expectEqualStrings(exp_row.name, row.name.data); 1378 age: usize,
1258 testing.expectEqual(exp_row.age, row.age); 1379 };
1380
1381 var iter = try stmt2.iterator(RowType, .{});
1382
1383 var rows = std.ArrayList(RowType).init(allocator);
1384 while (true) {
1385 const row = (try iter.nextAlloc(allocator, .{})) orelse break;
1386 try rows.append(row);
1387 }
1388
1389 // Check the data
1390 testing.expectEqual(expected_rows.items.len, rows.items.len);
1391
1392 for (rows.items) |row, j| {
1393 const exp_row = expected_rows.items[j];
1394 testing.expectEqualStrings(exp_row.name, row.name.data);
1395 testing.expectEqual(exp_row.age, row.age);
1396 }
1259 } 1397 }
1260} 1398}
1261 1399