From 49db791a43fdf4afecc27336d84ca6ab780b7f77 Mon Sep 17 00:00:00 2001 From: Vincent Rischmann Date: Sun, 14 Apr 2024 18:10:28 +0200 Subject: fix for latest zig Also some major refactoring around the query parsing state. --- build.zig | 2 +- query.zig | 332 ++++++++++++++++++++++++++++++++----------------------------- sqlite.zig | 15 +-- vtab.zig | 29 ++++-- 4 files changed, 203 insertions(+), 175 deletions(-) diff --git a/build.zig b/build.zig index 70b5b56..f483f66 100644 --- a/build.zig +++ b/build.zig @@ -279,7 +279,7 @@ pub fn build(b: *std.Build) !void { .flags = &[_][]const u8{"-std=c99"}, }); sqlite_lib.linkLibC(); - sqlite_lib.installHeader("c/sqlite3.h", "sqlite3.h"); + sqlite_lib.installHeader(.{ .path = "c/sqlite3.h" }, "sqlite3.h"); b.installArtifact(sqlite_lib); diff --git a/query.zig b/query.zig index 2f33188..dc1542f 100644 --- a/query.zig +++ b/query.zig @@ -20,168 +20,178 @@ fn isNamedIdentifierChar(c: u8) bool { } fn bindMarkerForName(comptime markers: []const BindMarker, comptime name: []const u8) ?BindMarker { - for (markers[0..]) |marker| { + for (markers) |marker| { if (marker.name != null and std.mem.eql(u8, marker.name.?, name)) return marker; } return null; } -pub fn ParsedQuery(comptime query: []const u8) ParsedQueryState(query.len) { - // This contains the final SQL query after parsing with our - // own typed bind markers removed. - comptime var buf: [query.len]u8 = undefined; - comptime var pos = 0; - comptime var state = .start; - - comptime var current_bind_marker_type: [256]u8 = undefined; - comptime var current_bind_marker_type_pos = 0; - - // becomes part of our result - comptime var bind_markers: [128]BindMarker = undefined; - comptime var nb_bind_markers: usize = 0; - - // used for capturing slices, such as bind parameter name - comptime var hold_pos = 0; - - inline for (query) |c| { - switch (state) { - .start => switch (c) { - '?', ':', '@', '$' => { - bind_markers[nb_bind_markers] = BindMarker{}; - current_bind_marker_type_pos = 0; - state = .bind_marker; - buf[pos] = c; - pos += 1; - }, - '\'', '"', '[', '`' => { - state = .inside_string; - buf[pos] = c; - pos += 1; - }, - else => { - buf[pos] = c; - pos += 1; - }, - }, - .inside_string => switch (c) { - '\'', '"', ']', '`' => { - state = .start; - buf[pos] = c; - pos += 1; - }, - else => { - buf[pos] = c; - pos += 1; - }, - }, - .bind_marker => switch (c) { - '?', ':', '@', '$' => @compileError("invalid multiple '?', ':', '$' or '@'."), - '{' => { - state = .bind_marker_type; - }, - else => { - if (isNamedIdentifierChar(c)) { - // This is the start of a named bind marker. - state = .bind_marker_identifier; - hold_pos = pos + 1; - } else { - // This is a unnamed, untyped bind marker. - state = .start; - - bind_markers[nb_bind_markers].typed = null; - nb_bind_markers += 1; - } - buf[pos] = c; - pos += 1; - }, - }, - .bind_marker_identifier => switch (c) { - '?', ':', '@', '$' => @compileError("unregconised multiple '?', ':', '$' or '@'."), - '{' => { - state = .bind_marker_type; - current_bind_marker_type_pos = 0; - }, - else => { - if (!isNamedIdentifierChar(c)) { - // This marks the end of the named bind marker. - state = .start; - const name = buf[hold_pos .. pos - 1]; - if (bindMarkerForName(bind_markers[0..nb_bind_markers], name) == null) { - bind_markers[nb_bind_markers].name = name; - nb_bind_markers += 1; - } - } - buf[pos] = c; - pos += 1; - }, - }, - .bind_marker_type => switch (c) { - '}' => { - state = .start; - - const type_info_string = current_bind_marker_type[0..current_bind_marker_type_pos]; - // Handles optional types - const typ = if (type_info_string[0] == '?') blk: { - const child_type = ParseType(type_info_string[1..]); - break :blk @Type(std.builtin.Type{ - .Optional = .{ - .child = child_type, - }, - }); - } else blk: { - break :blk ParseType(type_info_string); - }; - - bind_markers[nb_bind_markers].typed = typ; - nb_bind_markers += 1; - }, - else => { - current_bind_marker_type[current_bind_marker_type_pos] = c; - current_bind_marker_type_pos += 1; - }, - }, - else => { - @compileError("invalid state " ++ @tagName(state)); - }, - } - } - - // The last character was a bind marker prefix so this must be an untyped bind marker. - switch (state) { - .bind_marker => { - bind_markers[nb_bind_markers].typed = null; - nb_bind_markers += 1; - }, - .bind_marker_identifier => { - nb_bind_markers += 1; - }, - .start => {}, - else => @compileError("invalid final state " ++ @tagName(state) ++ ", this means you wrote an incomplete bind marker type"), - } +pub fn ParsedQuery(comptime tmp_query: []const u8) type { + return struct { + const Self = @This(); - var parsed_state = ParsedQueryState(query.len){ - .bind_markers = bind_markers, - .nb_bind_markers = nb_bind_markers, - .query = undefined, - .query_len = pos, - }; + const result = parse(); - std.mem.copyForwards(u8, &parsed_state.query, &buf); + pub const bind_markers = result.bind_markers[0..result.bind_markers_len]; - return parsed_state; -} + pub fn getQuery() []const u8 { + return Self.result.query[0..Self.result.query_len]; + } -pub fn ParsedQueryState(comptime max_query_len: usize) type { - return struct { - const Self = @This(); - bind_markers: [128]BindMarker, - nb_bind_markers: usize, - query: [max_query_len]u8, - query_len: usize, + const ParsedQueryResult = struct { + bind_markers: [128]BindMarker, + bind_markers_len: usize, + query: [tmp_query.len]u8, + query_len: usize, + }; - pub fn getQuery(comptime self: *const Self) []const u8 { - return self.query[0..self.query_len]; + fn parse() ParsedQueryResult { + // This contains the final SQL query after parsing with our + // own typed bind markers removed. + var buf: [tmp_query.len]u8 = undefined; + var pos = 0; + var state = .start; + + var current_bind_marker_type: [256]u8 = undefined; + var current_bind_marker_type_pos = 0; + + // becomes part of our result + var tmp_bind_markers: [128]BindMarker = undefined; + var nb_tmp_bind_markers: usize = 0; + + // used for capturing slices, such as bind parameter name + var hold_pos = 0; + + for (tmp_query) |c| { + switch (state) { + .start => switch (c) { + '?', ':', '@', '$' => { + tmp_bind_markers[nb_tmp_bind_markers] = BindMarker{}; + current_bind_marker_type_pos = 0; + state = .bind_marker; + buf[pos] = c; + pos += 1; + }, + '\'', '"', '[', '`' => { + state = .inside_string; + buf[pos] = c; + pos += 1; + }, + else => { + buf[pos] = c; + pos += 1; + }, + }, + .inside_string => switch (c) { + '\'', '"', ']', '`' => { + state = .start; + buf[pos] = c; + pos += 1; + }, + else => { + buf[pos] = c; + pos += 1; + }, + }, + .bind_marker => switch (c) { + '?', ':', '@', '$' => @compileError("invalid multiple '?', ':', '$' or '@'."), + '{' => { + state = .bind_marker_type; + }, + else => { + if (isNamedIdentifierChar(c)) { + // This is the start of a named bind marker. + state = .bind_marker_identifier; + hold_pos = pos + 1; + } else { + // This is a unnamed, untyped bind marker. + state = .start; + + tmp_bind_markers[nb_tmp_bind_markers].typed = null; + nb_tmp_bind_markers += 1; + } + buf[pos] = c; + pos += 1; + }, + }, + .bind_marker_identifier => switch (c) { + '?', ':', '@', '$' => @compileError("unregconised multiple '?', ':', '$' or '@'."), + '{' => { + state = .bind_marker_type; + current_bind_marker_type_pos = 0; + }, + else => { + if (!isNamedIdentifierChar(c)) { + // This marks the end of the named bind marker. + state = .start; + const name = buf[hold_pos .. pos - 1]; + // TODO(vincent): name retains a pointer to a comptime var, FIX ! + if (bindMarkerForName(tmp_bind_markers[0..nb_tmp_bind_markers], name) == null) { + const new_buf = buf; + tmp_bind_markers[nb_tmp_bind_markers].name = new_buf[hold_pos .. pos - 1]; + nb_tmp_bind_markers += 1; + } + } + buf[pos] = c; + pos += 1; + }, + }, + .bind_marker_type => switch (c) { + '}' => { + state = .start; + + const type_info_string = current_bind_marker_type[0..current_bind_marker_type_pos]; + // Handles optional types + const typ = if (type_info_string[0] == '?') blk: { + const child_type = ParseType(type_info_string[1..]); + break :blk @Type(std.builtin.Type{ + .Optional = .{ + .child = child_type, + }, + }); + } else blk: { + break :blk ParseType(type_info_string); + }; + + tmp_bind_markers[nb_tmp_bind_markers].typed = typ; + nb_tmp_bind_markers += 1; + }, + else => { + current_bind_marker_type[current_bind_marker_type_pos] = c; + current_bind_marker_type_pos += 1; + }, + }, + else => { + @compileError("invalid state " ++ @tagName(state)); + }, + } + } + + // The last character was a bind marker prefix so this must be an untyped bind marker. + switch (state) { + .bind_marker => { + tmp_bind_markers[nb_tmp_bind_markers].typed = null; + nb_tmp_bind_markers += 1; + }, + .bind_marker_identifier => { + nb_tmp_bind_markers += 1; + }, + .start => {}, + else => @compileError("invalid final state " ++ @tagName(state) ++ ", this means you wrote an incomplete bind marker type"), + } + + const final_bind_markers = tmp_bind_markers; + const final_bind_markers_len = nb_tmp_bind_markers; + const final_buf = buf; + const final_query_len = pos; + + return .{ + .bind_markers = final_bind_markers, + .bind_markers_len = final_bind_markers_len, + .query = final_buf, + .query_len = final_query_len, + }; } }; } @@ -246,7 +256,7 @@ test "parsed query: query" { inline for (testCases) |tc| { @setEvalBranchQuota(100000); - comptime var parsed_query = ParsedQuery(tc.query); + const parsed_query = ParsedQuery(tc.query); try testing.expectEqualStrings(tc.expected_query, parsed_query.getQuery()); } } @@ -296,7 +306,7 @@ test "parsed query: bind markers types" { @setEvalBranchQuota(100000); const parsed_query = comptime ParsedQuery(tc.query); - try testing.expectEqual(1, parsed_query.nb_bind_markers); + try testing.expectEqual(1, parsed_query.bind_markers.len); const bind_marker = parsed_query.bind_markers[0]; try testing.expectEqual(tc.expected_marker.typed, bind_marker.typed); @@ -344,7 +354,7 @@ test "parsed query: bind markers identifier" { inline for (testCases) |tc| { const parsed_query = comptime ParsedQuery(tc.query); - try testing.expectEqual(@as(usize, 1), parsed_query.nb_bind_markers); + try testing.expectEqual(@as(usize, 1), parsed_query.bind_markers.len); const bind_marker = parsed_query.bind_markers[0]; try testing.expectEqual(tc.expected_marker, bind_marker); @@ -388,9 +398,13 @@ test "parsed query: query bind identifier" { inline for (testCases) |tc| { @setEvalBranchQuota(100000); - comptime var parsed_query = ParsedQuery(tc.query); - try testing.expectEqualStrings(tc.expected_query, parsed_query.getQuery()); - try testing.expectEqual(tc.expected_nb_bind_markers, parsed_query.nb_bind_markers); + + comptime { + const parsed_query = ParsedQuery(tc.query); + + try testing.expectEqual(tc.expected_nb_bind_markers, parsed_query.bind_markers.len); + try testing.expectEqualStrings(tc.expected_query, parsed_query.getQuery()); + } } } @@ -416,9 +430,9 @@ test "parsed query: bind marker character inside string" { inline for (testCases) |tc| { @setEvalBranchQuota(100000); - comptime var parsed_query = ParsedQuery(tc.query); + const parsed_query = ParsedQuery(tc.query); - try testing.expectEqual(@as(usize, tc.exp_bind_markers), parsed_query.nb_bind_markers); + try testing.expectEqual(@as(usize, tc.exp_bind_markers), parsed_query.bind_markers.len); try testing.expectEqualStrings(tc.exp, parsed_query.getQuery()); } } diff --git a/sqlite.zig b/sqlite.zig index e19f83b..be46dea 100644 --- a/sqlite.zig +++ b/sqlite.zig @@ -1491,7 +1491,7 @@ pub fn Iterator(comptime Type: type) type { @compileError("enum column " ++ @typeName(FieldType) ++ " must have a BaseType of either string or int"); }, .Struct => |TI| { - if (TI.layout == .Packed) return @bitCast(try self.readInt(TI.backing_integer.?, i)); + if (TI.layout == .@"packed") return @bitCast(try self.readInt(TI.backing_integer.?, i)); const inner_value = try self.readField(FieldType.BaseType, options, i); return try FieldType.readField(options.allocator, inner_value); }, @@ -1512,8 +1512,11 @@ pub fn Iterator(comptime Type: type) type { /// }; /// pub fn StatementType(comptime opts: StatementOptions, comptime query: []const u8) type { - @setEvalBranchQuota(100000); - return Statement(opts, ParsedQuery(query)); + comptime { + @setEvalBranchQuota(100000); + const parsed_query = ParsedQuery(query); + return Statement(opts, parsed_query); + } } pub const StatementOptions = struct {}; @@ -1698,7 +1701,7 @@ pub const DynamicStatement = struct { } }, .Struct => |info| { - if (info.layout == .Packed) { + if (info.layout == .@"packed") { try self.bindField(info.backing_integer.?, options, field_name, i, @as(info.backing_integer.?, @bitCast(field))); return; } @@ -2066,9 +2069,9 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: anytype) type const StructTypeInfo = @typeInfo(StructType).Struct; - if (comptime query.nb_bind_markers != StructTypeInfo.fields.len) { + if (comptime query.bind_markers.len != StructTypeInfo.fields.len) { @compileError(std.fmt.comptimePrint("expected {d} bind parameters but got {d}", .{ - query.nb_bind_markers, + query.bind_markers.len, StructTypeInfo.fields.len, })); } diff --git a/vtab.zig b/vtab.zig index 9e0b7f3..8f21cc1 100644 --- a/vtab.zig +++ b/vtab.zig @@ -766,7 +766,8 @@ pub fn VirtualTable( // - const state = @fieldParentPtr(State, "vtab", vtab); + const nullable_state: ?*State = @fieldParentPtr("vtab", vtab); + const state = nullable_state orelse unreachable; var arena = heap.ArenaAllocator.init(state.module_context.allocator); defer arena.deinit(); @@ -788,7 +789,9 @@ pub fn VirtualTable( } fn xDisconnect(vtab: [*c]c.sqlite3_vtab) callconv(.C) c_int { - const state = @fieldParentPtr(State, "vtab", vtab); + const nullable_state: ?*State = @fieldParentPtr("vtab", vtab); + const state = nullable_state orelse unreachable; + state.deinit(); return c.SQLITE_OK; @@ -803,7 +806,8 @@ pub fn VirtualTable( } fn xOpen(vtab: [*c]c.sqlite3_vtab, vtab_cursor: [*c][*c]c.sqlite3_vtab_cursor) callconv(.C) c_int { - const state = @fieldParentPtr(State, "vtab", vtab); + const nullable_state: ?*State = @fieldParentPtr("vtab", vtab); + const state = nullable_state orelse unreachable; const cursor_state = CursorState.init(state.module_context, state.table) catch |err| { logger.err("unable to create cursor state, err: {!}", .{err}); @@ -815,14 +819,17 @@ pub fn VirtualTable( } fn xClose(vtab_cursor: [*c]c.sqlite3_vtab_cursor) callconv(.C) c_int { - const cursor_state = @fieldParentPtr(CursorState, "vtab_cursor", vtab_cursor); + const nullable_cursor_state: ?*CursorState = @fieldParentPtr("vtab_cursor", vtab_cursor); + const cursor_state = nullable_cursor_state orelse unreachable; + cursor_state.deinit(); return c.SQLITE_OK; } fn xEof(vtab_cursor: [*c]c.sqlite3_vtab_cursor) callconv(.C) c_int { - const cursor_state = @fieldParentPtr(CursorState, "vtab_cursor", vtab_cursor); + const nullable_cursor_state: ?*CursorState = @fieldParentPtr("vtab_cursor", vtab_cursor); + const cursor_state = nullable_cursor_state orelse unreachable; const cursor = cursor_state.cursor; var arena = heap.ArenaAllocator.init(cursor_state.module_context.allocator); @@ -859,7 +866,8 @@ pub fn VirtualTable( } fn xFilter(vtab_cursor: [*c]c.sqlite3_vtab_cursor, idx_num: c_int, idx_str: [*c]const u8, argc: c_int, argv: [*c]?*c.sqlite3_value) callconv(.C) c_int { - const cursor_state = @fieldParentPtr(CursorState, "vtab_cursor", vtab_cursor); + const nullable_cursor_state: ?*CursorState = @fieldParentPtr("vtab_cursor", vtab_cursor); + const cursor_state = nullable_cursor_state orelse unreachable; const cursor = cursor_state.cursor; var arena = heap.ArenaAllocator.init(cursor_state.module_context.allocator); @@ -884,7 +892,8 @@ pub fn VirtualTable( } fn xNext(vtab_cursor: [*c]c.sqlite3_vtab_cursor) callconv(.C) c_int { - const cursor_state = @fieldParentPtr(CursorState, "vtab_cursor", vtab_cursor); + const nullable_cursor_state: ?*CursorState = @fieldParentPtr("vtab_cursor", vtab_cursor); + const cursor_state = nullable_cursor_state orelse unreachable; const cursor = cursor_state.cursor; var arena = heap.ArenaAllocator.init(cursor_state.module_context.allocator); @@ -902,7 +911,8 @@ pub fn VirtualTable( } fn xColumn(vtab_cursor: [*c]c.sqlite3_vtab_cursor, ctx: ?*c.sqlite3_context, n: c_int) callconv(.C) c_int { - const cursor_state = @fieldParentPtr(CursorState, "vtab_cursor", vtab_cursor); + const nullable_cursor_state: ?*CursorState = @fieldParentPtr("vtab_cursor", vtab_cursor); + const cursor_state = nullable_cursor_state orelse unreachable; const cursor = cursor_state.cursor; var arena = heap.ArenaAllocator.init(cursor_state.module_context.allocator); @@ -945,7 +955,8 @@ pub fn VirtualTable( } fn xRowid(vtab_cursor: [*c]c.sqlite3_vtab_cursor, row_id_ptr: [*c]c.sqlite3_int64) callconv(.C) c_int { - const cursor_state = @fieldParentPtr(CursorState, "vtab_cursor", vtab_cursor); + const nullable_cursor_state: ?*CursorState = @fieldParentPtr("vtab_cursor", vtab_cursor); + const cursor_state = nullable_cursor_state orelse unreachable; const cursor = cursor_state.cursor; var arena = heap.ArenaAllocator.init(cursor_state.module_context.allocator); -- cgit v1.2.3