summaryrefslogtreecommitdiff
path: root/sqlite.zig
diff options
context:
space:
mode:
Diffstat (limited to 'sqlite.zig')
-rw-r--r--sqlite.zig64
1 files changed, 51 insertions, 13 deletions
diff --git a/sqlite.zig b/sqlite.zig
index d521175..8477151 100644
--- a/sqlite.zig
+++ b/sqlite.zig
@@ -21,6 +21,7 @@ const getDetailedErrorFromResultCode = errors.getDetailedErrorFromResultCode;
21 21
22const getTestDb = @import("test.zig").getTestDb; 22const getTestDb = @import("test.zig").getTestDb;
23pub const vtab = @import("vtab.zig"); 23pub const vtab = @import("vtab.zig");
24
24const helpers = @import("helpers.zig"); 25const helpers = @import("helpers.zig");
25 26
26test { 27test {
@@ -29,6 +30,43 @@ test {
29 30
30const logger = std.log.scoped(.sqlite); 31const logger = std.log.scoped(.sqlite);
31 32
33// Returns true if the passed type is a struct.
34fn isStruct(comptime T: type) bool {
35 const type_info = @typeInfo(T);
36 return type_info == .Struct;
37}
38
39// Returns true if the passed type will coerce to []const u8.
40//
41// NOTE(vincent): this is straight from the Zig stdlib before it was removed.
42fn isZigString(comptime T: type) bool {
43 return comptime blk: {
44 // Only pointer types can be strings, no optionals
45 const info = @typeInfo(T);
46 if (info != .Pointer) break :blk false;
47
48 const ptr = &info.Pointer;
49 // Check for CV qualifiers that would prevent coerction to []const u8
50 if (ptr.is_volatile or ptr.is_allowzero) break :blk false;
51
52 // If it's already a slice, simple check.
53 if (ptr.size == .Slice) {
54 break :blk ptr.child == u8;
55 }
56
57 // Otherwise check if it's an array type that coerces to slice.
58 if (ptr.size == .One) {
59 const child = @typeInfo(ptr.child);
60 if (child == .Array) {
61 const arr = &child.Array;
62 break :blk arr.child == u8;
63 }
64 }
65
66 break :blk false;
67 };
68}
69
32/// Text is used to represent a SQLite TEXT value when binding a parameter or reading a column. 70/// Text is used to represent a SQLite TEXT value when binding a parameter or reading a column.
33pub const Text = struct { data: []const u8 }; 71pub const Text = struct { data: []const u8 };
34 72
@@ -1062,7 +1100,7 @@ pub fn Iterator(comptime Type: type) type {
1062 .Enum => |TI| { 1100 .Enum => |TI| {
1063 debug.assert(columns == 1); 1101 debug.assert(columns == 1);
1064 1102
1065 if (comptime std.meta.trait.isZigString(Type.BaseType)) { 1103 if (comptime isZigString(Type.BaseType)) {
1066 @compileError("cannot read into type " ++ @typeName(Type) ++ " ; BaseType " ++ @typeName(Type.BaseType) ++ " requires allocation, use nextAlloc or oneAlloc"); 1104 @compileError("cannot read into type " ++ @typeName(Type) ++ " ; BaseType " ++ @typeName(Type.BaseType) ++ " requires allocation, use nextAlloc or oneAlloc");
1067 } 1105 }
1068 1106
@@ -1144,7 +1182,7 @@ pub fn Iterator(comptime Type: type) type {
1144 1182
1145 const inner_value = try self.readField(Type.BaseType, .{ .allocator = allocator }, 0); 1183 const inner_value = try self.readField(Type.BaseType, .{ .allocator = allocator }, 0);
1146 1184
1147 if (comptime std.meta.trait.isZigString(Type.BaseType)) { 1185 if (comptime isZigString(Type.BaseType)) {
1148 // The inner value is never returned to the user, we must free it ourselves. 1186 // The inner value is never returned to the user, we must free it ourselves.
1149 defer allocator.free(inner_value); 1187 defer allocator.free(inner_value);
1150 1188
@@ -1308,10 +1346,10 @@ pub fn Iterator(comptime Type: type) type {
1308 } 1346 }
1309 1347
1310 fn readPointer(self: *Self, comptime PointerType: type, options: anytype, i: usize) !PointerType { 1348 fn readPointer(self: *Self, comptime PointerType: type, options: anytype, i: usize) !PointerType {
1311 if (!comptime std.meta.trait.is(.Struct)(@TypeOf(options))) { 1349 if (!comptime isStruct(@TypeOf(options))) {
1312 @compileError("options passed to readPointer must be a struct"); 1350 @compileError("options passed to readPointer must be a struct");
1313 } 1351 }
1314 if (!comptime std.meta.trait.hasField("allocator")(@TypeOf(options))) { 1352 if (!@hasField(@TypeOf(options), "allocator")) {
1315 @compileError("options passed to readPointer must have an allocator field"); 1353 @compileError("options passed to readPointer must have an allocator field");
1316 } 1354 }
1317 1355
@@ -1339,7 +1377,7 @@ pub fn Iterator(comptime Type: type) type {
1339 } 1377 }
1340 1378
1341 fn readOptional(self: *Self, comptime OptionalType: type, options: anytype, _i: usize) !OptionalType { 1379 fn readOptional(self: *Self, comptime OptionalType: type, options: anytype, _i: usize) !OptionalType {
1342 if (!comptime std.meta.trait.is(.Struct)(@TypeOf(options))) { 1380 if (!comptime isStruct(@TypeOf(options))) {
1343 @compileError("options passed to readOptional must be a struct"); 1381 @compileError("options passed to readOptional must be a struct");
1344 } 1382 }
1345 1383
@@ -1384,7 +1422,7 @@ pub fn Iterator(comptime Type: type) type {
1384 // 1422 //
1385 // TODO(vincent): add comptime checks for the fields/columns. 1423 // TODO(vincent): add comptime checks for the fields/columns.
1386 fn readStruct(self: *Self, options: anytype) !Type { 1424 fn readStruct(self: *Self, options: anytype) !Type {
1387 if (!comptime std.meta.trait.is(.Struct)(@TypeOf(options))) { 1425 if (!comptime isStruct(@TypeOf(options))) {
1388 @compileError("options passed to readStruct must be a struct"); 1426 @compileError("options passed to readStruct must be a struct");
1389 } 1427 }
1390 1428
@@ -1402,7 +1440,7 @@ pub fn Iterator(comptime Type: type) type {
1402 } 1440 }
1403 1441
1404 fn readField(self: *Self, comptime FieldType: type, options: anytype, i: usize) !FieldType { 1442 fn readField(self: *Self, comptime FieldType: type, options: anytype, i: usize) !FieldType {
1405 if (!comptime std.meta.trait.is(.Struct)(@TypeOf(options))) { 1443 if (!comptime isStruct(@TypeOf(options))) {
1406 @compileError("options passed to readField must be a struct"); 1444 @compileError("options passed to readField must be a struct");
1407 } 1445 }
1408 1446
@@ -1410,13 +1448,13 @@ pub fn Iterator(comptime Type: type) type {
1410 1448
1411 return switch (FieldType) { 1449 return switch (FieldType) {
1412 Blob => blk: { 1450 Blob => blk: {
1413 if (!comptime std.meta.trait.hasField("allocator")(@TypeOf(options))) { 1451 if (!@hasField(@TypeOf(options), "allocator")) {
1414 @compileError("options passed to readPointer must have an allocator field when reading a Blob"); 1452 @compileError("options passed to readPointer must have an allocator field when reading a Blob");
1415 } 1453 }
1416 break :blk try self.readBytes(Blob, options.allocator, i, .Blob); 1454 break :blk try self.readBytes(Blob, options.allocator, i, .Blob);
1417 }, 1455 },
1418 Text => blk: { 1456 Text => blk: {
1419 if (!comptime std.meta.trait.hasField("allocator")(@TypeOf(options))) { 1457 if (!@hasField(@TypeOf(options), "allocator")) {
1420 @compileError("options passed to readField must have an allocator field when reading a Text"); 1458 @compileError("options passed to readField must have an allocator field when reading a Text");
1421 } 1459 }
1422 break :blk try self.readBytes(Text, options.allocator, i, .Text); 1460 break :blk try self.readBytes(Text, options.allocator, i, .Text);
@@ -1432,7 +1470,7 @@ pub fn Iterator(comptime Type: type) type {
1432 .Enum => |TI| { 1470 .Enum => |TI| {
1433 const inner_value = try self.readField(FieldType.BaseType, options, i); 1471 const inner_value = try self.readField(FieldType.BaseType, options, i);
1434 1472
1435 if (comptime std.meta.trait.isZigString(FieldType.BaseType)) { 1473 if (comptime isZigString(FieldType.BaseType)) {
1436 // The inner value is never returned to the user, we must free it ourselves. 1474 // The inner value is never returned to the user, we must free it ourselves.
1437 defer options.allocator.free(inner_value); 1475 defer options.allocator.free(inner_value);
1438 1476
@@ -1642,7 +1680,7 @@ pub const DynamicStatement = struct {
1642 return convertResultToError(result); 1680 return convertResultToError(result);
1643 }, 1681 },
1644 .Enum => { 1682 .Enum => {
1645 if (comptime std.meta.trait.isZigString(FieldType.BaseType)) { 1683 if (comptime isZigString(FieldType.BaseType)) {
1646 try self.bindField(FieldType.BaseType, options, field_name, i, @tagName(field)); 1684 try self.bindField(FieldType.BaseType, options, field_name, i, @tagName(field));
1647 } else if (@typeInfo(FieldType.BaseType) == .Int) { 1685 } else if (@typeInfo(FieldType.BaseType) == .Int) {
1648 try self.bindField(FieldType.BaseType, options, field_name, i, @intFromEnum(field)); 1686 try self.bindField(FieldType.BaseType, options, field_name, i, @intFromEnum(field));
@@ -1655,7 +1693,7 @@ pub const DynamicStatement = struct {
1655 try self.bindField(info.backing_integer.?, options, field_name, i, @as(info.backing_integer.?, @bitCast(field))); 1693 try self.bindField(info.backing_integer.?, options, field_name, i, @as(info.backing_integer.?, @bitCast(field)));
1656 return; 1694 return;
1657 } 1695 }
1658 if (!comptime std.meta.trait.hasFn("bindField")(FieldType)) { 1696 if (!comptime helpers.hasFn(FieldType, "bindField")) {
1659 @compileError("cannot bind field " ++ field_name ++ " of type " ++ @typeName(FieldType) ++ ", consider implementing the bindField() method"); 1697 @compileError("cannot bind field " ++ field_name ++ " of type " ++ @typeName(FieldType) ++ ", consider implementing the bindField() method");
1660 } 1698 }
1661 1699
@@ -2013,7 +2051,7 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: anytype) type
2013 /// The types are checked at comptime. 2051 /// The types are checked at comptime.
2014 fn bind(self: *Self, options: anytype, values: anytype) !void { 2052 fn bind(self: *Self, options: anytype, values: anytype) !void {
2015 const StructType = @TypeOf(values); 2053 const StructType = @TypeOf(values);
2016 if (!comptime std.meta.trait.is(.Struct)(@TypeOf(values))) { 2054 if (!comptime isStruct(StructType)) {
2017 @compileError("options passed to Statement.bind must be a struct (DynamicStatement supports runtime slices)"); 2055 @compileError("options passed to Statement.bind must be a struct (DynamicStatement supports runtime slices)");
2018 } 2056 }
2019 2057