From 6261f9b923fae73f5309611da991291313bb70bc Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Mon, 23 Aug 2021 16:02:57 -0700 Subject: add support for enum fields and lay groundwork for #39 --- sqlite.zig | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'sqlite.zig') diff --git a/sqlite.zig b/sqlite.zig index bbf7043..48b0f56 100644 --- a/sqlite.zig +++ b/sqlite.zig @@ -906,6 +906,17 @@ pub fn Iterator(comptime Type: type) type { .Array => try self.readArray(FieldType, i), .Pointer => try self.readPointer(FieldType, options.allocator, i), .Optional => try self.readOptional(FieldType, options, i), + .Enum => |TI| { + const innervalue = try self.readField(FieldType.BaseType, i, options); + + if (comptime std.meta.trait.isZigString(FieldType.BaseType)) { + return std.meta.stringToEnum(FieldType, innervalue) orelse @intToEnum(FieldType, 0); + } + if (@typeInfo(FieldType.BaseType) == .Int) { + return @intToEnum(FieldType, @intCast(TI.tag_type, innervalue)); + } + @compileError("enum column " ++ @typeName(FieldType) ++ " must have a BaseType of either string or int"); + }, else => @compileError("cannot populate field of type " ++ @typeName(FieldType)), }, }; @@ -1039,7 +1050,7 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: ParsedQuery) t inline for (StructTypeInfo.fields) |struct_field, _i| { const bind_marker = query.bind_markers[_i]; switch (bind_marker) { - .Typed => |typ| if (struct_field.field_type != typ) { + .Typed => |typ| if (struct_field.field_type != typ and struct_field.field_type.BaseType != typ) { @compileError("value type " ++ @typeName(struct_field.field_type) ++ " is not the bind marker type " ++ @typeName(typ)); }, .Untyped => {}, @@ -1089,6 +1100,15 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: ParsedQuery) t _ = c.sqlite3_bind_null(self.stmt, column); }, .Null => _ = c.sqlite3_bind_null(self.stmt, column), + .Enum => { + if (comptime std.meta.trait.isZigString(FieldType.BaseType)) { + return self.bindField(FieldType.BaseType, field_name, column, @tagName(field)); + } + if (@typeInfo(FieldType.BaseType) == .Int) { + return self.bindField(FieldType.BaseType, field_name, column, @enumToInt(field)); + } + @compileError("enum column " ++ @typeName(FieldType) ++ " must have a BaseType of either string or int to bind"); + }, else => @compileError("cannot bind field " ++ field_name ++ " of type " ++ @typeName(FieldType)), }, } -- cgit v1.2.3 From 54a2c14dd2206c1cd8ea8f8ef601895bc1b26fa9 Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Mon, 23 Aug 2021 16:03:40 -0700 Subject: tests- use select * when pulling into a struct --- sqlite.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sqlite.zig') diff --git a/sqlite.zig b/sqlite.zig index 48b0f56..fc15ae6 100644 --- a/sqlite.zig +++ b/sqlite.zig @@ -1391,7 +1391,7 @@ test "sqlite: read a single user into a struct" { var db = try getTestDb(); try addTestData(&db); - var stmt = try db.prepare("SELECT name, id, age, weight FROM user WHERE id = ?{usize}"); + var stmt = try db.prepare("SELECT * FROM user WHERE id = ?{usize}"); defer stmt.deinit(); var rows = try stmt.all(TestUser, &arena.allocator, .{}, .{ @@ -1452,7 +1452,7 @@ test "sqlite: read all users into a struct" { var db = try getTestDb(); try addTestData(&db); - var stmt = try db.prepare("SELECT name, id, age, weight FROM user"); + var stmt = try db.prepare("SELECT * FROM user"); defer stmt.deinit(); var rows = try stmt.all(TestUser, &arena.allocator, .{}, .{}); -- cgit v1.2.3 From 6a2ab02c93a401bc53ecb00f9c7ddcf4c7912f55 Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Mon, 23 Aug 2021 16:04:01 -0700 Subject: tests- add enum field cases --- sqlite.zig | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) (limited to 'sqlite.zig') diff --git a/sqlite.zig b/sqlite.zig index fc15ae6..dde8ace 100644 --- a/sqlite.zig +++ b/sqlite.zig @@ -1260,12 +1260,30 @@ const TestUser = struct { id: usize, age: usize, weight: f32, + favorite_color: Color, + + pub const Color = enum { + red, + majenta, + violet, + indigo, + blue, + cyan, + green, + lime, + yellow, + // + orange, + // + + pub const BaseType = []const u8; + }; }; const test_users = &[_]TestUser{ - .{ .name = "Vincent", .id = 20, .age = 33, .weight = 85.4 }, - .{ .name = "Julien", .id = 40, .age = 35, .weight = 100.3 }, - .{ .name = "José", .id = 60, .age = 40, .weight = 240.2 }, + .{ .name = "Vincent", .id = 20, .age = 33, .weight = 85.4, .favorite_color = .violet }, + .{ .name = "Julien", .id = 40, .age = 35, .weight = 100.3, .favorite_color = .green }, + .{ .name = "José", .id = 60, .age = 40, .weight = 240.2, .favorite_color = .indigo }, }; fn createTestTables(db: *Db) !void { @@ -1274,10 +1292,11 @@ fn createTestTables(db: *Db) !void { "DROP TABLE IF EXISTS article", "DROP TABLE IF EXISTS test_blob", \\CREATE TABLE user( - \\ id integer PRIMARY KEY, \\ name text, + \\ id integer PRIMARY KEY, \\ age integer, - \\ weight real + \\ weight real, + \\ favorite_color text \\) , \\CREATE TABLE article( @@ -1299,7 +1318,7 @@ fn addTestData(db: *Db) !void { try createTestTables(db); for (test_users) |user| { - try db.exec("INSERT INTO user(name, id, age, weight) VALUES(?{[]const u8}, ?{usize}, ?{usize}, ?{f32})", .{}, user); + try db.exec("INSERT INTO user(name, id, age, weight, favorite_color) VALUES(?{[]const u8}, ?{usize}, ?{usize}, ?{f32}, ?{[]const u8})", .{}, user); const rows_inserted = db.rowsAffected(); try testing.expectEqual(@as(usize, 1), rows_inserted); @@ -1771,13 +1790,13 @@ test "sqlite: statement reset" { // Add data - var stmt = try db.prepare("INSERT INTO user(name, id, age, weight) VALUES(?{[]const u8}, ?{usize}, ?{usize}, ?{f32})"); + var stmt = try db.prepare("INSERT INTO user(name, id, age, weight, favorite_color) VALUES(?{[]const u8}, ?{usize}, ?{usize}, ?{f32}, ?{[]const u8})"); defer stmt.deinit(); const users = &[_]TestUser{ - .{ .id = 200, .name = "Vincent", .age = 33, .weight = 10.0 }, - .{ .id = 400, .name = "Julien", .age = 35, .weight = 12.0 }, - .{ .id = 600, .name = "José", .age = 40, .weight = 14.0 }, + .{ .id = 200, .name = "Vincent", .age = 33, .weight = 10.0, .favorite_color = .violet }, + .{ .id = 400, .name = "Julien", .age = 35, .weight = 12.0, .favorite_color = .green }, + .{ .id = 600, .name = "José", .age = 40, .weight = 14.0, .favorite_color = .indigo }, }; for (users) |user| { @@ -1801,14 +1820,14 @@ test "sqlite: statement iterator" { try db.exec("DELETE FROM user", .{}, .{}); // Add data - var stmt = try db.prepare("INSERT INTO user(name, id, age, weight) VALUES(?{[]const u8}, ?{usize}, ?{usize}, ?{f32})"); + var stmt = try db.prepare("INSERT INTO user(name, id, age, weight, favorite_color) VALUES(?{[]const u8}, ?{usize}, ?{usize}, ?{f32}, ?{[]const u8})"); defer stmt.deinit(); var expected_rows = std.ArrayList(TestUser).init(allocator); var i: usize = 0; while (i < 20) : (i += 1) { const name = try std.fmt.allocPrint(allocator, "Vincent {d}", .{i}); - const user = TestUser{ .id = i, .name = name, .age = i + 200, .weight = @intToFloat(f32, i + 200) }; + const user = TestUser{ .id = i, .name = name, .age = i + 200, .weight = @intToFloat(f32, i + 200), .favorite_color = .indigo }; try expected_rows.append(user); -- cgit v1.2.3 From bfadc7bcc37a3e1d5b30f2b9f3a35e62b3b1e63e Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Tue, 24 Aug 2021 12:16:53 -0700 Subject: fix bind index --- sqlite.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sqlite.zig') diff --git a/sqlite.zig b/sqlite.zig index dde8ace..5b59963 100644 --- a/sqlite.zig +++ b/sqlite.zig @@ -1102,10 +1102,10 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: ParsedQuery) t .Null => _ = c.sqlite3_bind_null(self.stmt, column), .Enum => { if (comptime std.meta.trait.isZigString(FieldType.BaseType)) { - return self.bindField(FieldType.BaseType, field_name, column, @tagName(field)); + return self.bindField(FieldType.BaseType, field_name, i, @tagName(field)); } if (@typeInfo(FieldType.BaseType) == .Int) { - return self.bindField(FieldType.BaseType, field_name, column, @enumToInt(field)); + return self.bindField(FieldType.BaseType, field_name, i, @enumToInt(field)); } @compileError("enum column " ++ @typeName(FieldType) ++ " must have a BaseType of either string or int to bind"); }, -- cgit v1.2.3 From 81449a37e00dad10d580fe76dacd3e4ee2576dc7 Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Tue, 24 Aug 2021 12:17:42 -0700 Subject: enum tests now pass without orelse in read --- sqlite.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sqlite.zig') diff --git a/sqlite.zig b/sqlite.zig index 5b59963..a72a143 100644 --- a/sqlite.zig +++ b/sqlite.zig @@ -910,7 +910,7 @@ pub fn Iterator(comptime Type: type) type { const innervalue = try self.readField(FieldType.BaseType, i, options); if (comptime std.meta.trait.isZigString(FieldType.BaseType)) { - return std.meta.stringToEnum(FieldType, innervalue) orelse @intToEnum(FieldType, 0); + return std.meta.stringToEnum(FieldType, innervalue) orelse unreachable; } if (@typeInfo(FieldType.BaseType) == .Int) { return @intToEnum(FieldType, @intCast(TI.tag_type, innervalue)); -- cgit v1.2.3 From 2fc1ca7ec801ea97140998582e8e05431da24cbb Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Tue, 24 Aug 2021 16:04:32 -0700 Subject: fix type constraint checks for container and non-container types --- sqlite.zig | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'sqlite.zig') diff --git a/sqlite.zig b/sqlite.zig index a72a143..67b67f5 100644 --- a/sqlite.zig +++ b/sqlite.zig @@ -1050,8 +1050,24 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: ParsedQuery) t inline for (StructTypeInfo.fields) |struct_field, _i| { const bind_marker = query.bind_markers[_i]; switch (bind_marker) { - .Typed => |typ| if (struct_field.field_type != typ and struct_field.field_type.BaseType != typ) { - @compileError("value type " ++ @typeName(struct_field.field_type) ++ " is not the bind marker type " ++ @typeName(typ)); + .Typed => |typ| { + const FieldTypeInfo = @typeInfo(struct_field.field_type); + switch (FieldTypeInfo) { + .Struct, .Enum, .Union => { + if (@hasDecl(struct_field.field_type, "BaseType")) { + if (struct_field.field_type.BaseType != typ) { + @compileError("value type " ++ @typeName(struct_field.field_type.BaseType) ++ " is not the bind marker type " ++ @typeName(typ)); + } + } else if (struct_field.field_type != typ) { + @compileError("value type " ++ @typeName(struct_field.field_type) ++ " is not the bind marker type " ++ @typeName(typ)); + } + }, + else => { + if (struct_field.field_type != typ) { + @compileError("value type " ++ @typeName(struct_field.field_type) ++ " is not the bind marker type " ++ @typeName(typ)); + } + }, + } }, .Untyped => {}, } -- cgit v1.2.3 From 44029cd6e73fd0cdd418b48c0dcaaf7e1f5eca20 Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Tue, 24 Aug 2021 16:13:43 -0700 Subject: dry up the updated constraint check --- sqlite.zig | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) (limited to 'sqlite.zig') diff --git a/sqlite.zig b/sqlite.zig index 67b67f5..abeea55 100644 --- a/sqlite.zig +++ b/sqlite.zig @@ -1053,20 +1053,11 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: ParsedQuery) t .Typed => |typ| { const FieldTypeInfo = @typeInfo(struct_field.field_type); switch (FieldTypeInfo) { - .Struct, .Enum, .Union => { - if (@hasDecl(struct_field.field_type, "BaseType")) { - if (struct_field.field_type.BaseType != typ) { - @compileError("value type " ++ @typeName(struct_field.field_type.BaseType) ++ " is not the bind marker type " ++ @typeName(typ)); - } - } else if (struct_field.field_type != typ) { - @compileError("value type " ++ @typeName(struct_field.field_type) ++ " is not the bind marker type " ++ @typeName(typ)); - } - }, - else => { - if (struct_field.field_type != typ) { - @compileError("value type " ++ @typeName(struct_field.field_type) ++ " is not the bind marker type " ++ @typeName(typ)); - } - }, + .Struct, .Enum, .Union => comptime assertMarkerType( + if (@hasDecl(struct_field.field_type, "BaseType")) struct_field.field_type.BaseType else struct_field.field_type, + typ, + ), + else => comptime assertMarkerType(struct_field.field_type, typ), } }, .Untyped => {}, @@ -1078,6 +1069,12 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: ParsedQuery) t } } + fn assertMarkerType(comptime Actual: type, comptime Expected: type) void { + if (Actual != Expected) { + @compileError("value type " ++ @typeName(Actual) ++ " is not the bind marker type " ++ @typeName(Expected)); + } + } + fn bindField(self: *Self, comptime FieldType: type, comptime field_name: []const u8, i: c_int, field: FieldType) void { const field_type_info = @typeInfo(FieldType); const column = i + 1; -- cgit v1.2.3