feat: fill in query
This commit is contained in:
1 parent
2a8227b64d
commit
bda37d1ecf
1 file changed
+159
+159
@@ -3498,3 +3498,162 @@ test "a self-referencing foreign key restricts renaming a UNIQUE field still tar
|
||||
try std.testing.expectEqualStrings("B1-NEW", renamed.get("badge").?.text);
|
||||
}
|
||||
|
||||
test "createCheckConstraint rejects a row that violates it and accepts one that satisfies it" {
|
||||
const allocator = std.testing.allocator;
|
||||
var tmp = std.testing.tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
var db = try openTemp(&tmp, allocator);
|
||||
defer db.close();
|
||||
|
||||
var orders = try Table.open(&db, allocator, "orders");
|
||||
defer orders.deinit();
|
||||
try orders.createCheckConstraint("qty_positive", "qty gt i:0");
|
||||
try std.testing.expect(orders.hasCheckConstraint("qty_positive"));
|
||||
|
||||
try orders.put(.{ .int = 1 }, &.{.{ .name = "qty", .value = .{ .int = 5 } }});
|
||||
try std.testing.expectError(error.CheckViolation, orders.put(.{ .int = 2 }, &.{
|
||||
.{ .name = "qty", .value = .{ .int = 0 } },
|
||||
}));
|
||||
try std.testing.expectError(error.CheckViolation, orders.put(.{ .int = 3 }, &.{
|
||||
.{ .name = "qty", .value = .{ .int = -1 } },
|
||||
}));
|
||||
}
|
||||
|
||||
test "createCheckConstraint refuses to install over existing rows that already fail it" {
|
||||
const allocator = std.testing.allocator;
|
||||
var tmp = std.testing.tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
var db = try openTemp(&tmp, allocator);
|
||||
defer db.close();
|
||||
|
||||
var orders = try Table.open(&db, allocator, "orders");
|
||||
defer orders.deinit();
|
||||
try orders.put(.{ .int = 1 }, &.{.{ .name = "qty", .value = .{ .int = -5 } }});
|
||||
|
||||
try std.testing.expectError(error.CheckViolation, orders.createCheckConstraint("qty_positive", "qty gt i:0"));
|
||||
try std.testing.expect(!orders.hasCheckConstraint("qty_positive"));
|
||||
}
|
||||
|
||||
test "createCheckConstraint rejects a malformed expression before persisting anything" {
|
||||
const allocator = std.testing.allocator;
|
||||
var tmp = std.testing.tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
var db = try openTemp(&tmp, allocator);
|
||||
defer db.close();
|
||||
|
||||
var orders = try Table.open(&db, allocator, "orders");
|
||||
defer orders.deinit();
|
||||
try std.testing.expectError(error.InvalidCheckExpression, orders.createCheckConstraint("bad", "qty gt"));
|
||||
try std.testing.expect(!orders.hasCheckConstraint("bad"));
|
||||
}
|
||||
|
||||
test "dropCheckConstraint lifts the constraint, and a check constraint survives a table reopen" {
|
||||
const allocator = std.testing.allocator;
|
||||
var tmp = std.testing.tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
var db = try openTemp(&tmp, allocator);
|
||||
defer db.close();
|
||||
|
||||
{
|
||||
var orders = try Table.open(&db, allocator, "orders");
|
||||
defer orders.deinit();
|
||||
try orders.createCheckConstraint("qty_positive", "qty gt i:0");
|
||||
}
|
||||
|
||||
var reopened = try Table.open(&db, allocator, "orders");
|
||||
defer reopened.deinit();
|
||||
try std.testing.expect(reopened.hasCheckConstraint("qty_positive"));
|
||||
try std.testing.expectError(error.CheckViolation, reopened.put(.{ .int = 1 }, &.{
|
||||
.{ .name = "qty", .value = .{ .int = 0 } },
|
||||
}));
|
||||
|
||||
try reopened.dropCheckConstraint("qty_positive");
|
||||
try std.testing.expect(!reopened.hasCheckConstraint("qty_positive"));
|
||||
try reopened.put(.{ .int = 1 }, &.{.{ .name = "qty", .value = .{ .int = 0 } }});
|
||||
}
|
||||
|
||||
test "a row missing the checked field entirely is exempt (like is_null), but present-and-none is still checked" {
|
||||
const allocator = std.testing.allocator;
|
||||
var tmp = std.testing.tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
var db = try openTemp(&tmp, allocator);
|
||||
defer db.close();
|
||||
|
||||
var orders = try Table.open(&db, allocator, "orders");
|
||||
defer orders.deinit();
|
||||
try orders.createCheckConstraint("qty_known", "qty is_not_null");
|
||||
|
||||
try std.testing.expectError(error.CheckViolation, orders.put(.{ .int = 1 }, &.{}));
|
||||
}
|
||||
|
||||
test "multiple CHECK constraints on the same table all must pass" {
|
||||
const allocator = std.testing.allocator;
|
||||
var tmp = std.testing.tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
var db = try openTemp(&tmp, allocator);
|
||||
defer db.close();
|
||||
|
||||
var orders = try Table.open(&db, allocator, "orders");
|
||||
defer orders.deinit();
|
||||
try orders.createCheckConstraint("qty_positive", "qty gt i:0");
|
||||
try orders.createCheckConstraint("qty_capped", "qty lt i:1000");
|
||||
|
||||
try orders.put(.{ .int = 1 }, &.{.{ .name = "qty", .value = .{ .int = 500 } }});
|
||||
try std.testing.expectError(error.CheckViolation, orders.put(.{ .int = 2 }, &.{
|
||||
.{ .name = "qty", .value = .{ .int = 1000 } },
|
||||
}));
|
||||
}
|
||||
|
||||
test "ON DELETE CASCADE removes referencing rows instead of restricting the parent delete" {
|
||||
const allocator = std.testing.allocator;
|
||||
var tmp = std.testing.tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
var db = try openTemp(&tmp, allocator);
|
||||
defer db.close();
|
||||
|
||||
var customers = try Table.open(&db, allocator, "customers");
|
||||
defer customers.deinit();
|
||||
try customers.put(.{ .int = 1 }, &.{.{ .name = "name", .value = .{ .text = "Ada" } }});
|
||||
try customers.put(.{ .int = 2 }, &.{.{ .name = "name", .value = .{ .text = "Boris" } }});
|
||||
|
||||
var orders = try Table.open(&db, allocator, "orders");
|
||||
defer orders.deinit();
|
||||
try orders.createForeignKey("customer_id", "customers", pkField, .cascade);
|
||||
try orders.put(.{ .int = 100 }, &.{.{ .name = "customer_id", .value = .{ .int = 1 } }});
|
||||
try orders.put(.{ .int = 101 }, &.{.{ .name = "customer_id", .value = .{ .int = 1 } }});
|
||||
try orders.put(.{ .int = 200 }, &.{.{ .name = "customer_id", .value = .{ .int = 2 } }});
|
||||
|
||||
try customers.delete(.{ .int = 1 });
|
||||
|
||||
try std.testing.expect((try customers.get(.{ .int = 1 })) == null);
|
||||
try std.testing.expect((try orders.get(.{ .int = 100 })) == null);
|
||||
try std.testing.expect((try orders.get(.{ .int = 101 })) == null);
|
||||
|
||||
// Untouched: customer 2 and its own order were never in the cascade's
|
||||
// path.
|
||||
var otherCustomer = (try customers.get(.{ .int = 2 })).?;
|
||||
otherCustomer.deinit();
|
||||
var otherOrder = (try orders.get(.{ .int = 200 })).?;
|
||||
otherOrder.deinit();
|
||||
}
|
||||
|
||||
test "ON DELETE CASCADE keeps secondary indexes on the cascaded rows in sync" {
|
||||
const allocator = std.testing.allocator;
|
||||
var tmp = std.testing.tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
var db = try openTemp(&tmp, allocator);
|
||||
defer db.close();
|
||||
|
||||
var customers = try Table.open(&db, allocator, "customers");
|
||||
defer customers.deinit();
|
||||
try customers.put(.{ .int = 1 }, &.{.{ .name = "name", .value = .{ .text = "Ada" } }});
|
||||
|
||||
var orders = try Table.open(&db, allocator, "orders");
|
||||
defer orders.deinit();
|
||||
try orders.createIndex("status");
|
||||
try orders.createForeignKey("customer_id", "customers", pkField, .cascade);
|
||||
try orders.put(.{ .int = 100 }, &.{
|
||||
.{ .name = "customer_id", .value = .{ .int = 1 } },
|
||||
.{ .name = "status", .value = .{ .text = "open" } },
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user