Added the db metrics, use and init command
Now the DataEngine is deinit and init again everytime the user swap database using db use. If a db is not selected, it will display an error
This commit is contained in:
parent
ac4186529d
commit
e45d8579a9
178
src/cli.zig
178
src/cli.zig
@ -6,9 +6,13 @@ const cliToken = @import("tokenizers/cli.zig").Token;
|
||||
const ziqlTokenizer = @import("tokenizers/ziql.zig").Tokenizer;
|
||||
const ziqlToken = @import("tokenizers/ziql.zig").Token;
|
||||
const ziqlParser = @import("ziqlParser.zig").Parser;
|
||||
const utils = @import("utils.zig");
|
||||
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
|
||||
// TODO: Use some global var like that
|
||||
const version = "0.0.9";
|
||||
|
||||
fn send(comptime format: []const u8, args: anytype) void {
|
||||
stdout.print(format, args) catch |err| {
|
||||
std.log.err("Can't send: {any}", .{err});
|
||||
@ -23,6 +27,9 @@ const State = enum {
|
||||
expect_query,
|
||||
expect_schema_command,
|
||||
expect_path_to_schema,
|
||||
expect_db_command,
|
||||
expect_path_to_new_db,
|
||||
expect_path_to_db,
|
||||
quit,
|
||||
end,
|
||||
};
|
||||
@ -37,11 +44,19 @@ pub fn main() !void {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Use the path of an environment variable if one found, otherwise wait for the user to use the schema init
|
||||
checkAndCreateDirectories();
|
||||
var file_engine = FileEngine.init(allocator, null);
|
||||
const path_env_variable = utils.getEnvVariables(allocator, "ZIPPONDB_PATH");
|
||||
var file_engine: FileEngine = undefined;
|
||||
defer file_engine.deinit();
|
||||
|
||||
if (path_env_variable) |path| {
|
||||
std.debug.print("Environment variable found: {s}\n", .{path});
|
||||
file_engine = FileEngine.init(allocator, path_env_variable.?);
|
||||
defer allocator.free(path_env_variable.?);
|
||||
} else {
|
||||
file_engine = FileEngine.init(allocator, "");
|
||||
std.debug.print("No ZIPONDB_PATH envirionment variable found, please use the command:\n db use path/to/db \nor\n db new /path/to/dir\n", .{});
|
||||
}
|
||||
|
||||
const line_buf = try allocator.alloc(u8, 1024 * 50);
|
||||
defer allocator.free(line_buf);
|
||||
|
||||
@ -53,8 +68,6 @@ pub fn main() !void {
|
||||
const line = try std.io.getStdIn().reader().readUntilDelimiterOrEof(line_buf, '\n');
|
||||
|
||||
if (line) |line_str| {
|
||||
const time_initial = std.time.microTimestamp();
|
||||
|
||||
const null_term_line_str = try allocator.dupeZ(u8, line_str[0..line_str.len]);
|
||||
defer allocator.free(null_term_line_str);
|
||||
|
||||
@ -65,17 +78,34 @@ pub fn main() !void {
|
||||
while ((state != .end) and (state != .quit)) : (token = cliToker.next()) {
|
||||
switch (state) {
|
||||
.expect_main_command => switch (token.tag) {
|
||||
.keyword_run => state = .expect_query,
|
||||
.keyword_schema => state = .expect_schema_command,
|
||||
.keyword_run => {
|
||||
if (!file_engine.usable) {
|
||||
send("Error: No database selected. Please use db new or db use.", .{});
|
||||
state = .end;
|
||||
continue;
|
||||
}
|
||||
state = .expect_query;
|
||||
},
|
||||
.keyword_db => state = .expect_db_command,
|
||||
.keyword_schema => {
|
||||
if (!file_engine.usable) {
|
||||
send("Error: No database selected. Please use db new or db use.", .{});
|
||||
state = .end;
|
||||
continue;
|
||||
}
|
||||
state = .expect_schema_command;
|
||||
},
|
||||
.keyword_help => {
|
||||
send("{s}", .{
|
||||
\\Welcome to ZipponDB!
|
||||
\\Welcome to ZipponDB v0.1!
|
||||
\\
|
||||
\\run To run a query. Args => query: str, the query to execute.
|
||||
\\schema Build a new engine and print current schema.
|
||||
\\quit To stop the process without saving
|
||||
\\dump Create a new folder with all data as copy. Args => foldername: str, the name of the folder.
|
||||
\\bump Replace current data with a previous dump. Args => foldername: str, the name of the folder.
|
||||
\\Available commands:
|
||||
\\run To run a query.
|
||||
\\db Create or chose a database.
|
||||
\\schema Initialize the database schema.
|
||||
\\quit Stop the CLI with memory safety.
|
||||
\\
|
||||
\\ For more informations: https://github.com/MrBounty/ZipponDB
|
||||
\\
|
||||
});
|
||||
state = .end;
|
||||
@ -83,7 +113,73 @@ pub fn main() !void {
|
||||
.keyword_quit => state = .quit,
|
||||
.eof => state = .end,
|
||||
else => {
|
||||
send("Command need to start with a keyword, including: run, schema, help and quit\n", .{});
|
||||
send("Command need to start with a keyword, including: run, db, schema, help and quit", .{});
|
||||
state = .end;
|
||||
},
|
||||
},
|
||||
|
||||
.expect_db_command => switch (token.tag) {
|
||||
.keyword_new => state = .expect_path_to_new_db,
|
||||
.keyword_use => state = .expect_path_to_db,
|
||||
.keyword_metrics => {
|
||||
if (!file_engine.usable) {
|
||||
send("Error: No database selected. Please use db new or db use.", .{});
|
||||
state = .end;
|
||||
continue;
|
||||
}
|
||||
|
||||
var buffer = std.ArrayList(u8).init(allocator);
|
||||
defer buffer.deinit();
|
||||
|
||||
try file_engine.writeDbMetrics(&buffer);
|
||||
send("{s}", .{buffer.items});
|
||||
state = .end;
|
||||
},
|
||||
.keyword_help => {
|
||||
send("{s}", .{
|
||||
\\Available commands:
|
||||
\\new Create a new database using a path to a sub folder.
|
||||
\\use Select another ZipponDB folder to use as database.
|
||||
\\metrics Print some metrics of the current database.
|
||||
\\
|
||||
\\ For more informations: https://github.com/MrBounty/ZipponDB
|
||||
\\
|
||||
});
|
||||
state = .end;
|
||||
},
|
||||
else => {
|
||||
send("Error: db commands available: new, metrics, swap & help", .{});
|
||||
state = .end;
|
||||
},
|
||||
},
|
||||
|
||||
.expect_path_to_db => switch (token.tag) {
|
||||
.identifier => {
|
||||
file_engine.deinit();
|
||||
file_engine = FileEngine.init(allocator, cliToker.getTokenSlice(token));
|
||||
send("Successfully started using the database!", .{});
|
||||
state = .end;
|
||||
},
|
||||
else => {
|
||||
send("Error Expect a path to a ZipponDB folder.", .{});
|
||||
state = .end;
|
||||
},
|
||||
},
|
||||
|
||||
.expect_path_to_new_db => switch (token.tag) {
|
||||
.identifier => {
|
||||
checkAndCreateDirectories(cliToker.getTokenSlice(token), allocator) catch |err| {
|
||||
send("Error: Coulnt create database directories: {any}", .{err});
|
||||
state = .end;
|
||||
continue;
|
||||
};
|
||||
file_engine.deinit();
|
||||
file_engine = FileEngine.init(allocator, cliToker.getTokenSlice(token));
|
||||
send("Successfully initialized the database!", .{});
|
||||
state = .end;
|
||||
},
|
||||
else => {
|
||||
send("Error Expect a path to a folder.", .{});
|
||||
state = .end;
|
||||
},
|
||||
},
|
||||
@ -96,17 +192,19 @@ pub fn main() !void {
|
||||
state = .end;
|
||||
},
|
||||
.keyword_help => {
|
||||
send("The run command will take a ZiQL query between \" and run it. eg: run \"GRAB User\"\n", .{});
|
||||
send("The run command take a ZiQL query between \" and run it. eg: run \"GRAB User\"", .{});
|
||||
state = .end;
|
||||
},
|
||||
else => {
|
||||
send("After command run, need a string of a query, eg: \"GRAB User\"\n", .{});
|
||||
send("Error: After command run, need a query, eg: \"GRAB User\"", .{});
|
||||
state = .end;
|
||||
},
|
||||
},
|
||||
|
||||
.expect_schema_command => switch (token.tag) {
|
||||
.keyword_describe => {
|
||||
if (std.mem.eql(u8, file_engine.path_to_ZipponDB_dir, "")) send("Error: No database selected. Please use db bew or db use.", .{});
|
||||
|
||||
if (file_engine.null_terminated_schema_buff.len == 0) {
|
||||
send("Need to init the schema first. Please use the schema init path/to/schema command to start.", .{});
|
||||
} else {
|
||||
@ -117,16 +215,17 @@ pub fn main() !void {
|
||||
.keyword_init => state = .expect_path_to_schema,
|
||||
.keyword_help => {
|
||||
send("{s}", .{
|
||||
\\Here are all available options to use with the schema command:
|
||||
\\Available commands:
|
||||
\\describe Print the schema use by the currently selected database.
|
||||
\\init Take the path to a schema file and initialize the database.
|
||||
\\
|
||||
\\describe Print the schema use by the current engine.
|
||||
\\build Build a new engine using a schema file. Args => filename: str, path of schema file to use. Default 'schema.zipponschema'.
|
||||
\\ For more informations: https://github.com/MrBounty/ZipponDB
|
||||
\\
|
||||
});
|
||||
state = .end;
|
||||
},
|
||||
else => {
|
||||
std.debug.print("schema available options: describe, build & help\n", .{});
|
||||
send("Error: schema commands available: describe, init & help", .{});
|
||||
state = .end;
|
||||
},
|
||||
},
|
||||
@ -143,11 +242,11 @@ pub fn main() !void {
|
||||
state = .end;
|
||||
},
|
||||
};
|
||||
send("Successfully initialized the database!", .{});
|
||||
send("Successfully initialized the database schema!", .{});
|
||||
state = .end;
|
||||
},
|
||||
else => {
|
||||
send("Expected a path to a schema file after the schema init command.", .{});
|
||||
send("Error: Expect path to schema file.", .{});
|
||||
state = .end;
|
||||
},
|
||||
},
|
||||
@ -155,11 +254,6 @@ pub fn main() !void {
|
||||
.quit, .end => break,
|
||||
}
|
||||
}
|
||||
|
||||
const time_final = std.time.microTimestamp();
|
||||
const duration = time_final - time_initial;
|
||||
std.debug.print("Time: {d:.2}ms\n", .{@as(f64, @floatFromInt(duration)) / 1000.0});
|
||||
|
||||
if (state == .quit) break;
|
||||
}
|
||||
}
|
||||
@ -187,26 +281,38 @@ pub fn runQuery(null_term_query_str: [:0]const u8, file_engine: *FileEngine) voi
|
||||
}
|
||||
|
||||
// TODO: Put that in the FileEngine
|
||||
fn checkAndCreateDirectories() void {
|
||||
fn checkAndCreateDirectories(sub_path: []const u8, allocator: Allocator) !void {
|
||||
var path_buff = try std.fmt.allocPrint(allocator, "{s}/ZipponDB", .{sub_path});
|
||||
defer allocator.free(path_buff);
|
||||
|
||||
const cwd = std.fs.cwd();
|
||||
|
||||
cwd.makeDir("ZipponDB") catch |err| switch (err) {
|
||||
cwd.makeDir(path_buff) catch |err| switch (err) {
|
||||
error.PathAlreadyExists => {},
|
||||
else => @panic("Error other than path already exists when trying to create the ZipponDB directory.\n"),
|
||||
else => return err,
|
||||
};
|
||||
|
||||
cwd.makeDir("ZipponDB/DATA") catch |err| switch (err) {
|
||||
allocator.free(path_buff);
|
||||
path_buff = try std.fmt.allocPrint(allocator, "{s}/ZipponDB/DATA", .{sub_path});
|
||||
|
||||
cwd.makeDir(path_buff) catch |err| switch (err) {
|
||||
error.PathAlreadyExists => {},
|
||||
else => @panic("Error other than path already exists when trying to create the DATA directory.\n"),
|
||||
else => return err,
|
||||
};
|
||||
|
||||
cwd.makeDir("ZipponDB/BACKUP") catch |err| switch (err) {
|
||||
allocator.free(path_buff);
|
||||
path_buff = try std.fmt.allocPrint(allocator, "{s}/ZipponDB/BACKUP", .{sub_path});
|
||||
|
||||
cwd.makeDir(path_buff) catch |err| switch (err) {
|
||||
error.PathAlreadyExists => {},
|
||||
else => @panic("Error other than path already exists when trying to create the ENGINE directory.\n"),
|
||||
else => return err,
|
||||
};
|
||||
|
||||
cwd.makeDir("ZipponDB/LOG") catch |err| switch (err) {
|
||||
allocator.free(path_buff);
|
||||
path_buff = try std.fmt.allocPrint(allocator, "{s}/ZipponDB/LOG", .{sub_path});
|
||||
|
||||
cwd.makeDir(path_buff) catch |err| switch (err) {
|
||||
error.PathAlreadyExists => {},
|
||||
else => @panic("Error other than path already exists when trying to create the ENGINE directory.\n"),
|
||||
else => return err,
|
||||
};
|
||||
}
|
||||
|
@ -16,13 +16,14 @@ const AdditionalData = @import("ziqlParser.zig").Parser.AdditionalData;
|
||||
/// Or even get stats, whatever. If it touch files, it's here
|
||||
pub const FileEngine = struct {
|
||||
allocator: Allocator,
|
||||
path_to_ZipponDB_dir: []const u8, // The path to the DATA folder
|
||||
usable: bool,
|
||||
path_to_ZipponDB_dir: []const u8, // Make that into a list
|
||||
max_file_size: usize = 5e+4, // 50kb TODO: Change
|
||||
null_terminated_schema_buff: [:0]u8,
|
||||
struct_array: std.ArrayList(SchemaStruct),
|
||||
|
||||
pub fn init(allocator: Allocator, path: ?[]const u8) FileEngine {
|
||||
const path_to_ZipponDB_dir = path orelse "ZipponDB";
|
||||
pub fn init(allocator: Allocator, path: []const u8) FileEngine {
|
||||
const path_to_ZipponDB_dir = path;
|
||||
|
||||
var schema_buf = allocator.alloc(u8, 1024 * 50) catch @panic("Cant allocate the schema buffer");
|
||||
defer allocator.free(schema_buf);
|
||||
@ -41,6 +42,7 @@ pub const FileEngine = struct {
|
||||
.path_to_ZipponDB_dir = path_to_ZipponDB_dir,
|
||||
.null_terminated_schema_buff = null_terminated_schema_buff,
|
||||
.struct_array = struct_array,
|
||||
.usable = !std.mem.eql(u8, path, ""),
|
||||
};
|
||||
}
|
||||
|
||||
@ -78,6 +80,10 @@ pub const FileEngine = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub fn setPath(self: *FileEngine, path: []const u8) void {
|
||||
self.path_to_ZipponDB_dir = path;
|
||||
}
|
||||
|
||||
// TODO: A function that take a list of UUID and write into the buffer the message tot send
|
||||
// Like the other, write it line by line then if the UUID is found, you write the data
|
||||
// The output need to be in the JSON format, so change '' into ""
|
||||
@ -756,12 +762,62 @@ pub const FileEngine = struct {
|
||||
|
||||
var iter = member_dir.iterate();
|
||||
while (try iter.next()) |entry| {
|
||||
if (entry.kind != std.fs.Dir.Entry.Kind.file) continue;
|
||||
if (entry.kind != .file) continue;
|
||||
count += 1;
|
||||
}
|
||||
return count - 1;
|
||||
}
|
||||
|
||||
pub fn writeDbMetrics(self: *FileEngine, buffer: *std.ArrayList(u8)) !void {
|
||||
const path = try std.fmt.allocPrint(self.allocator, "{s}", .{self.path_to_ZipponDB_dir});
|
||||
defer self.allocator.free(path);
|
||||
|
||||
const main_dir = try std.fs.cwd().openDir(path, .{ .iterate = true });
|
||||
|
||||
const writer = buffer.writer();
|
||||
try writer.print("Database path: {s}\n", .{path});
|
||||
const main_size = try self.getDirTotalSize(main_dir);
|
||||
try writer.print("Total size: {d:.2}Mb\n", .{@as(f64, @floatFromInt(main_size)) / 1e-6});
|
||||
|
||||
const log_dir = try main_dir.openDir("LOG", .{ .iterate = true });
|
||||
const log_size = try self.getDirTotalSize(log_dir);
|
||||
try writer.print("LOG: {d:.2}Mb\n", .{@as(f64, @floatFromInt(log_size)) / 1e-6});
|
||||
|
||||
const backup_dir = try main_dir.openDir("BACKUP", .{ .iterate = true });
|
||||
const backup_size = try self.getDirTotalSize(backup_dir);
|
||||
try writer.print("BACKUP: {d:.2}Mb\n", .{@as(f64, @floatFromInt(backup_size)) / 1e-6});
|
||||
|
||||
const data_dir = try main_dir.openDir("DATA", .{ .iterate = true });
|
||||
const data_size = try self.getDirTotalSize(data_dir);
|
||||
try writer.print("DATA: {d:.2}Mb\n", .{@as(f64, @floatFromInt(data_size)) / 1e-6});
|
||||
|
||||
var iter = data_dir.iterate();
|
||||
while (try iter.next()) |entry| {
|
||||
if (entry.kind != .directory) continue;
|
||||
const sub_dir = try data_dir.openDir(entry.name, .{ .iterate = true });
|
||||
const size = try self.getDirTotalSize(sub_dir);
|
||||
try writer.print(" {s}: {d:.}Mb\n", .{ entry.name, @as(f64, @floatFromInt(size)) / 1e-6 });
|
||||
}
|
||||
}
|
||||
|
||||
// Maybe make it so it use itself to search if it find a directory
|
||||
fn getDirTotalSize(self: FileEngine, dir: std.fs.Dir) !u64 {
|
||||
var total: u64 = 0;
|
||||
var stat: std.fs.File.Stat = undefined;
|
||||
var iter = dir.iterate();
|
||||
while (try iter.next()) |entry| {
|
||||
if (entry.kind == .directory) {
|
||||
const sub_dir = try dir.openDir(entry.name, .{ .iterate = true });
|
||||
total += try self.getDirTotalSize(sub_dir);
|
||||
}
|
||||
|
||||
if (entry.kind != .file) continue;
|
||||
stat = try dir.statFile(entry.name);
|
||||
total += stat.size;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
const FileError = error{
|
||||
SchemaFileNotFound,
|
||||
SchemaNotConform,
|
||||
@ -934,7 +990,7 @@ pub const FileEngine = struct {
|
||||
test "Get list of UUID using condition" {
|
||||
const allocator = std.testing.allocator;
|
||||
|
||||
var file_engine = FileEngine.init(allocator, null);
|
||||
var file_engine = FileEngine.init(allocator, "ZipponDB");
|
||||
defer file_engine.deinit();
|
||||
|
||||
var uuid_array = std.ArrayList(UUID).init(allocator);
|
||||
|
@ -17,6 +17,10 @@ pub const Token = struct {
|
||||
.{ "init", .keyword_init },
|
||||
.{ "schema", .keyword_schema },
|
||||
.{ "quit", .keyword_quit },
|
||||
.{ "db", .keyword_db },
|
||||
.{ "new", .keyword_new },
|
||||
.{ "metrics", .keyword_metrics },
|
||||
.{ "use", .keyword_use },
|
||||
});
|
||||
|
||||
pub fn getKeyword(bytes: []const u8) ?Tag {
|
||||
@ -33,6 +37,10 @@ pub const Token = struct {
|
||||
keyword_schema,
|
||||
keyword_init,
|
||||
keyword_quit,
|
||||
keyword_db,
|
||||
keyword_new,
|
||||
keyword_metrics,
|
||||
keyword_use,
|
||||
|
||||
string_literal,
|
||||
identifier,
|
||||
|
@ -1,13 +1,13 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn getEnvVariables(allocator: std.mem.Allocator, variable: []const u8) ?[]const u8 {
|
||||
var env_map = try std.process.getEnvMap(allocator);
|
||||
var env_map = std.process.getEnvMap(allocator) catch return null;
|
||||
defer env_map.deinit();
|
||||
|
||||
var iter = env_map.iterator();
|
||||
|
||||
while (iter.next()) |entry| {
|
||||
if (std.mem.eql(u8, entry.key_ptr.*, variable)) return allocator.dupe(u8, entry.key_ptr.*);
|
||||
if (std.mem.eql(u8, entry.key_ptr.*, variable)) return allocator.dupe(u8, entry.value_ptr.*) catch return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -873,12 +873,13 @@ test "GRAB filter with int" {
|
||||
test "Specific query" {
|
||||
try testParsing("GRAB User");
|
||||
try testParsing("GRAB User {}");
|
||||
try testParsing("GRAB User [1]");
|
||||
}
|
||||
|
||||
fn testParsing(source: [:0]const u8) !void {
|
||||
const allocator = std.testing.allocator;
|
||||
|
||||
var file_engine = FileEngine.init(allocator, null);
|
||||
var file_engine = FileEngine.init(allocator, "ZipponDB");
|
||||
defer file_engine.deinit();
|
||||
|
||||
var tokenizer = Tokenizer.init(source);
|
||||
|
Loading…
x
Reference in New Issue
Block a user