diff --git a/src/repl.zig b/src/repl.zig index f50c014..999eef1 100644 --- a/src/repl.zig +++ b/src/repl.zig @@ -322,3 +322,146 @@ pub const Repl = struct { } } + /// `table select [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
[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
--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
[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] + /// [--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 ` + /// then `R `); `--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(); +