From 9b2fd439ca1e9ca77dc1cd137a224f5cf0b6464e Mon Sep 17 00:00:00 2001 From: Vincent Rischmann Date: Thu, 23 Nov 2023 20:53:08 +0100 Subject: fix preprocess_files --- tools/preprocess_files.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/preprocess_files.zig b/tools/preprocess_files.zig index 3a54a93..2bb8eb7 100644 --- a/tools/preprocess_files.zig +++ b/tools/preprocess_files.zig @@ -162,7 +162,7 @@ fn preprocessSqlite3HeaderFile(gpa: mem.Allocator) !void { // - var data = try readOriginalData(allocator, "c/sqlite3.h"); + const data = try readOriginalData(allocator, "c/sqlite3.h"); var processor = try Processor.init(allocator, data); @@ -212,7 +212,7 @@ fn preprocessSqlite3ExtHeaderFile(gpa: mem.Allocator) !void { // - var data = try readOriginalData(allocator, "c/sqlite3ext.h"); + const data = try readOriginalData(allocator, "c/sqlite3ext.h"); var processor = try Processor.init(allocator, data); -- cgit v1.2.3 From 40337a755b4855e3ca83659a9f943bb4cb9008f8 Mon Sep 17 00:00:00 2001 From: Vincent Rischmann Date: Thu, 23 Nov 2023 20:58:55 +0100 Subject: rewrite a helper to check if a type is a struct std.meta.trait doesn't exist anymore. --- sqlite.zig | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/sqlite.zig b/sqlite.zig index d521175..4fd06df 100644 --- a/sqlite.zig +++ b/sqlite.zig @@ -21,6 +21,7 @@ const getDetailedErrorFromResultCode = errors.getDetailedErrorFromResultCode; const getTestDb = @import("test.zig").getTestDb; pub const vtab = @import("vtab.zig"); + const helpers = @import("helpers.zig"); test { @@ -29,6 +30,11 @@ test { const logger = std.log.scoped(.sqlite); +fn isStruct(comptime typ: type) bool { + const type_info = @typeInfo(typ); + return type_info == .Struct; +} + /// Text is used to represent a SQLite TEXT value when binding a parameter or reading a column. pub const Text = struct { data: []const u8 }; @@ -1308,7 +1314,7 @@ pub fn Iterator(comptime Type: type) type { } fn readPointer(self: *Self, comptime PointerType: type, options: anytype, i: usize) !PointerType { - if (!comptime std.meta.trait.is(.Struct)(@TypeOf(options))) { + if (!comptime isStruct(@TypeOf(options))) { @compileError("options passed to readPointer must be a struct"); } if (!comptime std.meta.trait.hasField("allocator")(@TypeOf(options))) { @@ -1339,7 +1345,7 @@ pub fn Iterator(comptime Type: type) type { } fn readOptional(self: *Self, comptime OptionalType: type, options: anytype, _i: usize) !OptionalType { - if (!comptime std.meta.trait.is(.Struct)(@TypeOf(options))) { + if (!comptime isStruct(@TypeOf(options))) { @compileError("options passed to readOptional must be a struct"); } @@ -1384,7 +1390,7 @@ pub fn Iterator(comptime Type: type) type { // // TODO(vincent): add comptime checks for the fields/columns. fn readStruct(self: *Self, options: anytype) !Type { - if (!comptime std.meta.trait.is(.Struct)(@TypeOf(options))) { + if (!comptime isStruct(@TypeOf(options))) { @compileError("options passed to readStruct must be a struct"); } @@ -1402,7 +1408,7 @@ pub fn Iterator(comptime Type: type) type { } fn readField(self: *Self, comptime FieldType: type, options: anytype, i: usize) !FieldType { - if (!comptime std.meta.trait.is(.Struct)(@TypeOf(options))) { + if (!comptime isStruct(@TypeOf(options))) { @compileError("options passed to readField must be a struct"); } @@ -2013,7 +2019,7 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: anytype) type /// The types are checked at comptime. fn bind(self: *Self, options: anytype, values: anytype) !void { const StructType = @TypeOf(values); - if (!comptime std.meta.trait.is(.Struct)(@TypeOf(values))) { + if (!comptime isStruct(StructType)) { @compileError("options passed to Statement.bind must be a struct (DynamicStatement supports runtime slices)"); } -- cgit v1.2.3 From f523438c4b1acf3682f369f8c7a71cd2b1de5150 Mon Sep 17 00:00:00 2001 From: Vincent Rischmann Date: Thu, 23 Nov 2023 21:10:59 +0100 Subject: use our helper to check a typ has a specific declaration --- vtab.zig | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/vtab.zig b/vtab.zig index 6fbd865..7c2dafe 100644 --- a/vtab.zig +++ b/vtab.zig @@ -16,6 +16,15 @@ const helpers = @import("helpers.zig"); const logger = std.log.scoped(.vtab); +fn hasDecls(comptime T: type, comptime names: anytype) bool { + inline for (names) |name| { + if (!@hasDecl(T, name)) { + return false; + } + } + return true; +} + /// ModuleContext contains state that is needed by all implementations of virtual tables. /// /// Currently there's only an allocator. @@ -301,7 +310,7 @@ fn validateCursorType(comptime Table: type) void { // Validate the `init` function { - if (!meta.trait.hasDecls(Cursor, .{"InitError"})) { + if (!comptime hasDecls(Cursor, .{"InitError"})) { @compileError("the Cursor type must declare a InitError error set for the init function"); } @@ -340,7 +349,7 @@ fn validateCursorType(comptime Table: type) void { // Validate the `next` function { - if (!meta.trait.hasDecls(Cursor, .{"NextError"})) { + if (!comptime hasDecls(Cursor, .{"NextError"})) { @compileError("the Cursor type must declare a NextError error set for the next function"); } @@ -362,7 +371,7 @@ fn validateCursorType(comptime Table: type) void { // Validate the `hasNext` function { - if (!meta.trait.hasDecls(Cursor, .{"HasNextError"})) { + if (!comptime hasDecls(Cursor, .{"HasNextError"})) { @compileError("the Cursor type must declare a HasNextError error set for the hasNext function"); } @@ -384,7 +393,7 @@ fn validateCursorType(comptime Table: type) void { // Validate the `filter` function { - if (!meta.trait.hasDecls(Cursor, .{"FilterError"})) { + if (!comptime hasDecls(Cursor, .{"FilterError"})) { @compileError("the Cursor type must declare a FilterError error set for the filter function"); } @@ -408,10 +417,10 @@ fn validateCursorType(comptime Table: type) void { // Validate the `column` function { - if (!meta.trait.hasDecls(Cursor, .{"ColumnError"})) { + if (!comptime hasDecls(Cursor, .{"ColumnError"})) { @compileError("the Cursor type must declare a ColumnError error set for the column function"); } - if (!meta.trait.hasDecls(Cursor, .{"Column"})) { + if (!comptime hasDecls(Cursor, .{"Column"})) { @compileError("the Cursor type must declare a Column type for the return type of the column function"); } @@ -434,7 +443,7 @@ fn validateCursorType(comptime Table: type) void { // Validate the `rowId` function { - if (!meta.trait.hasDecls(Cursor, .{"RowIDError"})) { + if (!comptime hasDecls(Cursor, .{"RowIDError"})) { @compileError("the Cursor type must declare a RowIDError error set for the rowId function"); } @@ -459,7 +468,7 @@ fn validateCursorType(comptime Table: type) void { fn validateTableType(comptime Table: type) void { // Validate the `init` function { - if (!meta.trait.hasDecls(Table, .{"InitError"})) { + if (!comptime hasDecls(Table, .{"InitError"})) { @compileError("the Table type must declare a InitError error set for the init function"); } @@ -501,7 +510,7 @@ fn validateTableType(comptime Table: type) void { // Validate the `buildBestIndex` function { - if (!meta.trait.hasDecls(Table, .{"BuildBestIndexError"})) { + if (!comptime hasDecls(Table, .{"BuildBestIndexError"})) { @compileError("the Cursor type must declare a BuildBestIndexError error set for the buildBestIndex function"); } @@ -522,7 +531,7 @@ fn validateTableType(comptime Table: type) void { if (info.return_type.? != Table.BuildBestIndexError!void) @compileError(error_message); } - if (!meta.trait.hasDecls(Table, .{"Cursor"})) { + if (!comptime hasDecls(Table, .{"Cursor"})) { @compileError("the Table type must declare a Cursor type"); } } -- cgit v1.2.3 From 61b8b0978a576859ab2a55ba3c7575b003260292 Mon Sep 17 00:00:00 2001 From: Vincent Rischmann Date: Thu, 23 Nov 2023 21:14:16 +0100 Subject: add the isZigString helper --- sqlite.zig | 44 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/sqlite.zig b/sqlite.zig index 4fd06df..c3ecb84 100644 --- a/sqlite.zig +++ b/sqlite.zig @@ -30,11 +30,43 @@ test { const logger = std.log.scoped(.sqlite); -fn isStruct(comptime typ: type) bool { - const type_info = @typeInfo(typ); +// Returns true if the passed type is a struct. +fn isStruct(comptime T: type) bool { + const type_info = @typeInfo(T); return type_info == .Struct; } +// Returns true if the passed type will coerce to []const u8. +// +// NOTE(vincent): this is straight from the Zig stdlib before it was removed. +fn isZigString(comptime T: type) bool { + return comptime blk: { + // Only pointer types can be strings, no optionals + const info = @typeInfo(T); + if (info != .Pointer) break :blk false; + + const ptr = &info.Pointer; + // Check for CV qualifiers that would prevent coerction to []const u8 + if (ptr.is_volatile or ptr.is_allowzero) break :blk false; + + // If it's already a slice, simple check. + if (ptr.size == .Slice) { + break :blk ptr.child == u8; + } + + // Otherwise check if it's an array type that coerces to slice. + if (ptr.size == .One) { + const child = @typeInfo(ptr.child); + if (child == .Array) { + const arr = &child.Array; + break :blk arr.child == u8; + } + } + + break :blk false; + }; +} + /// Text is used to represent a SQLite TEXT value when binding a parameter or reading a column. pub const Text = struct { data: []const u8 }; @@ -1068,7 +1100,7 @@ pub fn Iterator(comptime Type: type) type { .Enum => |TI| { debug.assert(columns == 1); - if (comptime std.meta.trait.isZigString(Type.BaseType)) { + if (comptime isZigString(Type.BaseType)) { @compileError("cannot read into type " ++ @typeName(Type) ++ " ; BaseType " ++ @typeName(Type.BaseType) ++ " requires allocation, use nextAlloc or oneAlloc"); } @@ -1150,7 +1182,7 @@ pub fn Iterator(comptime Type: type) type { const inner_value = try self.readField(Type.BaseType, .{ .allocator = allocator }, 0); - if (comptime std.meta.trait.isZigString(Type.BaseType)) { + if (comptime isZigString(Type.BaseType)) { // The inner value is never returned to the user, we must free it ourselves. defer allocator.free(inner_value); @@ -1438,7 +1470,7 @@ pub fn Iterator(comptime Type: type) type { .Enum => |TI| { const inner_value = try self.readField(FieldType.BaseType, options, i); - if (comptime std.meta.trait.isZigString(FieldType.BaseType)) { + if (comptime isZigString(FieldType.BaseType)) { // The inner value is never returned to the user, we must free it ourselves. defer options.allocator.free(inner_value); @@ -1648,7 +1680,7 @@ pub const DynamicStatement = struct { return convertResultToError(result); }, .Enum => { - if (comptime std.meta.trait.isZigString(FieldType.BaseType)) { + if (comptime isZigString(FieldType.BaseType)) { try self.bindField(FieldType.BaseType, options, field_name, i, @tagName(field)); } else if (@typeInfo(FieldType.BaseType) == .Int) { try self.bindField(FieldType.BaseType, options, field_name, i, @intFromEnum(field)); -- cgit v1.2.3 From 7d4d8fa55b98269666a8fc1f73795a78c10bd073 Mon Sep 17 00:00:00 2001 From: Vincent Rischmann Date: Thu, 23 Nov 2023 21:35:16 +0100 Subject: add the fasFn helper --- helpers.zig | 16 ++++++++++++++++ sqlite.zig | 2 +- vtab.zig | 20 ++++++++++---------- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/helpers.zig b/helpers.zig index 1a56231..7bb695e 100644 --- a/helpers.zig +++ b/helpers.zig @@ -86,3 +86,19 @@ fn sliceFromValue(sqlite_value: *c.sqlite3_value) []const u8 { return value[0..size]; } + +// Returns true if the type T has a function named `name`. +pub fn hasFn(comptime T: type, comptime name: []const u8) bool { + if (!@hasDecl(T, name)) { + return false; + } + + const decl = @field(T, name); + const decl_type = @TypeOf(decl); + const decl_type_info = @typeInfo(decl_type); + + return switch (decl_type_info) { + .Fn => true, + else => false, + }; +} diff --git a/sqlite.zig b/sqlite.zig index c3ecb84..3759884 100644 --- a/sqlite.zig +++ b/sqlite.zig @@ -1693,7 +1693,7 @@ pub const DynamicStatement = struct { try self.bindField(info.backing_integer.?, options, field_name, i, @as(info.backing_integer.?, @bitCast(field))); return; } - if (!comptime std.meta.trait.hasFn("bindField")(FieldType)) { + if (!comptime helpers.hasFn(FieldType, "bindField")) { @compileError("cannot bind field " ++ field_name ++ " of type " ++ @typeName(FieldType) ++ ", consider implementing the bindField() method"); } diff --git a/vtab.zig b/vtab.zig index 7c2dafe..eb73990 100644 --- a/vtab.zig +++ b/vtab.zig @@ -318,7 +318,7 @@ fn validateCursorType(comptime Table: type) void { \\the Cursor.init function must have the signature `fn init(allocator: std.mem.Allocator, parent: *Table) InitError!*Cursor` ; - if (!meta.trait.hasFn("init")(Cursor)) { + if (!comptime helpers.hasFn(Cursor, "init")) { @compileError("the Cursor type must have an init function, " ++ error_message); } @@ -336,7 +336,7 @@ fn validateCursorType(comptime Table: type) void { \\the Cursor.deinit function must have the signature `fn deinit(cursor: *Cursor) void` ; - if (!meta.trait.hasFn("deinit")(Cursor)) { + if (!comptime helpers.hasFn(Cursor, "deinit")) { @compileError("the Cursor type must have a deinit function, " ++ error_message); } @@ -357,7 +357,7 @@ fn validateCursorType(comptime Table: type) void { \\the Cursor.next function must have the signature `fn next(cursor: *Cursor, diags: *sqlite.vtab.VTabDiagnostics) NextError!void` ; - if (!meta.trait.hasFn("next")(Cursor)) { + if (!comptime helpers.hasFn(Cursor, "next")) { @compileError("the Cursor type must have a next function, " ++ error_message); } @@ -379,7 +379,7 @@ fn validateCursorType(comptime Table: type) void { \\the Cursor.hasNext function must have the signature `fn hasNext(cursor: *Cursor, diags: *sqlite.vtab.VTabDiagnostics) HasNextError!bool` ; - if (!meta.trait.hasFn("hasNext")(Cursor)) { + if (!comptime helpers.hasFn(Cursor, "hasNext")) { @compileError("the Cursor type must have a hasNext function, " ++ error_message); } @@ -401,7 +401,7 @@ fn validateCursorType(comptime Table: type) void { \\the Cursor.filter function must have the signature `fn filter(cursor: *Cursor, diags: *sqlite.vtab.VTabDiagnostics, index: sqlite.vtab.IndexIdentifier, args: []FilterArg) FilterError!bool` ; - if (!meta.trait.hasFn("filter")(Cursor)) { + if (!comptime helpers.hasFn(Cursor, "filter")) { @compileError("the Cursor type must have a filter function, " ++ error_message); } @@ -428,7 +428,7 @@ fn validateCursorType(comptime Table: type) void { \\the Cursor.column function must have the signature `fn column(cursor: *Cursor, diags: *sqlite.vtab.VTabDiagnostics, column_number: i32) ColumnError!Column` ; - if (!meta.trait.hasFn("column")(Cursor)) { + if (!comptime helpers.hasFn(Cursor, "column")) { @compileError("the Cursor type must have a column function, " ++ error_message); } @@ -451,7 +451,7 @@ fn validateCursorType(comptime Table: type) void { \\the Cursor.rowId function must have the signature `fn rowId(cursor: *Cursor, diags: *sqlite.vtab.VTabDiagnostics) RowIDError!i64` ; - if (!meta.trait.hasFn("rowId")(Cursor)) { + if (!comptime helpers.hasFn(Cursor, "rowId")) { @compileError("the Cursor type must have a rowId function, " ++ error_message); } @@ -476,7 +476,7 @@ fn validateTableType(comptime Table: type) void { \\the Table.init function must have the signature `fn init(allocator: std.mem.Allocator, diags: *sqlite.vtab.VTabDiagnostics, args: []const ModuleArgument) InitError!*Table` ; - if (!meta.trait.hasFn("init")(Table)) { + if (!comptime helpers.hasFn(Table, "init")) { @compileError("the Table type must have a init function, " ++ error_message); } @@ -496,7 +496,7 @@ fn validateTableType(comptime Table: type) void { \\the Table.deinit function must have the signature `fn deinit(table: *Table, allocator: std.mem.Allocator) void` ; - if (!meta.trait.hasFn("deinit")(Table)) { + if (!comptime helpers.hasFn(Table, "deinit")) { @compileError("the Table type must have a deinit function, " ++ error_message); } @@ -518,7 +518,7 @@ fn validateTableType(comptime Table: type) void { \\the Table.buildBestIndex function must have the signature `fn buildBestIndex(table: *Table, diags: *sqlite.vtab.VTabDiagnostics, builder: *sqlite.vtab.BestIndexBuilder) BuildBestIndexError!void` ; - if (!meta.trait.hasFn("buildBestIndex")(Table)) { + if (!comptime helpers.hasFn(Table, "buildBestIndex")) { @compileError("the Table type must have a buildBestIndex function, " ++ error_message); } -- cgit v1.2.3 From a84d698f9a449e08b383f97d1fb81a206186da26 Mon Sep 17 00:00:00 2001 From: Vincent Rischmann Date: Thu, 23 Nov 2023 21:35:56 +0100 Subject: use @hasField --- sqlite.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sqlite.zig b/sqlite.zig index 3759884..8477151 100644 --- a/sqlite.zig +++ b/sqlite.zig @@ -1349,7 +1349,7 @@ pub fn Iterator(comptime Type: type) type { if (!comptime isStruct(@TypeOf(options))) { @compileError("options passed to readPointer must be a struct"); } - if (!comptime std.meta.trait.hasField("allocator")(@TypeOf(options))) { + if (!@hasField(@TypeOf(options), "allocator")) { @compileError("options passed to readPointer must have an allocator field"); } @@ -1448,13 +1448,13 @@ pub fn Iterator(comptime Type: type) type { return switch (FieldType) { Blob => blk: { - if (!comptime std.meta.trait.hasField("allocator")(@TypeOf(options))) { + if (!@hasField(@TypeOf(options), "allocator")) { @compileError("options passed to readPointer must have an allocator field when reading a Blob"); } break :blk try self.readBytes(Blob, options.allocator, i, .Blob); }, Text => blk: { - if (!comptime std.meta.trait.hasField("allocator")(@TypeOf(options))) { + if (!@hasField(@TypeOf(options), "allocator")) { @compileError("options passed to readField must have an allocator field when reading a Text"); } break :blk try self.readBytes(Text, options.allocator, i, .Text); -- cgit v1.2.3