diff options
| author | 2020-12-21 01:56:22 +0100 | |
|---|---|---|
| committer | 2020-12-21 01:56:22 +0100 | |
| commit | 41f65a47272efbf1bf11bcd042cc54f29d5cc106 (patch) | |
| tree | 05badf5ab323ca7790581a15b8c100f30099abb0 /sqlite.zig | |
| parent | actually check the value of is_id (diff) | |
| parent | add comments (diff) | |
| download | zig-sqlite-41f65a47272efbf1bf11bcd042cc54f29d5cc106.tar.gz zig-sqlite-41f65a47272efbf1bf11bcd042cc54f29d5cc106.tar.xz zig-sqlite-41f65a47272efbf1bf11bcd042cc54f29d5cc106.zip | |
Merge branch 'refactor'
Diffstat (limited to '')
| -rw-r--r-- | sqlite.zig | 206 |
1 files changed, 124 insertions, 82 deletions
| @@ -180,6 +180,9 @@ pub fn Iterator(comptime Type: type) type { | |||
| 180 | 180 | ||
| 181 | stmt: *c.sqlite3_stmt, | 181 | stmt: *c.sqlite3_stmt, |
| 182 | 182 | ||
| 183 | // next scans the next row using the preapred statement. | ||
| 184 | // | ||
| 185 | // If it returns null iterating is done. | ||
| 183 | pub fn next(self: *Self, options: anytype) !?Type { | 186 | pub fn next(self: *Self, options: anytype) !?Type { |
| 184 | var result = c.sqlite3_step(self.stmt); | 187 | var result = c.sqlite3_step(self.stmt); |
| 185 | if (result == c.SQLITE_DONE) { | 188 | if (result == c.SQLITE_DONE) { |
| @@ -196,21 +199,15 @@ pub fn Iterator(comptime Type: type) type { | |||
| 196 | switch (Type) { | 199 | switch (Type) { |
| 197 | []const u8, []u8 => { | 200 | []const u8, []u8 => { |
| 198 | debug.assert(columns == 1); | 201 | debug.assert(columns == 1); |
| 199 | var ret: Type = undefined; | 202 | return try self.readBytes(Type, 0, .Text, options); |
| 200 | try self.readBytes(options, .Text, 0, &ret); | ||
| 201 | return ret; | ||
| 202 | }, | 203 | }, |
| 203 | Blob => { | 204 | Blob => { |
| 204 | debug.assert(columns == 1); | 205 | debug.assert(columns == 1); |
| 205 | var ret: Type = undefined; | 206 | return try self.readBytes(Blob, 0, .Blob, options); |
| 206 | try self.readBytes(options, .Blob, 0, &ret.data); | ||
| 207 | return ret; | ||
| 208 | }, | 207 | }, |
| 209 | Text => { | 208 | Text => { |
| 210 | debug.assert(columns == 1); | 209 | debug.assert(columns == 1); |
| 211 | var ret: Type = undefined; | 210 | return try self.readBytes(Text, 0, .Text, options); |
| 212 | try self.readBytes(options, .Text, 0, &ret.data); | ||
| 213 | return ret; | ||
| 214 | }, | 211 | }, |
| 215 | else => {}, | 212 | else => {}, |
| 216 | } | 213 | } |
| @@ -218,24 +215,22 @@ pub fn Iterator(comptime Type: type) type { | |||
| 218 | switch (TypeInfo) { | 215 | switch (TypeInfo) { |
| 219 | .Int => { | 216 | .Int => { |
| 220 | debug.assert(columns == 1); | 217 | debug.assert(columns == 1); |
| 221 | return try self.readInt(options); | 218 | return try self.readInt(Type, 0, options); |
| 222 | }, | 219 | }, |
| 223 | .Float => { | 220 | .Float => { |
| 224 | debug.assert(columns == 1); | 221 | debug.assert(columns == 1); |
| 225 | return try self.readFloat(options); | 222 | return try self.readFloat(Type, 0, options); |
| 226 | }, | 223 | }, |
| 227 | .Bool => { | 224 | .Bool => { |
| 228 | debug.assert(columns == 1); | 225 | debug.assert(columns == 1); |
| 229 | return try self.readBool(options); | 226 | return try self.readBool(0, options); |
| 230 | }, | 227 | }, |
| 231 | .Void => { | 228 | .Void => { |
| 232 | debug.assert(columns == 1); | 229 | debug.assert(columns == 1); |
| 233 | }, | 230 | }, |
| 234 | .Array => { | 231 | .Array => { |
| 235 | debug.assert(columns == 1); | 232 | debug.assert(columns == 1); |
| 236 | var ret: Type = undefined; | 233 | return try self.readArray(Type, 0); |
| 237 | try self.readArray(Type, 0, &ret); | ||
| 238 | return ret; | ||
| 239 | }, | 234 | }, |
| 240 | .Struct => { | 235 | .Struct => { |
| 241 | std.debug.assert(columns == TypeInfo.Struct.fields.len); | 236 | std.debug.assert(columns == TypeInfo.Struct.fields.len); |
| @@ -245,10 +240,17 @@ pub fn Iterator(comptime Type: type) type { | |||
| 245 | } | 240 | } |
| 246 | } | 241 | } |
| 247 | 242 | ||
| 248 | fn readArray(self: *Self, comptime ArrayType: type, _i: usize, array: anytype) !void { | 243 | // readArray reads a sqlite BLOB or TEXT column into an array of u8. |
| 244 | // | ||
| 245 | // We also require the array to have a sentinel because otherwise we have no way | ||
| 246 | // of communicating the end of the data to the caller. | ||
| 247 | // | ||
| 248 | // If the array is too small for the data an error will be returned. | ||
| 249 | fn readArray(self: *Self, comptime ArrayType: type, _i: usize) error{ArrayTooSmall}!ArrayType { | ||
| 249 | const i = @intCast(c_int, _i); | 250 | const i = @intCast(c_int, _i); |
| 250 | const array_type_info = @typeInfo(ArrayType); | 251 | const array_type_info = @typeInfo(ArrayType); |
| 251 | 252 | ||
| 253 | var ret: ArrayType = undefined; | ||
| 252 | switch (array_type_info) { | 254 | switch (array_type_info) { |
| 253 | .Array => |arr| { | 255 | .Array => |arr| { |
| 254 | comptime if (arr.sentinel == null) { | 256 | comptime if (arr.sentinel == null) { |
| @@ -264,28 +266,32 @@ pub fn Iterator(comptime Type: type) type { | |||
| 264 | 266 | ||
| 265 | const ptr = @ptrCast([*c]const u8, data)[0..size]; | 267 | const ptr = @ptrCast([*c]const u8, data)[0..size]; |
| 266 | 268 | ||
| 267 | mem.copy(u8, array[0..], ptr); | 269 | mem.copy(u8, ret[0..], ptr); |
| 268 | array[size] = arr.sentinel.?; | 270 | ret[size] = arr.sentinel.?; |
| 269 | }, | 271 | }, |
| 270 | else => @compileError("cannot populate field " ++ field.name ++ " of type array of " ++ @typeName(arr.child)), | 272 | else => @compileError("cannot populate field " ++ field.name ++ " of type array of " ++ @typeName(arr.child)), |
| 271 | } | 273 | } |
| 272 | }, | 274 | }, |
| 273 | else => @compileError("cannot populate field " ++ field.name ++ " of type array of " ++ @typeName(arr.child)), | 275 | else => @compileError("cannot populate field " ++ field.name ++ " of type array of " ++ @typeName(arr.child)), |
| 274 | } | 276 | } |
| 277 | return ret; | ||
| 275 | } | 278 | } |
| 276 | 279 | ||
| 277 | fn readInt(self: *Self, options: anytype) !Type { | 280 | // readInt reads a sqlite INTEGER column into an integer. |
| 278 | const n = c.sqlite3_column_int64(self.stmt, 0); | 281 | fn readInt(self: *Self, comptime IntType: type, i: usize, options: anytype) !IntType { |
| 279 | return @intCast(Type, n); | 282 | const n = c.sqlite3_column_int64(self.stmt, @intCast(c_int, i)); |
| 283 | return @intCast(IntType, n); | ||
| 280 | } | 284 | } |
| 281 | 285 | ||
| 282 | fn readFloat(self: *Self, options: anytype) !Type { | 286 | // readFloat reads a sqlite REAL column into a float. |
| 283 | const d = c.sqlite3_column_double(self.stmt, 0); | 287 | fn readFloat(self: *Self, comptime FloatType: type, i: usize, options: anytype) !FloatType { |
| 284 | return @floatCast(Type, d); | 288 | const d = c.sqlite3_column_double(self.stmt, @intCast(c_int, i)); |
| 289 | return @floatCast(FloatType, d); | ||
| 285 | } | 290 | } |
| 286 | 291 | ||
| 287 | fn readBool(self: *Self, options: anytype) !Type { | 292 | // readFloat reads a sqlite INTEGER column into a bool (true is anything > 0, false is anything <= 0). |
| 288 | const d = c.sqlite3_column_int64(self.stmt, 0); | 293 | fn readBool(self: *Self, i: usize, options: anytype) !bool { |
| 294 | const d = c.sqlite3_column_int64(self.stmt, @intCast(c_int, i)); | ||
| 289 | return d > 0; | 295 | return d > 0; |
| 290 | } | 296 | } |
| 291 | 297 | ||
| @@ -294,34 +300,83 @@ pub fn Iterator(comptime Type: type) type { | |||
| 294 | Text, | 300 | Text, |
| 295 | }; | 301 | }; |
| 296 | 302 | ||
| 297 | fn readBytes(self: *Self, options: anytype, mode: ReadBytesMode, _i: usize, ptr: *[]const u8) !void { | 303 | // readBytes reads a sqlite BLOB or TEXT column. |
| 304 | // | ||
| 305 | // The mode controls which sqlite function is used to retrieve the data: | ||
| 306 | // * .Blob uses sqlite3_column_blob | ||
| 307 | // * .Text uses sqlite3_column_text | ||
| 308 | // | ||
| 309 | // When using .Blob you can only read into either []const u8, []u8 or Blob. | ||
| 310 | // When using .Text you can only read into either []const u8, []u8 or Text. | ||
| 311 | // | ||
| 312 | // The options must contain an `allocator` field which will be used to create a copy of the data. | ||
| 313 | fn readBytes(self: *Self, comptime BytesType: type, _i: usize, comptime mode: ReadBytesMode, options: anytype) !BytesType { | ||
| 298 | const i = @intCast(c_int, _i); | 314 | const i = @intCast(c_int, _i); |
| 315 | |||
| 316 | var ret: BytesType = switch (BytesType) { | ||
| 317 | Text, Blob => .{ .data = "" }, | ||
| 318 | else => "", // TODO(vincent): I think with a []u8 this will crash if the caller attempts to modify it... | ||
| 319 | }; | ||
| 320 | |||
| 299 | switch (mode) { | 321 | switch (mode) { |
| 300 | .Blob => { | 322 | .Blob => { |
| 301 | const data = c.sqlite3_column_blob(self.stmt, i); | 323 | const data = c.sqlite3_column_blob(self.stmt, i); |
| 302 | if (data == null) ptr.* = ""; | 324 | if (data == null) return ret; |
| 303 | 325 | ||
| 304 | const size = @intCast(usize, c.sqlite3_column_bytes(self.stmt, i)); | 326 | const size = @intCast(usize, c.sqlite3_column_bytes(self.stmt, i)); |
| 305 | 327 | const ptr = @ptrCast([*c]const u8, data)[0..size]; | |
| 306 | var tmp = try options.allocator.alloc(u8, size); | 328 | |
| 307 | mem.copy(u8, tmp, @ptrCast([*c]const u8, data)[0..size]); | 329 | return switch (BytesType) { |
| 308 | 330 | []const u8, []u8 => try options.allocator.dupe(u8, ptr), | |
| 309 | ptr.* = tmp; | 331 | Blob => blk: { |
| 332 | var tmp: Blob = undefined; | ||
| 333 | tmp.data = try options.allocator.dupe(u8, ptr); | ||
| 334 | break :blk tmp; | ||
| 335 | }, | ||
| 336 | else => @compileError("cannot read blob into type " ++ @typeName(BytesType)), | ||
| 337 | }; | ||
| 310 | }, | 338 | }, |
| 311 | .Text => { | 339 | .Text => { |
| 312 | const data = c.sqlite3_column_text(self.stmt, i); | 340 | const data = c.sqlite3_column_text(self.stmt, i); |
| 313 | if (data == null) ptr.* = ""; | 341 | if (data == null) return ret; |
| 314 | 342 | ||
| 315 | const size = @intCast(usize, c.sqlite3_column_bytes(self.stmt, i)); | 343 | const size = @intCast(usize, c.sqlite3_column_bytes(self.stmt, i)); |
| 316 | 344 | const ptr = @ptrCast([*c]const u8, data)[0..size]; | |
| 317 | var tmp = try options.allocator.alloc(u8, size); | 345 | |
| 318 | mem.copy(u8, tmp, @ptrCast([*c]const u8, data)[0..size]); | 346 | return switch (BytesType) { |
| 319 | 347 | []const u8, []u8 => try options.allocator.dupe(u8, ptr), | |
| 320 | ptr.* = tmp; | 348 | Text => blk: { |
| 349 | var tmp: Text = undefined; | ||
| 350 | tmp.data = try options.allocator.dupe(u8, ptr); | ||
| 351 | break :blk tmp; | ||
| 352 | }, | ||
| 353 | else => @compileError("cannot read text into type " ++ @typeName(BytesType)), | ||
| 354 | }; | ||
| 321 | }, | 355 | }, |
| 322 | } | 356 | } |
| 323 | } | 357 | } |
| 324 | 358 | ||
| 359 | // readStruct reads an entire sqlite row into a struct. | ||
| 360 | // | ||
| 361 | // Each field correspond to a column; its position in the struct determines the column used for it. | ||
| 362 | // For example, given the following query: | ||
| 363 | // | ||
| 364 | // SELECT id, name, age FROM user | ||
| 365 | // | ||
| 366 | // The struct must have the following fields: | ||
| 367 | // | ||
| 368 | // struct { | ||
| 369 | // id: usize, | ||
| 370 | // name: []const u8, | ||
| 371 | // age: u16, | ||
| 372 | // } | ||
| 373 | // | ||
| 374 | // The field `id` will be associated with the column `id` and so on. | ||
| 375 | // | ||
| 376 | // This function relies on the fact that there are the same number of fields than columns and | ||
| 377 | // that the order is correct. | ||
| 378 | // | ||
| 379 | // TODO(vincent): add comptime checks for the fields/columns. | ||
| 325 | fn readStruct(self: *Self, options: anytype) !Type { | 380 | fn readStruct(self: *Self, options: anytype) !Type { |
| 326 | var value: Type = undefined; | 381 | var value: Type = undefined; |
| 327 | 382 | ||
| @@ -329,38 +384,21 @@ pub fn Iterator(comptime Type: type) type { | |||
| 329 | const i = @as(usize, _i); | 384 | const i = @as(usize, _i); |
| 330 | const field_type_info = @typeInfo(field.field_type); | 385 | const field_type_info = @typeInfo(field.field_type); |
| 331 | 386 | ||
| 332 | switch (field.field_type) { | 387 | const ret = switch (field.field_type) { |
| 333 | []const u8, []u8 => { | 388 | []const u8, []u8 => try self.readBytes(field.field_type, i, .Blob, options), |
| 334 | try self.readBytes(options, .Blob, i, &@field(value, field.name)); | 389 | Blob => try self.readBytes(Blob, i, .Blob, options), |
| 335 | }, | 390 | Text => try self.readBytes(Text, i, .Text, options), |
| 336 | Blob => { | ||
| 337 | try self.readBytes(options, .Blob, i, &@field(value, field.name).data); | ||
| 338 | }, | ||
| 339 | Text => { | ||
| 340 | try self.readBytes(options, .Text, i, &@field(value, field.name).data); | ||
| 341 | }, | ||
| 342 | else => switch (field_type_info) { | 391 | else => switch (field_type_info) { |
| 343 | .Int => { | 392 | .Int => try self.readInt(field.field_type, i, options), |
| 344 | const n = c.sqlite3_column_int64(self.stmt, i); | 393 | .Float => try self.readFloat(field.field_type, i, options), |
| 345 | @field(value, field.name) = @intCast(field.field_type, n); | 394 | .Bool => try self.readBool(i, options), |
| 346 | }, | 395 | .Void => {}, |
| 347 | .Float => { | 396 | .Array => try self.readArray(field.field_type, i), |
| 348 | const f = c.sqlite3_column_double(self.stmt, i); | ||
| 349 | @field(value, field.name) = f; | ||
| 350 | }, | ||
| 351 | .Bool => { | ||
| 352 | const n = c.sqlite3_column_int64(self.stmt, i); | ||
| 353 | @field(value, field.name) = n > 0; | ||
| 354 | }, | ||
| 355 | .Void => { | ||
| 356 | @field(value, field.name) = {}; | ||
| 357 | }, | ||
| 358 | .Array => { | ||
| 359 | try self.readArray(field.field_type, i, &@field(value, field.name)); | ||
| 360 | }, | ||
| 361 | else => @compileError("cannot populate field " ++ field.name ++ " of type " ++ @typeName(field.field_type)), | 397 | else => @compileError("cannot populate field " ++ field.name ++ " of type " ++ @typeName(field.field_type)), |
| 362 | }, | 398 | }, |
| 363 | } | 399 | }; |
| 400 | |||
| 401 | @field(value, field.name) = ret; | ||
| 364 | } | 402 | } |
| 365 | 403 | ||
| 366 | return value; | 404 | return value; |
| @@ -647,12 +685,13 @@ const TestUser = struct { | |||
| 647 | id: usize, | 685 | id: usize, |
| 648 | name: []const u8, | 686 | name: []const u8, |
| 649 | age: usize, | 687 | age: usize, |
| 688 | weight: f32, | ||
| 650 | }; | 689 | }; |
| 651 | 690 | ||
| 652 | const test_users = &[_]TestUser{ | 691 | const test_users = &[_]TestUser{ |
| 653 | .{ .id = 20, .name = "Vincent", .age = 33 }, | 692 | .{ .id = 20, .name = "Vincent", .age = 33, .weight = 85.4 }, |
| 654 | .{ .id = 40, .name = "Julien", .age = 35 }, | 693 | .{ .id = 40, .name = "Julien", .age = 35, .weight = 100.3 }, |
| 655 | .{ .id = 60, .name = "José", .age = 40 }, | 694 | .{ .id = 60, .name = "José", .age = 40, .weight = 240.2 }, |
| 656 | }; | 695 | }; |
| 657 | 696 | ||
| 658 | fn addTestData(db: *Db) !void { | 697 | fn addTestData(db: *Db) !void { |
| @@ -660,7 +699,8 @@ fn addTestData(db: *Db) !void { | |||
| 660 | \\CREATE TABLE user( | 699 | \\CREATE TABLE user( |
| 661 | \\ id integer PRIMARY KEY, | 700 | \\ id integer PRIMARY KEY, |
| 662 | \\ name text, | 701 | \\ name text, |
| 663 | \\ age integer | 702 | \\ age integer, |
| 703 | \\ weight real | ||
| 664 | \\) | 704 | \\) |
| 665 | , | 705 | , |
| 666 | \\CREATE TABLE article( | 706 | \\CREATE TABLE article( |
| @@ -678,7 +718,7 @@ fn addTestData(db: *Db) !void { | |||
| 678 | } | 718 | } |
| 679 | 719 | ||
| 680 | for (test_users) |user| { | 720 | for (test_users) |user| { |
| 681 | try db.exec("INSERT INTO user(id, name, age) VALUES(?{usize}, ?{[]const u8}, ?{usize})", user); | 721 | try db.exec("INSERT INTO user(id, name, age, weight) VALUES(?{usize}, ?{[]const u8}, ?{usize}, ?{f32})", user); |
| 682 | 722 | ||
| 683 | const rows_inserted = db.rowsAffected(); | 723 | const rows_inserted = db.rowsAffected(); |
| 684 | testing.expectEqual(@as(usize, 1), rows_inserted); | 724 | testing.expectEqual(@as(usize, 1), rows_inserted); |
| @@ -755,7 +795,7 @@ test "sqlite: read a single user into a struct" { | |||
| 755 | try db.init(testing.allocator, .{ .mode = dbMode() }); | 795 | try db.init(testing.allocator, .{ .mode = dbMode() }); |
| 756 | try addTestData(&db); | 796 | try addTestData(&db); |
| 757 | 797 | ||
| 758 | var stmt = try db.prepare("SELECT id, name, age FROM user WHERE id = ?{usize}"); | 798 | var stmt = try db.prepare("SELECT id, name, age, weight FROM user WHERE id = ?{usize}"); |
| 759 | defer stmt.deinit(); | 799 | defer stmt.deinit(); |
| 760 | 800 | ||
| 761 | var rows = try stmt.all( | 801 | var rows = try stmt.all( |
| @@ -778,7 +818,7 @@ test "sqlite: read all users into a struct" { | |||
| 778 | try db.init(testing.allocator, .{ .mode = dbMode() }); | 818 | try db.init(testing.allocator, .{ .mode = dbMode() }); |
| 779 | try addTestData(&db); | 819 | try addTestData(&db); |
| 780 | 820 | ||
| 781 | var stmt = try db.prepare("SELECT id, name, age FROM user"); | 821 | var stmt = try db.prepare("SELECT id, name, age, weight FROM user"); |
| 782 | defer stmt.deinit(); | 822 | defer stmt.deinit(); |
| 783 | 823 | ||
| 784 | var rows = try stmt.all( | 824 | var rows = try stmt.all( |
| @@ -803,7 +843,7 @@ test "sqlite: read in an anonymous struct" { | |||
| 803 | try db.init(testing.allocator, .{ .mode = dbMode() }); | 843 | try db.init(testing.allocator, .{ .mode = dbMode() }); |
| 804 | try addTestData(&db); | 844 | try addTestData(&db); |
| 805 | 845 | ||
| 806 | var stmt = try db.prepare("SELECT id, name, name, age, id FROM user WHERE id = ?{usize}"); | 846 | var stmt = try db.prepare("SELECT id, name, name, age, id, weight FROM user WHERE id = ?{usize}"); |
| 807 | defer stmt.deinit(); | 847 | defer stmt.deinit(); |
| 808 | 848 | ||
| 809 | var row = try stmt.one( | 849 | var row = try stmt.one( |
| @@ -813,6 +853,7 @@ test "sqlite: read in an anonymous struct" { | |||
| 813 | name_2: [200:0xAD]u8, | 853 | name_2: [200:0xAD]u8, |
| 814 | age: usize, | 854 | age: usize, |
| 815 | is_id: bool, | 855 | is_id: bool, |
| 856 | weight: f64, | ||
| 816 | }, | 857 | }, |
| 817 | .{ .allocator = &arena.allocator }, | 858 | .{ .allocator = &arena.allocator }, |
| 818 | .{ .id = @as(usize, 20) }, | 859 | .{ .id = @as(usize, 20) }, |
| @@ -825,6 +866,7 @@ test "sqlite: read in an anonymous struct" { | |||
| 825 | testing.expectEqualStrings(exp.name, mem.spanZ(&row.?.name_2)); | 866 | testing.expectEqualStrings(exp.name, mem.spanZ(&row.?.name_2)); |
| 826 | testing.expectEqual(exp.age, row.?.age); | 867 | testing.expectEqual(exp.age, row.?.age); |
| 827 | testing.expect(row.?.is_id); | 868 | testing.expect(row.?.is_id); |
| 869 | testing.expectEqual(exp.weight, @floatCast(f32, row.?.weight)); | ||
| 828 | } | 870 | } |
| 829 | 871 | ||
| 830 | test "sqlite: read in a Text struct" { | 872 | test "sqlite: read in a Text struct" { |
| @@ -998,13 +1040,13 @@ test "sqlite: statement reset" { | |||
| 998 | 1040 | ||
| 999 | // Add data | 1041 | // Add data |
| 1000 | 1042 | ||
| 1001 | var stmt = try db.prepare("INSERT INTO user(id, name, age) VALUES(?{usize}, ?{[]const u8}, ?{usize})"); | 1043 | var stmt = try db.prepare("INSERT INTO user(id, name, age, weight) VALUES(?{usize}, ?{[]const u8}, ?{usize}, ?{f32})"); |
| 1002 | defer stmt.deinit(); | 1044 | defer stmt.deinit(); |
| 1003 | 1045 | ||
| 1004 | const users = &[_]TestUser{ | 1046 | const users = &[_]TestUser{ |
| 1005 | .{ .id = 200, .name = "Vincent", .age = 33 }, | 1047 | .{ .id = 200, .name = "Vincent", .age = 33, .weight = 10.0 }, |
| 1006 | .{ .id = 400, .name = "Julien", .age = 35 }, | 1048 | .{ .id = 400, .name = "Julien", .age = 35, .weight = 12.0 }, |
| 1007 | .{ .id = 600, .name = "José", .age = 40 }, | 1049 | .{ .id = 600, .name = "José", .age = 40, .weight = 14.0 }, |
| 1008 | }; | 1050 | }; |
| 1009 | 1051 | ||
| 1010 | for (users) |user| { | 1052 | for (users) |user| { |
| @@ -1029,14 +1071,14 @@ test "sqlite: statement iterator" { | |||
| 1029 | try db.exec("DELETE FROM user", .{}); | 1071 | try db.exec("DELETE FROM user", .{}); |
| 1030 | 1072 | ||
| 1031 | // Add data | 1073 | // Add data |
| 1032 | var stmt = try db.prepare("INSERT INTO user(id, name, age) VALUES(?{usize}, ?{[]const u8}, ?{usize})"); | 1074 | var stmt = try db.prepare("INSERT INTO user(id, name, age, weight) VALUES(?{usize}, ?{[]const u8}, ?{usize}, ?{f32})"); |
| 1033 | defer stmt.deinit(); | 1075 | defer stmt.deinit(); |
| 1034 | 1076 | ||
| 1035 | var expected_rows = std.ArrayList(TestUser).init(allocator); | 1077 | var expected_rows = std.ArrayList(TestUser).init(allocator); |
| 1036 | var i: usize = 0; | 1078 | var i: usize = 0; |
| 1037 | while (i < 20) : (i += 1) { | 1079 | while (i < 20) : (i += 1) { |
| 1038 | const name = try std.fmt.allocPrint(allocator, "Vincent {}", .{i}); | 1080 | const name = try std.fmt.allocPrint(allocator, "Vincent {}", .{i}); |
| 1039 | const user = TestUser{ .id = i, .name = name, .age = i + 200 }; | 1081 | const user = TestUser{ .id = i, .name = name, .age = i + 200, .weight = @intToFloat(f32, i + 200) }; |
| 1040 | 1082 | ||
| 1041 | try expected_rows.append(user); | 1083 | try expected_rows.append(user); |
| 1042 | 1084 | ||