diff options
Diffstat (limited to 'sqlite.zig')
| -rw-r--r-- | sqlite.zig | 84 |
1 files changed, 68 insertions, 16 deletions
| @@ -906,6 +906,17 @@ pub fn Iterator(comptime Type: type) type { | |||
| 906 | .Array => try self.readArray(FieldType, i), | 906 | .Array => try self.readArray(FieldType, i), |
| 907 | .Pointer => try self.readPointer(FieldType, options.allocator, i), | 907 | .Pointer => try self.readPointer(FieldType, options.allocator, i), |
| 908 | .Optional => try self.readOptional(FieldType, options, i), | 908 | .Optional => try self.readOptional(FieldType, options, i), |
| 909 | .Enum => |TI| { | ||
| 910 | const innervalue = try self.readField(FieldType.BaseType, i, options); | ||
| 911 | |||
| 912 | if (comptime std.meta.trait.isZigString(FieldType.BaseType)) { | ||
| 913 | return std.meta.stringToEnum(FieldType, innervalue) orelse unreachable; | ||
| 914 | } | ||
| 915 | if (@typeInfo(FieldType.BaseType) == .Int) { | ||
| 916 | return @intToEnum(FieldType, @intCast(TI.tag_type, innervalue)); | ||
| 917 | } | ||
| 918 | @compileError("enum column " ++ @typeName(FieldType) ++ " must have a BaseType of either string or int"); | ||
| 919 | }, | ||
| 909 | else => @compileError("cannot populate field of type " ++ @typeName(FieldType)), | 920 | else => @compileError("cannot populate field of type " ++ @typeName(FieldType)), |
| 910 | }, | 921 | }, |
| 911 | }; | 922 | }; |
| @@ -1039,8 +1050,15 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: ParsedQuery) t | |||
| 1039 | inline for (StructTypeInfo.fields) |struct_field, _i| { | 1050 | inline for (StructTypeInfo.fields) |struct_field, _i| { |
| 1040 | const bind_marker = query.bind_markers[_i]; | 1051 | const bind_marker = query.bind_markers[_i]; |
| 1041 | switch (bind_marker) { | 1052 | switch (bind_marker) { |
| 1042 | .Typed => |typ| if (struct_field.field_type != typ) { | 1053 | .Typed => |typ| { |
| 1043 | @compileError("value type " ++ @typeName(struct_field.field_type) ++ " is not the bind marker type " ++ @typeName(typ)); | 1054 | const FieldTypeInfo = @typeInfo(struct_field.field_type); |
| 1055 | switch (FieldTypeInfo) { | ||
| 1056 | .Struct, .Enum, .Union => comptime assertMarkerType( | ||
| 1057 | if (@hasDecl(struct_field.field_type, "BaseType")) struct_field.field_type.BaseType else struct_field.field_type, | ||
| 1058 | typ, | ||
| 1059 | ), | ||
| 1060 | else => comptime assertMarkerType(struct_field.field_type, typ), | ||
| 1061 | } | ||
| 1044 | }, | 1062 | }, |
| 1045 | .Untyped => {}, | 1063 | .Untyped => {}, |
| 1046 | } | 1064 | } |
| @@ -1051,6 +1069,12 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: ParsedQuery) t | |||
| 1051 | } | 1069 | } |
| 1052 | } | 1070 | } |
| 1053 | 1071 | ||
| 1072 | fn assertMarkerType(comptime Actual: type, comptime Expected: type) void { | ||
| 1073 | if (Actual != Expected) { | ||
| 1074 | @compileError("value type " ++ @typeName(Actual) ++ " is not the bind marker type " ++ @typeName(Expected)); | ||
| 1075 | } | ||
| 1076 | } | ||
| 1077 | |||
| 1054 | fn bindField(self: *Self, comptime FieldType: type, comptime field_name: []const u8, i: c_int, field: FieldType) void { | 1078 | fn bindField(self: *Self, comptime FieldType: type, comptime field_name: []const u8, i: c_int, field: FieldType) void { |
| 1055 | const field_type_info = @typeInfo(FieldType); | 1079 | const field_type_info = @typeInfo(FieldType); |
| 1056 | const column = i + 1; | 1080 | const column = i + 1; |
| @@ -1089,6 +1113,15 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: ParsedQuery) t | |||
| 1089 | _ = c.sqlite3_bind_null(self.stmt, column); | 1113 | _ = c.sqlite3_bind_null(self.stmt, column); |
| 1090 | }, | 1114 | }, |
| 1091 | .Null => _ = c.sqlite3_bind_null(self.stmt, column), | 1115 | .Null => _ = c.sqlite3_bind_null(self.stmt, column), |
| 1116 | .Enum => { | ||
| 1117 | if (comptime std.meta.trait.isZigString(FieldType.BaseType)) { | ||
| 1118 | return self.bindField(FieldType.BaseType, field_name, i, @tagName(field)); | ||
| 1119 | } | ||
| 1120 | if (@typeInfo(FieldType.BaseType) == .Int) { | ||
| 1121 | return self.bindField(FieldType.BaseType, field_name, i, @enumToInt(field)); | ||
| 1122 | } | ||
| 1123 | @compileError("enum column " ++ @typeName(FieldType) ++ " must have a BaseType of either string or int to bind"); | ||
| 1124 | }, | ||
| 1092 | else => @compileError("cannot bind field " ++ field_name ++ " of type " ++ @typeName(FieldType)), | 1125 | else => @compileError("cannot bind field " ++ field_name ++ " of type " ++ @typeName(FieldType)), |
| 1093 | }, | 1126 | }, |
| 1094 | } | 1127 | } |
| @@ -1240,12 +1273,30 @@ const TestUser = struct { | |||
| 1240 | id: usize, | 1273 | id: usize, |
| 1241 | age: usize, | 1274 | age: usize, |
| 1242 | weight: f32, | 1275 | weight: f32, |
| 1276 | favorite_color: Color, | ||
| 1277 | |||
| 1278 | pub const Color = enum { | ||
| 1279 | red, | ||
| 1280 | majenta, | ||
| 1281 | violet, | ||
| 1282 | indigo, | ||
| 1283 | blue, | ||
| 1284 | cyan, | ||
| 1285 | green, | ||
| 1286 | lime, | ||
| 1287 | yellow, | ||
| 1288 | // | ||
| 1289 | orange, | ||
| 1290 | // | ||
| 1291 | |||
| 1292 | pub const BaseType = []const u8; | ||
| 1293 | }; | ||
| 1243 | }; | 1294 | }; |
| 1244 | 1295 | ||
| 1245 | const test_users = &[_]TestUser{ | 1296 | const test_users = &[_]TestUser{ |
| 1246 | .{ .name = "Vincent", .id = 20, .age = 33, .weight = 85.4 }, | 1297 | .{ .name = "Vincent", .id = 20, .age = 33, .weight = 85.4, .favorite_color = .violet }, |
| 1247 | .{ .name = "Julien", .id = 40, .age = 35, .weight = 100.3 }, | 1298 | .{ .name = "Julien", .id = 40, .age = 35, .weight = 100.3, .favorite_color = .green }, |
| 1248 | .{ .name = "José", .id = 60, .age = 40, .weight = 240.2 }, | 1299 | .{ .name = "José", .id = 60, .age = 40, .weight = 240.2, .favorite_color = .indigo }, |
| 1249 | }; | 1300 | }; |
| 1250 | 1301 | ||
| 1251 | fn createTestTables(db: *Db) !void { | 1302 | fn createTestTables(db: *Db) !void { |
| @@ -1254,10 +1305,11 @@ fn createTestTables(db: *Db) !void { | |||
| 1254 | "DROP TABLE IF EXISTS article", | 1305 | "DROP TABLE IF EXISTS article", |
| 1255 | "DROP TABLE IF EXISTS test_blob", | 1306 | "DROP TABLE IF EXISTS test_blob", |
| 1256 | \\CREATE TABLE user( | 1307 | \\CREATE TABLE user( |
| 1257 | \\ id integer PRIMARY KEY, | ||
| 1258 | \\ name text, | 1308 | \\ name text, |
| 1309 | \\ id integer PRIMARY KEY, | ||
| 1259 | \\ age integer, | 1310 | \\ age integer, |
| 1260 | \\ weight real | 1311 | \\ weight real, |
| 1312 | \\ favorite_color text | ||
| 1261 | \\) | 1313 | \\) |
| 1262 | , | 1314 | , |
| 1263 | \\CREATE TABLE article( | 1315 | \\CREATE TABLE article( |
| @@ -1279,7 +1331,7 @@ fn addTestData(db: *Db) !void { | |||
| 1279 | try createTestTables(db); | 1331 | try createTestTables(db); |
| 1280 | 1332 | ||
| 1281 | for (test_users) |user| { | 1333 | for (test_users) |user| { |
| 1282 | try db.exec("INSERT INTO user(name, id, age, weight) VALUES(?{[]const u8}, ?{usize}, ?{usize}, ?{f32})", .{}, user); | 1334 | try db.exec("INSERT INTO user(name, id, age, weight, favorite_color) VALUES(?{[]const u8}, ?{usize}, ?{usize}, ?{f32}, ?{[]const u8})", .{}, user); |
| 1283 | 1335 | ||
| 1284 | const rows_inserted = db.rowsAffected(); | 1336 | const rows_inserted = db.rowsAffected(); |
| 1285 | try testing.expectEqual(@as(usize, 1), rows_inserted); | 1337 | try testing.expectEqual(@as(usize, 1), rows_inserted); |
| @@ -1371,7 +1423,7 @@ test "sqlite: read a single user into a struct" { | |||
| 1371 | var db = try getTestDb(); | 1423 | var db = try getTestDb(); |
| 1372 | try addTestData(&db); | 1424 | try addTestData(&db); |
| 1373 | 1425 | ||
| 1374 | var stmt = try db.prepare("SELECT name, id, age, weight FROM user WHERE id = ?{usize}"); | 1426 | var stmt = try db.prepare("SELECT * FROM user WHERE id = ?{usize}"); |
| 1375 | defer stmt.deinit(); | 1427 | defer stmt.deinit(); |
| 1376 | 1428 | ||
| 1377 | var rows = try stmt.all(TestUser, &arena.allocator, .{}, .{ | 1429 | var rows = try stmt.all(TestUser, &arena.allocator, .{}, .{ |
| @@ -1432,7 +1484,7 @@ test "sqlite: read all users into a struct" { | |||
| 1432 | var db = try getTestDb(); | 1484 | var db = try getTestDb(); |
| 1433 | try addTestData(&db); | 1485 | try addTestData(&db); |
| 1434 | 1486 | ||
| 1435 | var stmt = try db.prepare("SELECT name, id, age, weight FROM user"); | 1487 | var stmt = try db.prepare("SELECT * FROM user"); |
| 1436 | defer stmt.deinit(); | 1488 | defer stmt.deinit(); |
| 1437 | 1489 | ||
| 1438 | var rows = try stmt.all(TestUser, &arena.allocator, .{}, .{}); | 1490 | var rows = try stmt.all(TestUser, &arena.allocator, .{}, .{}); |
| @@ -1751,13 +1803,13 @@ test "sqlite: statement reset" { | |||
| 1751 | 1803 | ||
| 1752 | // Add data | 1804 | // Add data |
| 1753 | 1805 | ||
| 1754 | var stmt = try db.prepare("INSERT INTO user(name, id, age, weight) VALUES(?{[]const u8}, ?{usize}, ?{usize}, ?{f32})"); | 1806 | var stmt = try db.prepare("INSERT INTO user(name, id, age, weight, favorite_color) VALUES(?{[]const u8}, ?{usize}, ?{usize}, ?{f32}, ?{[]const u8})"); |
| 1755 | defer stmt.deinit(); | 1807 | defer stmt.deinit(); |
| 1756 | 1808 | ||
| 1757 | const users = &[_]TestUser{ | 1809 | const users = &[_]TestUser{ |
| 1758 | .{ .id = 200, .name = "Vincent", .age = 33, .weight = 10.0 }, | 1810 | .{ .id = 200, .name = "Vincent", .age = 33, .weight = 10.0, .favorite_color = .violet }, |
| 1759 | .{ .id = 400, .name = "Julien", .age = 35, .weight = 12.0 }, | 1811 | .{ .id = 400, .name = "Julien", .age = 35, .weight = 12.0, .favorite_color = .green }, |
| 1760 | .{ .id = 600, .name = "José", .age = 40, .weight = 14.0 }, | 1812 | .{ .id = 600, .name = "José", .age = 40, .weight = 14.0, .favorite_color = .indigo }, |
| 1761 | }; | 1813 | }; |
| 1762 | 1814 | ||
| 1763 | for (users) |user| { | 1815 | for (users) |user| { |
| @@ -1781,14 +1833,14 @@ test "sqlite: statement iterator" { | |||
| 1781 | try db.exec("DELETE FROM user", .{}, .{}); | 1833 | try db.exec("DELETE FROM user", .{}, .{}); |
| 1782 | 1834 | ||
| 1783 | // Add data | 1835 | // Add data |
| 1784 | var stmt = try db.prepare("INSERT INTO user(name, id, age, weight) VALUES(?{[]const u8}, ?{usize}, ?{usize}, ?{f32})"); | 1836 | var stmt = try db.prepare("INSERT INTO user(name, id, age, weight, favorite_color) VALUES(?{[]const u8}, ?{usize}, ?{usize}, ?{f32}, ?{[]const u8})"); |
| 1785 | defer stmt.deinit(); | 1837 | defer stmt.deinit(); |
| 1786 | 1838 | ||
| 1787 | var expected_rows = std.ArrayList(TestUser).init(allocator); | 1839 | var expected_rows = std.ArrayList(TestUser).init(allocator); |
| 1788 | var i: usize = 0; | 1840 | var i: usize = 0; |
| 1789 | while (i < 20) : (i += 1) { | 1841 | while (i < 20) : (i += 1) { |
| 1790 | const name = try std.fmt.allocPrint(allocator, "Vincent {d}", .{i}); | 1842 | const name = try std.fmt.allocPrint(allocator, "Vincent {d}", .{i}); |
| 1791 | const user = TestUser{ .id = i, .name = name, .age = i + 200, .weight = @intToFloat(f32, i + 200) }; | 1843 | const user = TestUser{ .id = i, .name = name, .age = i + 200, .weight = @intToFloat(f32, i + 200), .favorite_color = .indigo }; |
| 1792 | 1844 | ||
| 1793 | try expected_rows.append(user); | 1845 | try expected_rows.append(user); |
| 1794 | 1846 | ||