feat: grow txn_table

This commit is contained in:
dualfroz committed 2026-09-12 22:56:36 +02:00
1 parent 697e8c6461
commit 0631d1673d
1 file changed
+77
+77
View File
@@ -950,3 +950,80 @@ test "TableTxn put is restricted from changing a UNIQUE foreign key target a com
var db = try openTemp(&tmp, allocator); var db = try openTemp(&tmp, allocator);
defer db.close(); defer db.close();
{
var products = try querymod.Table.open(&db, allocator, "products");
defer products.deinit();
try products.createUniqueIndex("sku");
try products.put(.{ .int = 1 }, &.{.{ .name = "sku", .value = .{ .text = "WIDGET-1" } }});
var lineItems = try querymod.Table.open(&db, allocator, "line_items");
defer lineItems.deinit();
try lineItems.createForeignKey("product_sku", "products", "sku", .restrict);
try lineItems.put(.{ .int = 1 }, &.{.{ .name = "product_sku", .value = .{ .text = "WIDGET-1" } }});
}
var txn = Transaction.begin(&db);
defer txn.deinit();
var t = try TableTxn.open(&txn, allocator, "products");
defer t.deinit();
try std.testing.expectError(error.ForeignKeyInUse, t.put(.{ .int = 1 }, &.{
.{ .name = "sku", .value = .{ .text = "WIDGET-2" } },
}));
// Unchanged is always exempt.
try t.put(.{ .int = 1 }, &.{.{ .name = "sku", .value = .{ .text = "WIDGET-1" } }});
try txn.commit();
var plain = try querymod.Table.open(&db, allocator, "products");
defer plain.deinit();
var stillThere = (try plain.get(.{ .int = 1 })).?;
defer stillThere.deinit();
try std.testing.expectEqualStrings("WIDGET-1", stillThere.get("sku").?.text);
}
test "TableTxn put update-restrict sees a child row staged earlier in the same uncommitted transaction" {
const allocator = std.testing.allocator;
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
var db = try openTemp(&tmp, allocator);
defer db.close();
{
var products = try querymod.Table.open(&db, allocator, "products");
defer products.deinit();
try products.createUniqueIndex("sku");
try products.put(.{ .int = 1 }, &.{.{ .name = "sku", .value = .{ .text = "WIDGET-1" } }});
var lineItems = try querymod.Table.open(&db, allocator, "line_items");
defer lineItems.deinit();
try lineItems.createForeignKey("product_sku", "products", "sku", .restrict);
}
var txn = Transaction.begin(&db);
defer txn.deinit();
// The line item referencing "WIDGET-1" is only staged in this SAME
// transaction's write set, never committed - `Transaction.scanPrefix`
// is what makes this count as a live reference before commit.
var lineItemsTxn = try TableTxn.open(&txn, allocator, "line_items");
defer lineItemsTxn.deinit();
try lineItemsTxn.put(.{ .int = 1 }, &.{.{ .name = "product_sku", .value = .{ .text = "WIDGET-1" } }});
var productsTxn = try TableTxn.open(&txn, allocator, "products");
defer productsTxn.deinit();
try std.testing.expectError(error.ForeignKeyInUse, productsTxn.put(.{ .int = 1 }, &.{
.{ .name = "sku", .value = .{ .text = "WIDGET-2" } },
}));
// Once the referencing line item is removed in the same transaction,
// the rename goes through.
try lineItemsTxn.delete(.{ .int = 1 });
try productsTxn.put(.{ .int = 1 }, &.{.{ .name = "sku", .value = .{ .text = "WIDGET-2" } }});
try txn.commit();
var plain = try querymod.Table.open(&db, allocator, "products");
defer plain.deinit();
var renamed = (try plain.get(.{ .int = 1 })).?;
defer renamed.deinit();
try std.testing.expectEqualStrings("WIDGET-2", renamed.get("sku").?.text);
}