From 4684c6ad15ba6eb74a54f76d816573d8dd6b475d Mon Sep 17 00:00:00 2001 From: DualFroz Date: Thu, 10 Sep 2026 13:09:16 +0200 Subject: [PATCH] feat: flesh out repl --- src/repl.zig | 148 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/src/repl.zig b/src/repl.zig index 0f047e7..0b3a635 100644 --- a/src/repl.zig +++ b/src/repl.zig @@ -2052,3 +2052,151 @@ test "table window honors --limit/--offset in partition-then-order display order ); } +test "table window rejects an invalid spec 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, "table window orders bogus\n"); + defer allocator.free(out); + try std.testing.expectEqualStrings("error: InvalidArgument\n", out); +} + +test "table window lag/lead read the adjacent row's value within order, NULL at the partition edge" { + 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 put orders i:3 day=i:3 amount=i:30 + \\table window orders lag:amount,lead:amount order day + \\ + ); + defer allocator.free(out); + try std.testing.expectEqualStrings( + "OK\nOK\nOK\n" ++ + "__pk=1\tday=1\tamount=10\tlag_amount=(none)\tlead_amount=20\n" ++ + "__pk=2\tday=2\tamount=20\tlag_amount=10\tlead_amount=30\n" ++ + "__pk=3\tday=3\tamount=30\tlag_amount=20\tlead_amount=(none)\n", + out, + ); +} + +test "table window lag with an explicit offset and 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 put orders i:2 day=i:2 amount=i:20 + \\table window orders lag:amount:1:i:-1 order day + \\ + ); + defer allocator.free(out); + try std.testing.expectEqualStrings( + "OK\nOK\n" ++ + "__pk=1\tday=1\tamount=10\tlag_amount=-1\n" ++ + "__pk=2\tday=2\tamount=20\tlag_amount=10\n", + out, + ); +} + +test "table window lag/lead --range is rejected outright, like row_number" { + 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 amount=i:10 + \\table window orders lag:amount order amount --range + \\ + ); + defer allocator.free(out); + try std.testing.expectEqualStrings("OK\nerror: RangeNotApplicable\n", out); +} + +test "table window first_value/last_value --range: last_value shares the current tie group's last member" { + 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:1 amount=i:20 + \\table put orders i:3 day=i:2 amount=i:5 + \\table window orders first_value:amount,last_value:amount order day --range + \\ + ); + defer allocator.free(out); + try std.testing.expectEqualStrings( + "OK\nOK\nOK\n" ++ + "__pk=1\tday=1\tamount=10\tfirst_value_amount=10\tlast_value_amount=20\n" ++ + "__pk=2\tday=1\tamount=20\tfirst_value_amount=10\tlast_value_amount=20\n" ++ + "__pk=3\tday=2\tamount=5\tfirst_value_amount=10\tlast_value_amount=5\n", + out, + ); +} + +test "table window last_value without --range degenerates to the row's own field (ROWS default)" { + 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 last_value:amount order day + \\ + ); + defer allocator.free(out); + try std.testing.expectEqualStrings( + "OK\nOK\n" ++ + "__pk=1\tday=1\tamount=10\tlast_value_amount=10\n" ++ + "__pk=2\tday=2\tamount=20\tlast_value_amount=20\n", + out, + ); +} + +test "table window nth_value --range: position resolved against the CUMULATIVE frame, not just the current tie group" { + 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(); +