diff options
| -rw-r--r-- | query.zig | 17 | ||||
| -rw-r--r-- | sqlite.zig | 48 |
2 files changed, 54 insertions, 11 deletions
| @@ -125,11 +125,11 @@ pub fn ParsedQuery(comptime tmp_query: []const u8) type { | |||
| 125 | if (!isNamedIdentifierChar(c)) { | 125 | if (!isNamedIdentifierChar(c)) { |
| 126 | // This marks the end of the named bind marker. | 126 | // This marks the end of the named bind marker. |
| 127 | state = .start; | 127 | state = .start; |
| 128 | const name = buf[hold_pos .. pos - 1]; | 128 | const name = buf[hold_pos - 1 .. pos]; |
| 129 | // TODO(vincent): name retains a pointer to a comptime var, FIX ! | 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) { | 130 | if (bindMarkerForName(tmp_bind_markers[0..nb_tmp_bind_markers], name) == null) { |
| 131 | const new_buf = buf; | 131 | const new_buf = buf; |
| 132 | tmp_bind_markers[nb_tmp_bind_markers].name = new_buf[hold_pos .. pos - 1]; | 132 | tmp_bind_markers[nb_tmp_bind_markers].name = new_buf[hold_pos - 1 .. pos]; |
| 133 | nb_tmp_bind_markers += 1; | 133 | nb_tmp_bind_markers += 1; |
| 134 | } | 134 | } |
| 135 | } | 135 | } |
| @@ -175,6 +175,8 @@ pub fn ParsedQuery(comptime tmp_query: []const u8) type { | |||
| 175 | nb_tmp_bind_markers += 1; | 175 | nb_tmp_bind_markers += 1; |
| 176 | }, | 176 | }, |
| 177 | .bind_marker_identifier => { | 177 | .bind_marker_identifier => { |
| 178 | const new_buf = buf; | ||
| 179 | tmp_bind_markers[nb_tmp_bind_markers].name = @as([]const u8, new_buf[hold_pos - 1 .. pos]); | ||
| 178 | nb_tmp_bind_markers += 1; | 180 | nb_tmp_bind_markers += 1; |
| 179 | }, | 181 | }, |
| 180 | .start => {}, | 182 | .start => {}, |
| @@ -339,15 +341,15 @@ test "parsed query: bind markers identifier" { | |||
| 339 | }, | 341 | }, |
| 340 | .{ | 342 | .{ |
| 341 | .query = "foobar ?123", | 343 | .query = "foobar ?123", |
| 342 | .expected_marker = .{}, | 344 | .expected_marker = .{ .typed = null, .name = "123" }, |
| 343 | }, | 345 | }, |
| 344 | .{ | 346 | .{ |
| 345 | .query = "foobar :hola", | 347 | .query = "foobar :hola", |
| 346 | .expected_marker = .{}, | 348 | .expected_marker = .{ .typed = null, .name = "hola" }, |
| 347 | }, | 349 | }, |
| 348 | .{ | 350 | .{ |
| 349 | .query = "foobar @foo", | 351 | .query = "foobar @foo", |
| 350 | .expected_marker = .{}, | 352 | .expected_marker = .{ .typed = null, .name = "foo" }, |
| 351 | }, | 353 | }, |
| 352 | }; | 354 | }; |
| 353 | 355 | ||
| @@ -357,7 +359,10 @@ test "parsed query: bind markers identifier" { | |||
| 357 | try testing.expectEqual(@as(usize, 1), parsed_query.bind_markers.len); | 359 | try testing.expectEqual(@as(usize, 1), parsed_query.bind_markers.len); |
| 358 | 360 | ||
| 359 | const bind_marker = parsed_query.bind_markers[0]; | 361 | const bind_marker = parsed_query.bind_markers[0]; |
| 360 | try testing.expectEqual(tc.expected_marker, bind_marker); | 362 | if (bind_marker.name) |name| { |
| 363 | try testing.expectEqualStrings(tc.expected_marker.name.?, name); | ||
| 364 | } | ||
| 365 | try testing.expectEqual(tc.expected_marker.typed, bind_marker.typed); | ||
| 361 | } | 366 | } |
| 362 | } | 367 | } |
| 363 | 368 | ||
| @@ -2069,11 +2069,26 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: anytype) type | |||
| 2069 | 2069 | ||
| 2070 | const StructTypeInfo = @typeInfo(StructType).@"struct"; | 2070 | const StructTypeInfo = @typeInfo(StructType).@"struct"; |
| 2071 | 2071 | ||
| 2072 | if (comptime query.bind_markers.len != StructTypeInfo.fields.len) { | 2072 | comptime marker_len_check: { |
| 2073 | @compileError(std.fmt.comptimePrint("expected {d} bind parameters but got {d}", .{ | 2073 | if (query.bind_markers.len != StructTypeInfo.fields.len) { |
| 2074 | query.bind_markers.len, | 2074 | if (query.bind_markers.len > StructTypeInfo.fields.len) { |
| 2075 | StructTypeInfo.fields.len, | 2075 | var found_markers = 0; |
| 2076 | })); | 2076 | for (query.bind_markers) |bind_marker| { |
| 2077 | if (bind_marker.name) |name| { | ||
| 2078 | if (@hasField(StructType, name)) { | ||
| 2079 | found_markers += 1; | ||
| 2080 | } | ||
| 2081 | } | ||
| 2082 | } | ||
| 2083 | if (found_markers == query.bind_markers.len) { | ||
| 2084 | break :marker_len_check; | ||
| 2085 | } | ||
| 2086 | } | ||
| 2087 | @compileError(std.fmt.comptimePrint("expected {d} bind parameters but got {d}", .{ | ||
| 2088 | query.bind_markers.len, | ||
| 2089 | StructTypeInfo.fields.len, | ||
| 2090 | })); | ||
| 2091 | } | ||
| 2077 | } | 2092 | } |
| 2078 | 2093 | ||
| 2079 | inline for (StructTypeInfo.fields, 0..) |struct_field, _i| { | 2094 | inline for (StructTypeInfo.fields, 0..) |struct_field, _i| { |
| @@ -4021,3 +4036,26 @@ test "tagged union" { | |||
| 4021 | try testing.expectEqual(foobar.age, result.?.value); | 4036 | try testing.expectEqual(foobar.age, result.?.value); |
| 4022 | } | 4037 | } |
| 4023 | } | 4038 | } |
| 4039 | |||
| 4040 | test "reuse same field twice in query string" { | ||
| 4041 | var db = try getTestDb(); | ||
| 4042 | defer db.deinit(); | ||
| 4043 | try addTestData(&db); | ||
| 4044 | |||
| 4045 | const update_name = "NewUpdatedName"; | ||
| 4046 | |||
| 4047 | const update_name_query = "UPDATE user SET id = $id, name = $name WHERE id = $id"; | ||
| 4048 | try db.exec(update_name_query, .{}, .{ .id = 20, .name = update_name }); | ||
| 4049 | |||
| 4050 | const fetch_name_query = "SELECT name FROM user WHERE id = $id"; | ||
| 4051 | const name = try db.oneAlloc( | ||
| 4052 | []const u8, | ||
| 4053 | testing.allocator, | ||
| 4054 | fetch_name_query, | ||
| 4055 | .{}, | ||
| 4056 | .{ .id = 20 }, | ||
| 4057 | ); | ||
| 4058 | try testing.expect(name != null); | ||
| 4059 | defer testing.allocator.free(name.?); | ||
| 4060 | try testing.expectEqualStrings(name.?, update_name); | ||
| 4061 | } | ||