From 955aff0d09cfe3c86dfe574eb6500b987774796c Mon Sep 17 00:00:00 2001 From: MrBounty Date: Sun, 12 Jan 2025 00:37:57 +0100 Subject: [PATCH] Moved global error to lib and fuse to a unique one --- benchmark.zig | 7 +-- build.zig | 8 +++- lib/config.zig | 2 +- {src => lib}/errors.zig | 42 +++++------------- src/dataStructure/additionalData.zig | 2 +- src/dataStructure/filter.zig | 2 +- src/dataStructure/relationMap.zig | 2 +- src/entityWriter.zig | 2 +- src/fileEngine.zig | 2 +- src/main.zig | 4 +- src/schemaEngine.zig | 3 +- src/schemaParser.zig | 64 ++++++++++++++-------------- src/thread/engine.zig | 33 +++++++------- src/utils.zig | 2 +- src/ziqlParser.zig | 2 +- test.zig | 4 +- 16 files changed, 83 insertions(+), 98 deletions(-) rename {src => lib}/errors.zig (53%) diff --git a/benchmark.zig b/benchmark.zig index f0f3055..06e9730 100644 --- a/benchmark.zig +++ b/benchmark.zig @@ -4,7 +4,7 @@ const DBEngine = @import("src/main.zig").DBEngine; const ziqlTokenizer = @import("src/tokenizers/ziql.zig").Tokenizer; const ziqlToken = @import("src/tokenizers/ziql.zig").Token; const ziqlParser = @import("src/ziqlParser.zig").Parser; -const ZipponError = @import("src/stuffs/errors.zig").ZipponError; +const ZipponError = @import("error").ZipponError; const names = [_][]const u8{ "Alice", "Bob", "Charlie", "Dave", "Eve" }; const emails = [_][]const u8{ "alice@email.com", "bob@email.com", "charlie@email.com", "dave@email.com", "eve@email.com" }; @@ -31,7 +31,7 @@ pub fn myLog( } pub fn main() !void { - const to_test = [_]usize{500}; + const to_test = [_]usize{500_000}; { var line_buffer: [1024 * 1024]u8 = undefined; var db_engine = DBEngine.init("benchmark", "schema/example"); @@ -75,7 +75,7 @@ pub fn main() !void { for (users_count - 1) |_| { try writer.print( - "('{s}', '{s}', {d}, [ {d} ], {{}}, none, {s}, {s}, {s})", + "('{s}', '{s}', {d}, [ {d} ], none, none, {s}, {s}, {s})", .{ names[rng.uintAtMost(usize, names.len - 1)], emails[rng.uintAtMost(usize, emails.len - 1)], @@ -128,6 +128,7 @@ pub fn main() !void { "GRAB User {bday > 2000/01/01}", "GRAB User {age > 30 AND name = 'Charlie' AND bday > 2000/01/01}", "GRAB User {best_friend IN {name = 'Charlie'}}", + "DELETE User {}", }; // Run benchmarks diff --git a/build.zig b/build.zig index 2370fd9..5e746e1 100644 --- a/build.zig +++ b/build.zig @@ -21,6 +21,7 @@ pub fn build(b: *std.Build) void { exe.root_module.addImport("dtype", b.createModule(.{ .root_source_file = b.path("lib/types/out.zig") })); exe.root_module.addImport("config", b.createModule(.{ .root_source_file = b.path("lib/config.zig") })); exe.root_module.addImport("ZipponData", b.createModule(.{ .root_source_file = b.path("lib/zid.zig") })); + exe.root_module.addImport("error", b.createModule(.{ .root_source_file = b.path("lib/errors.zig") })); // Run // ----------------------------------------------- @@ -30,7 +31,7 @@ pub fn build(b: *std.Build) void { // Test // ----------------------------------------------- const tests1 = b.addTest(.{ - .root_source_file = b.path("src/stuffs/UUIDFileIndex.zig"), + .root_source_file = b.path("src/dataStructure/UUIDFileIndex.zig"), .target = target, .optimize = optimize, .name = "CLI tokenizer", @@ -78,10 +79,11 @@ pub fn build(b: *std.Build) void { tests5.root_module.addImport("dtype", b.createModule(.{ .root_source_file = b.path("lib/types/out.zig") })); tests5.root_module.addImport("config", b.createModule(.{ .root_source_file = b.path("lib/config.zig") })); tests5.root_module.addImport("ZipponData", b.createModule(.{ .root_source_file = b.path("lib/zid.zig") })); + tests5.root_module.addImport("error", b.createModule(.{ .root_source_file = b.path("lib/errors.zig") })); const run_tests5 = b.addRunArtifact(tests5); const tests6 = b.addTest(.{ - .root_source_file = b.path("src/stuffs/filter.zig"), + .root_source_file = b.path("src/dataStructure/filter.zig"), .target = target, .optimize = optimize, .name = "Filter tree", @@ -90,6 +92,7 @@ pub fn build(b: *std.Build) void { tests6.root_module.addImport("dtype", b.createModule(.{ .root_source_file = b.path("lib/types/out.zig") })); tests6.root_module.addImport("config", b.createModule(.{ .root_source_file = b.path("lib/config.zig") })); tests6.root_module.addImport("ZipponData", b.createModule(.{ .root_source_file = b.path("lib/zid.zig") })); + tests6.root_module.addImport("error", b.createModule(.{ .root_source_file = b.path("lib/errors.zig") })); const run_tests6 = b.addRunArtifact(tests6); const test_step = b.step("test", "Run unit tests"); @@ -111,6 +114,7 @@ pub fn build(b: *std.Build) void { benchmark.root_module.addImport("dtype", b.createModule(.{ .root_source_file = b.path("lib/types/out.zig") })); benchmark.root_module.addImport("config", b.createModule(.{ .root_source_file = b.path("lib/config.zig") })); benchmark.root_module.addImport("ZipponData", b.createModule(.{ .root_source_file = b.path("lib/zid.zig") })); + benchmark.root_module.addImport("error", b.createModule(.{ .root_source_file = b.path("lib/errors.zig") })); b.installArtifact(benchmark); const run_benchmark = b.addRunArtifact(benchmark); diff --git a/lib/config.zig b/lib/config.zig index 894fd66..0446c18 100644 --- a/lib/config.zig +++ b/lib/config.zig @@ -4,7 +4,7 @@ pub const CPU_CORE = 16; // Debug pub const PRINT_STATE = false; -pub const DONT_SEND = false; +pub const DONT_SEND = true; pub const DONT_SEND_ERROR = false; pub const RESET_LOG_AT_RESTART = false; // If true, will reset the log file at the start of the db, otherwise just keep adding to it diff --git a/src/errors.zig b/lib/errors.zig similarity index 53% rename from src/errors.zig rename to lib/errors.zig index 69e0d99..ba82637 100644 --- a/src/errors.zig +++ b/lib/errors.zig @@ -1,27 +1,4 @@ -// TODO: Only use a single and big ZipponError - -pub const ZiQlParserError = error{ - MemoryError, - SynthaxError, - MemberNotFound, - MemberMissing, - StructNotFound, - FeatureMissing, - ParsingValueError, - ConditionError, - WriteError, - AndOrError, - CantWriteEntity, -}; - -pub const SchemaParserError = error{ - SynthaxError, - FeatureMissing, - ValueParsingError, - MemoryError, -}; - -pub const FileEngineError = error{ +pub const ZipponError = error{ SchemaFileNotFound, SchemaNotConform, DATAFolderNotFound, @@ -30,22 +7,25 @@ pub const FileEngineError = error{ CantMakeFile, CantOpenDir, CantOpenFile, - MemoryError, StreamError, - ReadError, // TODO: Only use stream + ReadError, InvalidUUID, InvalidDate, InvalidFileIndex, DirIterError, - WriteError, FileStatError, DeleteFileError, RenameFileError, - StructNotFound, - MemberNotFound, ZipponDataError, AllocEncodError, + MemoryError, + SynthaxError, ThreadError, + CantWriteEntity, + WriteError, + ConditionError, + ParsingValueError, + MemberNotFound, + MemberMissing, + StructNotFound, }; - -pub const ZipponError = ZiQlParserError || FileEngineError || SchemaParserError; diff --git a/src/dataStructure/additionalData.zig b/src/dataStructure/additionalData.zig index 724a857..918c86c 100644 --- a/src/dataStructure/additionalData.zig +++ b/src/dataStructure/additionalData.zig @@ -6,7 +6,7 @@ const DataType = dtype.DataType; // TODO: Put this in a data structure directory -const ZipponError = @import("../errors.zig").ZipponError; +const ZipponError = @import("error").ZipponError; /// This is the [] part pub const AdditionalData = @This(); diff --git a/src/dataStructure/filter.zig b/src/dataStructure/filter.zig index 085d6d3..5f9fe2c 100644 --- a/src/dataStructure/filter.zig +++ b/src/dataStructure/filter.zig @@ -10,7 +10,7 @@ const std = @import("std"); const s2t = @import("dtype").s2t; -const ZipponError = @import("../errors.zig").ZipponError; +const ZipponError = @import("error").ZipponError; const DataType = @import("dtype").DataType; const DateTime = @import("dtype").DateTime; const UUID = @import("dtype").UUID; diff --git a/src/dataStructure/relationMap.zig b/src/dataStructure/relationMap.zig index 64fb6c7..c43bf80 100644 --- a/src/dataStructure/relationMap.zig +++ b/src/dataStructure/relationMap.zig @@ -21,7 +21,7 @@ const std = @import("std"); const AdditionalData = @import("additionalData.zig").AdditionalData; -const ZipponError = @import("../errors.zig").ZipponError; +const ZipponError = @import("error").ZipponError; pub const JsonString = struct { slice: []const u8 = "", diff --git a/src/entityWriter.zig b/src/entityWriter.zig index 4906b2f..bc5f72b 100644 --- a/src/entityWriter.zig +++ b/src/entityWriter.zig @@ -8,7 +8,7 @@ const DataType = dtype.DataType; const DateTime = dtype.DateTime; const UUID = dtype.UUID; -const ZipponError = @import("errors.zig").ZipponError; +const ZipponError = @import("error").ZipponError; pub fn writeEntityTable( writer: anytype, diff --git a/src/fileEngine.zig b/src/fileEngine.zig index 8b4239b..6918906 100644 --- a/src/fileEngine.zig +++ b/src/fileEngine.zig @@ -21,7 +21,7 @@ const RelationMap = @import("dataStructure/relationMap.zig"); const JsonString = @import("dataStructure/relationMap.zig").JsonString; const ConditionValue = @import("dataStructure/filter.zig").ConditionValue; -const ZipponError = @import("errors.zig").ZipponError; +const ZipponError = @import("error").ZipponError; const config = @import("config"); const BUFFER_SIZE = config.BUFFER_SIZE; diff --git a/src/main.zig b/src/main.zig index 782eb9a..8beb4ff 100644 --- a/src/main.zig +++ b/src/main.zig @@ -15,7 +15,7 @@ const ziqlTokenizer = @import("tokenizers/ziql.zig").Tokenizer; const ziqlToken = @import("tokenizers/ziql.zig").Token; const ziqlParser = @import("ziqlParser.zig"); -const ZipponError = @import("errors.zig").ZipponError; +const ZipponError = @import("error").ZipponError; const config = @import("config"); const BUFFER_SIZE = config.BUFFER_SIZE; @@ -86,7 +86,7 @@ pub const DBEngine = struct { pub fn init(potential_main_path: ?[]const u8, potential_schema_path: ?[]const u8) DBEngine { var self = DBEngine{}; - self.thread_engine = ThreadEngine.init() catch @panic("TODO"); + self.thread_engine = ThreadEngine.init(); const potential_main_path_or_environment_variable = potential_main_path orelse utils.getEnvVariable("ZIPPONDB_PATH"); if (potential_main_path_or_environment_variable) |main_path| { diff --git a/src/schemaEngine.zig b/src/schemaEngine.zig index 621b774..41098a4 100644 --- a/src/schemaEngine.zig +++ b/src/schemaEngine.zig @@ -3,7 +3,6 @@ const zid = @import("ZipponData"); const Allocator = std.mem.Allocator; const Parser = @import("schemaParser.zig"); const Tokenizer = @import("tokenizers/schema.zig").Tokenizer; -const ZipponError = @import("errors.zig").ZipponError; const dtype = @import("dtype"); const DataType = dtype.DataType; const AdditionalData = @import("dataStructure/additionalData.zig"); @@ -14,6 +13,8 @@ const UUID = dtype.UUID; const UUIDFileIndex = @import("dataStructure/UUIDFileIndex.zig"); const FileEngine = @import("fileEngine.zig"); +const ZipponError = @import("error").ZipponError; + // TODO: Create a schemaEngine directory and add this as core and the parser with it const config = @import("config"); diff --git a/src/schemaParser.zig b/src/schemaParser.zig index e9974d8..a95d9c3 100644 --- a/src/schemaParser.zig +++ b/src/schemaParser.zig @@ -10,7 +10,7 @@ const Loc = @import("tokenizers/shared/loc.zig").Loc; const send = @import("utils.zig").send; const printError = @import("utils.zig").printError; -const SchemaParserError = @import("errors.zig").SchemaParserError; +const ZipponError = @import("error").ZipponError; const State = enum { end, @@ -60,15 +60,15 @@ pub fn parse(self: *Parser, struct_array: *std.ArrayList(SchemaStruct)) !void { .identifier => { state = .expect_l_paren; name = self.toker.getTokenSlice(token); - member_list.append("id") catch return SchemaParserError.MemoryError; - type_list.append(.self) catch return SchemaParserError.MemoryError; + member_list.append("id") catch return ZipponError.MemoryError; + type_list.append(.self) catch return ZipponError.MemoryError; }, .eof => state = .end, else => { std.debug.print("{s}\n", .{self.toker.getTokenSlice(token)}); return printError( "Error parsing schema: Expected a struct name", - SchemaParserError.SynthaxError, + ZipponError.SynthaxError, self.toker.buffer, token.loc.start, token.loc.end, @@ -80,7 +80,7 @@ pub fn parse(self: *Parser, struct_array: *std.ArrayList(SchemaStruct)) !void { .l_paren => state = .expect_member_name, else => return printError( "Error parsing schema: Expected (", - SchemaParserError.SynthaxError, + ZipponError.SynthaxError, self.toker.buffer, token.loc.start, token.loc.end, @@ -95,7 +95,7 @@ pub fn parse(self: *Parser, struct_array: *std.ArrayList(SchemaStruct)) !void { .r_paren => state = .add_struct, else => return printError( "Error parsing schema: Expected member name or )", - SchemaParserError.SynthaxError, + ZipponError.SynthaxError, self.toker.buffer, token.loc.start, token.loc.end, @@ -105,10 +105,10 @@ pub fn parse(self: *Parser, struct_array: *std.ArrayList(SchemaStruct)) !void { .add_struct => { struct_array.append(try SchemaStruct.init( name, - member_list.toOwnedSlice() catch return SchemaParserError.MemoryError, - type_list.toOwnedSlice() catch return SchemaParserError.MemoryError, + member_list.toOwnedSlice() catch return ZipponError.MemoryError, + type_list.toOwnedSlice() catch return ZipponError.MemoryError, try links.clone(), - )) catch return SchemaParserError.MemoryError; + )) catch return ZipponError.MemoryError; links.deinit(); links = std.StringHashMap([]const u8).init(self.allocator); @@ -122,7 +122,7 @@ pub fn parse(self: *Parser, struct_array: *std.ArrayList(SchemaStruct)) !void { .expect_member_name => { state = .expect_two_dot; - member_list.append(self.toker.getTokenSlice(token)) catch return SchemaParserError.MemoryError; + member_list.append(self.toker.getTokenSlice(token)) catch return ZipponError.MemoryError; member_token = token; }, @@ -130,7 +130,7 @@ pub fn parse(self: *Parser, struct_array: *std.ArrayList(SchemaStruct)) !void { .two_dot => state = .expect_value_type, else => return printError( "Error parsing schema: Expected :", - SchemaParserError.SynthaxError, + ZipponError.SynthaxError, self.toker.buffer, token.loc.start, token.loc.end, @@ -140,41 +140,41 @@ pub fn parse(self: *Parser, struct_array: *std.ArrayList(SchemaStruct)) !void { .expect_value_type => switch (token.tag) { .type_int => { state = .expect_comma; - type_list.append(.int) catch return SchemaParserError.MemoryError; + type_list.append(.int) catch return ZipponError.MemoryError; }, .type_str => { state = .expect_comma; - type_list.append(.str) catch return SchemaParserError.MemoryError; + type_list.append(.str) catch return ZipponError.MemoryError; }, .type_float => { state = .expect_comma; - type_list.append(.float) catch return SchemaParserError.MemoryError; + type_list.append(.float) catch return ZipponError.MemoryError; }, .type_bool => { state = .expect_comma; - type_list.append(.bool) catch return SchemaParserError.MemoryError; + type_list.append(.bool) catch return ZipponError.MemoryError; }, .type_date => { state = .expect_comma; - type_list.append(.date) catch return SchemaParserError.MemoryError; + type_list.append(.date) catch return ZipponError.MemoryError; }, .type_time => { state = .expect_comma; - type_list.append(.time) catch return SchemaParserError.MemoryError; + type_list.append(.time) catch return ZipponError.MemoryError; }, .type_datetime => { state = .expect_comma; - type_list.append(.datetime) catch return SchemaParserError.MemoryError; + type_list.append(.datetime) catch return ZipponError.MemoryError; }, .identifier => { state = .expect_comma; - type_list.append(.link) catch return SchemaParserError.MemoryError; - links.put(self.toker.getTokenSlice(member_token), self.toker.getTokenSlice(token)) catch return SchemaParserError.MemoryError; + type_list.append(.link) catch return ZipponError.MemoryError; + links.put(self.toker.getTokenSlice(member_token), self.toker.getTokenSlice(token)) catch return ZipponError.MemoryError; }, .lr_bracket => state = .expext_array_type, else => return printError( "Error parsing schema: Expected data type", - SchemaParserError.SynthaxError, + ZipponError.SynthaxError, self.toker.buffer, token.loc.start, token.loc.end, @@ -184,40 +184,40 @@ pub fn parse(self: *Parser, struct_array: *std.ArrayList(SchemaStruct)) !void { .expext_array_type => switch (token.tag) { .type_int => { state = .expect_comma; - type_list.append(.int_array) catch return SchemaParserError.MemoryError; + type_list.append(.int_array) catch return ZipponError.MemoryError; }, .type_str => { state = .expect_comma; - type_list.append(.str_array) catch return SchemaParserError.MemoryError; + type_list.append(.str_array) catch return ZipponError.MemoryError; }, .type_float => { state = .expect_comma; - type_list.append(.float_array) catch return SchemaParserError.MemoryError; + type_list.append(.float_array) catch return ZipponError.MemoryError; }, .type_bool => { state = .expect_comma; - type_list.append(.bool_array) catch return SchemaParserError.MemoryError; + type_list.append(.bool_array) catch return ZipponError.MemoryError; }, .type_date => { state = .expect_comma; - type_list.append(.date_array) catch return SchemaParserError.MemoryError; + type_list.append(.date_array) catch return ZipponError.MemoryError; }, .type_time => { state = .expect_comma; - type_list.append(.time_array) catch return SchemaParserError.MemoryError; + type_list.append(.time_array) catch return ZipponError.MemoryError; }, .type_datetime => { state = .expect_comma; - type_list.append(.datetime_array) catch return SchemaParserError.MemoryError; + type_list.append(.datetime_array) catch return ZipponError.MemoryError; }, .identifier => { state = .expect_comma; - type_list.append(.link_array) catch return SchemaParserError.MemoryError; - links.put(self.toker.getTokenSlice(member_token), self.toker.getTokenSlice(token)) catch return SchemaParserError.MemoryError; + type_list.append(.link_array) catch return ZipponError.MemoryError; + links.put(self.toker.getTokenSlice(member_token), self.toker.getTokenSlice(token)) catch return ZipponError.MemoryError; }, else => return printError( "Error parsing schema: Expected data type", - SchemaParserError.SynthaxError, + ZipponError.SynthaxError, self.toker.buffer, token.loc.start, token.loc.end, @@ -228,7 +228,7 @@ pub fn parse(self: *Parser, struct_array: *std.ArrayList(SchemaStruct)) !void { .comma => state = .expect_member_name_OR_r_paren, else => return printError( "Error parsing schema: Expected ,", - SchemaParserError.SynthaxError, + ZipponError.SynthaxError, self.toker.buffer, token.loc.start, token.loc.end, diff --git a/src/thread/engine.zig b/src/thread/engine.zig index e575c6d..33da7e4 100644 --- a/src/thread/engine.zig +++ b/src/thread/engine.zig @@ -1,38 +1,37 @@ const std = @import("std"); +const U64 = std.atomic.Value(u64); const Pool = std.Thread.Pool; -const Allocator = std.mem.Allocator; +const ZipponError = @import("error").ZipponError; const CPU_CORE = @import("config").CPU_CORE; const log = std.log.scoped(.thread); -const ZipponError = @import("../errors.zig").ZipponError; -pub const Self = @This(); +const allocator = std.heap.page_allocator; -var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); -const allocator = arena.allocator(); +var thread_arena: std.heap.ThreadSafeAllocator = undefined; +var thread_pool: Pool = undefined; + +pub const ThreadEngine = @This(); thread_arena: *std.heap.ThreadSafeAllocator, thread_pool: *Pool, -pub fn init() ZipponError!Self { - const thread_arena = allocator.create(std.heap.ThreadSafeAllocator) catch return ZipponError.MemoryError; - thread_arena.* = std.heap.ThreadSafeAllocator{ +pub fn init() ThreadEngine { + thread_arena = std.heap.ThreadSafeAllocator{ .child_allocator = allocator, }; - const thread_pool = allocator.create(Pool) catch return ZipponError.MemoryError; - thread_pool.init(Pool.Options{ + thread_pool.init(std.Thread.Pool.Options{ .allocator = thread_arena.allocator(), .n_jobs = CPU_CORE, - }) catch return ZipponError.ThreadError; + }) catch @panic("=("); - return Self{ - .thread_pool = thread_pool, - .thread_arena = thread_arena, + return ThreadEngine{ + .thread_pool = &thread_pool, + .thread_arena = &thread_arena, }; } -pub fn deinit(self: *Self) void { - self.thread_pool.deinit(); - arena.deinit(); +pub fn deinit(_: ThreadEngine) void { + thread_pool.deinit(); } diff --git a/src/utils.zig b/src/utils.zig index d416b4d..fcd2641 100644 --- a/src/utils.zig +++ b/src/utils.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const ZipponError = @import("errors.zig").ZipponError; +const ZipponError = @import("error").ZipponError; const config = @import("config"); const log = std.log.scoped(.utils); diff --git a/src/ziqlParser.zig b/src/ziqlParser.zig index 020b8e0..01e5322 100644 --- a/src/ziqlParser.zig +++ b/src/ziqlParser.zig @@ -18,7 +18,7 @@ const AdditionalDataMember = @import("dataStructure/additionalData.zig").Additio const send = @import("utils.zig").send; const printError = @import("utils.zig").printError; -const ZipponError = @import("errors.zig").ZipponError; +const ZipponError = @import("error").ZipponError; const PRINT_STATE = @import("config").PRINT_STATE; const log = std.log.scoped(.ziqlParser); diff --git a/test.zig b/test.zig index 268744b..a6bbeac 100644 --- a/test.zig +++ b/test.zig @@ -3,7 +3,7 @@ const Allocator = std.mem.Allocator; const Parser = @import("src/ziqlParser.zig").Parser; const Tokenizer = @import("src/tokenizers/ziql.zig").Tokenizer; const DBEngine = @import("src/main.zig").DBEngine; -const ZipponError = @import("src/stuffs/errors.zig").ZipponError; +const ZipponError = @import("error").ZipponError; const DB = struct { path: []const u8, @@ -150,7 +150,7 @@ test "3 struct both side" { try testParsing(db, "DELETE Post {}"); try testParsing(db, "ADD User (name = 'Bob', email='bob@email.com', age=55, friends=none, posts=none, comments=none, bday=2000/01/01)"); try testParsing(db, "ADD Post (text = 'Hello every body', at=NOW, from=none, comments=none)"); - try testParsing(db, "ADD Post (text = 'Hello every body', at=NOW, from={}, comments=none) -> new_post -> UPDATE User {} TO (posts APPEND new_post)"); + //try testParsing(db, "ADD Post (text = 'Hello every body', at=NOW, from={}, comments=none) -> new_post -> UPDATE User {} TO (posts APPEND new_post)"); // try testParsing(db, "ADD Post (text = 'Hello every body', at=NOW, from={} APPEND TO posts, comments=none)"); Maybe I can use that to be like the above query // ADD Post (text = 'Hello every body', at=NOW, from={} TO last_post, comments=none) And this for a single link // try testParsing(db, "ADD Post (text = 'Hello every body', at=NOW, from={} APPEND TO [posts, last_post], comments=none)"); Can be an array to add it to multiple list