test(sql): cover createUniqueCompositeIndexAllowsMultipleRowsWithANul...

This commit is contained in:
dualfroz committed 2026-09-12 20:35:02 +02:00
1 parent 743ca28b0c
commit e5569c3dc1
1 file changed
+157
@@ -1113,3 +1113,160 @@ class DiskExecutorTest {
assertEquals(2, db.select("SELECT * FROM registrations").rows.size) assertEquals(2, db.select("SELECT * FROM registrations").rows.size)
} }
@Test
fun createUniqueCompositeIndexAllowsMultipleRowsWithANullInEitherColumn() {
val db = newDatabase()
db.run("CREATE TABLE registrations (event_id INTEGER, user_id INTEGER)")
db.run("CREATE UNIQUE INDEX idx_reg ON registrations(event_id, user_id)")
db.run("INSERT INTO registrations VALUES (1, NULL), (1, NULL), (NULL, 100), (NULL, 100)")
assertEquals(4, db.select("SELECT * FROM registrations").rows.size)
}
@Test
fun createUniqueCompositeIndexOverExistingDuplicateCombinationsFailsAndLeavesNoTraceOfTheIndex() {
val db = newDatabase()
db.run("CREATE TABLE registrations (event_id INTEGER, user_id INTEGER)")
db.run("INSERT INTO registrations VALUES (1, 100), (1, 100)")
assertFailsWith<DiskTableException> { db.run("CREATE UNIQUE INDEX idx_reg ON registrations(event_id, user_id)") }
assertTrue(db.catalog.table("registrations").secondaryIndexes.isEmpty())
}
@Test
fun createUniqueCompositeIndexRejectsAnUpdateThatWouldDuplicateAnotherRowsCombination() {
val db = newDatabase()
db.run("CREATE TABLE registrations (id INTEGER PRIMARY KEY, event_id INTEGER, user_id INTEGER)")
db.run("CREATE UNIQUE INDEX idx_reg ON registrations(event_id, user_id)")
db.run("INSERT INTO registrations VALUES (1, 1, 100), (2, 1, 200)")
assertFailsWith<DiskTableException> { db.run("UPDATE registrations SET user_id = 100 WHERE id = 2") }
assertEquals(int(200), db.select("SELECT user_id FROM registrations WHERE id = 2").rows.single().single())
}
@Test
fun createUniqueCompositeIndexDataAndConstraintSurviveClosingAndReopeningTheCatalog() {
val db = newDatabase()
db.run("CREATE TABLE registrations (event_id INTEGER, user_id INTEGER)")
db.run("CREATE UNIQUE INDEX idx_reg ON registrations(event_id, user_id)")
db.run("INSERT INTO registrations VALUES (1, 100)")
db.reopen()
assertFailsWith<DiskTableException> { db.run("INSERT INTO registrations VALUES (1, 100)") }
val handle = db.catalog.table("registrations").secondaryIndexes.single()
assertEquals(listOf("event_id", "user_id"), handle.columnNames)
assertTrue(handle.unique)
}
// --- index pushdown: proven by DiskTable's access counters, not just by correct results,
// since a full scan and an index lookup return identical rows for these queries.
@Test
fun whereEqualityOnThePrimaryKeyUsesAPointLookupNotAFullScan() {
val db = newDatabase()
db.seedUsers()
val table = db.catalog.table("users")
val result = db.select("SELECT name FROM users WHERE id = 2")
assertEquals(listOf(listOf(text("linus"))), result.rows)
assertEquals(1, table.pointLookupCount)
assertEquals(0, table.scanCount)
}
@Test
fun aMissingPrimaryKeyPointLookupReturnsNoRowsWithoutScanning() {
val db = newDatabase()
db.seedUsers()
val table = db.catalog.table("users")
val result = db.select("SELECT name FROM users WHERE id = 999")
assertEquals(emptyList<List<Value>>(), result.rows)
assertEquals(1, table.pointLookupCount)
assertEquals(0, table.scanCount)
}
@Test
fun aRangeOnThePrimaryKeyUsesARangeScanNotAFullScan() {
val db = newDatabase()
db.seedUsers()
val table = db.catalog.table("users")
val result = db.select("SELECT name FROM users WHERE id > 1 AND id <= 3 ORDER BY id")
assertEquals(listOf(listOf(text("linus")), listOf(text("grace"))), result.rows)
assertEquals(1, table.rangeScanCount)
assertEquals(0, table.scanCount)
}
@Test
fun equalityOnAnUnindexedColumnStillFallsBackToAFullScan() {
val db = newDatabase()
db.seedUsers()
val table = db.catalog.table("users")
val result = db.select("SELECT id FROM users WHERE name = 'linus'")
assertEquals(listOf(listOf(int(2))), result.rows)
assertEquals(1, table.scanCount)
assertEquals(0, table.pointLookupCount)
}
@Test
fun equalityOnASecondaryIndexedColumnUsesTheSecondaryIndexNotAFullScan() {
val db = newDatabase()
db.seedUsers()
db.run("CREATE INDEX idx_age ON users(age)")
val table = db.catalog.table("users")
val scansBeforeSelect = table.scanCount // backfilling the new index already scanned once
val result = db.select("SELECT name FROM users WHERE age = 54")
assertEquals(listOf(listOf(text("linus"))), result.rows)
assertEquals(1, table.secondaryLookupCount)
assertEquals(scansBeforeSelect, table.scanCount)
}
@Test
fun equalityOnAllColumnsOfACompositeSecondaryIndexUsesTheSecondaryIndexNotAFullScan() {
val db = newDatabase()
db.run("CREATE TABLE users (id INTEGER PRIMARY KEY, age INTEGER, name TEXT)")
db.run("INSERT INTO users VALUES (1, 36, 'ada'), (2, 36, 'grace')")
db.run("CREATE INDEX idx_both ON users(age, name)")
val table = db.catalog.table("users")
val scansBeforeSelect = table.scanCount // backfilling the new index already scanned once
val result = db.select("SELECT id FROM users WHERE age = 36 AND name = 'grace'")
assertEquals(listOf(listOf(int(2))), result.rows)
assertEquals(1, table.secondaryLookupCount)
assertEquals(scansBeforeSelect, table.scanCount)
}
@Test
fun equalityOnOnlyOneColumnOfACompositeSecondaryIndexFallsBackToAFullScan() {
val db = newDatabase()
db.run("CREATE TABLE users (id INTEGER PRIMARY KEY, age INTEGER, name TEXT)")
db.run("INSERT INTO users VALUES (1, 36, 'ada'), (2, 36, 'grace')")
db.run("CREATE INDEX idx_both ON users(age, name)")
val table = db.catalog.table("users")
val scansBeforeSelect = table.scanCount
val result = db.select("SELECT id FROM users WHERE age = 36")
assertEquals(2, result.rows.size)
assertEquals(0, table.secondaryLookupCount)
assertEquals(scansBeforeSelect + 1, table.scanCount)
}
@Test
fun aRangeOnASingleColumnSecondaryIndexUsesARangeScanNotAFullScan() {
val db = newDatabase()
db.seedUsers()
db.run("CREATE INDEX idx_age ON users(age)")
val table = db.catalog.table("users")
val scansBeforeSelect = table.scanCount // backfilling the new index already scanned once
val result = db.select("SELECT name FROM users WHERE age > 40 AND age <= 85 ORDER BY age")
assertEquals(listOf(listOf(text("linus")), listOf(text("grace"))), result.rows)
assertEquals(1, table.rangeScanCount)
assertEquals(0, table.secondaryLookupCount)
assertEquals(scansBeforeSelect, table.scanCount)
}
@Test
fun aRangeOnTheFirstColumnOfACompositeSecondaryIndexUsesARangeScanNotAFullScan() {
val db = newDatabase()
db.run("CREATE TABLE order_lines (order_id INTEGER, line_no INTEGER, sku TEXT)")
db.run("INSERT INTO order_lines VALUES (1, 1, 'apple'), (2, 1, 'kiwi'), (3, 1, 'mango')")
db.run("CREATE INDEX idx_order_sku ON order_lines(order_id, sku)")
val table = db.catalog.table("order_lines")
val scansBeforeSelect = table.scanCount // backfilling the new index already scanned once
val result = db.select("SELECT sku FROM order_lines WHERE order_id > 1 ORDER BY order_id")
assertEquals(listOf(listOf(text("kiwi")), listOf(text("mango"))), result.rows)
assertEquals(1, table.rangeScanCount)
assertEquals(scansBeforeSelect, table.scanCount)
}