summaryrefslogtreecommitdiff
path: root/query.zig
diff options
context:
space:
mode:
authorGravatar Vincent Rischmann2024-04-14 18:10:28 +0200
committerGravatar Vincent Rischmann2024-04-14 18:10:28 +0200
commit49db791a43fdf4afecc27336d84ca6ab780b7f77 (patch)
tree32caf521bef501420e75ace3939d4aa47f827bdc /query.zig
parentMerge pull request #154 from JacobCrabill/dev/zig-0.12-modules (diff)
downloadzig-sqlite-49db791a43fdf4afecc27336d84ca6ab780b7f77.tar.gz
zig-sqlite-49db791a43fdf4afecc27336d84ca6ab780b7f77.tar.xz
zig-sqlite-49db791a43fdf4afecc27336d84ca6ab780b7f77.zip
fix for latest zig
Also some major refactoring around the query parsing state.
Diffstat (limited to '')
-rw-r--r--query.zig332
1 files changed, 173 insertions, 159 deletions
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}