test(sql): cover aDeclaredColumnCollationKeepsComparingTheSameWayAfte...

This commit is contained in:
dualfroz committed 2026-09-11 16:52:20 +02:00
1 parent ffb95375fe
commit 19e84498c6
1 file changed
+154
@@ -308,3 +308,157 @@ class DiskExecutorColumnCollationTest {
assertFailsWith<DiskTableException> { db.run("CREATE UNIQUE INDEX idxU2 ON u2 (name)") }
}
@Test
fun aDeclaredColumnCollationKeepsComparingTheSameWayAfterARealFileCloseAndReopen() {
val db = newDatabase()
db.seedNames()
assertEquals(
listOf(int(1), int(2)),
db.select("SELECT id FROM people WHERE name = 'BOB' ORDER BY id").rows.map { it.single() }
)
db.reopen()
assertEquals(
listOf(int(1), int(2)),
db.select("SELECT id FROM people WHERE name = 'BOB' ORDER BY id").rows.map { it.single() }
)
}
/**
* [DiskIndexAccessPlanner]'s equivalent of [kestrel.ColumnCollationTest.indexedEqualityHonorsTheColumnsDeclaredCollation]:
* [DiskTable.findBySecondary] used to encode both the inserted row's key AND the search
* literal straight to bytes via [kestrel.store.codec.KeyCodec], with no regard for the
* column's declared `COLLATE` - so once `CREATE INDEX` made the planner choose an index scan
* over the full scan the un-indexed [bareEqualityHonorsTheColumnsDeclaredCollationWithoutAnExplicitClause]
* exercises, the case-variant row silently dropped out of the result instead of raising an
* error, since [DiskExecutor] only re-checks WHERE against candidates the chosen access plan
* already returned.
*/
@Test
fun indexedEqualityHonorsTheColumnsDeclaredCollation() {
val db = newDatabase()
db.seedNames()
val scanned = db.select("SELECT id FROM people WHERE name = 'BOB' ORDER BY id").rows
db.run("CREATE INDEX idxName ON people (name)")
val plan = db.explain("EXPLAIN SELECT id FROM people WHERE name = 'BOB'")
assertTrue(plan.any { it.contains("Index Scan using idxName") }, "expected index scan, got $plan")
val indexed = db.select("SELECT id FROM people WHERE name = 'BOB' ORDER BY id").rows
assertEquals(scanned, indexed)
assertEquals(listOf(listOf(int(1)), listOf(int(2))), indexed)
}
/** Mirrors [indexedEqualityHonorsTheColumnsDeclaredCollation] for a trailing-space collation instead of case. */
@Test
fun indexedEqualityHonorsAnRtrimDeclaredCollation() {
val db = newDatabase()
db.run("CREATE TABLE tags (id INTEGER PRIMARY KEY, tag TEXT COLLATE RTRIM)")
db.run("INSERT INTO tags VALUES (1, 'a'), (2, 'a '), (3, 'b')")
db.run("CREATE INDEX idxTag ON tags (tag)")
val plan = db.explain("EXPLAIN SELECT id FROM tags WHERE tag = 'a'")
assertTrue(plan.any { it.contains("Index Scan using idxTag") }, "expected index scan, got $plan")
assertEquals(listOf(listOf(int(1)), listOf(int(2))), db.select("SELECT id FROM tags WHERE tag = 'a' ORDER BY id").rows)
}
/**
* Mirrors [kestrel.ColumnCollationTest.compositeIndexEqualityHonorsPerColumnDeclaredCollationIndependently]:
* a composite secondary index's key is `encodeKey(first) ++ encodeKey(last) ++ rid` - each
* column position must fold through its OWN declared collation independently (`first` is
* NOCASE, `last` stays BINARY) rather than, say, folding the whole composite key uniformly or
* not at all.
*/
@Test
fun compositeIndexEqualityHonorsPerColumnDeclaredCollationIndependently() {
val db = newDatabase()
db.run("CREATE TABLE contacts (id INTEGER PRIMARY KEY, first TEXT COLLATE NOCASE, last TEXT)")
db.run("INSERT INTO contacts VALUES (1, 'Bob', 'Smith'), (2, 'bob', 'SMITH')")
db.run("CREATE INDEX idxContact ON contacts (first, last)")
val plan = db.explain("EXPLAIN SELECT id FROM contacts WHERE first = 'BOB' AND last = 'Smith'")
assertTrue(plan.any { it.contains("Index Scan using idxContact") }, "got $plan")
assertEquals(
listOf(listOf(int(1))),
db.select("SELECT id FROM contacts WHERE first = 'BOB' AND last = 'Smith'").rows
)
}
/**
* Safety net mirroring [kestrel.ColumnCollationTest.explicitCollateOverrideOnAnIndexedColumnFallsBackToASeqScanInsteadOfMisusingTheIndex]:
* [DiskIndexAccessPlanner.asColumnPredicate] only recognizes a bare [kestrel.sql.ast.ColumnExpression],
* so an explicit `COLLATE`-wrapped operand is excluded from pushdown entirely instead of being
* (wrongly) served by an index whose physical bytes are folded for the column's OWN collation.
*/
@Test
fun explicitCollateOverrideOnAnIndexedColumnFallsBackToASeqScanInsteadOfMisusingTheIndex() {
val db = newDatabase()
db.seedNames()
db.run("CREATE INDEX idxName ON people (name)")
val plan = db.explain("EXPLAIN SELECT id FROM people WHERE name COLLATE BINARY = 'BOB'")
assertTrue(plan.any { it.contains("Seq Scan") }, "expected seq scan fallback, got $plan")
assertEquals(emptyList(), db.select("SELECT id FROM people WHERE name COLLATE BINARY = 'BOB'").rows)
}
/** Control negative: an index over a column with no declared `COLLATE` behaves exactly as before this session. */
@Test
fun indexedEqualityWithNoDeclaredCollationStaysBinary() {
val db = newDatabase()
db.run("CREATE TABLE plain3 (id INTEGER PRIMARY KEY, name TEXT)")
db.run("INSERT INTO plain3 VALUES (1, 'Bob'), (2, 'bob')")
db.run("CREATE INDEX idxPlain3 ON plain3 (name)")
val plan = db.explain("EXPLAIN SELECT id FROM plain3 WHERE name = 'Bob'")
assertTrue(plan.any { it.contains("Index Scan using idxPlain3") }, "got $plan")
assertEquals(listOf(listOf(int(1))), db.select("SELECT id FROM plain3 WHERE name = 'Bob'").rows)
}
/**
* [DiskTable.findByPrimaryKey]'s point-lookup counterpart of [indexedEqualityHonorsTheColumnsDeclaredCollation]:
* a single-column, inline `PRIMARY KEY` is backed by its own B+Tree exactly like a secondary
* index, so `WHERE name = 'BOB'` against a `NOCASE`-declared `PRIMARY KEY` column must be
* planned as [DiskAccessPlan.PrimaryKeyPoint] and still find the case-variant row.
*/
@Test
fun primaryKeyPointLookupHonorsTheColumnsDeclaredCollation() {
val db = newDatabase()
db.run("CREATE TABLE pk1 (name TEXT COLLATE NOCASE PRIMARY KEY, hits INTEGER)")
db.run("INSERT INTO pk1 VALUES ('Bob', 10)")
val plan = db.explain("EXPLAIN SELECT hits FROM pk1 WHERE name = 'BOB'")
assertTrue(plan.any { it.contains("Primary Key Scan") }, "expected primary key point lookup, got $plan")
assertEquals(listOf(listOf(int(10))), db.select("SELECT hits FROM pk1 WHERE name = 'BOB'").rows)
}
/**
* [encodePrimaryKey]'s composite counterpart of [primaryKeyPointLookupHonorsTheColumnsDeclaredCollation]:
* a table-level composite `PRIMARY KEY (region, code)` folds each position through ITS OWN
* declared collation independently ([DiskAccessPlan.CompositePrimaryKeyPoint] mirrors
* [compositeIndexEqualityHonorsPerColumnDeclaredCollationIndependently] but for the PK's own
* B+Tree rather than a secondary index) - `region` is NOCASE (matches both rows) but `code`
* stays plain BINARY (only an exact-case match counts), so only row 1 survives both conjuncts.
*/
@Test
fun compositePrimaryKeyPointLookupHonorsPerColumnDeclaredCollationIndependently() {
val db = newDatabase()
db.run("CREATE TABLE zones (region TEXT COLLATE NOCASE, code TEXT, hits INTEGER, PRIMARY KEY (region, code))")
db.run("INSERT INTO zones VALUES ('EU', 'west', 1), ('eu', 'WEST', 2)")
val plan = db.explain("EXPLAIN SELECT hits FROM zones WHERE region = 'eu' AND code = 'west'")
assertTrue(plan.any { it.contains("Primary Key Scan") }, "expected composite primary key point lookup, got $plan")
assertEquals(listOf(listOf(int(1))), db.select("SELECT hits FROM zones WHERE region = 'eu' AND code = 'west'").rows)
}
/**
* [DiskTable.rangeByPrimaryKey]'s composite counterpart of [rangeQueryOnAPrimaryKeyHonorsTheColumnsDeclaredCollation]:
* a table-level composite `PRIMARY KEY (order_id, sku)` ranged on its LAST column (`sku`, the
* only shape [DiskAccessPlan.CompositePrimaryKeyRange] plans - see its own doc) after an
* equality prefix on `order_id` (plain BINARY, irrelevant here) still folds the bound through
* `sku`'s own declared `NOCASE` collation, same as the single-column case - `'Mango'` only
* survives `sku > 'lemon'` once both sides are canonicalized to `'MANGO' > 'LEMON'`.
*/
@Test
fun rangeQueryOnTheLastColumnOfACompositePrimaryKeyAfterAnEqualityPrefixHonorsTheColumnsDeclaredCollation() {
val db = newDatabase()
db.run("CREATE TABLE order_lines4 (order_id INTEGER, sku TEXT COLLATE NOCASE, PRIMARY KEY (order_id, sku))")
db.run("INSERT INTO order_lines4 VALUES (1, 'Apple'), (1, 'Mango'), (1, 'kiwi'), (2, 'Zebra')")
val plan = db.explain("EXPLAIN SELECT sku FROM order_lines4 WHERE order_id = 1 AND sku > 'lemon'")
assertTrue(plan.any { it.contains("Primary Key Range Scan") }, "expected primary key range scan, got $plan")
assertEquals(
listOf(listOf(Value.TextValue("Mango"))),
db.select("SELECT sku FROM order_lines4 WHERE order_id = 1 AND sku > 'lemon'").rows
)
}