summaryrefslogtreecommitdiff
path: root/sqlite.zig
diff options
context:
space:
mode:
authorGravatar Vincent Rischmann2020-11-12 15:38:56 +0100
committerGravatar Vincent Rischmann2020-11-12 15:38:56 +0100
commit90f72d99945e8c414541b33c61e517f8c3591e2c (patch)
treefec9b6bce198be7ccde75edd57a1306deab66027 /sqlite.zig
parentMerge branch 'bind-parameters-types' into master (diff)
downloadzig-sqlite-90f72d99945e8c414541b33c61e517f8c3591e2c.tar.gz
zig-sqlite-90f72d99945e8c414541b33c61e517f8c3591e2c.tar.xz
zig-sqlite-90f72d99945e8c414541b33c61e517f8c3591e2c.zip
add Statement.reset and test it
Diffstat (limited to 'sqlite.zig')
-rw-r--r--sqlite.zig115
1 files changed, 88 insertions, 27 deletions
diff --git a/sqlite.zig b/sqlite.zig
index 53e1a6a..9e26cd6 100644
--- a/sqlite.zig
+++ b/sqlite.zig
@@ -186,6 +186,9 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: ParsedQuery) t
186 }; 186 };
187 } 187 }
188 188
189 /// deinit releases the prepared statement.
190 ///
191 /// After a call to `deinit` the statement must not be used.
189 pub fn deinit(self: *Self) void { 192 pub fn deinit(self: *Self) void {
190 const result = c.sqlite3_finalize(self.stmt); 193 const result = c.sqlite3_finalize(self.stmt);
191 if (result != c.SQLITE_OK) { 194 if (result != c.SQLITE_OK) {
@@ -193,6 +196,32 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: ParsedQuery) t
193 } 196 }
194 } 197 }
195 198
199 /// reset resets the prepared statement to make it reusable.
200 pub fn reset(self: *Self) void {
201 const result = c.sqlite3_clear_bindings(self.stmt);
202 if (result != c.SQLITE_OK) {
203 logger.err("unable to clear prepared statement bindings, result: {}", .{result});
204 }
205
206 const result2 = c.sqlite3_reset(self.stmt);
207 if (result2 != c.SQLITE_OK) {
208 logger.err("unable to reset prepared statement, result: {}", .{result2});
209 }
210 }
211
212 /// bind binds values to every bind marker in the prepared statement.
213 ///
214 /// The `values` variable must be a struct where each field has the type of the corresponding bind marker.
215 /// For example this query:
216 /// SELECT 1 FROM user WHERE name = ?{text} AND age < ?{u32}
217 ///
218 /// Has two bind markers, so `values` must have at least the following fields:
219 /// struct {
220 /// name: Text,
221 /// age: u32
222 /// }
223 ///
224 /// The types are checked at comptime.
196 pub fn bind(self: *Self, values: anytype) void { 225 pub fn bind(self: *Self, values: anytype) void {
197 const StructType = @TypeOf(values); 226 const StructType = @TypeOf(values);
198 const StructTypeInfo = @typeInfo(StructType).Struct; 227 const StructTypeInfo = @typeInfo(StructType).Struct;
@@ -242,7 +271,7 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: ParsedQuery) t
242 271
243 /// exec executes a statement which does not return data. 272 /// exec executes a statement which does not return data.
244 /// 273 ///
245 /// The `values` tuple is used for the bind parameters. It must have as many fields as there are bind markers 274 /// The `values` variable is used for the bind parameters. It must have as many fields as there are bind markers
246 /// in the input query string. 275 /// in the input query string.
247 /// 276 ///
248 pub fn exec(self: *Self, values: anytype) !void { 277 pub fn exec(self: *Self, values: anytype) !void {
@@ -461,6 +490,27 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: ParsedQuery) t
461 }; 490 };
462} 491}
463 492
493const AllDDL = &[_][]const u8{
494 \\CREATE TABLE user(
495 \\ id integer PRIMARY KEY,
496 \\ name text,
497 \\ age integer
498 \\)
499 ,
500 \\CREATE TABLE article(
501 \\ id integer PRIMARY KEY,
502 \\ author_id integer,
503 \\ data text,
504 \\ FOREIGN KEY(author_id) REFERENCES user(id)
505 \\)
506};
507
508const TestUser = struct {
509 id: usize,
510 name: []const u8,
511 age: usize,
512};
513
464test "sqlite: db init" { 514test "sqlite: db init" {
465 var db: Db = undefined; 515 var db: Db = undefined;
466 try db.init(testing.allocator, .{ .mode = dbMode() }); 516 try db.init(testing.allocator, .{ .mode = dbMode() });
@@ -476,34 +526,12 @@ test "sqlite: statement exec" {
476 try db.init(testing.allocator, .{ .mode = dbMode() }); 526 try db.init(testing.allocator, .{ .mode = dbMode() });
477 527
478 // Create the tables 528 // Create the tables
479 529 inline for (AllDDL) |ddl| {
480 comptime const all_ddl = &[_][]const u8{
481 \\CREATE TABLE user(
482 \\ id integer PRIMARY KEY,
483 \\ name text,
484 \\ age integer
485 \\)
486 ,
487 \\CREATE TABLE article(
488 \\ id integer PRIMARY KEY,
489 \\ author_id integer,
490 \\ data text,
491 \\ FOREIGN KEY(author_id) REFERENCES user(id)
492 \\)
493 };
494 inline for (all_ddl) |ddl| {
495 try db.exec(ddl, .{}); 530 try db.exec(ddl, .{});
496 } 531 }
497 532
498 // Add data 533 // Add data
499 534 const users = &[_]TestUser{
500 const User = struct {
501 id: usize,
502 name: []const u8,
503 age: usize,
504 };
505
506 const users = &[_]User{
507 .{ .id = 20, .name = "Vincent", .age = 33 }, 535 .{ .id = 20, .name = "Vincent", .age = 33 },
508 .{ .id = 40, .name = "Julien", .age = 35 }, 536 .{ .id = 40, .name = "Julien", .age = 35 },
509 .{ .id = 60, .name = "José", .age = 40 }, 537 .{ .id = 60, .name = "José", .age = 40 },
@@ -522,7 +550,7 @@ test "sqlite: statement exec" {
522 var stmt = try db.prepare("SELECT id, name, age FROM user WHERE id = ?{usize}"); 550 var stmt = try db.prepare("SELECT id, name, age FROM user WHERE id = ?{usize}");
523 defer stmt.deinit(); 551 defer stmt.deinit();
524 552
525 var rows = try stmt.all(User, .{ .allocator = allocator }, .{ .id = @as(usize, 20) }); 553 var rows = try stmt.all(TestUser, .{ .allocator = allocator }, .{ .id = @as(usize, 20) });
526 for (rows) |row| { 554 for (rows) |row| {
527 testing.expectEqual(users[0].id, row.id); 555 testing.expectEqual(users[0].id, row.id);
528 testing.expectEqualStrings(users[0].name, row.name); 556 testing.expectEqualStrings(users[0].name, row.name);
@@ -536,7 +564,7 @@ test "sqlite: statement exec" {
536 var stmt = try db.prepare("SELECT id, name, age FROM user"); 564 var stmt = try db.prepare("SELECT id, name, age FROM user");
537 defer stmt.deinit(); 565 defer stmt.deinit();
538 566
539 var rows = try stmt.all(User, .{ .allocator = allocator }, .{}); 567 var rows = try stmt.all(TestUser, .{ .allocator = allocator }, .{});
540 testing.expectEqual(@as(usize, 3), rows.len); 568 testing.expectEqual(@as(usize, 3), rows.len);
541 for (rows) |row, i| { 569 for (rows) |row, i| {
542 const exp = users[i]; 570 const exp = users[i];
@@ -624,6 +652,39 @@ test "sqlite: statement exec" {
624 } 652 }
625} 653}
626 654
655test "sqlite: statement reset" {
656 var arena = std.heap.ArenaAllocator.init(testing.allocator);
657 defer arena.deinit();
658 var allocator = &arena.allocator;
659
660 var db: Db = undefined;
661 try db.init(testing.allocator, .{ .mode = dbMode() });
662
663 // Create the tables
664 inline for (AllDDL) |ddl| {
665 try db.exec(ddl, .{});
666 }
667
668 // Add data
669
670 var stmt = try db.prepare("INSERT INTO user(id, name, age) VALUES(?{usize}, ?{[]const u8}, ?{usize})");
671 defer stmt.deinit();
672
673 const users = &[_]TestUser{
674 .{ .id = 20, .name = "Vincent", .age = 33 },
675 .{ .id = 40, .name = "Julien", .age = 35 },
676 .{ .id = 60, .name = "José", .age = 40 },
677 };
678
679 for (users) |user| {
680 stmt.reset();
681 try stmt.exec(user);
682
683 const rows_inserted = db.rowsAffected();
684 testing.expectEqual(@as(usize, 1), rows_inserted);
685 }
686}
687
627fn dbMode() Db.Mode { 688fn dbMode() Db.Mode {
628 return if (build_options.is_ci) blk: { 689 return if (build_options.is_ci) blk: {
629 break :blk .{ .Memory = {} }; 690 break :blk .{ .Memory = {} };