summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Vincent Rischmann2022-04-21 21:48:22 +0200
committerGravatar Vincent Rischmann2022-04-21 22:56:40 +0200
commit7be2855f9f4e4f747bf59091cc0725c9275a208c (patch)
treeabac4fb8bfbeac6a82a599cae77fba9464062a1d
parentMerge branch 'update-readme' (diff)
downloadzig-sqlite-7be2855f9f4e4f747bf59091cc0725c9275a208c.tar.gz
zig-sqlite-7be2855f9f4e4f747bf59091cc0725c9275a208c.tar.xz
zig-sqlite-7be2855f9f4e4f747bf59091cc0725c9275a208c.zip
cleanup the temporary values correctly for enums
-rw-r--r--sqlite.zig68
1 files changed, 42 insertions, 26 deletions
diff --git a/sqlite.zig b/sqlite.zig
index d45bda3..5baa403 100644
--- a/sqlite.zig
+++ b/sqlite.zig
@@ -2,6 +2,7 @@ const std = @import("std");
2const builtin = @import("builtin"); 2const builtin = @import("builtin");
3const build_options = @import("build_options"); 3const build_options = @import("build_options");
4const debug = std.debug; 4const debug = std.debug;
5const heap = std.heap;
5const io = std.io; 6const io = std.io;
6const mem = std.mem; 7const mem = std.mem;
7const testing = std.testing; 8const 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
3300test "sqlite: bind custom type" { 3307test "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