From 755c0ece36776a70afcbe4f9e673fa4602b71931 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Thu, 10 Sep 2026 13:45:14 +0200 Subject: [PATCH] feat: continue work on repl --- src/repl.zig | 155 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) diff --git a/src/repl.zig b/src/repl.zig index 0b3a635..e9ae03e 100644 --- a/src/repl.zig +++ b/src/repl.zig @@ -2200,3 +2200,158 @@ test "table window nth_value --range: position resolved against the CUMULATIVE f var repl = Repl.init(allocator, &db); defer repl.deinit(); + 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 nth_value:amount:2 order day --range + \\ + ); + defer allocator.free(out); + // day=1's tie group has 2 members - position 2 is its second member (20). + // day=2's frame is UNBOUNDED PRECEDING through its own tie group, so it + // already includes day=1's rows too - position 2 was already resolved + // to 20 there and stays 20, even though day=2's OWN group has just 1 row. + try std.testing.expectEqualStrings( + "OK\nOK\nOK\n" ++ + "__pk=1\tday=1\tamount=10\tnth_value_amount=20\n" ++ + "__pk=2\tday=1\tamount=20\tnth_value_amount=20\n" ++ + "__pk=3\tday=2\tamount=5\tnth_value_amount=20\n", + out, + ); +} + +test "table window nth_value ROWS default: NULL until the frame grows to reach the requested position" { + 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 day=i:1 amount=i:10 + \\table put orders i:2 day=i:2 amount=i:20 + \\table window orders nth_value:amount:2 order day + \\ + ); + defer allocator.free(out); + try std.testing.expectEqualStrings( + "OK\nOK\n" ++ + "__pk=1\tday=1\tamount=10\tnth_value_amount=(none)\n" ++ + "__pk=2\tday=2\tamount=20\tnth_value_amount=20\n", + out, + ); +} + +test "table window nth_value with a caller-supplied default instead of NULL" { + 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 day=i:1 amount=i:10 + \\table window orders nth_value:amount:5:i:-1 order day + \\ + ); + defer allocator.free(out); + try std.testing.expectEqualStrings( + "OK\n__pk=1\tday=1\tamount=10\tnth_value_amount=-1\n", + out, + ); +} + +test "table window nth_value with no field or no position reports InvalidArgument" { + 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 day=i:1 amount=i:10 + \\table window orders nth_value order day + \\table window orders nth_value:amount order day + \\ + ); + defer allocator.free(out); + try std.testing.expectEqualStrings( + "OK\nerror: InvalidArgument\nerror: InvalidArgument\n", + out, + ); +} + +test "table window count_distinct runs a per-row distinct total, repeats do not grow it" { + 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 day=i:1 tag=s:a + \\table put orders i:2 day=i:2 tag=s:b + \\table put orders i:3 day=i:3 tag=s:a + \\table window orders count_distinct:tag order day + \\ + ); + defer allocator.free(out); + try std.testing.expectEqualStrings( + "OK\nOK\nOK\n" ++ + "__pk=1\tday=1\ttag=a\tcount_distinct_tag=1\n" ++ + "__pk=2\tday=2\ttag=b\tcount_distinct_tag=2\n" ++ + "__pk=3\tday=3\ttag=a\tcount_distinct_tag=2\n", + out, + ); +} + +test "table window count_distinct with no field reports InvalidArgument, unlike bare count" { + 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 tag=s:a + \\table window orders count_distinct + \\ + ); + defer allocator.free(out); + try std.testing.expectEqualStrings("OK\nerror: InvalidArgument\n", out); +} + +test "unknown command and wrong arg counts report errors without crashing" { + 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, "bogus\nput onlyone\nget\nput a 1\nget a\n"); + defer allocator.free(out); + try std.testing.expectEqualStrings( + "error: UnknownCommand\nerror: WrongArgCount\nerror: WrongArgCount\nOK\n1\n", + out, + ); +} +