diff options
| author | 2021-04-14 01:30:09 +0200 | |
|---|---|---|
| committer | 2021-08-02 14:59:48 +0200 | |
| commit | 11564a35dd1409124352903e4f4099c3061d9570 (patch) | |
| tree | c340fab5961b2fd0f437c6be6a20e5517d4aa0bb /sqlite.zig | |
| parent | Merge branch 'improve-readme' (diff) | |
| download | zig-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.zig | 68 |
1 files changed, 47 insertions, 21 deletions
| @@ -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 | ||
| 1954 | test "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 | |||
| 1947 | fn getTestDb() !Db { | 1973 | fn 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); |