diff options
Diffstat (limited to '')
| -rw-r--r-- | sqlite.zig | 68 |
1 files changed, 42 insertions, 26 deletions
| @@ -2,6 +2,7 @@ const std = @import("std"); | |||
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const build_options = @import("build_options"); | 3 | const build_options = @import("build_options"); |
| 4 | const debug = std.debug; | 4 | const debug = std.debug; |
| 5 | const heap = std.heap; | ||
| 5 | const io = std.io; | 6 | const io = std.io; |
| 6 | const mem = std.mem; | 7 | const mem = std.mem; |
| 7 | const testing = std.testing; | 8 | const testing = std.testing; |
| @@ -1071,8 +1072,8 @@ pub fn Iterator(comptime Type: type) type { | |||
| 1071 | } | 1072 | } |
| 1072 | 1073 | ||
| 1073 | if (@typeInfo(Type.BaseType) == .Int) { | 1074 | if (@typeInfo(Type.BaseType) == .Int) { |
| 1074 | const innervalue = try self.readField(Type.BaseType, options, 0); | 1075 | const inner_value = try self.readField(Type.BaseType, options, 0); |
| 1075 | return @intToEnum(Type, @intCast(TI.tag_type, innervalue)); | 1076 | return @intToEnum(Type, @intCast(TI.tag_type, inner_value)); |
| 1076 | } | 1077 | } |
| 1077 | 1078 | ||
| 1078 | @compileError("enum column " ++ @typeName(Type) ++ " must have a BaseType of either string or int"); | 1079 | @compileError("enum column " ++ @typeName(Type) ++ " must have a BaseType of either string or int"); |
| @@ -1146,15 +1147,17 @@ pub fn Iterator(comptime Type: type) type { | |||
| 1146 | .Enum => |TI| { | 1147 | .Enum => |TI| { |
| 1147 | debug.assert(columns == 1); | 1148 | debug.assert(columns == 1); |
| 1148 | 1149 | ||
| 1149 | const innervalue = try self.readField(Type.BaseType, .{ | 1150 | const inner_value = try self.readField(Type.BaseType, .{ .allocator = allocator }, 0); |
| 1150 | .allocator = allocator, | ||
| 1151 | }, 0); | ||
| 1152 | 1151 | ||
| 1153 | if (comptime std.meta.trait.isZigString(Type.BaseType)) { | 1152 | if (comptime std.meta.trait.isZigString(Type.BaseType)) { |
| 1154 | return std.meta.stringToEnum(Type, innervalue) orelse unreachable; | 1153 | // The inner value is never returned to the user, we must free it ourselves. |
| 1154 | defer allocator.free(inner_value); | ||
| 1155 | |||
| 1156 | // TODO(vincent): don't use unreachable | ||
| 1157 | return std.meta.stringToEnum(Type, inner_value) orelse unreachable; | ||
| 1155 | } | 1158 | } |
| 1156 | if (@typeInfo(Type.BaseType) == .Int) { | 1159 | if (@typeInfo(Type.BaseType) == .Int) { |
| 1157 | return @intToEnum(Type, @intCast(TI.tag_type, innervalue)); | 1160 | return @intToEnum(Type, @intCast(TI.tag_type, inner_value)); |
| 1158 | } | 1161 | } |
| 1159 | @compileError("enum column " ++ @typeName(Type) ++ " must have a BaseType of either string or int"); | 1162 | @compileError("enum column " ++ @typeName(Type) ++ " must have a BaseType of either string or int"); |
| 1160 | }, | 1163 | }, |
| @@ -1438,19 +1441,23 @@ pub fn Iterator(comptime Type: type) type { | |||
| 1438 | .Pointer => try self.readPointer(FieldType, options, i), | 1441 | .Pointer => try self.readPointer(FieldType, options, i), |
| 1439 | .Optional => try self.readOptional(FieldType, options, i), | 1442 | .Optional => try self.readOptional(FieldType, options, i), |
| 1440 | .Enum => |TI| { | 1443 | .Enum => |TI| { |
| 1441 | const innervalue = try self.readField(FieldType.BaseType, options, i); | 1444 | const inner_value = try self.readField(FieldType.BaseType, options, i); |
| 1442 | 1445 | ||
| 1443 | if (comptime std.meta.trait.isZigString(FieldType.BaseType)) { | 1446 | if (comptime std.meta.trait.isZigString(FieldType.BaseType)) { |
| 1444 | return std.meta.stringToEnum(FieldType, innervalue) orelse unreachable; | 1447 | // The inner value is never returned to the user, we must free it ourselves. |
| 1448 | defer options.allocator.free(inner_value); | ||
| 1449 | |||
| 1450 | // TODO(vincent): don't use unreachable | ||
| 1451 | return std.meta.stringToEnum(FieldType, inner_value) orelse unreachable; | ||
| 1445 | } | 1452 | } |
| 1446 | if (@typeInfo(FieldType.BaseType) == .Int) { | 1453 | if (@typeInfo(FieldType.BaseType) == .Int) { |
| 1447 | return @intToEnum(FieldType, @intCast(TI.tag_type, innervalue)); | 1454 | return @intToEnum(FieldType, @intCast(TI.tag_type, inner_value)); |
| 1448 | } | 1455 | } |
| 1449 | @compileError("enum column " ++ @typeName(FieldType) ++ " must have a BaseType of either string or int"); | 1456 | @compileError("enum column " ++ @typeName(FieldType) ++ " must have a BaseType of either string or int"); |
| 1450 | }, | 1457 | }, |
| 1451 | .Struct => { | 1458 | .Struct => { |
| 1452 | const innervalue = try self.readField(FieldType.BaseType, options, i); | 1459 | const inner_value = try self.readField(FieldType.BaseType, options, i); |
| 1453 | return try FieldType.readField(options.allocator, innervalue); | 1460 | return try FieldType.readField(options.allocator, inner_value); |
| 1454 | }, | 1461 | }, |
| 1455 | else => @compileError("cannot populate field of type " ++ @typeName(FieldType)), | 1462 | else => @compileError("cannot populate field of type " ++ @typeName(FieldType)), |
| 1456 | }, | 1463 | }, |
| @@ -3298,22 +3305,23 @@ const MyData = struct { | |||
| 3298 | }; | 3305 | }; |
| 3299 | 3306 | ||
| 3300 | test "sqlite: bind custom type" { | 3307 | test "sqlite: bind custom type" { |
| 3301 | var arena = std.heap.ArenaAllocator.init(testing.allocator); | ||
| 3302 | defer arena.deinit(); | ||
| 3303 | var allocator = arena.allocator(); | ||
| 3304 | |||
| 3305 | var db = try getTestDb(); | 3308 | var db = try getTestDb(); |
| 3306 | defer db.deinit(); | 3309 | defer db.deinit(); |
| 3307 | try addTestData(&db); | 3310 | try addTestData(&db); |
| 3308 | 3311 | ||
| 3309 | const my_data = MyData{ | ||
| 3310 | .data = [_]u8{'x'} ** 16, | ||
| 3311 | }; | ||
| 3312 | |||
| 3313 | { | 3312 | { |
| 3314 | // insertion | 3313 | var i: usize = 0; |
| 3315 | var stmt = try db.prepare("INSERT INTO article(data) VALUES(?)"); | 3314 | while (i < 20) : (i += 1) { |
| 3316 | try stmt.execAlloc(allocator, .{}, .{my_data}); | 3315 | var my_data: MyData = undefined; |
| 3316 | mem.set(u8, &my_data.data, @intCast(u8, i)); | ||
| 3317 | |||
| 3318 | var arena = heap.ArenaAllocator.init(testing.allocator); | ||
| 3319 | defer arena.deinit(); | ||
| 3320 | |||
| 3321 | // insertion | ||
| 3322 | var stmt = try db.prepare("INSERT INTO article(data) VALUES(?)"); | ||
| 3323 | try stmt.execAlloc(arena.allocator(), .{}, .{my_data}); | ||
| 3324 | } | ||
| 3317 | } | 3325 | } |
| 3318 | { | 3326 | { |
| 3319 | // reading back | 3327 | // reading back |
| @@ -3327,10 +3335,18 @@ test "sqlite: bind custom type" { | |||
| 3327 | is_published: bool, | 3335 | is_published: bool, |
| 3328 | }; | 3336 | }; |
| 3329 | 3337 | ||
| 3330 | const row = try stmt.oneAlloc(Article, allocator, .{}, .{}); | 3338 | var arena = heap.ArenaAllocator.init(testing.allocator); |
| 3339 | defer arena.deinit(); | ||
| 3331 | 3340 | ||
| 3332 | try testing.expect(row != null); | 3341 | const rows = try stmt.all(Article, arena.allocator(), .{}, .{}); |
| 3333 | try testing.expectEqualSlices(u8, &my_data.data, &row.?.data.data); | 3342 | try testing.expectEqual(@as(usize, 20), rows.len); |
| 3343 | |||
| 3344 | for (rows) |row, i| { | ||
| 3345 | var exp_data: MyData = undefined; | ||
| 3346 | mem.set(u8, &exp_data.data, @intCast(u8, i)); | ||
| 3347 | |||
| 3348 | try testing.expectEqualSlices(u8, &exp_data.data, &row.data.data); | ||
| 3349 | } | ||
| 3334 | } | 3350 | } |
| 3335 | } | 3351 | } |
| 3336 | 3352 | ||