feat: extend main with JoinMode
This commit is contained in:
1 parent
431669c07a
commit
f398f09cb8
1 file changed
+173
+173
@@ -1695,6 +1695,179 @@ fn runTable(allocator: std.mem.Allocator, stdout: anytype, stderr: anytype, db:
|
|||||||
}
|
}
|
||||||
const pred: ?cobalt.Predicate = if (parsed) |p| p.predicate else null;
|
const pred: ?cobalt.Predicate = if (parsed) |p| p.predicate else null;
|
||||||
|
|
||||||
|
var plan = try table.explain(allocator, pred);
|
||||||
|
defer plan.deinit(allocator);
|
||||||
|
try printPlan(stdout, plan);
|
||||||
|
} else if (std.mem.eql(u8, sub, "update")) {
|
||||||
|
if (args.len < 1) return fail(stderr, "table update needs <table> --set field=value[,field=value...] [expr...]");
|
||||||
|
var table = try cobalt.Table.open(db, allocator, args[0]);
|
||||||
|
defer table.deinit();
|
||||||
|
|
||||||
|
const setf = extractSetFlag(allocator, args[1..]) catch |err| return fail(stderr, if (err == error.MissingSetFlag)
|
||||||
|
"table update requires --set field=value[,field=value...]"
|
||||||
|
else
|
||||||
|
"invalid --set value");
|
||||||
|
defer allocator.free(setf.sets);
|
||||||
|
defer allocator.free(setf.rest);
|
||||||
|
|
||||||
|
const exprText = try std.mem.join(allocator, " ", setf.rest);
|
||||||
|
defer allocator.free(exprText);
|
||||||
|
|
||||||
|
var parsed: ?cobalt.expr.Parsed = null;
|
||||||
|
defer if (parsed) |*p| p.deinit();
|
||||||
|
if (exprText.len > 0) {
|
||||||
|
parsed = cobalt.expr.parse(allocator, exprText) catch |err| return fail(stderr, @errorName(err));
|
||||||
|
}
|
||||||
|
const pred: ?cobalt.Predicate = if (parsed) |p| p.predicate else null;
|
||||||
|
|
||||||
|
const updated = table.updateWhere(pred, setf.sets) catch |err| return fail(stderr, @errorName(err));
|
||||||
|
try stdout.print("{d}\n", .{updated});
|
||||||
|
} else if (std.mem.eql(u8, sub, "delete")) {
|
||||||
|
if (args.len < 1) return fail(stderr, "table delete needs <table> [expr...]");
|
||||||
|
var table = try cobalt.Table.open(db, allocator, args[0]);
|
||||||
|
defer table.deinit();
|
||||||
|
|
||||||
|
const exprText = try std.mem.join(allocator, " ", args[1..]);
|
||||||
|
defer allocator.free(exprText);
|
||||||
|
|
||||||
|
var parsed: ?cobalt.expr.Parsed = null;
|
||||||
|
defer if (parsed) |*p| p.deinit();
|
||||||
|
if (exprText.len > 0) {
|
||||||
|
parsed = cobalt.expr.parse(allocator, exprText) catch |err| return fail(stderr, @errorName(err));
|
||||||
|
}
|
||||||
|
const pred: ?cobalt.Predicate = if (parsed) |p| p.predicate else null;
|
||||||
|
|
||||||
|
const deleted = table.deleteWhere(pred) catch |err| return fail(stderr, @errorName(err));
|
||||||
|
try stdout.print("{d}\n", .{deleted});
|
||||||
|
} else if (std.mem.eql(u8, sub, "index")) {
|
||||||
|
if (args.len < 3) return fail(stderr, "table index needs create|drop <table> <field...>");
|
||||||
|
const action = args[0];
|
||||||
|
var positional = std.ArrayList([]const u8).init(allocator);
|
||||||
|
defer positional.deinit();
|
||||||
|
var unique = false;
|
||||||
|
for (args[1..]) |a| {
|
||||||
|
if (std.mem.eql(u8, a, "--unique")) {
|
||||||
|
unique = true;
|
||||||
|
} else {
|
||||||
|
try positional.append(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (positional.items.len < 2) return fail(stderr, "table index needs create|drop <table> <field...>");
|
||||||
|
var table = try cobalt.Table.open(db, allocator, positional.items[0]);
|
||||||
|
defer table.deinit();
|
||||||
|
const fields = positional.items[1..];
|
||||||
|
if (std.mem.eql(u8, action, "create")) {
|
||||||
|
if (unique) {
|
||||||
|
if (fields.len != 1) return fail(stderr, "--unique only supports a single field, not a composite index");
|
||||||
|
table.createUniqueIndex(fields[0]) catch |err| return fail(stderr, @errorName(err));
|
||||||
|
} else if (fields.len == 1) {
|
||||||
|
table.createIndex(fields[0]) catch |err| return fail(stderr, @errorName(err));
|
||||||
|
} else {
|
||||||
|
const name = try std.mem.join(allocator, "+", fields);
|
||||||
|
defer allocator.free(name);
|
||||||
|
table.createCompositeIndex(name, fields) catch |err| return fail(stderr, @errorName(err));
|
||||||
|
}
|
||||||
|
try stdout.writeAll("OK\n");
|
||||||
|
} else if (std.mem.eql(u8, action, "drop")) {
|
||||||
|
if (fields.len == 1) {
|
||||||
|
try table.dropIndex(fields[0]);
|
||||||
|
} else {
|
||||||
|
const name = try std.mem.join(allocator, "+", fields);
|
||||||
|
defer allocator.free(name);
|
||||||
|
try table.dropCompositeIndex(name);
|
||||||
|
}
|
||||||
|
try stdout.writeAll("OK\n");
|
||||||
|
} else {
|
||||||
|
return fail(stderr, "unknown table index action (expected create/drop)");
|
||||||
|
}
|
||||||
|
} else if (std.mem.eql(u8, sub, "fk")) {
|
||||||
|
if (args.len < 2) return fail(stderr, "table fk needs create|drop <table> ...");
|
||||||
|
const action = args[0];
|
||||||
|
var table = try cobalt.Table.open(db, allocator, args[1]);
|
||||||
|
defer table.deinit();
|
||||||
|
if (std.mem.eql(u8, action, "create")) {
|
||||||
|
if (args.len < 5) return fail(stderr, "table fk create needs <table> <field> <refTable> <refField> [restrict|cascade]");
|
||||||
|
const onDelete: cobalt.index.OnDelete = if (args.len < 6 or std.mem.eql(u8, args[5], "restrict"))
|
||||||
|
.restrict
|
||||||
|
else if (std.mem.eql(u8, args[5], "cascade"))
|
||||||
|
.cascade
|
||||||
|
else
|
||||||
|
return fail(stderr, "table fk create: last argument must be restrict or cascade");
|
||||||
|
table.createForeignKey(args[2], args[3], args[4], onDelete) catch |err| return fail(stderr, @errorName(err));
|
||||||
|
try stdout.writeAll("OK\n");
|
||||||
|
} else if (std.mem.eql(u8, action, "drop")) {
|
||||||
|
if (args.len < 3) return fail(stderr, "table fk drop needs <table> <field>");
|
||||||
|
try table.dropForeignKey(args[2]);
|
||||||
|
try stdout.writeAll("OK\n");
|
||||||
|
} else {
|
||||||
|
return fail(stderr, "unknown table fk action (expected create/drop)");
|
||||||
|
}
|
||||||
|
} else if (std.mem.eql(u8, sub, "check")) {
|
||||||
|
if (args.len < 2) return fail(stderr, "table check needs create|drop <table> ...");
|
||||||
|
const action = args[0];
|
||||||
|
var table = try cobalt.Table.open(db, allocator, args[1]);
|
||||||
|
defer table.deinit();
|
||||||
|
if (std.mem.eql(u8, action, "create")) {
|
||||||
|
if (args.len < 4) return fail(stderr, "table check create needs <table> <name> <expr...>");
|
||||||
|
const source = try std.mem.join(allocator, " ", args[3..]);
|
||||||
|
defer allocator.free(source);
|
||||||
|
table.createCheckConstraint(args[2], source) catch |err| return fail(stderr, @errorName(err));
|
||||||
|
try stdout.writeAll("OK\n");
|
||||||
|
} else if (std.mem.eql(u8, action, "drop")) {
|
||||||
|
if (args.len < 3) return fail(stderr, "table check drop needs <table> <name>");
|
||||||
|
try table.dropCheckConstraint(args[2]);
|
||||||
|
try stdout.writeAll("OK\n");
|
||||||
|
} else {
|
||||||
|
return fail(stderr, "unknown table check action (expected create/drop)");
|
||||||
|
}
|
||||||
|
} else if (std.mem.eql(u8, sub, "join")) {
|
||||||
|
const JoinMode = enum { inner, left, right, full };
|
||||||
|
var mode: JoinMode = .inner;
|
||||||
|
var positional = std.ArrayList([]const u8).init(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 = extractPageFlags(allocator, positional.items) catch return fail(stderr, "invalid --limit/--offset value");
|
||||||
|
defer allocator.free(pf.rest);
|
||||||
|
if (pf.rest.len < 4) return fail(stderr, "table join needs [--left|--right|--full] <leftTable> <leftField> <rightTable> <rightField> [--limit N] [--offset N]");
|
||||||
|
const jargs = pf.rest;
|
||||||
|
const page = cobalt.query.Page{ .offset = pf.page.offset, .limit = pf.page.limit };
|
||||||
|
|
||||||
|
var left = try cobalt.Table.open(db, allocator, jargs[0]);
|
||||||
|
defer left.deinit();
|
||||||
|
var right = try cobalt.Table.open(db, allocator, jargs[2]);
|
||||||
|
defer right.deinit();
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
.inner => {
|
||||||
|
const joined = cobalt.join.innerJoin(allocator, &left, jargs[1], null, &right, jargs[3], null) catch |err| return fail(stderr, @errorName(err));
|
||||||
|
const paged = cobalt.join.applyPage(cobalt.join.JoinedRow, allocator, joined, page) catch |err| return fail(stderr, @errorName(err));
|
||||||
|
defer cobalt.join.freeJoined(allocator, paged);
|
||||||
|
for (paged) |pair| {
|
||||||
|
try stdout.writeAll("L ");
|
||||||
|
try printRow(stdout, pair.left);
|
||||||
|
try stdout.writeAll("R ");
|
||||||
|
try printRow(stdout, pair.right);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.left => {
|
||||||
|
const joined = cobalt.join.leftOuterJoin(allocator, &left, jargs[1], null, &right, jargs[3], null) catch |err| return fail(stderr, @errorName(err));
|
||||||
|
const paged = cobalt.join.applyPage(cobalt.join.OuterJoinedRow, allocator, joined, page) catch |err| return fail(stderr, @errorName(err));
|
||||||
|
defer cobalt.join.freeOuterJoined(allocator, paged);
|
||||||
|
for (paged) |pair| {
|
||||||
|
try stdout.writeAll("L ");
|
||||||
|
try printRow(stdout, pair.left);
|
||||||
|
if (pair.right) |r| {
|
||||||
|
try stdout.writeAll("R ");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn runVerify(allocator: std.mem.Allocator, stdout: anytype, dir: []const u8, count: usize) !void {
|
fn runVerify(allocator: std.mem.Allocator, stdout: anytype, dir: []const u8, count: usize) !void {
|
||||||
|
|||||||
Reference in new issue
Block a user