Array and link are empty by default if not in ADD action. Also can use * to return everything execpt link

The * is to be use like that GRAB User [*, friends]
Because if I just do GRAB User [friends], I only get friends
So I would of need to specify all of them like GRAB User [name, age,
email, .., friends]
This commit is contained in:
Adrien Bouvais 2025-01-31 10:05:55 +01:00
parent 30a7a83e56
commit b757e01b64
8 changed files with 80 additions and 43 deletions

View File

@ -20,10 +20,10 @@ pub fn init(allocator: Allocator) AdditionalData {
};
}
pub fn populateWithEverythingExceptLink(self: *AdditionalData, members: [][]const u8, dtypes: []DataType) !void {
pub fn populateWithEverythingExceptLink(self: *AdditionalData, members: [][]const u8, dtypes: []DataType) ZipponError!void {
for (members, dtypes, 0..) |member, dt, i| {
if (dt == .link or dt == .link_array) continue;
try self.childrens.append(AdditionalDataMember.init(self.allocator, member, i));
self.childrens.append(AdditionalDataMember.init(self.allocator, member, i)) catch return ZipponError.MemoryError;
}
}

View File

@ -66,8 +66,8 @@ pub const ConditionValue = union(enum) {
float_array: []const f64,
bool_array: []const bool,
unix_array: []const u64,
link: *std.AutoHashMap(UUID, void),
link_array: *std.AutoHashMap(UUID, void),
link: *const std.AutoHashMap(UUID, void),
link_array: *const std.AutoHashMap(UUID, void),
pub fn init(dtype: DataType, value: []const u8) ConditionValue {
return switch (dtype) {
@ -134,11 +134,11 @@ pub const ConditionValue = union(enum) {
return ConditionValue{ .unix_array = value };
}
pub fn initLink(value: *std.AutoHashMap(UUID, void)) ConditionValue {
pub fn initLink(value: *const std.AutoHashMap(UUID, void)) ConditionValue {
return ConditionValue{ .link = value };
}
pub fn initArrayLink(value: *std.AutoHashMap(UUID, void)) ConditionValue {
pub fn initArrayLink(value: *const std.AutoHashMap(UUID, void)) ConditionValue {
return ConditionValue{ .link_array = value };
}
};

View File

@ -366,7 +366,7 @@ pub fn parseEntitiesRelationMap(
dir,
sstruct.zid_schema,
relation_map.additional_data,
try self.schema_engine.structName2DataType(struct_name),
sstruct.types,
&sync_context,
},
);

View File

@ -13,14 +13,27 @@ const JsonString = RelationMap.JsonString;
const ZipponError = @import("error").ZipponError;
var empty_map_buf: [20]u8 = undefined;
var fa = std.heap.FixedBufferAllocator.init(&empty_map_buf);
const empty_map_allocator = fa.allocator();
const empty_int: [0]i32 = .{};
const empty_float: [0]f64 = .{};
const empty_unix: [0]u64 = .{};
const empty_bool: [0]bool = .{};
const empty_str: [0][]const u8 = .{};
const zero_link = std.AutoHashMap(UUID, void).init(empty_map_allocator);
const empty_link = std.AutoHashMap(UUID, void).init(empty_map_allocator);
// I need to redo how SchemaStruct work because it is a mess
// I mean I use wayyyyyyyyyyyyyyyyyyyyyyy too much structName2SchemaStruct or stuff like that
// I mean that not good to always for loop and compare when a map would work
pub fn memberName2DataType(self: *Self, struct_name: []const u8, member_name: []const u8) ZipponError!DataType {
for (try self.structName2structMembers(struct_name), 0..) |mn, i| {
const dtypes = try self.structName2DataType(struct_name);
if (std.mem.eql(u8, mn, member_name)) return dtypes[i];
const sstruct = try self.structName2SchemaStruct(struct_name);
for (sstruct.members, 0..) |mn, i| {
if (std.mem.eql(u8, mn, member_name)) return sstruct.types[i];
}
return ZipponError.MemberNotFound;
@ -47,7 +60,7 @@ pub fn structName2structMembers(self: Self, struct_name: []const u8) ZipponError
return self.struct_array[i].members;
}
// TODO: This is the first one I want to change to use a map
// Return the SchemaStruct based on it's name
pub fn structName2SchemaStruct(self: Self, struct_name: []const u8) ZipponError!SchemaStruct {
var i: usize = 0;
@ -60,20 +73,6 @@ pub fn structName2SchemaStruct(self: Self, struct_name: []const u8) ZipponError!
return self.struct_array[i];
}
pub fn structName2DataType(self: Self, struct_name: []const u8) ZipponError![]const DataType {
var i: u16 = 0;
while (i < self.struct_array.len) : (i += 1) {
if (std.mem.eql(u8, self.struct_array[i].name, struct_name)) break;
}
if (i == self.struct_array.len and !std.mem.eql(u8, self.struct_array[i].name, struct_name)) {
return ZipponError.StructNotFound;
}
return self.struct_array[i].types;
}
/// Chech if the name of a struct is in the current schema
pub fn isStructNameExists(self: Self, struct_name: []const u8) bool {
var i: u16 = 0;
@ -98,23 +97,43 @@ pub fn linkedStructName(self: Self, struct_name: []const u8, member_name: []cons
return sstruct;
}
pub fn checkIfAllMemberInMap(
/// Use with NewData to check if all member are in the map, otherwise write the missing one to be send with the error
/// Also if array and link are missing, to make them empty instead
pub fn checkIfAllMemberInMapAndAddEmptyMissingArray(
self: Self,
struct_name: []const u8,
map: *std.StringHashMap(ValueOrArray),
error_message_buffer: *std.ArrayList(u8),
writer: std.ArrayList(u8).Writer,
) ZipponError!bool {
const all_struct_member = try self.structName2structMembers(struct_name);
const sstruct = try self.structName2SchemaStruct(struct_name);
var count: u16 = 0;
const writer = error_message_buffer.writer();
for (all_struct_member) |mn| {
for (sstruct.members, sstruct.types) |mn, dt| {
if (std.mem.eql(u8, mn, "id")) continue;
if (map.contains(mn)) count += 1 else writer.print(" {s},", .{mn}) catch return ZipponError.WriteError;
if (map.contains(mn)) count += 1 else {
if (dt.is_array() or dt == .link) {
map.put(
mn,
switch (dt) {
.int_array => ValueOrArray{ .value = try ConditionValue.initArrayInt(&empty_int) },
.float_array => ValueOrArray{ .value = try ConditionValue.initArrayFloat(&empty_float) },
.str_array => ValueOrArray{ .value = try ConditionValue.initArrayStr(&empty_str) },
.bool_array => ValueOrArray{ .value = try ConditionValue.initArrayBool(&empty_bool) },
.date_array => ValueOrArray{ .value = try ConditionValue.initArrayUnix(&empty_unix) },
.time_array => ValueOrArray{ .value = try ConditionValue.initArrayUnix(&empty_unix) },
.datetime_array => ValueOrArray{ .value = try ConditionValue.initArrayUnix(&empty_unix) },
.link_array => ValueOrArray{ .value = ConditionValue.initArrayLink(&empty_link) },
.link => ValueOrArray{ .value = ConditionValue.initLink(&zero_link) },
else => unreachable,
},
) catch return ZipponError.MemoryError;
count += 1;
}
writer.print(" {s},", .{mn}) catch return ZipponError.WriteError;
}
}
return ((count == all_struct_member.len - 1) and (count == map.count()));
return ((count == sstruct.members.len - 1) and (count == map.count()));
}
pub fn isUUIDExist(self: Self, struct_name: []const u8, uuid: UUID) bool {

View File

@ -89,11 +89,21 @@ test "GRAB filter with date" { // OK
try testParsing(db, "GRAB User {last_order > 2000/01/01-12:45}");
}
test "Specific query" { // OK
// FIXME: GRAB User [1] return nothing
test "Specific query" { // NOT OK
const db = DB{ .path = "test1", .schema = "schema/test" };
try testParsing(db, "GRAB User");
try testParsing(db, "GRAB User {}");
try testParsing(db, "GRAB User [1]");
try testParsing(db, "GRAB User [*, friends]");
}
test "Specific query ADD" { // OK
const db = DB{ .path = "test1", .schema = "schema/test" };
try testParsing(db, "ADD User (name = 'Bob1', email='bob@email.com', age=55, best_friend=none, friends=none, bday=2000/01/01, a_time=12:04, last_order=2000/01/01-12:45)");
try testParsing(db, "ADD User (name = 'Bob2', email='bob@email.com', age=55, best_friend=none, bday=2000/01/01, a_time=12:04, last_order=2000/01/01-12:45)");
try testParsing(db, "ADD User (name = 'Bob3', email='bob@email.com', age=55, bday=2000/01/01, a_time=12:04, last_order=2000/01/01-12:45)");
try testParsing(db, "GRAB User {name IN ['Bob1', 'Bob2', 'Bob3']}");
}
// Array manipulation
@ -188,14 +198,6 @@ test "GRAB Relationship dot" { // TODO: Make this a reality
try testParsing(db, "GRAB User [1] {}");
}
// Cleaning
// ===============================================================
test "DELETE" {
const db = DB{ .path = "test1", .schema = "schema/test" };
try testParsing(db, "DELETE User {}");
}
// 3 Struct Relationship
// ===============================================================

View File

@ -353,7 +353,11 @@ pub fn parse(self: *Self, parent_allocator: Allocator, buffer: [:0]const u8) Zip
const error_message_buffer_writer = error_message_buffer.writer();
error_message_buffer_writer.writeAll("Error missing: ") catch return ZipponError.WriteError;
if (!(self.schema_engine.checkIfAllMemberInMap(struct_name, &data_map, &error_message_buffer) catch {
if (!(self.schema_engine.checkIfAllMemberInMapAndAddEmptyMissingArray(
struct_name,
&data_map,
error_message_buffer.writer(),
) catch {
return ZipponError.StructNotFound;
})) {
_ = error_message_buffer.pop();

View File

@ -85,6 +85,12 @@ pub fn parseAdditionalData(
state = .expect_comma_OR_r_bracket_OR_l_bracket;
},
.star => {
const sstruct = try self.schema_engine.structName2SchemaStruct(struct_name);
try additional_data.populateWithEverythingExceptLink(sstruct.members, sstruct.types);
last_member = undefined;
state = .expect_comma_OR_r_bracket_OR_l_bracket;
},
else => return printError(
"Error: Expected a member name.",
ZipponError.SynthaxError,

View File

@ -76,6 +76,7 @@ pub const Token = struct {
uuid_literal,
identifier,
equal,
star, // *
bang, // !
pipe, // |
l_paren, // (
@ -171,6 +172,11 @@ pub const Tokenizer = struct {
'=' => {
state = .equal;
},
'*' => {
result.tag = .star;
self.index += 1;
break;
},
'!' => {
state = .bang;
},