feat: extend index
This commit is contained in:
1 parent
910afabd97
commit
9f207fd036
1 file changed
+155
+155
@@ -1268,3 +1268,158 @@ test "rowKey and indexEntryKey stay within their own namespace" {
|
|||||||
try std.testing.expectEqual(@as(u8, 'I'), ik[0]);
|
try std.testing.expectEqual(@as(u8, 'I'), ik[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "prefixUpperBound bounds a prefix scan" {
|
||||||
|
const allocator = std.testing.allocator;
|
||||||
|
const upper = (try prefixUpperBound(allocator, "abc")).?;
|
||||||
|
defer allocator.free(upper);
|
||||||
|
try std.testing.expect(std.mem.order(u8, "abc", upper) == .lt);
|
||||||
|
try std.testing.expect(std.mem.order(u8, "abd", upper) != .lt);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "IndexSet add remove save load round trip" {
|
||||||
|
const allocator = std.testing.allocator;
|
||||||
|
var tmp = std.testing.tmpDir(.{});
|
||||||
|
defer tmp.cleanup();
|
||||||
|
const dbPath = try tmp.dir.realpathAlloc(allocator, ".");
|
||||||
|
defer allocator.free(dbPath);
|
||||||
|
|
||||||
|
var db = try Db.open(allocator, dbPath, .{});
|
||||||
|
defer db.close();
|
||||||
|
|
||||||
|
var set = try IndexSet.load(allocator, &db, "users");
|
||||||
|
defer set.deinit();
|
||||||
|
try std.testing.expectEqual(@as(usize, 0), set.fields.items.len);
|
||||||
|
|
||||||
|
try set.add("email");
|
||||||
|
try set.add("age");
|
||||||
|
try set.add("email");
|
||||||
|
try std.testing.expectEqual(@as(usize, 2), set.fields.items.len);
|
||||||
|
try set.save(&db, "users");
|
||||||
|
|
||||||
|
var reloaded = try IndexSet.load(allocator, &db, "users");
|
||||||
|
defer reloaded.deinit();
|
||||||
|
try std.testing.expect(reloaded.has("email"));
|
||||||
|
try std.testing.expect(reloaded.has("age"));
|
||||||
|
try std.testing.expect(!reloaded.has("missing"));
|
||||||
|
|
||||||
|
try std.testing.expect(reloaded.remove("email"));
|
||||||
|
try std.testing.expect(!reloaded.remove("email"));
|
||||||
|
try reloaded.save(&db, "users");
|
||||||
|
|
||||||
|
var third = try IndexSet.load(allocator, &db, "users");
|
||||||
|
defer third.deinit();
|
||||||
|
try std.testing.expect(!third.has("email"));
|
||||||
|
try std.testing.expect(third.has("age"));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "UniqueSet add remove save load round trip" {
|
||||||
|
const allocator = std.testing.allocator;
|
||||||
|
var tmp = std.testing.tmpDir(.{});
|
||||||
|
defer tmp.cleanup();
|
||||||
|
const dbPath = try tmp.dir.realpathAlloc(allocator, ".");
|
||||||
|
defer allocator.free(dbPath);
|
||||||
|
|
||||||
|
var db = try Db.open(allocator, dbPath, .{});
|
||||||
|
defer db.close();
|
||||||
|
|
||||||
|
var set = try UniqueSet.load(allocator, &db, "users");
|
||||||
|
defer set.deinit();
|
||||||
|
try std.testing.expectEqual(@as(usize, 0), set.fields.items.len);
|
||||||
|
|
||||||
|
try set.add("email");
|
||||||
|
try set.add("ssn");
|
||||||
|
try set.add("email");
|
||||||
|
try std.testing.expectEqual(@as(usize, 2), set.fields.items.len);
|
||||||
|
try set.save(&db, "users");
|
||||||
|
|
||||||
|
var reloaded = try UniqueSet.load(allocator, &db, "users");
|
||||||
|
defer reloaded.deinit();
|
||||||
|
try std.testing.expect(reloaded.has("email"));
|
||||||
|
try std.testing.expect(reloaded.has("ssn"));
|
||||||
|
try std.testing.expect(!reloaded.has("missing"));
|
||||||
|
|
||||||
|
try std.testing.expect(reloaded.remove("email"));
|
||||||
|
try std.testing.expect(!reloaded.remove("email"));
|
||||||
|
try reloaded.save(&db, "users");
|
||||||
|
|
||||||
|
var third = try UniqueSet.load(allocator, &db, "users");
|
||||||
|
defer third.deinit();
|
||||||
|
try std.testing.expect(!third.has("email"));
|
||||||
|
try std.testing.expect(third.has("ssn"));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "UniqueSet and IndexSet on the same table never collide, each keeps its own membership" {
|
||||||
|
const allocator = std.testing.allocator;
|
||||||
|
var tmp = std.testing.tmpDir(.{});
|
||||||
|
defer tmp.cleanup();
|
||||||
|
const dbPath = try tmp.dir.realpathAlloc(allocator, ".");
|
||||||
|
defer allocator.free(dbPath);
|
||||||
|
|
||||||
|
var db = try Db.open(allocator, dbPath, .{});
|
||||||
|
defer db.close();
|
||||||
|
|
||||||
|
var indexes = try IndexSet.load(allocator, &db, "users");
|
||||||
|
defer indexes.deinit();
|
||||||
|
try indexes.add("email");
|
||||||
|
try indexes.add("city");
|
||||||
|
try indexes.save(&db, "users");
|
||||||
|
|
||||||
|
var unique = try UniqueSet.load(allocator, &db, "users");
|
||||||
|
defer unique.deinit();
|
||||||
|
try unique.add("email");
|
||||||
|
try unique.save(&db, "users");
|
||||||
|
|
||||||
|
// "city" is indexed but not unique, "email" is both - the two sets
|
||||||
|
// must agree on "email" and disagree on "city" after an independent
|
||||||
|
// reload of each, proving neither save() overwrote the other's key.
|
||||||
|
var reloadedIndexes = try IndexSet.load(allocator, &db, "users");
|
||||||
|
defer reloadedIndexes.deinit();
|
||||||
|
var reloadedUnique = try UniqueSet.load(allocator, &db, "users");
|
||||||
|
defer reloadedUnique.deinit();
|
||||||
|
|
||||||
|
try std.testing.expect(reloadedIndexes.has("email"));
|
||||||
|
try std.testing.expect(reloadedIndexes.has("city"));
|
||||||
|
try std.testing.expect(reloadedUnique.has("email"));
|
||||||
|
try std.testing.expect(!reloadedUnique.has("city"));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "ForeignKeySet add remove save load round trip" {
|
||||||
|
const allocator = std.testing.allocator;
|
||||||
|
var tmp = std.testing.tmpDir(.{});
|
||||||
|
defer tmp.cleanup();
|
||||||
|
const dbPath = try tmp.dir.realpathAlloc(allocator, ".");
|
||||||
|
defer allocator.free(dbPath);
|
||||||
|
|
||||||
|
var db = try Db.open(allocator, dbPath, .{});
|
||||||
|
defer db.close();
|
||||||
|
|
||||||
|
var set = try ForeignKeySet.load(allocator, &db, "orders");
|
||||||
|
defer set.deinit();
|
||||||
|
try std.testing.expectEqual(@as(usize, 0), set.specs.items.len);
|
||||||
|
|
||||||
|
try set.add("customer_id", "customers", "__pk", .cascade);
|
||||||
|
try set.add("customer_id", "customers", "some_other_field", .restrict);
|
||||||
|
try std.testing.expectEqual(@as(usize, 1), set.specs.items.len);
|
||||||
|
try set.save(&db, "orders");
|
||||||
|
|
||||||
|
var reloaded = try ForeignKeySet.load(allocator, &db, "orders");
|
||||||
|
defer reloaded.deinit();
|
||||||
|
try std.testing.expect(reloaded.has("customer_id"));
|
||||||
|
try std.testing.expect(!reloaded.has("missing"));
|
||||||
|
const spec = reloaded.find("customer_id").?;
|
||||||
|
try std.testing.expectEqualStrings("customers", spec.refTable);
|
||||||
|
try std.testing.expectEqualStrings("__pk", spec.refField);
|
||||||
|
// First `add` wins (mirrors `has` guard above) - `onDelete` survives the
|
||||||
|
// save/load round trip as the mode from that first call, not the
|
||||||
|
// no-op second one.
|
||||||
|
try std.testing.expectEqual(OnDelete.cascade, spec.onDelete);
|
||||||
|
|
||||||
|
try std.testing.expect(reloaded.remove("customer_id"));
|
||||||
|
try std.testing.expect(!reloaded.remove("customer_id"));
|
||||||
|
try reloaded.save(&db, "orders");
|
||||||
|
|
||||||
|
var third = try ForeignKeySet.load(allocator, &db, "orders");
|
||||||
|
defer third.deinit();
|
||||||
|
try std.testing.expect(!third.has("customer_id"));
|
||||||
|
}
|
||||||
|
|
||||||
Reference in new issue
Block a user