summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar thisLight2021-09-21 14:11:51 +0800
committerGravatar Vincent Rischmann2021-10-19 08:40:36 +0200
commit17586530e5f71ed61cabf6919838b89beb03663e (patch)
treee3b6d75a2c06e4dd8457729c6734d7dbb6a87bd9
parentremove the tools directory (diff)
downloadzig-sqlite-17586530e5f71ed61cabf6919838b89beb03663e.tar.gz
zig-sqlite-17586530e5f71ed61cabf6919838b89beb03663e.tar.xz
zig-sqlite-17586530e5f71ed61cabf6919838b89beb03663e.zip
ParsedQuery: support named parameters.
Besides ParsedQuery, BindMarker have been reworked to fit new need of named parameters.
Diffstat (limited to '')
-rw-r--r--query.zig133
1 files changed, 115 insertions, 18 deletions
diff --git a/query.zig b/query.zig
index 6663f52..3c32fcc 100644
--- a/query.zig
+++ b/query.zig
@@ -7,9 +7,9 @@ const Blob = @import("sqlite.zig").Blob;
7/// Text is used to represent a SQLite TEXT value when binding a parameter or reading a column. 7/// Text is used to represent a SQLite TEXT value when binding a parameter or reading a column.
8pub const Text = struct { data: []const u8 }; 8pub const Text = struct { data: []const u8 };
9 9
10const BindMarker = union(enum) { 10const BindMarker = struct {
11 Typed: type, 11 typed: ?type = null, // null == untyped
12 Untyped: void, 12 identifier: ?[]const u8 = null,
13}; 13};
14 14
15pub const ParsedQuery = struct { 15pub const ParsedQuery = struct {
@@ -29,6 +29,9 @@ pub const ParsedQuery = struct {
29 comptime var current_bind_marker_type: [256]u8 = undefined; 29 comptime var current_bind_marker_type: [256]u8 = undefined;
30 comptime var current_bind_marker_type_pos = 0; 30 comptime var current_bind_marker_type_pos = 0;
31 31
32 comptime var current_bind_marker_id: [256]u8 = undefined;
33 comptime var current_bind_marker_id_pos = 0;
34
32 comptime var parsed_query: ParsedQuery = undefined; 35 comptime var parsed_query: ParsedQuery = undefined;
33 parsed_query.nb_bind_markers = 0; 36 parsed_query.nb_bind_markers = 0;
34 37
@@ -47,16 +50,48 @@ pub const ParsedQuery = struct {
47 }, 50 },
48 .BindMarker => switch (c) { 51 .BindMarker => switch (c) {
49 '{' => { 52 '{' => {
53 parsed_query.bind_markers[parsed_query.nb_bind_markers] = BindMarker{};
50 state = .BindMarkerType; 54 state = .BindMarkerType;
51 current_bind_marker_type_pos = 0; 55 current_bind_marker_type_pos = 0;
52 }, 56 },
53 else => { 57 else => {
54 // This is a bind marker without a type. 58 if (std.ascii.isAlpha(c) or std.ascii.isDigit(c)){
55 state = .Start; 59 parsed_query.bind_markers[parsed_query.nb_bind_markers] = BindMarker{};
56 60 state = .BindMarkerIdentifier;
57 parsed_query.bind_markers[parsed_query.nb_bind_markers] = BindMarker{ .Untyped = {} }; 61 current_bind_marker_id_pos = 0;
58 parsed_query.nb_bind_markers += 1; 62 current_bind_marker_id[current_bind_marker_id_pos] = c;
63 current_bind_marker_id_pos += 1;
64 } else {
65 // This is a bind marker without a type.
66 state = .Start;
67
68 parsed_query.bind_markers[parsed_query.nb_bind_markers] = BindMarker{};
69 parsed_query.nb_bind_markers += 1;
70 }
71 buf[pos] = c;
72 pos += 1;
73 },
74 },
75 .BindMarkerIdentifier => switch (c) {
76 '{' => {
77 state = .BindMarkerType;
78 current_bind_marker_type_pos = 0;
59 79
80 // A bind marker with id and type: ?AAA{[]const u8}, we don't need move the pointer.
81 if (current_bind_marker_id_pos > 0){
82 parsed_query.bind_markers[parsed_query.nb_bind_markers].identifier = std.fmt.comptimePrint("{s}", .{current_bind_marker_id[0..current_bind_marker_id_pos]});
83 }
84 },
85 else => {
86 if (std.ascii.isAlpha(c) or std.ascii.isDigit(c)){
87 current_bind_marker_id[current_bind_marker_id_pos] = c;
88 current_bind_marker_id_pos += 1;
89 } else {
90 state = .Start;
91 if (current_bind_marker_id_pos > 0) {
92 parsed_query.bind_markers[parsed_query.nb_bind_markers].identifier = std.fmt.comptimePrint("{s}", .{current_bind_marker_id[0..current_bind_marker_id_pos]});
93 }
94 }
60 buf[pos] = c; 95 buf[pos] = c;
61 pos += 1; 96 pos += 1;
62 }, 97 },
@@ -67,7 +102,7 @@ pub const ParsedQuery = struct {
67 102
68 const typ = parseType(current_bind_marker_type[0..current_bind_marker_type_pos]); 103 const typ = parseType(current_bind_marker_type[0..current_bind_marker_type_pos]);
69 104
70 parsed_query.bind_markers[parsed_query.nb_bind_markers] = BindMarker{ .Typed = typ }; 105 parsed_query.bind_markers[parsed_query.nb_bind_markers].typed = typ;
71 parsed_query.nb_bind_markers += 1; 106 parsed_query.nb_bind_markers += 1;
72 }, 107 },
73 else => { 108 else => {
@@ -83,7 +118,10 @@ pub const ParsedQuery = struct {
83 118
84 // The last character was ? so this must be an untyped bind marker. 119 // The last character was ? so this must be an untyped bind marker.
85 if (state == .BindMarker) { 120 if (state == .BindMarker) {
86 parsed_query.bind_markers[parsed_query.nb_bind_markers] = BindMarker{ .Untyped = {} }; 121 parsed_query.bind_markers[parsed_query.nb_bind_markers].typed = null;
122 parsed_query.nb_bind_markers += 1;
123 } else if (state == .BindMarkerIdentifier) {
124 parsed_query.bind_markers[parsed_query.nb_bind_markers].identifier = std.fmt.comptimePrint("{s}", .{current_bind_marker_id[0..current_bind_marker_id_pos]});
87 parsed_query.nb_bind_markers += 1; 125 parsed_query.nb_bind_markers += 1;
88 } 126 }
89 127
@@ -175,19 +213,19 @@ test "parsed query: bind markers types" {
175 const testCases = &[_]testCase{ 213 const testCases = &[_]testCase{
176 .{ 214 .{
177 .query = "foobar ?{usize}", 215 .query = "foobar ?{usize}",
178 .expected_marker = .{ .Typed = usize }, 216 .expected_marker = .{ .typed = usize },
179 }, 217 },
180 .{ 218 .{
181 .query = "foobar ?{text}", 219 .query = "foobar ?{text}",
182 .expected_marker = .{ .Typed = Text }, 220 .expected_marker = .{ .typed = Text },
183 }, 221 },
184 .{ 222 .{
185 .query = "foobar ?{blob}", 223 .query = "foobar ?{blob}",
186 .expected_marker = .{ .Typed = Blob }, 224 .expected_marker = .{ .typed = Blob },
187 }, 225 },
188 .{ 226 .{
189 .query = "foobar ?", 227 .query = "foobar ?",
190 .expected_marker = .{ .Untyped = {} }, 228 .expected_marker = .{ .typed = null },
191 }, 229 },
192 }; 230 };
193 231
@@ -197,9 +235,68 @@ test "parsed query: bind markers types" {
197 try testing.expectEqual(1, parsed_query.nb_bind_markers); 235 try testing.expectEqual(1, parsed_query.nb_bind_markers);
198 236
199 const bind_marker = parsed_query.bind_markers[0]; 237 const bind_marker = parsed_query.bind_markers[0];
200 switch (tc.expected_marker) { 238 try testing.expectEqual(tc.expected_marker.typed, bind_marker.typed);
201 .Typed => |typ| try testing.expectEqual(typ, bind_marker.Typed), 239 }
202 .Untyped => |typ| try testing.expectEqual(typ, bind_marker.Untyped), 240}
203 } 241
242test "parsed query: bind markers identifier" {
243 const testCase = struct {
244 query: []const u8,
245 expected_marker: BindMarker,
246 };
247
248 const testCases = &[_]testCase{
249 .{
250 .query = "foobar ?ABC{usize}",
251 .expected_marker = .{ .identifier = "ABC" },
252 },
253 .{
254 .query = "foobar ?123{text}",
255 .expected_marker = .{ .identifier = "123" },
256 },
257 .{
258 .query = "foobar ?abc{blob}",
259 .expected_marker = .{ .identifier = "abc" },
260 },
261 .{
262 .query = "foobar ?123",
263 .expected_marker = .{ .identifier = "123" },
264 },
265 };
266
267 inline for (testCases) |tc| {
268 comptime var parsed_query = ParsedQuery.from(tc.query);
269
270 try testing.expectEqual(1, parsed_query.nb_bind_markers);
271
272 const bind_marker = parsed_query.bind_markers[0];
273 try testing.expectEqualStrings(tc.expected_marker.identifier.?, bind_marker.identifier.?);
274 }
275}
276
277test "parsed query: query bind identifier" {
278 const testCase = struct {
279 query: []const u8,
280 expected_query: []const u8,
281 };
282
283 const testCases = &[_]testCase{
284 .{
285 .query = "INSERT INTO user(id, name, age) VALUES(?id{usize}, ?name{[]const u8}, ?age{u32})",
286 .expected_query = "INSERT INTO user(id, name, age) VALUES(?id, ?name, ?age)",
287 },
288 .{
289 .query = "SELECT id, name, age FROM user WHER age > ?ageGT{u32} AND age < ?ageLT{u32}",
290 .expected_query = "SELECT id, name, age FROM user WHER age > ?ageGT AND age < ?ageLT",
291 },
292 .{
293 .query = "SELECT id, name, age FROM user WHER age > ?ageGT AND age < ?ageLT",
294 .expected_query = "SELECT id, name, age FROM user WHER age > ?ageGT AND age < ?ageLT",
295 },
296 };
297
298 inline for (testCases) |tc| {
299 comptime var parsed_query = ParsedQuery.from(tc.query);
300 try testing.expectEqualStrings(tc.expected_query, parsed_query.getQuery());
204 } 301 }
205} 302}