test(sql): cover DiskExecutorSequenceTest and tempFile
This commit is contained in:
1 parent
e0a11b123c
commit
5d31d6daa0
1 file changed
+160
@@ -0,0 +1,160 @@
|
|||||||
|
package kestrel.store.sql
|
||||||
|
|
||||||
|
import kestrel.KestrelException
|
||||||
|
import kestrel.engine.AlterSequenceResult
|
||||||
|
import kestrel.engine.CreateSequenceResult
|
||||||
|
import kestrel.engine.DropSequenceResult
|
||||||
|
import kestrel.engine.SelectResult
|
||||||
|
import kestrel.engine.StatementResult
|
||||||
|
import kestrel.sql.ast.Statement
|
||||||
|
import kestrel.sql.lexer.Lexer
|
||||||
|
import kestrel.sql.parser.Parser
|
||||||
|
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
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exercises `CREATE`/`DROP`/`ALTER SEQUENCE` and `NEXTVAL`/`CURRVAL` through [DiskExecutor] against a
|
||||||
|
* real [DiskCatalog] backed by temp files - see [DiskExecutorTest] for that general end-to-end
|
||||||
|
* pattern. Most scenarios here are ported 1:1 from [kestrel.SequenceTest] (the in-memory engine's
|
||||||
|
* equivalent), since [DiskExecutor] reuses the exact same [kestrel.catalog.Sequence]/
|
||||||
|
* [kestrel.catalog.SequenceDefinition] arithmetic - the three exceptions are the "ROLLBACK undoes
|
||||||
|
* CREATE/DROP/ALTER SEQUENCE" tests, which do not translate: `CREATE`/`DROP`/`ALTER SEQUENCE` are DDL,
|
||||||
|
* and DDL is rejected inside an explicit disk transaction entirely (see
|
||||||
|
* [rollbackCannotWrapCreateSequenceEitherBecauseItIsRejectedInsideAnExplicitTransaction] and its two
|
||||||
|
* siblings) rather than being undoable by it, unlike the in-memory engine's whole-catalog
|
||||||
|
* snapshot/restore. [sequencePositionSurvivesAReopen] and
|
||||||
|
* [alterSequenceRestartPositionSurvivesAReopen] instead cover what is actually new on the disk-backed
|
||||||
|
* path: [DiskCatalog.nextValue]'s counter write bypasses the WAL entirely (see its own doc), so
|
||||||
|
* nothing but this kind of end-to-end reopen test proves it is still durable.
|
||||||
|
*/
|
||||||
|
class DiskExecutorSequenceTest {
|
||||||
|
private fun tempFile(prefix: String): File = Files.createTempFile(prefix, ".db").toFile().also { it.deleteOnExit() }
|
||||||
|
|
||||||
|
private fun parse(sql: String): List<Statement> = Parser(Lexer(sql).tokenize()).parseProgram()
|
||||||
|
|
||||||
|
private fun int(value: Long) = Value.IntegerValue(value)
|
||||||
|
|
||||||
|
private class Database(val dataFile: File, val walFile: File) {
|
||||||
|
var catalog = DiskCatalog.open(dataFile, walFile)
|
||||||
|
private set
|
||||||
|
var executor = DiskExecutor(catalog)
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun run(sql: String): List<StatementResult> = Parser(Lexer(sql).tokenize()).parseProgram().map { executor.execute(it) }
|
||||||
|
fun select(sql: String): SelectResult = run(sql).single() as SelectResult
|
||||||
|
|
||||||
|
/** Closes and reopens the catalog against the same files, simulating a fresh process. */
|
||||||
|
fun reopen() {
|
||||||
|
catalog.close()
|
||||||
|
catalog = DiskCatalog.open(dataFile, walFile)
|
||||||
|
executor = DiskExecutor(catalog)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun newDatabase(): Database = Database(tempFile("kestrel-disk-seq-data"), tempFile("kestrel-disk-seq-wal"))
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun createSequenceReturnsItsNameAndNextvalStartsAtOneByDefault() {
|
||||||
|
val db = newDatabase()
|
||||||
|
assertEquals(CreateSequenceResult("seq"), db.run("CREATE SEQUENCE seq").single())
|
||||||
|
db.run("CREATE TABLE t (id INTEGER)")
|
||||||
|
db.run("INSERT INTO t VALUES (NEXTVAL('seq')), (NEXTVAL('seq')), (NEXTVAL('seq'))")
|
||||||
|
assertEquals(listOf(listOf(int(1)), listOf(int(2)), listOf(int(3))), db.select("SELECT id FROM t ORDER BY id").rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun createSequenceOptionsAreHonored() {
|
||||||
|
val db = newDatabase()
|
||||||
|
db.run("CREATE SEQUENCE seq START WITH 100 INCREMENT BY 10")
|
||||||
|
db.run("CREATE TABLE t (id INTEGER)")
|
||||||
|
db.run("INSERT INTO t VALUES (NEXTVAL('seq')), (NEXTVAL('seq'))")
|
||||||
|
assertEquals(listOf(listOf(int(100)), listOf(int(110))), db.select("SELECT id FROM t ORDER BY id").rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun descendingSequenceDefaultsToMaxvalueAndCountsDown() {
|
||||||
|
val db = newDatabase()
|
||||||
|
db.run("CREATE SEQUENCE seq INCREMENT BY -1")
|
||||||
|
db.run("CREATE TABLE t (id INTEGER)")
|
||||||
|
db.run("INSERT INTO t VALUES (NEXTVAL('seq')), (NEXTVAL('seq'))")
|
||||||
|
assertEquals(listOf(listOf(int(-2)), listOf(int(-1))), db.select("SELECT id FROM t ORDER BY id").rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun exhaustedAscendingSequenceWithoutCycleThrows() {
|
||||||
|
val db = newDatabase()
|
||||||
|
db.run("CREATE SEQUENCE seq MAXVALUE 2")
|
||||||
|
db.run("CREATE TABLE t (id INTEGER)")
|
||||||
|
db.run("INSERT INTO t VALUES (NEXTVAL('seq'))")
|
||||||
|
db.run("INSERT INTO t VALUES (NEXTVAL('seq'))")
|
||||||
|
assertFailsWith<KestrelException> { db.run("INSERT INTO t VALUES (NEXTVAL('seq'))") }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun cyclingAscendingSequenceWrapsToMinvalue() {
|
||||||
|
val db = newDatabase()
|
||||||
|
db.run("CREATE SEQUENCE seq MAXVALUE 2 CYCLE")
|
||||||
|
db.run("CREATE TABLE t (id INTEGER)")
|
||||||
|
db.run("INSERT INTO t VALUES (NEXTVAL('seq')), (NEXTVAL('seq')), (NEXTVAL('seq'))")
|
||||||
|
assertEquals(listOf(listOf(int(1)), listOf(int(2)), listOf(int(1))), db.select("SELECT id FROM t").rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dropSequenceReturnsItsNameAndFurtherNextvalFails() {
|
||||||
|
val db = newDatabase()
|
||||||
|
db.run("CREATE SEQUENCE seq")
|
||||||
|
assertEquals(DropSequenceResult("seq"), db.run("DROP SEQUENCE seq").single())
|
||||||
|
db.run("CREATE TABLE t (id INTEGER)")
|
||||||
|
assertFailsWith<DiskCatalogException> { db.run("INSERT INTO t VALUES (NEXTVAL('seq'))") }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun duplicateCreateSequenceFails() {
|
||||||
|
val db = newDatabase()
|
||||||
|
db.run("CREATE SEQUENCE seq")
|
||||||
|
assertFailsWith<DiskCatalogException> { db.run("CREATE SEQUENCE seq") }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun droppingAnUnknownSequenceFails() {
|
||||||
|
val db = newDatabase()
|
||||||
|
assertFailsWith<DiskCatalogException> { db.run("DROP SEQUENCE nope") }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun alterSequenceReturnsItsNameAndRestartWithResetsPosition() {
|
||||||
|
val db = newDatabase()
|
||||||
|
db.run("CREATE SEQUENCE seq")
|
||||||
|
db.run("CREATE TABLE t (id INTEGER)")
|
||||||
|
db.run("INSERT INTO t VALUES (NEXTVAL('seq')), (NEXTVAL('seq'))")
|
||||||
|
assertEquals(AlterSequenceResult("seq"), db.run("ALTER SEQUENCE seq RESTART WITH 50").single())
|
||||||
|
db.run("INSERT INTO t VALUES (NEXTVAL('seq'))")
|
||||||
|
assertEquals(listOf(listOf(int(1)), listOf(int(2)), listOf(int(50))), db.select("SELECT id FROM t ORDER BY id").rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun bareRestartResetsToOriginalStartValue() {
|
||||||
|
val db = newDatabase()
|
||||||
|
db.run("CREATE SEQUENCE seq START WITH 5")
|
||||||
|
db.run("CREATE TABLE t (id INTEGER)")
|
||||||
|
db.run("INSERT INTO t VALUES (NEXTVAL('seq')), (NEXTVAL('seq'))")
|
||||||
|
db.run("ALTER SEQUENCE seq RESTART")
|
||||||
|
db.run("INSERT INTO t VALUES (NEXTVAL('seq'))")
|
||||||
|
assertEquals(listOf(listOf(int(5)), listOf(int(5)), listOf(int(6))), db.select("SELECT id FROM t ORDER BY id").rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun alterSequenceIncrementByWithoutRestartPreservesCurrentPosition() {
|
||||||
|
val db = newDatabase()
|
||||||
|
db.run("CREATE SEQUENCE seq")
|
||||||
|
db.run("CREATE TABLE t (id INTEGER)")
|
||||||
|
db.run("INSERT INTO t VALUES (NEXTVAL('seq'))")
|
||||||
|
db.run("ALTER SEQUENCE seq INCREMENT BY 100")
|
||||||
|
db.run("INSERT INTO t VALUES (NEXTVAL('seq'))")
|
||||||
|
assertEquals(listOf(listOf(int(1)), listOf(int(101))), db.select("SELECT id FROM t ORDER BY id").rows)
|
||||||
|
}
|
||||||
|
|
||||||
Reference in new issue
Block a user