feat: add cmdTableSelect and cmdTableExplain to repl

This commit is contained in:
dualfroz committed 2026-09-09 18:20:37 +02:00
1 parent 73b247d29d
commit 6820cfca72
1 file changed
+143
+143
View File
@@ -322,3 +322,146 @@ pub const Repl = struct {
} }
} }
/// `table select <table> [expr...] [--limit N] [--offset N] [--order-by
/// field[:desc][,field[:desc]...]]`: interactive counterpart of the
/// one-shot CLI's `table select` (main.zig's `runTable`) over
/// query.zig's `Table.selectPageOrdered`, same expr grammar (expr.zig)
/// and `--limit`/`--offset`/`--order-by` conventions.
fn cmdTableSelect(self: *Repl, args: []const []const u8, writer: anytype) !void {
if (args.len < 1) return ReplError.WrongArgCount;
var table = try Table.open(self.db, self.allocator, args[0]);
defer table.deinit();
const sel = try extractSelectFlags(self.allocator, args[1..]);
defer self.allocator.free(sel.rest);
defer self.allocator.free(sel.flags.orderBy);
const exprText = try std.mem.join(self.allocator, " ", sel.rest);
defer self.allocator.free(exprText);
var parsed: ?exprmod.Parsed = null;
defer if (parsed) |*p| p.deinit();
if (exprText.len > 0) {
parsed = try exprmod.parse(self.allocator, exprText);
}
const pred: ?querymod.Predicate = if (parsed) |p| p.predicate else null;
const rows = try table.selectPageOrdered(pred, .{ .offset = sel.flags.page.offset, .limit = sel.flags.page.limit }, sel.flags.orderBy);
defer querymod.freeRows(self.allocator, rows);
for (rows) |r| try printRow(writer, r);
}
/// `table explain <table> [expr...]`: interactive counterpart of the
/// one-shot CLI's `table explain` (main.zig's `runTable`), reporting
/// which strategy `table select` would use for the same predicate
/// without running it - `query.Table.explain` over the same expr
/// grammar `cmdTableSelect` already uses.
fn cmdTableExplain(self: *Repl, args: []const []const u8, writer: anytype) !void {
if (args.len < 1) return ReplError.WrongArgCount;
var table = try Table.open(self.db, self.allocator, args[0]);
defer table.deinit();
const exprText = try std.mem.join(self.allocator, " ", args[1..]);
defer self.allocator.free(exprText);
var parsed: ?exprmod.Parsed = null;
defer if (parsed) |*p| p.deinit();
if (exprText.len > 0) {
parsed = try exprmod.parse(self.allocator, exprText);
}
const pred: ?querymod.Predicate = if (parsed) |p| p.predicate else null;
var plan = try table.explain(self.allocator, pred);
defer plan.deinit(self.allocator);
try printPlan(writer, plan);
}
/// `table update <table> --set field=value[,field=value...] [expr...]`:
/// interactive counterpart of the one-shot CLI's `table update`
/// (main.zig's `runTable`) over `query.Table.updateWhere`, same
/// `--set`/expr grammar. Prints the number of rows updated.
fn cmdTableUpdate(self: *Repl, args: []const []const u8, writer: anytype) !void {
if (args.len < 1) return ReplError.WrongArgCount;
var table = try Table.open(self.db, self.allocator, args[0]);
defer table.deinit();
const setf = try extractSetFlag(self.allocator, args[1..]);
defer self.allocator.free(setf.sets);
defer self.allocator.free(setf.rest);
const exprText = try std.mem.join(self.allocator, " ", setf.rest);
defer self.allocator.free(exprText);
var parsed: ?exprmod.Parsed = null;
defer if (parsed) |*p| p.deinit();
if (exprText.len > 0) {
parsed = try exprmod.parse(self.allocator, exprText);
}
const pred: ?querymod.Predicate = if (parsed) |p| p.predicate else null;
const updated = try table.updateWhere(pred, setf.sets);
try writer.print("{d}\n", .{updated});
}
/// `table delete <table> [expr...]`: interactive counterpart of the
/// one-shot CLI's `table delete` (main.zig's `runTable`) over
/// `query.Table.deleteWhere`, same expr grammar as `cmdTableSelect`/
/// `cmdTableExplain` (empty expr deletes every row). Prints the number
/// of rows deleted.
fn cmdTableDelete(self: *Repl, args: []const []const u8, writer: anytype) !void {
if (args.len < 1) return ReplError.WrongArgCount;
var table = try Table.open(self.db, self.allocator, args[0]);
defer table.deinit();
const exprText = try std.mem.join(self.allocator, " ", args[1..]);
defer self.allocator.free(exprText);
var parsed: ?exprmod.Parsed = null;
defer if (parsed) |*p| p.deinit();
if (exprText.len > 0) {
parsed = try exprmod.parse(self.allocator, exprText);
}
const pred: ?querymod.Predicate = if (parsed) |p| p.predicate else null;
const deleted = try table.deleteWhere(pred);
try writer.print("{d}\n", .{deleted});
}
/// `table join [--left|--right|--full] <leftTable> <leftField>
/// <rightTable> <rightField> [--limit N] [--offset N]`: interactive
/// counterpart of the one-shot CLI's `table join` over join.zig's four
/// join functions. With no flag, prints inner-join pairs (`L <row>`
/// then `R <row>`); `--left`/`--right`/`--full` print `L (nil)`/
/// `R (nil)` for the unmatched side exactly like the CLI.
/// `--limit`/`--offset` window the already-joined result through
/// `join.applyPage`, same convention as `table select`'s paging (0 or
/// omitted = unlimited). Predicates on either side are not exposed here
/// (same deliberately minimal CLI surface as `table join` itself - the
/// richer path is the `join.zig` library API, as its own tests cover).
fn cmdTableJoin(self: *Repl, args: []const []const u8, writer: anytype) !void {
const JoinMode = enum { inner, left, right, full };
var mode: JoinMode = .inner;
var positional = std.ArrayList([]const u8).init(self.allocator);
defer positional.deinit();
for (args) |a| {
if (std.mem.eql(u8, a, "--left")) {
mode = .left;
} else if (std.mem.eql(u8, a, "--right")) {
mode = .right;
} else if (std.mem.eql(u8, a, "--full")) {
mode = .full;
} else {
try positional.append(a);
}
}
const pf = try extractPageFlags(self.allocator, positional.items);
defer self.allocator.free(pf.rest);
if (pf.rest.len != 4) return ReplError.WrongArgCount;
const jargs = pf.rest;
const page = querymod.Page{ .offset = pf.page.offset, .limit = pf.page.limit };
var left = try Table.open(self.db, self.allocator, jargs[0]);
defer left.deinit();
var right = try Table.open(self.db, self.allocator, jargs[2]);
defer right.deinit();