From 46e03bc174699a147377c4a989f0263a5c8d5e3b Mon Sep 17 00:00:00 2001 From: DualFroz Date: Thu, 10 Sep 2026 13:06:40 +0200 Subject: [PATCH] feat: grow repl --- src/repl.zig | 162 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) diff --git a/src/repl.zig b/src/repl.zig index ad2a65b..0f047e7 100644 --- a/src/repl.zig +++ b/src/repl.zig @@ -1890,3 +1890,165 @@ test "table window row_number annotates every row without collapsing, restarting ); } +test "table window with a comma-separated spec list annotates every row with all columns, sharing one partition/order" { + const allocator = std.testing.allocator; + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + var db = try openTemp(&tmp, allocator); + defer db.close(); + + var repl = Repl.init(allocator, &db); + defer repl.deinit(); + + const out = try runScript(allocator, &repl, + \\table put orders i:1 city=warsaw total=i:100 + \\table put orders i:2 city=warsaw total=i:250 + \\table put orders i:3 city=krakow total=i:40 + \\table window orders row_number,sum:total partition city order total + \\ + ); + defer allocator.free(out); + try std.testing.expectEqualStrings( + "OK\nOK\nOK\n" ++ + "__pk=1\tcity=warsaw\ttotal=100\trow_number=1\tsum_total=100\n" ++ + "__pk=2\tcity=warsaw\ttotal=250\trow_number=2\tsum_total=350\n" ++ + "__pk=3\tcity=krakow\ttotal=40\trow_number=1\tsum_total=40\n", + out, + ); +} + +test "table window sum:field with order runs a running total along the sorted sequence" { + const allocator = std.testing.allocator; + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + var db = try openTemp(&tmp, allocator); + defer db.close(); + + var repl = Repl.init(allocator, &db); + defer repl.deinit(); + + const out = try runScript(allocator, &repl, + \\table put orders i:1 city=warsaw total=i:100 + \\table put orders i:2 city=warsaw total=i:250 + \\table put orders i:3 city=krakow total=i:40 + \\table window orders sum:total order total + \\ + ); + defer allocator.free(out); + try std.testing.expectEqualStrings( + "OK\nOK\nOK\n" ++ + "__pk=3\tcity=krakow\ttotal=40\tsum_total=40\n" ++ + "__pk=1\tcity=warsaw\ttotal=100\tsum_total=140\n" ++ + "__pk=2\tcity=warsaw\ttotal=250\tsum_total=390\n", + out, + ); +} + +test "table window --range shares a tied group's combined total, without it each tied row gets its own running total" { + const allocator = std.testing.allocator; + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + var db = try openTemp(&tmp, allocator); + defer db.close(); + + var repl = Repl.init(allocator, &db); + defer repl.deinit(); + + // Two rows tied at day=1 (10, 20), then one row at day=2 (5). + const out = try runScript(allocator, &repl, + \\table put orders i:1 day=i:1 amount=i:10 + \\table put orders i:2 day=i:1 amount=i:20 + \\table put orders i:3 day=i:2 amount=i:5 + \\table window orders sum:amount order day --range + \\table window orders sum:amount order day + \\ + ); + defer allocator.free(out); + try std.testing.expectEqualStrings( + "OK\nOK\nOK\n" ++ + // --range: both day=1 rows share the tie group's combined total. + "__pk=1\tday=1\tamount=10\tsum_amount=30\n" ++ + "__pk=2\tday=1\tamount=20\tsum_amount=30\n" ++ + "__pk=3\tday=2\tamount=5\tsum_amount=35\n" ++ + // default (ROWS): each physical row keeps its own running total. + "__pk=1\tday=1\tamount=10\tsum_amount=10\n" ++ + "__pk=2\tday=1\tamount=20\tsum_amount=30\n" ++ + "__pk=3\tday=2\tamount=5\tsum_amount=35\n", + out, + ); +} + +test "table window --range is rejected together with a ranking kind, not silently ignored" { + const allocator = std.testing.allocator; + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + var db = try openTemp(&tmp, allocator); + defer db.close(); + + var repl = Repl.init(allocator, &db); + defer repl.deinit(); + + const out = try runScript(allocator, &repl, + \\table put orders i:1 total=i:100 + \\table window orders row_number order total --range + \\ + ); + defer allocator.free(out); + try std.testing.expectEqualStrings("OK\nerror: RangeNotApplicable\n", out); +} + +test "table window sum:field with a partition but no order stamps the same whole-partition total on every row" { + const allocator = std.testing.allocator; + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + var db = try openTemp(&tmp, allocator); + defer db.close(); + + var repl = Repl.init(allocator, &db); + defer repl.deinit(); + + const out = try runScript(allocator, &repl, + \\table put orders i:1 city=warsaw total=i:100 + \\table put orders i:2 city=warsaw total=i:250 + \\table put orders i:3 city=krakow total=i:40 + \\table window orders sum:total partition city + \\ + ); + defer allocator.free(out); + try std.testing.expectEqualStrings( + "OK\nOK\nOK\n" ++ + "__pk=1\tcity=warsaw\ttotal=100\tsum_total=350\n" ++ + "__pk=2\tcity=warsaw\ttotal=250\tsum_total=350\n" ++ + "__pk=3\tcity=krakow\ttotal=40\tsum_total=40\n", + out, + ); +} + +test "table window honors --limit/--offset in partition-then-order display order, not raw scan order" { + const allocator = std.testing.allocator; + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + var db = try openTemp(&tmp, allocator); + defer db.close(); + + var repl = Repl.init(allocator, &db); + defer repl.deinit(); + + const out = try runScript(allocator, &repl, + \\table put orders i:1 city=warsaw total=i:100 + \\table put orders i:2 city=warsaw total=i:250 + \\table put orders i:3 city=krakow total=i:40 + \\table window orders row_number order total --offset 1 --limit 1 + \\ + ); + defer allocator.free(out); + // Sorted by total ascending: krakow/40 (rownum 1), warsaw/100 (rownum + // 2), warsaw/250 (rownum 3) - offset 1 limit 1 must land on the SECOND + // entry of that sorted walk, not the second row `table put` inserted. + try std.testing.expectEqualStrings( + "OK\nOK\nOK\n" ++ + "__pk=1\tcity=warsaw\ttotal=100\trow_number=2\n", + out, + ); +} +