test(sql): cover equalityOnEveryCompositePrimaryKeyColumnIsAPointLook...
This commit is contained in:
1 parent
acedd5f727
commit
9071d0d3c8
1 file changed
+160
@@ -153,3 +153,163 @@ class DiskIndexAccessPlannerTest {
|
||||
compositePrimaryKey = listOf("order_id", "line_no")
|
||||
)
|
||||
|
||||
@Test
|
||||
fun equalityOnEveryCompositePrimaryKeyColumnIsAPointLookupInSchemaOrder() {
|
||||
val predicate = and(
|
||||
cmp(col("line_no"), BinaryOperator.Equal, int(2)),
|
||||
cmp(col("order_id"), BinaryOperator.Equal, int(1))
|
||||
)
|
||||
val plan = planner.plan(orderLineSchema, emptyList(), predicate)
|
||||
val point = assertIs<DiskAccessPlan.CompositePrimaryKeyPoint>(plan)
|
||||
assertEquals(listOf(Value.IntegerValue(1), Value.IntegerValue(2)), point.keys)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun equalityOnOnlySomeCompositePrimaryKeyColumnsFallsBackToAFullScan() {
|
||||
// A partial equality match with no range on the remaining column either cannot be
|
||||
// turned into a point lookup (line_no is left entirely unbound) - see
|
||||
// DiskAccessPlan.CompositePrimaryKeyRange's own documentation for the narrower case where
|
||||
// a range on the remaining last column IS pushed down instead.
|
||||
val plan = planner.plan(orderLineSchema, emptyList(), cmp(col("order_id"), BinaryOperator.Equal, int(1)))
|
||||
assertEquals(DiskAccessPlan.FullScan, plan)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun aRangeOnTheLastColumnOfACompositePrimaryKeyAfterAnEqualityPrefixIsPushedDown() {
|
||||
val predicate = and(
|
||||
cmp(col("order_id"), BinaryOperator.Equal, int(1)),
|
||||
cmp(col("line_no"), BinaryOperator.Greater, int(2))
|
||||
)
|
||||
val plan = planner.plan(orderLineSchema, emptyList(), predicate)
|
||||
val range = assertIs<DiskAccessPlan.CompositePrimaryKeyRange>(plan)
|
||||
assertEquals(listOf(Value.IntegerValue(1)), range.prefix)
|
||||
assertEquals(Value.IntegerValue(2), range.lower)
|
||||
assertEquals(false, range.lowerInclusive)
|
||||
assertNull(range.upper)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun aTwoSidedRangeOnTheLastColumnOfACompositePrimaryKeyCombinesBothBounds() {
|
||||
val predicate = and(
|
||||
cmp(col("order_id"), BinaryOperator.Equal, int(1)),
|
||||
and(
|
||||
cmp(col("line_no"), BinaryOperator.GreaterEqual, int(2)),
|
||||
cmp(col("line_no"), BinaryOperator.LessEqual, int(8))
|
||||
)
|
||||
)
|
||||
val range = assertIs<DiskAccessPlan.CompositePrimaryKeyRange>(planner.plan(orderLineSchema, emptyList(), predicate))
|
||||
assertEquals(listOf(Value.IntegerValue(1)), range.prefix)
|
||||
assertEquals(Value.IntegerValue(2), range.lower)
|
||||
assertEquals(true, range.lowerInclusive)
|
||||
assertEquals(Value.IntegerValue(8), range.upper)
|
||||
assertEquals(true, range.upperInclusive)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun aRangeOnTheFirstColumnOfACompositePrimaryKeyWithNoEqualityPrefixIsNotPushedDown() {
|
||||
// order_id is not the LAST composite PRIMARY KEY column - see
|
||||
// DiskAccessPlan.CompositePrimaryKeyRange's own documentation on why leaving line_no
|
||||
// entirely unconstrained after a range on order_id is a deliberate scope cut.
|
||||
val plan = planner.plan(orderLineSchema, emptyList(), cmp(col("order_id"), BinaryOperator.Greater, int(1)))
|
||||
assertEquals(DiskAccessPlan.FullScan, plan)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun anEqualityPrefixOnACompositePrimaryKeyIsPreferredOverARangeOnTheSameLastColumn() {
|
||||
val predicate = and(
|
||||
cmp(col("order_id"), BinaryOperator.Equal, int(1)),
|
||||
and(
|
||||
cmp(col("line_no"), BinaryOperator.Equal, int(2)),
|
||||
cmp(col("line_no"), BinaryOperator.Greater, int(0))
|
||||
)
|
||||
)
|
||||
val point = assertIs<DiskAccessPlan.CompositePrimaryKeyPoint>(planner.plan(orderLineSchema, emptyList(), predicate))
|
||||
assertEquals(listOf(Value.IntegerValue(1), Value.IntegerValue(2)), point.keys)
|
||||
}
|
||||
|
||||
private val orderLineWithWarehouseSchema = TableSchema(
|
||||
"order_lines3",
|
||||
listOf(
|
||||
Column("warehouse", ColumnType.Text, notNull = true, primaryKey = false),
|
||||
Column("order_id", ColumnType.Integer, notNull = true, primaryKey = false),
|
||||
Column("line_no", ColumnType.Integer, notNull = true, primaryKey = false)
|
||||
),
|
||||
compositePrimaryKey = listOf("warehouse", "order_id", "line_no")
|
||||
)
|
||||
|
||||
@Test
|
||||
fun aRangeOnTheLastColumnOfAThreeColumnCompositePrimaryKeyAfterATwoColumnEqualityPrefixIsPushedDown() {
|
||||
val predicate = and(
|
||||
cmp(col("warehouse"), BinaryOperator.Equal, text("west")),
|
||||
and(
|
||||
cmp(col("order_id"), BinaryOperator.Equal, int(1)),
|
||||
cmp(col("line_no"), BinaryOperator.Less, int(10))
|
||||
)
|
||||
)
|
||||
val range = assertIs<DiskAccessPlan.CompositePrimaryKeyRange>(planner.plan(orderLineWithWarehouseSchema, emptyList(), predicate))
|
||||
assertEquals(listOf(Value.TextValue("west"), Value.IntegerValue(1)), range.prefix)
|
||||
assertNull(range.lower)
|
||||
assertEquals(Value.IntegerValue(10), range.upper)
|
||||
assertEquals(false, range.upperInclusive)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun aRangeOnAMiddleColumnOfAThreeColumnCompositePrimaryKeyLeavingTheLastColumnUnconstrainedIsNotPushedDown() {
|
||||
// line_no would be left entirely unconstrained after a range on order_id - the same
|
||||
// "must always fill every PRIMARY KEY column" restriction as the two-column schema's
|
||||
// aRangeOnTheFirstColumnOfACompositePrimaryKeyWithNoEqualityPrefixIsNotPushedDown, just one
|
||||
// column deeper.
|
||||
val predicate = and(
|
||||
cmp(col("warehouse"), BinaryOperator.Equal, text("west")),
|
||||
cmp(col("order_id"), BinaryOperator.Greater, int(1))
|
||||
)
|
||||
val plan = planner.plan(orderLineWithWarehouseSchema, emptyList(), predicate)
|
||||
assertEquals(DiskAccessPlan.FullScan, plan)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun comparingToANullLiteralIsNeverPushedDown() {
|
||||
// "id = NULL" is never true under three-valued SQL logic and must not be
|
||||
// treated as an equality lookup for NULL (which the encoding cannot represent).
|
||||
val plan = planner.plan(schema, emptyList(), cmp(col("id"), BinaryOperator.Equal, LiteralExpression(Value.NullValue)))
|
||||
assertEquals(DiskAccessPlan.FullScan, plan)
|
||||
}
|
||||
|
||||
/**
|
||||
* A real [SecondaryIndexHandle] backed by a genuine, temp-file [DiskCatalog].
|
||||
* The planner only ever reads a handle's `name`/`columnName` - never its
|
||||
* live [kestrel.store.engine.TransactionalIndex] - but that index has no
|
||||
* lightweight fake (its constructor is private and wired to a real
|
||||
* [kestrel.store.page.PageStore]/[kestrel.store.txn.TransactionManager]),
|
||||
* so real disk infrastructure is the simplest way to obtain a valid one.
|
||||
*/
|
||||
private fun secondaryHandleStub(name: String, columnName: String): SecondaryIndexHandle {
|
||||
val dataFile = Files.createTempFile("kestrel-planner-test-data", ".db").toFile().also { it.deleteOnExit() }
|
||||
val walFile = Files.createTempFile("kestrel-planner-test-wal", ".db").toFile().also { it.deleteOnExit() }
|
||||
val catalog = DiskCatalog.open(dataFile, walFile)
|
||||
catalog.createTable("stub", schema.columns)
|
||||
return catalog.createIndex("stub", name, columnName)
|
||||
}
|
||||
|
||||
/** Composite-column counterpart of [secondaryHandleStub], over [orderLineSchema]'s columns. */
|
||||
private fun compositeSecondaryHandleStub(name: String, columnNames: List<String>): SecondaryIndexHandle {
|
||||
val dataFile = Files.createTempFile("kestrel-planner-test-data", ".db").toFile().also { it.deleteOnExit() }
|
||||
val walFile = Files.createTempFile("kestrel-planner-test-wal", ".db").toFile().also { it.deleteOnExit() }
|
||||
val catalog = DiskCatalog.open(dataFile, walFile)
|
||||
catalog.createTable("stub", orderLineSchema.columns)
|
||||
return catalog.createIndex("stub", name, columnNames)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun equalityOnEveryColumnOfACompositeSecondaryIndexUsesIt() {
|
||||
val handle = compositeSecondaryHandleStub("idx_order_sku", listOf("order_id", "sku"))
|
||||
val predicate = and(
|
||||
cmp(col("sku"), BinaryOperator.Equal, text("widget")),
|
||||
cmp(col("order_id"), BinaryOperator.Equal, int(1))
|
||||
)
|
||||
val plan = planner.plan(orderLineSchema, listOf(handle), predicate)
|
||||
val secondary = assertIs<DiskAccessPlan.Secondary>(plan)
|
||||
assertEquals("idx_order_sku", secondary.indexName)
|
||||
assertEquals(listOf(Value.IntegerValue(1), Value.TextValue("widget")), secondary.keys)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user