summaryrefslogtreecommitdiff
path: root/sqlite.zig
diff options
context:
space:
mode:
authorGravatar Vincent Rischmann2021-04-14 01:30:09 +0200
committerGravatar Vincent Rischmann2021-08-02 14:59:48 +0200
commit11564a35dd1409124352903e4f4099c3061d9570 (patch)
treec340fab5961b2fd0f437c6be6a20e5517d4aa0bb /sqlite.zig
parentMerge branch 'improve-readme' (diff)
downloadzig-sqlite-11564a35dd1409124352903e4f4099c3061d9570.tar.gz
zig-sqlite-11564a35dd1409124352903e4f4099c3061d9570.tar.xz
zig-sqlite-11564a35dd1409124352903e4f4099c3061d9570.zip
modify exec to take a QueryOptions
Fixes #35
Diffstat (limited to 'sqlite.zig')
-rw-r--r--sqlite.zig68
1 files changed, 47 insertions, 21 deletions
diff --git a/sqlite.zig b/sqlite.zig
index 3c2471d..b0f3216 100644
--- a/sqlite.zig
+++ b/sqlite.zig
@@ -200,7 +200,7 @@ pub const Diagnostics = struct {
200 pub fn format(self: @This(), comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { 200 pub fn format(self: @This(), comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
201 if (self.err) |err| { 201 if (self.err) |err| {
202 if (self.message.len > 0) { 202 if (self.message.len > 0) {
203 _ = try writer.print("{{message: {s}, detailed error: {}}}", .{ self.message, err }); 203 _ = try writer.print("{{message: {s}, detailed error: {s}}}", .{ self.message, err });
204 return; 204 return;
205 } 205 }
206 206
@@ -425,10 +425,10 @@ pub const Db = struct {
425 } 425 }
426 426
427 /// exec is a convenience function which prepares a statement and executes it directly. 427 /// exec is a convenience function which prepares a statement and executes it directly.
428 pub fn exec(self: *Self, comptime query: []const u8, values: anytype) !void { 428 pub fn exec(self: *Self, comptime query: []const u8, options: QueryOptions, values: anytype) !void {
429 var stmt = try self.prepare(query); 429 var stmt = try self.prepareWithDiags(query, options);
430 defer stmt.deinit(); 430 defer stmt.deinit();
431 try stmt.exec(values); 431 try stmt.exec(options, values);
432 } 432 }
433 433
434 /// one is a convenience function which prepares a statement and reads a single row from the result set. 434 /// one is a convenience function which prepares a statement and reads a single row from the result set.
@@ -1059,17 +1059,24 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: ParsedQuery) t
1059 1059
1060 /// exec executes a statement which does not return data. 1060 /// exec executes a statement which does not return data.
1061 /// 1061 ///
1062 /// The `options` tuple is used to provide additional state in some cases.
1063 ///
1062 /// The `values` variable is used for the bind parameters. It must have as many fields as there are bind markers 1064 /// The `values` variable is used for the bind parameters. It must have as many fields as there are bind markers
1063 /// in the input query string. 1065 /// in the input query string.
1064 /// 1066 ///
1065 pub fn exec(self: *Self, values: anytype) !void { 1067 pub fn exec(self: *Self, options: QueryOptions, values: anytype) !void {
1066 self.bind(values); 1068 self.bind(values);
1067 1069
1070 var dummy_diags = Diagnostics{};
1071 var diags = options.diags orelse &dummy_diags;
1072
1068 const result = c.sqlite3_step(self.stmt); 1073 const result = c.sqlite3_step(self.stmt);
1069 switch (result) { 1074 switch (result) {
1070 c.SQLITE_DONE => {}, 1075 c.SQLITE_DONE => {},
1071 c.SQLITE_BUSY => return errorFromResultCode(result), 1076 else => {
1072 else => std.debug.panic("invalid result {}", .{result}), 1077 diags.err = getLastDetailedErrorFromDb(self.db);
1078 return errorFromResultCode(result);
1079 },
1073 } 1080 }
1074 } 1081 }
1075 1082
@@ -1227,7 +1234,7 @@ fn createTestTables(db: *Db) !void {
1227 1234
1228 // Create the tables 1235 // Create the tables
1229 inline for (AllDDL) |ddl| { 1236 inline for (AllDDL) |ddl| {
1230 try db.exec(ddl, .{}); 1237 try db.exec(ddl, .{}, .{});
1231 } 1238 }
1232} 1239}
1233 1240
@@ -1235,7 +1242,7 @@ fn addTestData(db: *Db) !void {
1235 try createTestTables(db); 1242 try createTestTables(db);
1236 1243
1237 for (test_users) |user| { 1244 for (test_users) |user| {
1238 try db.exec("INSERT INTO user(name, id, age, weight) VALUES(?{[]const u8}, ?{usize}, ?{usize}, ?{f32})", user); 1245 try db.exec("INSERT INTO user(name, id, age, weight) VALUES(?{[]const u8}, ?{usize}, ?{usize}, ?{f32})", .{}, user);
1239 1246
1240 const rows_inserted = db.rowsAffected(); 1247 const rows_inserted = db.rowsAffected();
1241 try testing.expectEqual(@as(usize, 1), rows_inserted); 1248 try testing.expectEqual(@as(usize, 1), rows_inserted);
@@ -1288,7 +1295,7 @@ test "sqlite: last insert row id" {
1288 var db = try getTestDb(); 1295 var db = try getTestDb();
1289 try createTestTables(&db); 1296 try createTestTables(&db);
1290 1297
1291 try db.exec("INSERT INTO user(name, age) VALUES(?, ?{u32})", .{ 1298 try db.exec("INSERT INTO user(name, age) VALUES(?, ?{u32})", .{}, .{
1292 .name = "test-user", 1299 .name = "test-user",
1293 .age = @as(u32, 400), 1300 .age = @as(u32, 400),
1294 }); 1301 });
@@ -1303,7 +1310,7 @@ test "sqlite: statement exec" {
1303 1310
1304 // Test with a Blob struct 1311 // Test with a Blob struct
1305 { 1312 {
1306 try db.exec("INSERT INTO user(id, name, age) VALUES(?{usize}, ?{blob}, ?{u32})", .{ 1313 try db.exec("INSERT INTO user(id, name, age) VALUES(?{usize}, ?{blob}, ?{u32})", .{}, .{
1307 .id = @as(usize, 200), 1314 .id = @as(usize, 200),
1308 .name = Blob{ .data = "hello" }, 1315 .name = Blob{ .data = "hello" },
1309 .age = @as(u32, 20), 1316 .age = @as(u32, 20),
@@ -1312,7 +1319,7 @@ test "sqlite: statement exec" {
1312 1319
1313 // Test with a Text struct 1320 // Test with a Text struct
1314 { 1321 {
1315 try db.exec("INSERT INTO user(id, name, age) VALUES(?{usize}, ?{text}, ?{u32})", .{ 1322 try db.exec("INSERT INTO user(id, name, age) VALUES(?{usize}, ?{text}, ?{u32})", .{}, .{
1316 .id = @as(usize, 201), 1323 .id = @as(usize, 201),
1317 .name = Text{ .data = "hello" }, 1324 .name = Text{ .data = "hello" },
1318 .age = @as(u32, 20), 1325 .age = @as(u32, 20),
@@ -1583,7 +1590,7 @@ test "sqlite: insert bool and bind bool" {
1583 var db = try getTestDb(); 1590 var db = try getTestDb();
1584 try addTestData(&db); 1591 try addTestData(&db);
1585 1592
1586 try db.exec("INSERT INTO article(id, author_id, is_published) VALUES(?{usize}, ?{usize}, ?{bool})", .{ 1593 try db.exec("INSERT INTO article(id, author_id, is_published) VALUES(?{usize}, ?{usize}, ?{bool})", .{}, .{
1587 .id = @as(usize, 1), 1594 .id = @as(usize, 1),
1588 .author_id = @as(usize, 20), 1595 .author_id = @as(usize, 20),
1589 .is_published = true, 1596 .is_published = true,
@@ -1605,7 +1612,7 @@ test "sqlite: bind string literal" {
1605 var db = try getTestDb(); 1612 var db = try getTestDb();
1606 try addTestData(&db); 1613 try addTestData(&db);
1607 1614
1608 try db.exec("INSERT INTO article(id, data) VALUES(?, ?)", .{ 1615 try db.exec("INSERT INTO article(id, data) VALUES(?, ?)", .{}, .{
1609 @as(usize, 10), 1616 @as(usize, 10),
1610 "foobar", 1617 "foobar",
1611 }); 1618 });
@@ -1682,7 +1689,7 @@ test "sqlite: optional" {
1682 var db = try getTestDb(); 1689 var db = try getTestDb();
1683 try addTestData(&db); 1690 try addTestData(&db);
1684 1691
1685 try db.exec("INSERT INTO article(author_id, data, is_published) VALUES(?, ?, ?)", .{ 1, null, true }); 1692 try db.exec("INSERT INTO article(author_id, data, is_published) VALUES(?, ?, ?)", .{}, .{ 1, null, true });
1686 1693
1687 var stmt = try db.prepare("SELECT data, is_published FROM article"); 1694 var stmt = try db.prepare("SELECT data, is_published FROM article");
1688 defer stmt.deinit(); 1695 defer stmt.deinit();
@@ -1718,7 +1725,7 @@ test "sqlite: statement reset" {
1718 1725
1719 for (users) |user| { 1726 for (users) |user| {
1720 stmt.reset(); 1727 stmt.reset();
1721 try stmt.exec(user); 1728 try stmt.exec(.{}, user);
1722 1729
1723 const rows_inserted = db.rowsAffected(); 1730 const rows_inserted = db.rowsAffected();
1724 try testing.expectEqual(@as(usize, 1), rows_inserted); 1731 try testing.expectEqual(@as(usize, 1), rows_inserted);
@@ -1734,7 +1741,7 @@ test "sqlite: statement iterator" {
1734 try addTestData(&db); 1741 try addTestData(&db);
1735 1742
1736 // Cleanup first 1743 // Cleanup first
1737 try db.exec("DELETE FROM user", .{}); 1744 try db.exec("DELETE FROM user", .{}, .{});
1738 1745
1739 // Add data 1746 // Add data
1740 var stmt = try db.prepare("INSERT INTO user(name, id, age, weight) VALUES(?{[]const u8}, ?{usize}, ?{usize}, ?{f32})"); 1747 var stmt = try db.prepare("INSERT INTO user(name, id, age, weight) VALUES(?{[]const u8}, ?{usize}, ?{usize}, ?{f32})");
@@ -1749,7 +1756,7 @@ test "sqlite: statement iterator" {
1749 try expected_rows.append(user); 1756 try expected_rows.append(user);
1750 1757
1751 stmt.reset(); 1758 stmt.reset();
1752 try stmt.exec(user); 1759 try stmt.exec(.{}, user);
1753 1760
1754 const rows_inserted = db.rowsAffected(); 1761 const rows_inserted = db.rowsAffected();
1755 try testing.expectEqual(@as(usize, 1), rows_inserted); 1762 try testing.expectEqual(@as(usize, 1), rows_inserted);
@@ -1822,14 +1829,14 @@ test "sqlite: blob open, reopen" {
1822 const blob_data2 = "\xCA\xFE\xBA\xBEfoobar"; 1829 const blob_data2 = "\xCA\xFE\xBA\xBEfoobar";
1823 1830
1824 // Insert two blobs with a set length 1831 // Insert two blobs with a set length
1825 try db.exec("CREATE TABLE test_blob(id integer primary key, data blob)", .{}); 1832 try db.exec("CREATE TABLE test_blob(id integer primary key, data blob)", .{}, .{});
1826 1833
1827 try db.exec("INSERT INTO test_blob(data) VALUES(?)", .{ 1834 try db.exec("INSERT INTO test_blob(data) VALUES(?)", .{}, .{
1828 .data = ZeroBlob{ .length = blob_data1.len * 2 }, 1835 .data = ZeroBlob{ .length = blob_data1.len * 2 },
1829 }); 1836 });
1830 const rowid1 = db.getLastInsertRowID(); 1837 const rowid1 = db.getLastInsertRowID();
1831 1838
1832 try db.exec("INSERT INTO test_blob(data) VALUES(?)", .{ 1839 try db.exec("INSERT INTO test_blob(data) VALUES(?)", .{}, .{
1833 .data = ZeroBlob{ .length = blob_data2.len * 2 }, 1840 .data = ZeroBlob{ .length = blob_data2.len * 2 },
1834 }); 1841 });
1835 const rowid2 = db.getLastInsertRowID(); 1842 const rowid2 = db.getLastInsertRowID();
@@ -1944,6 +1951,25 @@ test "sqlite: diagnostics format" {
1944 } 1951 }
1945} 1952}
1946 1953
1954test "sqlite: exec with diags, failing statement" {
1955 var db = try getTestDb();
1956
1957 var diags = Diagnostics{};
1958
1959 const result = blk: {
1960 var stmt = try db.prepareWithDiags("ROLLBACK", .{ .diags = &diags });
1961 break :blk stmt.exec(.{ .diags = &diags }, .{});
1962 };
1963
1964 try testing.expectError(error.SQLiteError, result);
1965 try testing.expect(diags.err != null);
1966 try testing.expectEqualStrings("cannot rollback - no transaction is active", diags.err.?.message);
1967
1968 const detailed_err = db.getDetailedError();
1969 try testing.expectEqual(@as(usize, 1), detailed_err.code);
1970 try testing.expectEqualStrings("cannot rollback - no transaction is active", detailed_err.message);
1971}
1972
1947fn getTestDb() !Db { 1973fn getTestDb() !Db {
1948 var buf: [1024]u8 = undefined; 1974 var buf: [1024]u8 = undefined;
1949 var fba = std.heap.FixedBufferAllocator.init(&buf); 1975 var fba = std.heap.FixedBufferAllocator.init(&buf);