feat: add fourGroupRows and firstLettersOf to aggregate
This commit is contained in:
1 parent
b8abb45b61
commit
289055c4e5
1 file changed
+161
@@ -810,3 +810,164 @@ test "count_distinct on zero observed values reports zero, not none" {
|
||||
try std.testing.expectEqual(@as(i64, 0), values[0].int);
|
||||
}
|
||||
|
||||
test "count_distinct propagates OutOfMemory without leaking already-tracked distinct values" {
|
||||
const allocator = std.testing.allocator;
|
||||
var rows = std.ArrayList(Row).init(allocator);
|
||||
defer {
|
||||
for (rows.items) |*r| r.deinit();
|
||||
rows.deinit();
|
||||
}
|
||||
const tags = [_][]const u8{ "red", "blue", "green" };
|
||||
for (tags) |t| {
|
||||
try rows.append(try makeRow(allocator, &.{
|
||||
.{ .name = "g", .value = .{ .text = "x" } },
|
||||
.{ .name = "tag", .value = .{ .text = t } },
|
||||
}));
|
||||
}
|
||||
|
||||
var failing = std.testing.FailingAllocator.init(allocator, .{ .fail_index = 6 });
|
||||
const specs = [_]AggSpec{.{ .field = "tag", .kind = .count_distinct }};
|
||||
var gb = GroupBy.init(failing.allocator(), "g", &specs);
|
||||
defer gb.deinit();
|
||||
|
||||
var sawError = false;
|
||||
for (rows.items) |*r| {
|
||||
gb.observe(r) catch {
|
||||
sawError = true;
|
||||
break;
|
||||
};
|
||||
}
|
||||
try std.testing.expect(sawError);
|
||||
}
|
||||
|
||||
test "rows missing the group field are excluded from every group" {
|
||||
const allocator = std.testing.allocator;
|
||||
var rows = std.ArrayList(Row).init(allocator);
|
||||
defer {
|
||||
for (rows.items) |*r| r.deinit();
|
||||
rows.deinit();
|
||||
}
|
||||
try rows.append(try makeRow(allocator, &.{.{ .name = "team", .value = .{ .text = "a" } }}));
|
||||
try rows.append(try makeRow(allocator, &.{})); // no "team" field at all
|
||||
|
||||
const specs = [_]AggSpec{.{ .field = "", .kind = .count }};
|
||||
const groups = try run(allocator, rows.items, "team", &specs);
|
||||
defer freeGroupRows(allocator, groups);
|
||||
|
||||
try std.testing.expectEqual(@as(usize, 1), groups.len);
|
||||
try std.testing.expectEqual(@as(i64, 1), groups[0].values[0].int);
|
||||
}
|
||||
|
||||
test "group order is first-seen, not sorted" {
|
||||
const allocator = std.testing.allocator;
|
||||
var rows = std.ArrayList(Row).init(allocator);
|
||||
defer {
|
||||
for (rows.items) |*r| r.deinit();
|
||||
rows.deinit();
|
||||
}
|
||||
const order = [_][]const u8{ "z", "a", "m" };
|
||||
for (order) |g| {
|
||||
try rows.append(try makeRow(allocator, &.{.{ .name = "g", .value = .{ .text = g } }}));
|
||||
}
|
||||
|
||||
const specs = [_]AggSpec{.{ .field = "", .kind = .count }};
|
||||
const groups = try run(allocator, rows.items, "g", &specs);
|
||||
defer freeGroupRows(allocator, groups);
|
||||
|
||||
try std.testing.expectEqual(@as(usize, 3), groups.len);
|
||||
try std.testing.expectEqualStrings("z", groups[0].group.text);
|
||||
try std.testing.expectEqualStrings("a", groups[1].group.text);
|
||||
try std.testing.expectEqualStrings("m", groups[2].group.text);
|
||||
}
|
||||
|
||||
test "runGlobal aggregates every row into a single result with no grouping" {
|
||||
const allocator = std.testing.allocator;
|
||||
var rows = std.ArrayList(Row).init(allocator);
|
||||
defer {
|
||||
for (rows.items) |*r| r.deinit();
|
||||
rows.deinit();
|
||||
}
|
||||
var i: i64 = 1;
|
||||
while (i <= 4) : (i += 1) {
|
||||
try rows.append(try makeRow(allocator, &.{.{ .name = "x", .value = .{ .int = i } }}));
|
||||
}
|
||||
|
||||
const specs = [_]AggSpec{
|
||||
.{ .field = "", .kind = .count },
|
||||
.{ .field = "x", .kind = .sum },
|
||||
.{ .field = "x", .kind = .max },
|
||||
};
|
||||
const values = try runGlobal(allocator, rows.items, &specs);
|
||||
defer freeGlobalResult(allocator, values);
|
||||
|
||||
try std.testing.expectEqual(@as(i64, 4), values[0].int);
|
||||
try std.testing.expectEqual(@as(i64, 10), values[1].int);
|
||||
try std.testing.expectEqual(@as(i64, 4), values[2].int);
|
||||
}
|
||||
|
||||
test "runGlobal on zero rows still reports identity results, not an error" {
|
||||
const allocator = std.testing.allocator;
|
||||
const rows: []const Row = &.{};
|
||||
const specs = [_]AggSpec{
|
||||
.{ .field = "", .kind = .count },
|
||||
.{ .field = "x", .kind = .sum },
|
||||
.{ .field = "x", .kind = .max },
|
||||
};
|
||||
const values = try runGlobal(allocator, rows, &specs);
|
||||
defer freeGlobalResult(allocator, values);
|
||||
|
||||
try std.testing.expectEqual(@as(i64, 0), values[0].int);
|
||||
try std.testing.expectEqual(@as(i64, 0), values[1].int);
|
||||
try std.testing.expectEqual(rowmod.ValueTag.none, @as(rowmod.ValueTag, values[2]));
|
||||
}
|
||||
|
||||
test "GroupBy propagates OutOfMemory without leaking any accumulated group" {
|
||||
const allocator = std.testing.allocator;
|
||||
var rows = std.ArrayList(Row).init(allocator);
|
||||
defer {
|
||||
for (rows.items) |*r| r.deinit();
|
||||
rows.deinit();
|
||||
}
|
||||
const groupNames = [_][]const u8{ "a", "b", "c" };
|
||||
for (groupNames) |g| {
|
||||
try rows.append(try makeRow(allocator, &.{
|
||||
.{ .name = "g", .value = .{ .text = g } },
|
||||
.{ .name = "x", .value = .{ .int = 1 } },
|
||||
}));
|
||||
}
|
||||
|
||||
var failing = std.testing.FailingAllocator.init(allocator, .{ .fail_index = 6 });
|
||||
const specs = [_]AggSpec{.{ .field = "x", .kind = .sum }};
|
||||
var gb = GroupBy.init(failing.allocator(), "g", &specs);
|
||||
defer gb.deinit();
|
||||
|
||||
var sawError = false;
|
||||
for (rows.items) |*r| {
|
||||
gb.observe(r) catch {
|
||||
sawError = true;
|
||||
break;
|
||||
};
|
||||
}
|
||||
try std.testing.expect(sawError);
|
||||
}
|
||||
|
||||
fn fourGroupRows(allocator: Allocator) ![]GroupRow {
|
||||
var rows = std.ArrayList(Row).init(allocator);
|
||||
defer {
|
||||
for (rows.items) |*r| r.deinit();
|
||||
rows.deinit();
|
||||
}
|
||||
const groupNames = [_][]const u8{ "a", "b", "c", "d" };
|
||||
for (groupNames) |g| {
|
||||
try rows.append(try makeRow(allocator, &.{.{ .name = "g", .value = .{ .text = g } }}));
|
||||
}
|
||||
const specs = [_]AggSpec{.{ .field = "", .kind = .count }};
|
||||
return run(allocator, rows.items, "g", &specs);
|
||||
}
|
||||
|
||||
fn firstLettersOf(rows: []const GroupRow) [4]u8 {
|
||||
var out: [4]u8 = undefined;
|
||||
for (rows, 0..) |r, i| out[i] = r.group.text[0];
|
||||
return out;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user