summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--build.zig2
-rw-r--r--query.zig332
-rw-r--r--sqlite.zig15
-rw-r--r--vtab.zig29
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 {
279 .flags = &[_][]const u8{"-std=c99"}, 279 .flags = &[_][]const u8{"-std=c99"},
280 }); 280 });
281 sqlite_lib.linkLibC(); 281 sqlite_lib.linkLibC();
282 sqlite_lib.installHeader("c/sqlite3.h", "sqlite3.h"); 282 sqlite_lib.installHeader(.{ .path = "c/sqlite3.h" }, "sqlite3.h");
283 283
284 b.installArtifact(sqlite_lib); 284 b.installArtifact(sqlite_lib);
285 285
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 {
20} 20}
21 21
22fn bindMarkerForName(comptime markers: []const BindMarker, comptime name: []const u8) ?BindMarker { 22fn bindMarkerForName(comptime markers: []const BindMarker, comptime name: []const u8) ?BindMarker {
23 for (markers[0..]) |marker| { 23 for (markers) |marker| {
24 if (marker.name != null and std.mem.eql(u8, marker.name.?, name)) 24 if (marker.name != null and std.mem.eql(u8, marker.name.?, name))
25 return marker; 25 return marker;
26 } 26 }
27 return null; 27 return null;
28} 28}
29 29
30pub fn ParsedQuery(comptime query: []const u8) ParsedQueryState(query.len) { 30pub fn ParsedQuery(comptime tmp_query: []const u8) type {
31 // This contains the final SQL query after parsing with our 31 return struct {
32 // own typed bind markers removed. 32 const Self = @This();
33 comptime var buf: [query.len]u8 = undefined;
34 comptime var pos = 0;
35 comptime var state = .start;
36
37 comptime var current_bind_marker_type: [256]u8 = undefined;
38 comptime var current_bind_marker_type_pos = 0;
39
40 // becomes part of our result
41 comptime var bind_markers: [128]BindMarker = undefined;
42 comptime var nb_bind_markers: usize = 0;
43
44 // used for capturing slices, such as bind parameter name
45 comptime var hold_pos = 0;
46
47 inline for (query) |c| {
48 switch (state) {
49 .start => switch (c) {
50 '?', ':', '@', '$' => {
51 bind_markers[nb_bind_markers] = BindMarker{};
52 current_bind_marker_type_pos = 0;
53 state = .bind_marker;
54 buf[pos] = c;
55 pos += 1;
56 },
57 '\'', '"', '[', '`' => {
58 state = .inside_string;
59 buf[pos] = c;
60 pos += 1;
61 },
62 else => {
63 buf[pos] = c;
64 pos += 1;
65 },
66 },
67 .inside_string => switch (c) {
68 '\'', '"', ']', '`' => {
69 state = .start;
70 buf[pos] = c;
71 pos += 1;
72 },
73 else => {
74 buf[pos] = c;
75 pos += 1;
76 },
77 },
78 .bind_marker => switch (c) {
79 '?', ':', '@', '$' => @compileError("invalid multiple '?', ':', '$' or '@'."),
80 '{' => {
81 state = .bind_marker_type;
82 },
83 else => {
84 if (isNamedIdentifierChar(c)) {
85 // This is the start of a named bind marker.
86 state = .bind_marker_identifier;
87 hold_pos = pos + 1;
88 } else {
89 // This is a unnamed, untyped bind marker.
90 state = .start;
91
92 bind_markers[nb_bind_markers].typed = null;
93 nb_bind_markers += 1;
94 }
95 buf[pos] = c;
96 pos += 1;
97 },
98 },
99 .bind_marker_identifier => switch (c) {
100 '?', ':', '@', '$' => @compileError("unregconised multiple '?', ':', '$' or '@'."),
101 '{' => {
102 state = .bind_marker_type;
103 current_bind_marker_type_pos = 0;
104 },
105 else => {
106 if (!isNamedIdentifierChar(c)) {
107 // This marks the end of the named bind marker.
108 state = .start;
109 const name = buf[hold_pos .. pos - 1];
110 if (bindMarkerForName(bind_markers[0..nb_bind_markers], name) == null) {
111 bind_markers[nb_bind_markers].name = name;
112 nb_bind_markers += 1;
113 }
114 }
115 buf[pos] = c;
116 pos += 1;
117 },
118 },
119 .bind_marker_type => switch (c) {
120 '}' => {
121 state = .start;
122
123 const type_info_string = current_bind_marker_type[0..current_bind_marker_type_pos];
124 // Handles optional types
125 const typ = if (type_info_string[0] == '?') blk: {
126 const child_type = ParseType(type_info_string[1..]);
127 break :blk @Type(std.builtin.Type{
128 .Optional = .{
129 .child = child_type,
130 },
131 });
132 } else blk: {
133 break :blk ParseType(type_info_string);
134 };
135
136 bind_markers[nb_bind_markers].typed = typ;
137 nb_bind_markers += 1;
138 },
139 else => {
140 current_bind_marker_type[current_bind_marker_type_pos] = c;
141 current_bind_marker_type_pos += 1;
142 },
143 },
144 else => {
145 @compileError("invalid state " ++ @tagName(state));
146 },
147 }
148 }
149
150 // The last character was a bind marker prefix so this must be an untyped bind marker.
151 switch (state) {
152 .bind_marker => {
153 bind_markers[nb_bind_markers].typed = null;
154 nb_bind_markers += 1;
155 },
156 .bind_marker_identifier => {
157 nb_bind_markers += 1;
158 },
159 .start => {},
160 else => @compileError("invalid final state " ++ @tagName(state) ++ ", this means you wrote an incomplete bind marker type"),
161 }
162 33
163 var parsed_state = ParsedQueryState(query.len){ 34 const result = parse();
164 .bind_markers = bind_markers,
165 .nb_bind_markers = nb_bind_markers,
166 .query = undefined,
167 .query_len = pos,
168 };
169 35
170 std.mem.copyForwards(u8, &parsed_state.query, &buf); 36 pub const bind_markers = result.bind_markers[0..result.bind_markers_len];
171 37
172 return parsed_state; 38 pub fn getQuery() []const u8 {
173} 39 return Self.result.query[0..Self.result.query_len];
40 }
174 41
175pub fn ParsedQueryState(comptime max_query_len: usize) type { 42 const ParsedQueryResult = struct {
176 return struct { 43 bind_markers: [128]BindMarker,
177 const Self = @This(); 44 bind_markers_len: usize,
178 bind_markers: [128]BindMarker, 45 query: [tmp_query.len]u8,
179 nb_bind_markers: usize, 46 query_len: usize,
180 query: [max_query_len]u8, 47 };
181 query_len: usize,
182 48
183 pub fn getQuery(comptime self: *const Self) []const u8 { 49 fn parse() ParsedQueryResult {
184 return self.query[0..self.query_len]; 50 // This contains the final SQL query after parsing with our
51 // own typed bind markers removed.
52 var buf: [tmp_query.len]u8 = undefined;
53 var pos = 0;
54 var state = .start;
55
56 var current_bind_marker_type: [256]u8 = undefined;
57 var current_bind_marker_type_pos = 0;
58
59 // becomes part of our result
60 var tmp_bind_markers: [128]BindMarker = undefined;
61 var nb_tmp_bind_markers: usize = 0;
62
63 // used for capturing slices, such as bind parameter name
64 var hold_pos = 0;
65
66 for (tmp_query) |c| {
67 switch (state) {
68 .start => switch (c) {
69 '?', ':', '@', '$' => {
70 tmp_bind_markers[nb_tmp_bind_markers] = BindMarker{};
71 current_bind_marker_type_pos = 0;
72 state = .bind_marker;
73 buf[pos] = c;
74 pos += 1;
75 },
76 '\'', '"', '[', '`' => {
77 state = .inside_string;
78 buf[pos] = c;
79 pos += 1;
80 },
81 else => {
82 buf[pos] = c;
83 pos += 1;
84 },
85 },
86 .inside_string => switch (c) {
87 '\'', '"', ']', '`' => {
88 state = .start;
89 buf[pos] = c;
90 pos += 1;
91 },
92 else => {
93 buf[pos] = c;
94 pos += 1;
95 },
96 },
97 .bind_marker => switch (c) {
98 '?', ':', '@', '$' => @compileError("invalid multiple '?', ':', '$' or '@'."),
99 '{' => {
100 state = .bind_marker_type;
101 },
102 else => {
103 if (isNamedIdentifierChar(c)) {
104 // This is the start of a named bind marker.
105 state = .bind_marker_identifier;
106 hold_pos = pos + 1;
107 } else {
108 // This is a unnamed, untyped bind marker.
109 state = .start;
110
111 tmp_bind_markers[nb_tmp_bind_markers].typed = null;
112 nb_tmp_bind_markers += 1;
113 }
114 buf[pos] = c;
115 pos += 1;
116 },
117 },
118 .bind_marker_identifier => switch (c) {
119 '?', ':', '@', '$' => @compileError("unregconised multiple '?', ':', '$' or '@'."),
120 '{' => {
121 state = .bind_marker_type;
122 current_bind_marker_type_pos = 0;
123 },
124 else => {
125 if (!isNamedIdentifierChar(c)) {
126 // This marks the end of the named bind marker.
127 state = .start;
128 const name = buf[hold_pos .. pos - 1];
129 // TODO(vincent): name retains a pointer to a comptime var, FIX !
130 if (bindMarkerForName(tmp_bind_markers[0..nb_tmp_bind_markers], name) == null) {
131 const new_buf = buf;
132 tmp_bind_markers[nb_tmp_bind_markers].name = new_buf[hold_pos .. pos - 1];
133 nb_tmp_bind_markers += 1;
134 }
135 }
136 buf[pos] = c;
137 pos += 1;
138 },
139 },
140 .bind_marker_type => switch (c) {
141 '}' => {
142 state = .start;
143
144 const type_info_string = current_bind_marker_type[0..current_bind_marker_type_pos];
145 // Handles optional types
146 const typ = if (type_info_string[0] == '?') blk: {
147 const child_type = ParseType(type_info_string[1..]);
148 break :blk @Type(std.builtin.Type{
149 .Optional = .{
150 .child = child_type,
151 },
152 });
153 } else blk: {
154 break :blk ParseType(type_info_string);
155 };
156
157 tmp_bind_markers[nb_tmp_bind_markers].typed = typ;
158 nb_tmp_bind_markers += 1;
159 },
160 else => {
161 current_bind_marker_type[current_bind_marker_type_pos] = c;
162 current_bind_marker_type_pos += 1;
163 },
164 },
165 else => {
166 @compileError("invalid state " ++ @tagName(state));
167 },
168 }
169 }
170
171 // The last character was a bind marker prefix so this must be an untyped bind marker.
172 switch (state) {
173 .bind_marker => {
174 tmp_bind_markers[nb_tmp_bind_markers].typed = null;
175 nb_tmp_bind_markers += 1;
176 },
177 .bind_marker_identifier => {
178 nb_tmp_bind_markers += 1;
179 },
180 .start => {},
181 else => @compileError("invalid final state " ++ @tagName(state) ++ ", this means you wrote an incomplete bind marker type"),
182 }
183
184 const final_bind_markers = tmp_bind_markers;
185 const final_bind_markers_len = nb_tmp_bind_markers;
186 const final_buf = buf;
187 const final_query_len = pos;
188
189 return .{
190 .bind_markers = final_bind_markers,
191 .bind_markers_len = final_bind_markers_len,
192 .query = final_buf,
193 .query_len = final_query_len,
194 };
185 } 195 }
186 }; 196 };
187} 197}
@@ -246,7 +256,7 @@ test "parsed query: query" {
246 256
247 inline for (testCases) |tc| { 257 inline for (testCases) |tc| {
248 @setEvalBranchQuota(100000); 258 @setEvalBranchQuota(100000);
249 comptime var parsed_query = ParsedQuery(tc.query); 259 const parsed_query = ParsedQuery(tc.query);
250 try testing.expectEqualStrings(tc.expected_query, parsed_query.getQuery()); 260 try testing.expectEqualStrings(tc.expected_query, parsed_query.getQuery());
251 } 261 }
252} 262}
@@ -296,7 +306,7 @@ test "parsed query: bind markers types" {
296 @setEvalBranchQuota(100000); 306 @setEvalBranchQuota(100000);
297 const parsed_query = comptime ParsedQuery(tc.query); 307 const parsed_query = comptime ParsedQuery(tc.query);
298 308
299 try testing.expectEqual(1, parsed_query.nb_bind_markers); 309 try testing.expectEqual(1, parsed_query.bind_markers.len);
300 310
301 const bind_marker = parsed_query.bind_markers[0]; 311 const bind_marker = parsed_query.bind_markers[0];
302 try testing.expectEqual(tc.expected_marker.typed, bind_marker.typed); 312 try testing.expectEqual(tc.expected_marker.typed, bind_marker.typed);
@@ -344,7 +354,7 @@ test "parsed query: bind markers identifier" {
344 inline for (testCases) |tc| { 354 inline for (testCases) |tc| {
345 const parsed_query = comptime ParsedQuery(tc.query); 355 const parsed_query = comptime ParsedQuery(tc.query);
346 356
347 try testing.expectEqual(@as(usize, 1), parsed_query.nb_bind_markers); 357 try testing.expectEqual(@as(usize, 1), parsed_query.bind_markers.len);
348 358
349 const bind_marker = parsed_query.bind_markers[0]; 359 const bind_marker = parsed_query.bind_markers[0];
350 try testing.expectEqual(tc.expected_marker, bind_marker); 360 try testing.expectEqual(tc.expected_marker, bind_marker);
@@ -388,9 +398,13 @@ test "parsed query: query bind identifier" {
388 398
389 inline for (testCases) |tc| { 399 inline for (testCases) |tc| {
390 @setEvalBranchQuota(100000); 400 @setEvalBranchQuota(100000);
391 comptime var parsed_query = ParsedQuery(tc.query); 401
392 try testing.expectEqualStrings(tc.expected_query, parsed_query.getQuery()); 402 comptime {
393 try testing.expectEqual(tc.expected_nb_bind_markers, parsed_query.nb_bind_markers); 403 const parsed_query = ParsedQuery(tc.query);
404
405 try testing.expectEqual(tc.expected_nb_bind_markers, parsed_query.bind_markers.len);
406 try testing.expectEqualStrings(tc.expected_query, parsed_query.getQuery());
407 }
394 } 408 }
395} 409}
396 410
@@ -416,9 +430,9 @@ test "parsed query: bind marker character inside string" {
416 430
417 inline for (testCases) |tc| { 431 inline for (testCases) |tc| {
418 @setEvalBranchQuota(100000); 432 @setEvalBranchQuota(100000);
419 comptime var parsed_query = ParsedQuery(tc.query); 433 const parsed_query = ParsedQuery(tc.query);
420 434
421 try testing.expectEqual(@as(usize, tc.exp_bind_markers), parsed_query.nb_bind_markers); 435 try testing.expectEqual(@as(usize, tc.exp_bind_markers), parsed_query.bind_markers.len);
422 try testing.expectEqualStrings(tc.exp, parsed_query.getQuery()); 436 try testing.expectEqualStrings(tc.exp, parsed_query.getQuery());
423 } 437 }
424} 438}
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 {
1491 @compileError("enum column " ++ @typeName(FieldType) ++ " must have a BaseType of either string or int"); 1491 @compileError("enum column " ++ @typeName(FieldType) ++ " must have a BaseType of either string or int");
1492 }, 1492 },
1493 .Struct => |TI| { 1493 .Struct => |TI| {
1494 if (TI.layout == .Packed) return @bitCast(try self.readInt(TI.backing_integer.?, i)); 1494 if (TI.layout == .@"packed") return @bitCast(try self.readInt(TI.backing_integer.?, i));
1495 const inner_value = try self.readField(FieldType.BaseType, options, i); 1495 const inner_value = try self.readField(FieldType.BaseType, options, i);
1496 return try FieldType.readField(options.allocator, inner_value); 1496 return try FieldType.readField(options.allocator, inner_value);
1497 }, 1497 },
@@ -1512,8 +1512,11 @@ pub fn Iterator(comptime Type: type) type {
1512/// }; 1512/// };
1513/// 1513///
1514pub fn StatementType(comptime opts: StatementOptions, comptime query: []const u8) type { 1514pub fn StatementType(comptime opts: StatementOptions, comptime query: []const u8) type {
1515 @setEvalBranchQuota(100000); 1515 comptime {
1516 return Statement(opts, ParsedQuery(query)); 1516 @setEvalBranchQuota(100000);
1517 const parsed_query = ParsedQuery(query);
1518 return Statement(opts, parsed_query);
1519 }
1517} 1520}
1518 1521
1519pub const StatementOptions = struct {}; 1522pub const StatementOptions = struct {};
@@ -1698,7 +1701,7 @@ pub const DynamicStatement = struct {
1698 } 1701 }
1699 }, 1702 },
1700 .Struct => |info| { 1703 .Struct => |info| {
1701 if (info.layout == .Packed) { 1704 if (info.layout == .@"packed") {
1702 try self.bindField(info.backing_integer.?, options, field_name, i, @as(info.backing_integer.?, @bitCast(field))); 1705 try self.bindField(info.backing_integer.?, options, field_name, i, @as(info.backing_integer.?, @bitCast(field)));
1703 return; 1706 return;
1704 } 1707 }
@@ -2066,9 +2069,9 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: anytype) type
2066 2069
2067 const StructTypeInfo = @typeInfo(StructType).Struct; 2070 const StructTypeInfo = @typeInfo(StructType).Struct;
2068 2071
2069 if (comptime query.nb_bind_markers != StructTypeInfo.fields.len) { 2072 if (comptime query.bind_markers.len != StructTypeInfo.fields.len) {
2070 @compileError(std.fmt.comptimePrint("expected {d} bind parameters but got {d}", .{ 2073 @compileError(std.fmt.comptimePrint("expected {d} bind parameters but got {d}", .{
2071 query.nb_bind_markers, 2074 query.bind_markers.len,
2072 StructTypeInfo.fields.len, 2075 StructTypeInfo.fields.len,
2073 })); 2076 }));
2074 } 2077 }
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(
766 766
767 // 767 //
768 768
769 const state = @fieldParentPtr(State, "vtab", vtab); 769 const nullable_state: ?*State = @fieldParentPtr("vtab", vtab);
770 const state = nullable_state orelse unreachable;
770 771
771 var arena = heap.ArenaAllocator.init(state.module_context.allocator); 772 var arena = heap.ArenaAllocator.init(state.module_context.allocator);
772 defer arena.deinit(); 773 defer arena.deinit();
@@ -788,7 +789,9 @@ pub fn VirtualTable(
788 } 789 }
789 790
790 fn xDisconnect(vtab: [*c]c.sqlite3_vtab) callconv(.C) c_int { 791 fn xDisconnect(vtab: [*c]c.sqlite3_vtab) callconv(.C) c_int {
791 const state = @fieldParentPtr(State, "vtab", vtab); 792 const nullable_state: ?*State = @fieldParentPtr("vtab", vtab);
793 const state = nullable_state orelse unreachable;
794
792 state.deinit(); 795 state.deinit();
793 796
794 return c.SQLITE_OK; 797 return c.SQLITE_OK;
@@ -803,7 +806,8 @@ pub fn VirtualTable(
803 } 806 }
804 807
805 fn xOpen(vtab: [*c]c.sqlite3_vtab, vtab_cursor: [*c][*c]c.sqlite3_vtab_cursor) callconv(.C) c_int { 808 fn xOpen(vtab: [*c]c.sqlite3_vtab, vtab_cursor: [*c][*c]c.sqlite3_vtab_cursor) callconv(.C) c_int {
806 const state = @fieldParentPtr(State, "vtab", vtab); 809 const nullable_state: ?*State = @fieldParentPtr("vtab", vtab);
810 const state = nullable_state orelse unreachable;
807 811
808 const cursor_state = CursorState.init(state.module_context, state.table) catch |err| { 812 const cursor_state = CursorState.init(state.module_context, state.table) catch |err| {
809 logger.err("unable to create cursor state, err: {!}", .{err}); 813 logger.err("unable to create cursor state, err: {!}", .{err});
@@ -815,14 +819,17 @@ pub fn VirtualTable(
815 } 819 }
816 820
817 fn xClose(vtab_cursor: [*c]c.sqlite3_vtab_cursor) callconv(.C) c_int { 821 fn xClose(vtab_cursor: [*c]c.sqlite3_vtab_cursor) callconv(.C) c_int {
818 const cursor_state = @fieldParentPtr(CursorState, "vtab_cursor", vtab_cursor); 822 const nullable_cursor_state: ?*CursorState = @fieldParentPtr("vtab_cursor", vtab_cursor);
823 const cursor_state = nullable_cursor_state orelse unreachable;
824
819 cursor_state.deinit(); 825 cursor_state.deinit();
820 826
821 return c.SQLITE_OK; 827 return c.SQLITE_OK;
822 } 828 }
823 829
824 fn xEof(vtab_cursor: [*c]c.sqlite3_vtab_cursor) callconv(.C) c_int { 830 fn xEof(vtab_cursor: [*c]c.sqlite3_vtab_cursor) callconv(.C) c_int {
825 const cursor_state = @fieldParentPtr(CursorState, "vtab_cursor", vtab_cursor); 831 const nullable_cursor_state: ?*CursorState = @fieldParentPtr("vtab_cursor", vtab_cursor);
832 const cursor_state = nullable_cursor_state orelse unreachable;
826 const cursor = cursor_state.cursor; 833 const cursor = cursor_state.cursor;
827 834
828 var arena = heap.ArenaAllocator.init(cursor_state.module_context.allocator); 835 var arena = heap.ArenaAllocator.init(cursor_state.module_context.allocator);
@@ -859,7 +866,8 @@ pub fn VirtualTable(
859 } 866 }
860 867
861 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 { 868 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 {
862 const cursor_state = @fieldParentPtr(CursorState, "vtab_cursor", vtab_cursor); 869 const nullable_cursor_state: ?*CursorState = @fieldParentPtr("vtab_cursor", vtab_cursor);
870 const cursor_state = nullable_cursor_state orelse unreachable;
863 const cursor = cursor_state.cursor; 871 const cursor = cursor_state.cursor;
864 872
865 var arena = heap.ArenaAllocator.init(cursor_state.module_context.allocator); 873 var arena = heap.ArenaAllocator.init(cursor_state.module_context.allocator);
@@ -884,7 +892,8 @@ pub fn VirtualTable(
884 } 892 }
885 893
886 fn xNext(vtab_cursor: [*c]c.sqlite3_vtab_cursor) callconv(.C) c_int { 894 fn xNext(vtab_cursor: [*c]c.sqlite3_vtab_cursor) callconv(.C) c_int {
887 const cursor_state = @fieldParentPtr(CursorState, "vtab_cursor", vtab_cursor); 895 const nullable_cursor_state: ?*CursorState = @fieldParentPtr("vtab_cursor", vtab_cursor);
896 const cursor_state = nullable_cursor_state orelse unreachable;
888 const cursor = cursor_state.cursor; 897 const cursor = cursor_state.cursor;
889 898
890 var arena = heap.ArenaAllocator.init(cursor_state.module_context.allocator); 899 var arena = heap.ArenaAllocator.init(cursor_state.module_context.allocator);
@@ -902,7 +911,8 @@ pub fn VirtualTable(
902 } 911 }
903 912
904 fn xColumn(vtab_cursor: [*c]c.sqlite3_vtab_cursor, ctx: ?*c.sqlite3_context, n: c_int) callconv(.C) c_int { 913 fn xColumn(vtab_cursor: [*c]c.sqlite3_vtab_cursor, ctx: ?*c.sqlite3_context, n: c_int) callconv(.C) c_int {
905 const cursor_state = @fieldParentPtr(CursorState, "vtab_cursor", vtab_cursor); 914 const nullable_cursor_state: ?*CursorState = @fieldParentPtr("vtab_cursor", vtab_cursor);
915 const cursor_state = nullable_cursor_state orelse unreachable;
906 const cursor = cursor_state.cursor; 916 const cursor = cursor_state.cursor;
907 917
908 var arena = heap.ArenaAllocator.init(cursor_state.module_context.allocator); 918 var arena = heap.ArenaAllocator.init(cursor_state.module_context.allocator);
@@ -945,7 +955,8 @@ pub fn VirtualTable(
945 } 955 }
946 956
947 fn xRowid(vtab_cursor: [*c]c.sqlite3_vtab_cursor, row_id_ptr: [*c]c.sqlite3_int64) callconv(.C) c_int { 957 fn xRowid(vtab_cursor: [*c]c.sqlite3_vtab_cursor, row_id_ptr: [*c]c.sqlite3_int64) callconv(.C) c_int {
948 const cursor_state = @fieldParentPtr(CursorState, "vtab_cursor", vtab_cursor); 958 const nullable_cursor_state: ?*CursorState = @fieldParentPtr("vtab_cursor", vtab_cursor);
959 const cursor_state = nullable_cursor_state orelse unreachable;
949 const cursor = cursor_state.cursor; 960 const cursor = cursor_state.cursor;
950 961
951 var arena = heap.ArenaAllocator.init(cursor_state.module_context.allocator); 962 var arena = heap.ArenaAllocator.init(cursor_state.module_context.allocator);