From b2dd6fe6274263e7d348462faa0df58391d497f1 Mon Sep 17 00:00:00 2001 From: MrBounty Date: Sat, 28 Dec 2024 20:12:20 +0100 Subject: [PATCH] Basic working relationship It is working ! The basic GRAB [name, best_friend] work and do return the best friend infos. I added more test and some do not work yet, working on it now --- src/entityWriter.zig | 42 +++++++++++++++++++++++++----------- src/fileEngine.zig | 13 +++++------ src/stuffs/relationMap.zig | 32 +++++++++++++++++++++------ src/ziqlParser.zig | 44 ++++++++++++++++++++++---------------- 4 files changed, 89 insertions(+), 42 deletions(-) diff --git a/src/entityWriter.zig b/src/entityWriter.zig index 0f3ae2a..ba536ff 100644 --- a/src/entityWriter.zig +++ b/src/entityWriter.zig @@ -54,7 +54,7 @@ pub const EntityWriter = struct { } const uuid = try UUID.parse("00000000-0000-0000-0000-000000000000"); // Maybe pass that comptime to prevent parsing it everytime if (!std.meta.eql(v, uuid.bytes)) { - try writer.print("\"{{|<{s}>|}}\"", .{v}); + try writer.print("{{|<{s}>|}}", .{v}); } else { try writer.print("{{}}", .{}); } @@ -82,7 +82,7 @@ pub const EntityWriter = struct { .IntArray => while (iter.next()) |v| writer.print("{d}, ", .{v.Int}) catch return ZipponError.WriteError, .FloatArray => while (iter.next()) |v| writer.print("{d}", .{v.Float}) catch return ZipponError.WriteError, .StrArray => while (iter.next()) |v| writer.print("\"{s}\"", .{v.Str}) catch return ZipponError.WriteError, - .UUIDArray => while (iter.next()) |v| writer.print("\"{{|<{s}>|}}\"", .{UUID.format_bytes(v.UUID)}) catch return ZipponError.WriteError, + .UUIDArray => while (iter.next()) |v| writer.print("{{|<{s}>|}},", .{v.UUID}) catch return ZipponError.WriteError, .BoolArray => while (iter.next()) |v| writer.print("{any}", .{v.Bool}) catch return ZipponError.WriteError, .UnixArray => while (iter.next()) |v| { const datetime = DateTime.initUnix(v.Unix); @@ -102,27 +102,45 @@ pub const EntityWriter = struct { /// Take a string in the JSON format and look for {|<[16]u8>|}, then will look into the map and check if it can find this UUID /// If it find it, it ill replace the {|<[16]u8>|} will the value - pub fn updateWithRelation(writer: anytype, input: []const u8, to_add: std.AutoHashMap([16]u8, JsonString)) ZipponError!void { + pub fn updateWithRelation(writer: anytype, input: []const u8, map: std.AutoHashMap([16]u8, JsonString)) ZipponError!void { var uuid_bytes: [16]u8 = undefined; var start: usize = 0; - while (std.mem.indexOf(u8, input[start..], "{|<[")) |pos| { - const pattern_start = start + pos; - const pattern_end = std.mem.indexOf(u8, input[pattern_start..], "]>|}") orelse break; - const full_pattern_end = pattern_start + pattern_end + 4; + while (std.mem.indexOf(u8, input[start..], "{|<")) |pos| { + const pattern_start = start + pos + 3; + const pattern_end = pattern_start + 16; // Write the text before the pattern - writer.writeAll(input[start..pattern_start]) catch return ZipponError.WriteError; + writer.writeAll(input[start .. pattern_start - 3]) catch return ZipponError.WriteError; - for (pattern_start + 3..pattern_end - 3, 0..) |i, j| uuid_bytes[j] = input[i]; - if (to_add.get(uuid_bytes)) |json_string| { + if (input[pattern_start - 4] == '[') { + start = try updateArray(writer, input, map, pattern_start - 3); + continue; + } + + @memcpy(uuid_bytes[0..], input[pattern_start..pattern_end]); + if (map.get(uuid_bytes)) |json_string| { writer.writeAll(json_string.slice) catch return ZipponError.WriteError; } else { - writer.writeAll(input[pattern_start..pattern_end]) catch return ZipponError.WriteError; + writer.writeAll(input[pattern_start - 3 .. pattern_end + 3]) catch return ZipponError.WriteError; } - start = full_pattern_end; + start = pattern_end; } // Write any remaining text writer.writeAll(input[start..]) catch return ZipponError.WriteError; } + + fn updateArray(writer: anytype, input: []const u8, map: std.AutoHashMap([16]u8, JsonString), origin: usize) ZipponError!usize { + var uuid_bytes: [16]u8 = undefined; + var start = origin; + while (input.len > start + 23 and std.mem.eql(u8, input[start .. start + 3], "{|<") and std.mem.eql(u8, input[start + 19 .. start + 23], ">|},")) : (start += 23) { + @memcpy(uuid_bytes[0..], input[start + 3 .. start + 19]); + if (map.get(uuid_bytes)) |json_string| { + writer.writeAll(json_string.slice) catch return ZipponError.WriteError; + } else { + writer.writeAll(input[start .. start + 23]) catch return ZipponError.WriteError; + } + } + return start; + } }; diff --git a/src/fileEngine.zig b/src/fileEngine.zig index 7545489..51e778a 100644 --- a/src/fileEngine.zig +++ b/src/fileEngine.zig @@ -460,6 +460,8 @@ pub const FileEngine = struct { // Here I take the JSON string and I parse it to find all {|<>|} and add them to the relation map with an empty JsonString for (relation_maps) |*relation_map| try relation_map.populate(buff.items); + if (relation_maps.len > 0) std.debug.print("{d} {d}\n", .{ relation_maps.len, relation_maps[0].map.count() }); + // I then call parseEntitiesRelationMap on each // This will update the buff items to be the same Json but with {|<[16]u8>|} replaced with the right Json for (relation_maps) |*relation_map| try self.parseEntitiesRelationMap(struct_name, relation_map, &buff); @@ -520,7 +522,6 @@ pub const FileEngine = struct { // Once the new input received, call parseEntitiesRelationMap again the string still contain {|<>|} because of sub relationship // The buffer contain the string with {|<>|} and need to be updated at the end // TODO: Use the new function in SchemaEngine to reduce the number of files to parse - // TODO: Add recursion taking example on parseEntities pub fn parseEntitiesRelationMap( self: *FileEngine, struct_name: []const u8, @@ -599,7 +600,7 @@ pub const FileEngine = struct { for (thread_map_list) |map| { var iter = map.iterator(); while (iter.next()) |entry| { - if (entry.value_ptr.init) relation_map.map.put(entry.key_ptr.*, entry.value_ptr.*) catch return ZipponError.MemoryError; + if (entry.value_ptr.init) relation_map.*.map.put(entry.key_ptr.*, entry.value_ptr.*) catch return ZipponError.MemoryError; } } @@ -817,12 +818,12 @@ pub const FileEngine = struct { }; defer new_writer.deinit(); + var finish_writing = false; while (iter.next() catch |err| { sync_context.logError("Parsing files", err); return; }) |row| { - if (sync_context.checkStructLimit()) break; - if (filter == null or filter.?.evaluate(row)) { + if (!finish_writing and (filter == null or filter.?.evaluate(row))) { // Add the unchanged Data in the new_data_buff new_data_buff[0] = row[0]; for (sstruct.members, 0..) |member, i| { @@ -838,13 +839,13 @@ pub const FileEngine = struct { return; }; - writer.print("{{\"{s}\"}},", .{UUID.format_bytes(row[0].UUID)}) catch |err| { + writer.print("\"{s}\",", .{UUID.format_bytes(row[0].UUID)}) catch |err| { sync_context.logError("Error initializing DataWriter", err); zid.deleteFile(new_path, dir) catch {}; return; }; - if (sync_context.incrementAndCheckStructLimit()) break; + finish_writing = sync_context.incrementAndCheckStructLimit(); } else { new_writer.write(row) catch |err| { sync_context.logError("Error initializing DataWriter", err); diff --git a/src/stuffs/relationMap.zig b/src/stuffs/relationMap.zig index 0605ab9..bbcc5b3 100644 --- a/src/stuffs/relationMap.zig +++ b/src/stuffs/relationMap.zig @@ -16,6 +16,8 @@ // Then for each RelationMap, I parse the files again this time to update the first JSON that now have {|<>|} // With a sub additionalData. If there is an additional data relation, I recurcive. // So I need an option in parseEntity to either write the first JSON or update the existing one +// +// FIXME: I think if 2 different struct have the same member name it can cause issue but maybe not tho const std = @import("std"); const AdditionalData = @import("additionalData.zig").AdditionalData; @@ -37,20 +39,38 @@ pub const RelationMap = struct { var uuid_bytes: [16]u8 = undefined; var start: usize = 0; while (std.mem.indexOf(u8, input[start..], "{|<")) |pos| { - const pattern_start = start + pos; - const pattern_end = std.mem.indexOf(u8, input[pattern_start..], ">|}") orelse break; - const full_pattern_end = pattern_start + pattern_end + 3; + const pattern_start = start + pos + 3; + const pattern_end = pattern_start + 16; - const member_end = pattern_start - 3; // This should be " = " + const member_end = if (input[pattern_start - 4] == '[') pattern_start - 6 else pattern_start - 5; // This should be ": {|<" var member_start = member_end - 1; while (input[member_start] != ' ') : (member_start -= 1) {} + member_start += 1; + + std.debug.print("MEMBER: {c} - {s}\n", .{ input[pattern_start - 4], input[member_start..member_end] }); if (!std.mem.eql(u8, input[member_start..member_end], self.member_name)) continue; - for (pattern_start + 3..pattern_end - 3, 0..) |i, j| uuid_bytes[j] = input[i]; + if (input[pattern_start - 4] == '[') { + start = try self.populateArray(input, pattern_start - 3); + continue; + } + + @memcpy(uuid_bytes[0..], input[pattern_start..pattern_end]); self.map.put(uuid_bytes, JsonString{}) catch return ZipponError.MemoryError; - start = full_pattern_end; + start = pattern_end + 3; } } + + // Array are pack in format {|<[16]u8>|},{|<[16]u8>|},{|<[16]u8>|},{|<[16]u8>|}, + fn populateArray(self: *RelationMap, input: []const u8, origin: usize) ZipponError!usize { + var uuid_bytes: [16]u8 = undefined; + var start = origin; + while (input.len > start + 23 and std.mem.eql(u8, input[start .. start + 3], "{|<") and std.mem.eql(u8, input[start + 19 .. start + 23], ">|},")) : (start += 23) { + for (start + 3..start + 19, 0..) |i, j| uuid_bytes[j] = input[i]; + self.map.put(uuid_bytes, JsonString{}) catch return ZipponError.MemoryError; + } + return start; + } }; diff --git a/src/ziqlParser.zig b/src/ziqlParser.zig index 2b80481..dd6e79d 100644 --- a/src/ziqlParser.zig +++ b/src/ziqlParser.zig @@ -129,7 +129,7 @@ pub const Parser = struct { // Check if the struct name is in the schema struct_name = self.toker.getTokenSlice(token); if (token.tag != .identifier) return printError( - "Error: Missing struct name", + "Error: Missing struct name.", ZiQlParserError.StructNotFound, self.toker.buffer, token.loc.start, @@ -152,13 +152,12 @@ pub const Parser = struct { keep_next = true; switch (token.tag) { .l_bracket => state = .parse_additional_data, - .l_brace => state = switch (action) { + .l_brace, .eof => state = switch (action) { .GRAB => .filter_and_send, .UPDATE => .filter_and_update, .DELETE => .filter_and_delete, else => unreachable, }, - .eof => state = .filter_and_send, else => return printError( "Error: Expect [ for additional data or { for a filter", ZiQlParserError.SynthaxError, @@ -1053,7 +1052,20 @@ pub const Parser = struct { } }; -// TODO: Check if what is send is expected +test "Synthax error" { + try expectParsingError("ADD User (name = 'Bob', email='bob@email.com', age=-55, scores=[ 1 ], best_friend=7db1f06d-a5a7-4917-8cc6-4d490191c9c1, bday=2000/01/01, a_time=12:04:54.8741, last_order=2000/01/01-12:45)", ZiQlParserError.SynthaxError); + try expectParsingError("GRAB {}", ZiQlParserError.StructNotFound); + try expectParsingError("GRAB User {qwe = 'qwe'}", ZiQlParserError.MemberNotFound); + try expectParsingError("ADD User (name='Bob')", ZiQlParserError.MemberMissing); + try expectParsingError("GRAB User {name='Bob'", ZiQlParserError.SynthaxError); + try expectParsingError("GRAB User {age = 50 name='Bob'}", ZiQlParserError.SynthaxError); + try expectParsingError("GRAB User {age <14 AND (age>55}", ZiQlParserError.SynthaxError); + try expectParsingError("GRAB User {name < 'Hello'}", ZiQlParserError.ConditionError); +} + +test "Clear" { + try testParsing("DELETE User {}"); +} test "ADD" { try testParsing("ADD User (name = 'Bob', email='bob@email.com', age=55, scores=[ 1 ], best_friend=none, friends=none, bday=2000/01/01, a_time=12:04, last_order=2000/01/01-12:45)"); @@ -1061,7 +1073,6 @@ test "ADD" { try testParsing("ADD User (name = 'Bob', email='bob@email.com', age=-55, scores=[ 33 ], best_friend=none, friends=none, bday=2000/01/04, a_time=12:04:54.8741, last_order=2000/01/01-12:45)"); try testParsing("ADD User (name = 'Boba', email='boba@email.com', age=20, scores=[ ], best_friend=none, friends=none, bday=2000/06/06, a_time=04:04:54.8741, last_order=2000/01/01-12:45)"); - // This need to take the first User named Bob as it is a unique link try testParsing("ADD User (name = 'Bob', email='bob@email.com', age=-55, scores=[ 1 ], best_friend=none, friends=none, bday=2000/01/01, a_time=12:04:54.8741, last_order=2000/01/01-12:45)"); try testParsing("ADD User (name = 'Bou', email='bob@email.com', age=66, scores=[ 1 ], best_friend={name = 'Boba'}, friends={name = 'Bob'}, bday=2000/01/01, a_time=02:04:54.8741, last_order=2000/01/01-12:45)"); try testParsing("ADD User (name = 'Bobibou', email='bob@email.com', age=66, scores=[ 1 ], best_friend={name = 'Boba'}, friends=[1]{name = 'Bob'}, bday=2000/01/01, a_time=02:04:54.8741, last_order=2000/01/01-12:45)"); @@ -1077,7 +1088,7 @@ test "GRAB filter with string" { test "GRAB with additional data" { try testParsing("GRAB User [1] {age < 18}"); try testParsing("GRAB User [id, name] {age < 18}"); - try testParsing("GRAB User [100; name] {age < 18}"); + try testParsing("GRAB User [100; name, age] {age < 18}"); } test "UPDATE" { @@ -1106,7 +1117,6 @@ test "Specific query" { try testParsing("GRAB User [1]"); } -// FIXME: This make the following query return only 1 thing, to check test "UPDATE relationship" { try testParsing("UPDATE User [1] {name='Bob'} TO (best_friend = {name='Boba'} )"); try testParsing("GRAB User {}"); @@ -1122,7 +1132,16 @@ test "GRAB Relationship Filter" { test "GRAB Relationship AdditionalData" { try testParsing("GRAB User [name, friends] {}"); try testParsing("GRAB User [name, best_friend] {}"); +} + +test "GRAB Relationship AdditionalData Filtered" { try testParsing("GRAB User [2; name, best_friend] {best_friend != none}"); + try testParsing("GRAB User [2; name, best_friend] {name = 'Bob'}"); +} + +test "GRAB Relationship Sub AdditionalData" { + try testParsing("GRAB User [name, friends [name]] {}"); + try testParsing("GRAB User [name, best_friend [name, friends [age]]] {}"); } test "GRAB Relationship dot" { @@ -1133,17 +1152,6 @@ test "DELETE" { try testParsing("DELETE User {}"); } -test "Synthax error" { - try expectParsingError("ADD User (name = 'Bob', email='bob@email.com', age=-55, scores=[ 1 ], best_friend=7db1f06d-a5a7-4917-8cc6-4d490191c9c1, bday=2000/01/01, a_time=12:04:54.8741, last_order=2000/01/01-12:45)", ZiQlParserError.SynthaxError); - try expectParsingError("GRAB {}", ZiQlParserError.StructNotFound); - try expectParsingError("GRAB User {qwe = 'qwe'}", ZiQlParserError.MemberNotFound); - try expectParsingError("ADD User (name='Bob')", ZiQlParserError.MemberMissing); - try expectParsingError("GRAB User {name='Bob'", ZiQlParserError.SynthaxError); - try expectParsingError("GRAB User {age = 50 name='Bob'}", ZiQlParserError.SynthaxError); - try expectParsingError("GRAB User {age <14 AND (age>55}", ZiQlParserError.SynthaxError); - try expectParsingError("GRAB User {name < 'Hello'}", ZiQlParserError.ConditionError); -} - const DBEngine = @import("main.zig").DBEngine; fn testParsing(source: [:0]const u8) !void {