test(sql): cover DiskCatalogTest and tempFile
This commit is contained in:
1 parent
910c0761fa
commit
65424efff3
1 file changed
+159
@@ -0,0 +1,159 @@
|
|||||||
|
package kestrel.store.sql
|
||||||
|
|
||||||
|
import kestrel.catalog.Column
|
||||||
|
import kestrel.catalog.SequenceDefinition
|
||||||
|
import kestrel.catalog.UniqueConstraint
|
||||||
|
import kestrel.storage.ColumnType
|
||||||
|
import kestrel.storage.Row
|
||||||
|
import kestrel.storage.Value
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.file.Files
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertFailsWith
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
class DiskCatalogTest {
|
||||||
|
private fun tempFile(prefix: String): File = Files.createTempFile(prefix, ".db").toFile().also { it.deleteOnExit() }
|
||||||
|
|
||||||
|
private val usersColumns = listOf(
|
||||||
|
Column("id", ColumnType.Integer, notNull = true, primaryKey = true),
|
||||||
|
Column("name", ColumnType.Text, notNull = true, primaryKey = false)
|
||||||
|
)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun creatingTheSameTableTwiceFails() {
|
||||||
|
val catalog = DiskCatalog.open(tempFile("kestrel-cat-data"), tempFile("kestrel-cat-wal"))
|
||||||
|
catalog.createTable("users", usersColumns)
|
||||||
|
assertFailsWith<DiskCatalogException> { catalog.createTable("users", usersColumns) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun droppedTableIsNoLongerListedOrQueryable() {
|
||||||
|
val catalog = DiskCatalog.open(tempFile("kestrel-cat-data"), tempFile("kestrel-cat-wal"))
|
||||||
|
catalog.createTable("users", usersColumns)
|
||||||
|
catalog.dropTable("users")
|
||||||
|
|
||||||
|
assertTrue(catalog.tableNames.isEmpty())
|
||||||
|
assertFailsWith<DiskCatalogException> { catalog.table("users") }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun reopeningAFreshProcessSeesTablesAndRowsThroughNewObjectsEntirely() {
|
||||||
|
val dataFile = tempFile("kestrel-cat-data")
|
||||||
|
val walFile = tempFile("kestrel-cat-wal")
|
||||||
|
|
||||||
|
val catalog1 = DiskCatalog.open(dataFile, walFile)
|
||||||
|
val table1 = catalog1.createTable("users", usersColumns)
|
||||||
|
val txn1 = catalog1.beginTransaction()
|
||||||
|
table1.insert(txn1, Row(listOf(Value.IntegerValue(1), Value.TextValue("alice"))))
|
||||||
|
table1.insert(txn1, Row(listOf(Value.IntegerValue(2), Value.TextValue("bob"))))
|
||||||
|
catalog1.commit(txn1)
|
||||||
|
catalog1.close()
|
||||||
|
|
||||||
|
val catalog2 = DiskCatalog.open(dataFile, walFile)
|
||||||
|
assertEquals(listOf("users"), catalog2.tableNames)
|
||||||
|
val table2 = catalog2.table("users")
|
||||||
|
val reader = catalog2.beginTransaction()
|
||||||
|
assertEquals(setOf(1L, 2L), table2.scan(reader).map { (it.second.values[0] as Value.IntegerValue).value }.toSet())
|
||||||
|
val (_, aliceRow) = table2.findByPrimaryKey(reader, Value.IntegerValue(1))!!
|
||||||
|
assertEquals("alice", (aliceRow.values[1] as Value.TextValue).value)
|
||||||
|
catalog2.commit(reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun aCommittedWriteThatNeverFlushedTheBufferPoolSurvivesAReopen() {
|
||||||
|
val dataFile = tempFile("kestrel-cat-data")
|
||||||
|
val walFile = tempFile("kestrel-cat-wal")
|
||||||
|
|
||||||
|
val catalog1 = DiskCatalog.open(dataFile, walFile)
|
||||||
|
val table1 = catalog1.createTable("users", usersColumns)
|
||||||
|
val txn = catalog1.beginTransaction()
|
||||||
|
table1.insert(txn, Row(listOf(Value.IntegerValue(1), Value.TextValue("alice"))))
|
||||||
|
catalog1.commit(txn)
|
||||||
|
// No catalog1.close(): the dirty pages may still only live in the buffer pool.
|
||||||
|
|
||||||
|
val catalog2 = DiskCatalog.open(dataFile, walFile)
|
||||||
|
val table2 = catalog2.table("users")
|
||||||
|
val reader = catalog2.beginTransaction()
|
||||||
|
assertEquals("alice", table2.findByPrimaryKey(reader, Value.IntegerValue(1))!!.second.values[1].let { (it as Value.TextValue).value })
|
||||||
|
catalog2.commit(reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun aPrimaryKeyIndexThatSplitsSurvivesAReopenWithTheCorrectRoot() {
|
||||||
|
val dataFile = tempFile("kestrel-cat-data")
|
||||||
|
val walFile = tempFile("kestrel-cat-wal")
|
||||||
|
|
||||||
|
val catalog1 = DiskCatalog.open(dataFile, walFile)
|
||||||
|
val table1 = catalog1.createTable("users", usersColumns)
|
||||||
|
val txn = catalog1.beginTransaction()
|
||||||
|
for (id in 1..300L) table1.insert(txn, Row(listOf(Value.IntegerValue(id), Value.TextValue("user$id"))))
|
||||||
|
catalog1.commit(txn)
|
||||||
|
catalog1.close()
|
||||||
|
|
||||||
|
val catalog2 = DiskCatalog.open(dataFile, walFile)
|
||||||
|
val table2 = catalog2.table("users")
|
||||||
|
val reader = catalog2.beginTransaction()
|
||||||
|
assertEquals(300, table2.scan(reader).size)
|
||||||
|
for (id in listOf(1L, 150L, 300L)) {
|
||||||
|
assertEquals("user$id", table2.findByPrimaryKey(reader, Value.IntegerValue(id))!!.second.values[1].let { (it as Value.TextValue).value })
|
||||||
|
}
|
||||||
|
val range = table2.rangeByPrimaryKey(reader, Value.IntegerValue(100), true, Value.IntegerValue(105), true)
|
||||||
|
assertEquals((100..105L).toList(), range.map { (it.second.values[0] as Value.IntegerValue).value })
|
||||||
|
catalog2.commit(reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun aSecondaryIndexCreatedBeforeCloseSurvivesAReopen() {
|
||||||
|
val dataFile = tempFile("kestrel-cat-data")
|
||||||
|
val walFile = tempFile("kestrel-cat-wal")
|
||||||
|
val columns = usersColumns + Column("dept", ColumnType.Text, notNull = false, primaryKey = false)
|
||||||
|
|
||||||
|
val catalog1 = DiskCatalog.open(dataFile, walFile)
|
||||||
|
val table1 = catalog1.createTable("users", columns)
|
||||||
|
catalog1.createIndex("users", "idx_dept", "dept")
|
||||||
|
val txn = catalog1.beginTransaction()
|
||||||
|
table1.insert(txn, Row(listOf(Value.IntegerValue(1), Value.TextValue("alice"), Value.TextValue("eng"))))
|
||||||
|
table1.insert(txn, Row(listOf(Value.IntegerValue(2), Value.TextValue("bob"), Value.TextValue("eng"))))
|
||||||
|
catalog1.commit(txn)
|
||||||
|
catalog1.close()
|
||||||
|
|
||||||
|
val catalog2 = DiskCatalog.open(dataFile, walFile)
|
||||||
|
val table2 = catalog2.table("users")
|
||||||
|
val reader = catalog2.beginTransaction()
|
||||||
|
val engineers = table2.findBySecondary(reader, "idx_dept", Value.TextValue("eng"))
|
||||||
|
assertEquals(setOf("alice", "bob"), engineers.map { (it.second.values[1] as Value.TextValue).value }.toSet())
|
||||||
|
catalog2.commit(reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val orderLineColumns = listOf(
|
||||||
|
Column("order_id", ColumnType.Integer, notNull = true, primaryKey = false),
|
||||||
|
Column("line_no", ColumnType.Integer, notNull = true, primaryKey = false),
|
||||||
|
Column("sku", ColumnType.Text, notNull = false, primaryKey = false)
|
||||||
|
)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun aTableCannotDeclareBothAnInlinePrimaryKeyColumnAndACompositePrimaryKey() {
|
||||||
|
val catalog = DiskCatalog.open(tempFile("kestrel-cat-data"), tempFile("kestrel-cat-wal"))
|
||||||
|
assertFailsWith<DiskCatalogException> {
|
||||||
|
catalog.createTable("users", usersColumns, compositePrimaryKey = listOf("name"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun aCompositePrimaryKeyReferencingAnUnknownColumnIsRejected() {
|
||||||
|
val catalog = DiskCatalog.open(tempFile("kestrel-cat-data"), tempFile("kestrel-cat-wal"))
|
||||||
|
assertFailsWith<DiskCatalogException> {
|
||||||
|
catalog.createTable("order_lines", orderLineColumns, compositePrimaryKey = listOf("order_id", "missing"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun aCompositePrimaryKeyListingTheSameColumnTwiceIsRejected() {
|
||||||
|
val catalog = DiskCatalog.open(tempFile("kestrel-cat-data"), tempFile("kestrel-cat-wal"))
|
||||||
|
assertFailsWith<DiskCatalogException> {
|
||||||
|
catalog.createTable("order_lines", orderLineColumns, compositePrimaryKey = listOf("order_id", "order_id"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Reference in new issue
Block a user