From d08db6f8344ee29459008e635c5e05c91bec9b54 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Wed, 9 Sep 2026 23:21:47 +0200 Subject: [PATCH] test: cover SequenceTest and run --- src/test/kotlin/kestrel/SequenceTest.kt | 162 ++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 src/test/kotlin/kestrel/SequenceTest.kt diff --git a/src/test/kotlin/kestrel/SequenceTest.kt b/src/test/kotlin/kestrel/SequenceTest.kt new file mode 100644 index 0000000..6f7068c --- /dev/null +++ b/src/test/kotlin/kestrel/SequenceTest.kt @@ -0,0 +1,162 @@ +package kestrel + +import kestrel.catalog.Catalog +import kestrel.engine.Executor +import kestrel.engine.SelectResult +import kestrel.engine.StatementResult +import kestrel.sql.lexer.Lexer +import kestrel.sql.parser.Parser +import kestrel.storage.Value +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith + +class SequenceTest { + private val catalog = Catalog() + private val executor = Executor(catalog) + + private fun run(sql: String): List = + Parser(Lexer(sql).tokenize()).parseProgram().map { executor.execute(it) } + + private fun select(sql: String): SelectResult = run(sql).single() as SelectResult + + private fun int(value: Long) = Value.IntegerValue(value) + + @Test + fun nextvalStartsAtOneAndIncrementsByOneByDefault() { + run("CREATE SEQUENCE seq") + run("CREATE TABLE t (id INTEGER)") + run("INSERT INTO t VALUES (NEXTVAL('seq')), (NEXTVAL('seq')), (NEXTVAL('seq'))") + assertEquals(listOf(listOf(int(1)), listOf(int(2)), listOf(int(3))), select("SELECT id FROM t ORDER BY id").rows) + } + + @Test + fun createSequenceOptionsAreHonored() { + run("CREATE SEQUENCE seq START WITH 100 INCREMENT BY 10") + run("CREATE TABLE t (id INTEGER)") + run("INSERT INTO t VALUES (NEXTVAL('seq')), (NEXTVAL('seq'))") + assertEquals(listOf(listOf(int(100)), listOf(int(110))), select("SELECT id FROM t ORDER BY id").rows) + } + + @Test + fun descendingSequenceDefaultsToMaxvalueAndCountsDown() { + run("CREATE SEQUENCE seq INCREMENT BY -1") + run("CREATE TABLE t (id INTEGER)") + run("INSERT INTO t VALUES (NEXTVAL('seq')), (NEXTVAL('seq'))") + assertEquals(listOf(listOf(int(-2)), listOf(int(-1))), select("SELECT id FROM t ORDER BY id").rows) + } + + @Test + fun exhaustedAscendingSequenceWithoutCycleThrows() { + run("CREATE SEQUENCE seq MAXVALUE 2") + run("CREATE TABLE t (id INTEGER)") + run("INSERT INTO t VALUES (NEXTVAL('seq'))") + run("INSERT INTO t VALUES (NEXTVAL('seq'))") + assertFailsWith { run("INSERT INTO t VALUES (NEXTVAL('seq'))") } + } + + @Test + fun cyclingAscendingSequenceWrapsToMinvalue() { + run("CREATE SEQUENCE seq MAXVALUE 2 CYCLE") + run("CREATE TABLE t (id INTEGER)") + run("INSERT INTO t VALUES (NEXTVAL('seq')), (NEXTVAL('seq')), (NEXTVAL('seq'))") + assertEquals(listOf(listOf(int(1)), listOf(int(2)), listOf(int(1))), select("SELECT id FROM t").rows) + } + + @Test + fun dropSequenceRemovesItAndFurtherNextvalFails() { + run("CREATE SEQUENCE seq") + run("DROP SEQUENCE seq") + run("CREATE TABLE t (id INTEGER)") + assertFailsWith { run("INSERT INTO t VALUES (NEXTVAL('seq'))") } + } + + @Test + fun duplicateCreateSequenceFails() { + run("CREATE SEQUENCE seq") + assertFailsWith { run("CREATE SEQUENCE seq") } + } + + @Test + fun droppingAnUnknownSequenceFails() { + assertFailsWith { run("DROP SEQUENCE nope") } + } + + @Test + fun alterSequenceRestartWithResetsPosition() { + run("CREATE SEQUENCE seq") + run("CREATE TABLE t (id INTEGER)") + run("INSERT INTO t VALUES (NEXTVAL('seq')), (NEXTVAL('seq'))") + run("ALTER SEQUENCE seq RESTART WITH 50") + run("INSERT INTO t VALUES (NEXTVAL('seq'))") + assertEquals(listOf(listOf(int(1)), listOf(int(2)), listOf(int(50))), select("SELECT id FROM t ORDER BY id").rows) + } + + @Test + fun bareRestartResetsToOriginalStartValue() { + run("CREATE SEQUENCE seq START WITH 5") + run("CREATE TABLE t (id INTEGER)") + run("INSERT INTO t VALUES (NEXTVAL('seq')), (NEXTVAL('seq'))") + run("ALTER SEQUENCE seq RESTART") + run("INSERT INTO t VALUES (NEXTVAL('seq'))") + assertEquals(listOf(listOf(int(5)), listOf(int(5)), listOf(int(6))), select("SELECT id FROM t ORDER BY id").rows) + } + + @Test + fun alterSequenceIncrementByWithoutRestartPreservesCurrentPosition() { + run("CREATE SEQUENCE seq") + run("CREATE TABLE t (id INTEGER)") + run("INSERT INTO t VALUES (NEXTVAL('seq'))") + run("ALTER SEQUENCE seq INCREMENT BY 100") + run("INSERT INTO t VALUES (NEXTVAL('seq'))") + assertEquals(listOf(listOf(int(1)), listOf(int(101))), select("SELECT id FROM t ORDER BY id").rows) + } + + @Test + fun alterSequenceRestartValueOutOfNewBoundsFails() { + run("CREATE SEQUENCE seq MAXVALUE 10") + assertFailsWith { run("ALTER SEQUENCE seq RESTART WITH 20") } + } + + @Test + fun alteringAnUnknownSequenceFails() { + assertFailsWith { run("ALTER SEQUENCE nope INCREMENT BY 2") } + } + + @Test + fun currvalReturnsLastNextvalInThisSession() { + run("CREATE SEQUENCE seq") + run("CREATE TABLE t (id INTEGER, again INTEGER)") + run("INSERT INTO t VALUES (NEXTVAL('seq'), CURRVAL('seq'))") + assertEquals(listOf(listOf(int(1), int(1))), select("SELECT id, again FROM t").rows) + } + + @Test + fun currvalBeforeAnyNextvalInThisSessionFails() { + run("CREATE SEQUENCE seq") + run("CREATE TABLE t (id INTEGER)") + assertFailsWith { run("INSERT INTO t VALUES (CURRVAL('seq'))") } + } + + @Test + fun alterSequenceInvalidatesRememberedCurrvalForThatSession() { + run("CREATE SEQUENCE seq") + run("CREATE TABLE t (id INTEGER)") + run("INSERT INTO t VALUES (NEXTVAL('seq'))") + run("ALTER SEQUENCE seq INCREMENT BY 1") + assertFailsWith { run("INSERT INTO t VALUES (CURRVAL('seq'))") } + } + + @Test + fun rollbackDoesNotUndoANextvalAdvance() { + run("CREATE SEQUENCE seq") + run("CREATE TABLE t (id INTEGER)") + run("BEGIN") + run("INSERT INTO t VALUES (NEXTVAL('seq'))") + run("ROLLBACK") + run("INSERT INTO t VALUES (NEXTVAL('seq'))") + // The rolled-back row never landed, but NEXTVAL's advance to 1 survives - the surviving + // insert gets 2, not a reused 1. + assertEquals(listOf(listOf(int(2))), select("SELECT id FROM t").rows) + } +