feat: flesh out main

This commit is contained in:
dualfroz committed 2026-09-07 14:51:29 +02:00
1 parent f398f09cb8
commit c164c23f4e
1 file changed
+173
+173
View File
@@ -1868,6 +1868,179 @@ fn runTable(allocator: std.mem.Allocator, stdout: anytype, stderr: anytype, db:
try printRow(stdout, pair.left); try printRow(stdout, pair.left);
if (pair.right) |r| { if (pair.right) |r| {
try stdout.writeAll("R "); try stdout.writeAll("R ");
try printRow(stdout, r);
} else {
try stdout.writeAll("R (nil)\n");
}
}
},
.right => {
const joined = cobalt.join.rightOuterJoin(allocator, &left, jargs[1], null, &right, jargs[3], null) catch |err| return fail(stderr, @errorName(err));
const paged = cobalt.join.applyPage(cobalt.join.RightOuterJoinedRow, allocator, joined, page) catch |err| return fail(stderr, @errorName(err));
defer cobalt.join.freeRightOuterJoined(allocator, paged);
for (paged) |pair| {
if (pair.left) |l| {
try stdout.writeAll("L ");
try printRow(stdout, l);
} else {
try stdout.writeAll("L (nil)\n");
}
try stdout.writeAll("R ");
try printRow(stdout, pair.right);
}
},
.full => {
const joined = cobalt.join.fullOuterJoin(allocator, &left, jargs[1], null, &right, jargs[3], null) catch |err| return fail(stderr, @errorName(err));
const paged = cobalt.join.applyPage(cobalt.join.FullOuterJoinedRow, allocator, joined, page) catch |err| return fail(stderr, @errorName(err));
defer cobalt.join.freeFullOuterJoined(allocator, paged);
for (paged) |pair| {
if (pair.left) |l| {
try stdout.writeAll("L ");
try printRow(stdout, l);
} else {
try stdout.writeAll("L (nil)\n");
}
if (pair.right) |r| {
try stdout.writeAll("R ");
try printRow(stdout, r);
} else {
try stdout.writeAll("R (nil)\n");
}
}
},
}
} else if (std.mem.eql(u8, sub, "aggregate")) {
if (args.len < 3) return fail(stderr, "table aggregate needs <table> <groupField|*> <spec>[,<spec>...] [expr...] [having <expr>] [order <field[:desc]>[,field...]] [--limit N] [--offset N]");
var table = try cobalt.Table.open(db, allocator, args[0]);
defer table.deinit();
const groupField = args[1];
const specs = parseAggSpecs(allocator, args[2]) catch return fail(stderr, "invalid aggregate spec (expected count | count:field | sum:field | avg:field | min:field | max:field | distinct:field, comma-separated)");
defer allocator.free(specs);
const flags = extractPageFlags(allocator, args[3..]) catch return fail(stderr, "invalid --limit/--offset value");
defer allocator.free(flags.rest);
const split = splitAggFilterArgs(flags.rest);
const exprText = try std.mem.join(allocator, " ", split.whereArgs);
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;
var havingParsed: ?cobalt.expr.Parsed = null;
defer if (havingParsed) |*p| p.deinit();
var havingPred: ?cobalt.Predicate = null;
if (split.havingArgs) |havingArgs| {
const havingText = try std.mem.join(allocator, " ", havingArgs);
defer allocator.free(havingText);
if (havingText.len == 0) return fail(stderr, "having requires an expression");
havingParsed = cobalt.expr.parse(allocator, havingText) catch |err| return fail(stderr, @errorName(err));
havingPred = havingParsed.?.predicate;
}
var orderBy: []cobalt.query.OrderBy = &.{};
defer allocator.free(orderBy);
if (split.orderArgs) |orderArgs| {
if (orderArgs.len != 1) return fail(stderr, "order requires exactly one comma-separated field list");
orderBy = parseOrderByList(allocator, orderArgs[0]) catch return fail(stderr, "invalid order field list");
}
const rows = try table.select(pred);
defer cobalt.query.freeRows(allocator, rows);
if (std.mem.eql(u8, groupField, "*")) {
const values = cobalt.aggregate.runGlobal(allocator, rows, specs) catch |err| return fail(stderr, @errorName(err));
defer cobalt.aggregate.freeGlobalResult(allocator, values);
const matches = if (havingPred) |hp|
cobalt.aggregate.globalMatchesHaving(allocator, specs, values, hp) catch |err| return fail(stderr, @errorName(err))
else
true;
if (matches) {
for (specs, values, 0..) |spec, v, i| {
if (i > 0) try stdout.writeAll("\t");
try printAggLabel(stdout, spec);
try stdout.writeAll("=");
try printValue(stdout, v);
}
try stdout.writeAll("\n");
}
} else {
const unpaged = cobalt.aggregate.run(allocator, rows, groupField, specs) catch |err| return fail(stderr, @errorName(err));
const filtered = if (havingPred) |hp|
cobalt.aggregate.filterHaving(allocator, unpaged, groupField, specs, hp) catch |err| return fail(stderr, @errorName(err))
else
unpaged;
if (orderBy.len > 0) {
cobalt.aggregate.sortGroupsBy(allocator, filtered, groupField, specs, orderBy) catch |err| return fail(stderr, @errorName(err));
}
const groups = cobalt.aggregate.applyPageGroups(allocator, filtered, .{ .offset = flags.page.offset, .limit = flags.page.limit }) catch |err| return fail(stderr, @errorName(err));
defer {
for (groups) |*g| g.deinit(allocator);
allocator.free(groups);
}
for (groups) |g| {
try stdout.writeAll("group=");
try printValue(stdout, g.group);
for (specs, g.values) |spec, v| {
try stdout.writeAll("\t");
try printAggLabel(stdout, spec);
try stdout.writeAll("=");
try printValue(stdout, v);
}
try stdout.writeAll("\n");
}
}
} else if (std.mem.eql(u8, sub, "window")) {
if (args.len < 2) return fail(stderr, "table window needs <table> <kind[:field]>[,<kind[:field]>...] [expr...] [partition <field>[,field...]] [order <field[:desc]>[,field...]] [--limit N] [--offset N] [--range]");
var table = try cobalt.Table.open(db, allocator, args[0]);
defer table.deinit();
const specs = parseWindowSpecs(allocator, args[1]) catch return fail(stderr, "invalid window spec (expected row_number | rank | dense_rank | sum:field | avg:field | count | count:field | count_distinct:field | min:field | max:field | first_value:field | last_value:field | nth_value:field:n[:default] | lag:field[:offset[:default]] | lead:field[:offset[:default]], comma-separated)");
defer allocator.free(specs);
const flags = extractPageFlags(allocator, args[2..]) catch return fail(stderr, "invalid --limit/--offset value");
defer allocator.free(flags.rest);
const rangeFlag = try extractRangeFlag(allocator, flags.rest);
defer allocator.free(rangeFlag.rest);
const frame: cobalt.window.FrameMode = if (rangeFlag.range) .range else .rows;
const split = splitWindowArgs(rangeFlag.rest);
const exprText = try std.mem.join(allocator, " ", split.whereArgs);
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;
var partitionBy: [][]const u8 = &.{};
defer allocator.free(partitionBy);
if (split.partitionArgs) |partitionArgs| {
if (partitionArgs.len != 1) return fail(stderr, "partition requires exactly one comma-separated field list");
partitionBy = parsePartitionList(allocator, partitionArgs[0]) catch return fail(stderr, "invalid partition field list");
}
var orderBy: []cobalt.query.OrderBy = &.{};
defer allocator.free(orderBy);
if (split.orderArgs) |orderArgs| {
if (orderArgs.len != 1) return fail(stderr, "order requires exactly one comma-separated field list");
orderBy = parseOrderByList(allocator, orderArgs[0]) catch return fail(stderr, "invalid order field list");
}
const rows = try table.select(pred);
defer cobalt.query.freeRows(allocator, rows);
const results = cobalt.window.computeMany(allocator, rows, partitionBy, orderBy, specs, frame) catch |err| return fail(stderr, @errorName(err));
defer cobalt.window.freeManyResults(allocator, results);
} }
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 {