feat: add cmdBegin and cmdCommit to repl
This commit is contained in:
1 parent
9425a74f56
commit
73b247d29d
1 file changed
+163
+163
@@ -159,3 +159,166 @@ pub const Repl = struct {
|
||||
return ReplError.UnknownCommand;
|
||||
}
|
||||
|
||||
fn cmdBegin(self: *Repl, args: []const []const u8, writer: anytype) !void {
|
||||
if (args.len != 0) return ReplError.WrongArgCount;
|
||||
if (self.txn != null) return ReplError.TransactionAlreadyActive;
|
||||
self.txn = Transaction.begin(self.db);
|
||||
try writer.writeAll("OK\n");
|
||||
}
|
||||
|
||||
fn cmdCommit(self: *Repl, args: []const []const u8, writer: anytype) !void {
|
||||
if (args.len != 0) return ReplError.WrongArgCount;
|
||||
if (self.txn == null) return ReplError.NoActiveTransaction;
|
||||
try self.txn.?.commit();
|
||||
self.discardTxnState();
|
||||
try writer.writeAll("OK\n");
|
||||
}
|
||||
|
||||
fn cmdRollback(self: *Repl, args: []const []const u8, writer: anytype) !void {
|
||||
if (args.len != 0) return ReplError.WrongArgCount;
|
||||
if (self.txn == null) return ReplError.NoActiveTransaction;
|
||||
self.discardTxnState();
|
||||
try writer.writeAll("OK\n");
|
||||
}
|
||||
|
||||
fn cmdPut(self: *Repl, args: []const []const u8, writer: anytype) !void {
|
||||
if (args.len != 2) return ReplError.WrongArgCount;
|
||||
if (self.txn) |*t| {
|
||||
try t.put(args[0], args[1]);
|
||||
} else {
|
||||
try self.db.put(args[0], args[1]);
|
||||
}
|
||||
try writer.writeAll("OK\n");
|
||||
}
|
||||
|
||||
fn cmdGet(self: *Repl, args: []const []const u8, writer: anytype) !void {
|
||||
if (args.len != 1) return ReplError.WrongArgCount;
|
||||
const value = if (self.txn) |*t|
|
||||
try t.get(self.allocator, args[0])
|
||||
else
|
||||
try self.db.get(self.allocator, args[0]);
|
||||
defer if (value) |v| self.allocator.free(v);
|
||||
if (value) |v| {
|
||||
try writer.print("{s}\n", .{v});
|
||||
} else {
|
||||
try writer.writeAll("(nil)\n");
|
||||
}
|
||||
}
|
||||
|
||||
fn cmdDel(self: *Repl, args: []const []const u8, writer: anytype) !void {
|
||||
if (args.len != 1) return ReplError.WrongArgCount;
|
||||
if (self.txn) |*t| {
|
||||
try t.delete(args[0]);
|
||||
} else {
|
||||
try self.db.delete(args[0]);
|
||||
}
|
||||
try writer.writeAll("OK\n");
|
||||
}
|
||||
|
||||
fn cmdScan(self: *Repl, args: []const []const u8, writer: anytype) !void {
|
||||
if (args.len > 1) return ReplError.WrongArgCount;
|
||||
const prefix = if (args.len == 1) args[0] else "";
|
||||
var iter = try self.db.scanPrefix(self.allocator, prefix);
|
||||
defer iter.deinit();
|
||||
while (try iter.next()) |entry| {
|
||||
try writer.print("{s}\t{s}\n", .{ entry.key, entry.value });
|
||||
}
|
||||
}
|
||||
|
||||
/// `expire`/`persist`/`ttl`/`expire-sweep` mirror the one-shot CLI's
|
||||
/// same-named commands (main.zig) over `ttl.zig`'s flat-key TTL layer.
|
||||
/// Like `scan` above, these always act on the live, already-committed
|
||||
/// `Db` regardless of any open transaction: a TTL marker lives under its
|
||||
/// own keyspace tag, set/cleared directly against `self.db`, never
|
||||
/// staged through `Transaction` - there is no OCC-safe way to make
|
||||
/// "expire this key in N seconds" conditional on a commit that hasn't
|
||||
/// happened yet, so (deliberately, like `coll`'s ops) this bypasses the
|
||||
/// active transaction entirely rather than pretending to participate in
|
||||
/// it.
|
||||
fn cmdExpire(self: *Repl, args: []const []const u8, writer: anytype) !void {
|
||||
if (args.len != 2) return ReplError.WrongArgCount;
|
||||
const seconds = std.fmt.parseInt(u64, args[1], 10) catch return ReplError.InvalidArgument;
|
||||
const existing = try self.db.get(self.allocator, args[0]) orelse return ReplError.NoSuchKey;
|
||||
self.allocator.free(existing);
|
||||
try ttlmod.setExpireSeconds(self.db, self.allocator, args[0], std.time.nanoTimestamp(), seconds);
|
||||
try writer.writeAll("OK\n");
|
||||
}
|
||||
|
||||
fn cmdPersist(self: *Repl, args: []const []const u8, writer: anytype) !void {
|
||||
if (args.len != 1) return ReplError.WrongArgCount;
|
||||
try ttlmod.clearExpire(self.db, self.allocator, args[0]);
|
||||
try writer.writeAll("OK\n");
|
||||
}
|
||||
|
||||
fn cmdTtl(self: *Repl, args: []const []const u8, writer: anytype) !void {
|
||||
if (args.len != 1) return ReplError.WrongArgCount;
|
||||
switch (try ttlmod.remaining(self.db, self.allocator, args[0], std.time.nanoTimestamp())) {
|
||||
.noKey => try writer.writeAll("-2\n"),
|
||||
.noExpiry => try writer.writeAll("-1\n"),
|
||||
.seconds => |s| try writer.print("{d}\n", .{s}),
|
||||
}
|
||||
}
|
||||
|
||||
fn cmdExpireSweep(self: *Repl, args: []const []const u8, writer: anytype) !void {
|
||||
if (args.len != 0) return ReplError.WrongArgCount;
|
||||
const reaped = try ttlmod.sweepExpired(self.db, self.allocator, std.time.nanoTimestamp());
|
||||
try writer.print("reaped: {d}\n", .{reaped});
|
||||
}
|
||||
|
||||
fn cmdStats(self: *Repl, args: []const []const u8, writer: anytype) !void {
|
||||
if (args.len != 0) return ReplError.WrongArgCount;
|
||||
try self.db.stats().print(writer);
|
||||
}
|
||||
|
||||
fn cmdFlush(self: *Repl, args: []const []const u8, writer: anytype) !void {
|
||||
if (args.len != 0) return ReplError.WrongArgCount;
|
||||
try self.db.flush();
|
||||
try writer.writeAll("OK\n");
|
||||
}
|
||||
|
||||
fn cmdCompact(self: *Repl, args: []const []const u8, writer: anytype) !void {
|
||||
if (args.len != 0) return ReplError.WrongArgCount;
|
||||
try self.db.compact();
|
||||
try writer.writeAll("OK\n");
|
||||
}
|
||||
|
||||
fn cmdTable(self: *Repl, args: []const []const u8, writer: anytype) !void {
|
||||
if (args.len < 1) return ReplError.WrongArgCount;
|
||||
const op = args[0];
|
||||
const rest = args[1..];
|
||||
|
||||
if (std.mem.eql(u8, op, "select")) return self.cmdTableSelect(rest, writer);
|
||||
if (std.mem.eql(u8, op, "explain")) return self.cmdTableExplain(rest, writer);
|
||||
if (std.mem.eql(u8, op, "update")) return self.cmdTableUpdate(rest, writer);
|
||||
if (std.mem.eql(u8, op, "delete")) return self.cmdTableDelete(rest, writer);
|
||||
if (std.mem.eql(u8, op, "join")) return self.cmdTableJoin(rest, writer);
|
||||
if (std.mem.eql(u8, op, "aggregate")) return self.cmdTableAggregate(rest, writer);
|
||||
if (std.mem.eql(u8, op, "window")) return self.cmdTableWindow(rest, writer);
|
||||
|
||||
if (rest.len < 2) return ReplError.WrongArgCount;
|
||||
const tableName = rest[0];
|
||||
const pk = try Value.parseCli(rest[1]);
|
||||
|
||||
if (std.mem.eql(u8, op, "put")) {
|
||||
var fields = std.ArrayList(Field).init(self.allocator);
|
||||
defer fields.deinit();
|
||||
for (rest[2..]) |raw| try fields.append(try parseField(raw));
|
||||
try self.tablePut(tableName, pk, fields.items);
|
||||
try writer.writeAll("OK\n");
|
||||
} else if (std.mem.eql(u8, op, "get")) {
|
||||
if (rest.len != 2) return ReplError.WrongArgCount;
|
||||
var row = try self.tableGet(tableName, pk) orelse {
|
||||
try writer.writeAll("(nil)\n");
|
||||
return;
|
||||
};
|
||||
defer row.deinit();
|
||||
try printRow(writer, row);
|
||||
} else if (std.mem.eql(u8, op, "del")) {
|
||||
if (rest.len != 2) return ReplError.WrongArgCount;
|
||||
try self.tableDel(tableName, pk);
|
||||
try writer.writeAll("OK\n");
|
||||
} else {
|
||||
return ReplError.UnknownCommand;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user