test: cover tableName and parsesCreateTableWithoutTempIsNotTemporary

This commit is contained in:
dualfroz committed 2026-09-09 14:58:43 +02:00
1 parent 4a817d0837
commit 1e0560573b
1 file changed
+187 -2
+187 -2
View File
@@ -1,29 +1,91 @@
package kestrel
import kestrel.catalog.ForeignKeyAction
import kestrel.sql.ast.AddColumn
import kestrel.sql.ast.AlterSequenceStatement
import kestrel.sql.ast.AlterTableStatement
import kestrel.sql.ast.Assignment
import kestrel.sql.ast.BetweenExpression
import kestrel.sql.ast.BinaryExpression
import kestrel.sql.ast.BinaryOperator
import kestrel.sql.ast.CallExpression
import kestrel.sql.ast.CaseExpression
import kestrel.sql.ast.CastExpression
import kestrel.sql.ast.ChangeColumnType
import kestrel.sql.ast.CollateExpression
import kestrel.sql.ast.ColumnExpression
import kestrel.sql.ast.CreateIndexStatement
import kestrel.sql.ast.CreateSequenceStatement
import kestrel.sql.ast.CreateTableStatement
import kestrel.sql.ast.CreateTriggerStatement
import kestrel.sql.ast.CreateViewStatement
import kestrel.sql.ast.CubeSpec
import kestrel.sql.ast.CurrentValueExpression
import kestrel.sql.ast.DeallocateStatement
import kestrel.sql.ast.DeleteStatement
import kestrel.sql.ast.DropColumn
import kestrel.sql.ast.DropMode
import kestrel.sql.ast.DropSequenceStatement
import kestrel.sql.ast.DropTableStatement
import kestrel.sql.ast.DropTriggerStatement
import kestrel.sql.ast.DropViewStatement
import kestrel.sql.ast.ExecuteStatement
import kestrel.sql.ast.ExistsExpression
import kestrel.sql.ast.ExplainStatement
import kestrel.sql.ast.Expression
import kestrel.sql.ast.ExpressionProjection
import kestrel.sql.ast.FrameBoundType
import kestrel.sql.ast.FrameExclude
import kestrel.sql.ast.FrameUnit
import kestrel.sql.ast.FunctionExpression
import kestrel.sql.ast.GroupingSetsSpec
import kestrel.sql.ast.InExpression
import kestrel.sql.ast.InSubqueryExpression
import kestrel.sql.ast.InsertStatement
import kestrel.sql.ast.IsNullExpression
import kestrel.sql.ast.JoinType
import kestrel.sql.ast.LikeExpression
import kestrel.sql.ast.LiteralExpression
import kestrel.sql.ast.MergeActionExpression
import kestrel.sql.ast.MergeMatchedDelete
import kestrel.sql.ast.MergeMatchedUpdate
import kestrel.sql.ast.MergeNotMatchedInsert
import kestrel.sql.ast.MergeStatement
import kestrel.sql.ast.NextValueExpression
import kestrel.sql.ast.OrderKey
import kestrel.sql.ast.ParameterExpression
import kestrel.sql.ast.PrepareStatement
import kestrel.sql.ast.QueryStatement
import kestrel.sql.ast.RenameColumn
import kestrel.sql.ast.RenameTable
import kestrel.sql.ast.RollupSpec
import kestrel.sql.ast.ScalarSubqueryExpression
import kestrel.sql.ast.SelectStatement
import kestrel.sql.ast.SetOperationStatement
import kestrel.sql.ast.SetOperator
import kestrel.sql.ast.StarProjection
import kestrel.sql.ast.Statement
import kestrel.sql.ast.SubquerySource
import kestrel.sql.ast.TableSource
import kestrel.sql.ast.TriggerEvent
import kestrel.sql.ast.TriggerTiming
import kestrel.sql.ast.TruncateTableStatement
import kestrel.sql.ast.UnaryExpression
import kestrel.sql.ast.UnaryOperator
import kestrel.sql.ast.UpdateStatement
import kestrel.sql.ast.WindowExpression
import kestrel.sql.ast.WindowFunctionKind
import kestrel.sql.ast.WithQuery
import kestrel.sql.lexer.Lexer
import kestrel.sql.parser.Parser
import kestrel.storage.Collation
import kestrel.storage.ColumnType
import kestrel.storage.Value
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertTrue
@@ -36,6 +98,8 @@ class ParserTest {
private fun whereOf(sql: String): Expression = (parseOne(sql) as SelectStatement).where!!
private fun tableName(select: QueryStatement): String = ((select as SelectStatement).from as TableSource).name
private fun firstProjection(sql: String): Expression =
((parseOne(sql) as SelectStatement).projection as ExpressionProjection).items.first().expression
@@ -55,6 +119,38 @@ class ParserTest {
assertTrue(!statement.columns[2].notNull)
}
@Test
fun parsesCreateTableWithoutTempIsNotTemporary() {
val statement = parseOne("CREATE TABLE users (id INTEGER PRIMARY KEY)") as CreateTableStatement
assertTrue(!statement.temporary)
}
@Test
fun parsesCreateTempTable() {
val statement = parseOne("CREATE TEMP TABLE scratch (id INTEGER PRIMARY KEY, note TEXT)") as CreateTableStatement
assertTrue(statement.temporary)
assertEquals("scratch", statement.tableName)
assertEquals(2, statement.columns.size)
}
@Test
fun parsesCreateTemporaryTable() {
val statement = parseOne("CREATE TEMPORARY TABLE scratch (id INTEGER PRIMARY KEY)") as CreateTableStatement
assertTrue(statement.temporary)
}
@Test
fun tempKeywordIsCaseInsensitive() {
val statement = parseOne("create temp table scratch (id integer primary key)") as CreateTableStatement
assertTrue(statement.temporary)
}
@Test
fun tempOnAnythingOtherThanTableIsAParseError() {
assertFailsWith<ParseException> { parse("CREATE TEMP VIEW v AS SELECT 1") }
assertFailsWith<ParseException> { parse("CREATE TEMP INDEX idx ON t (a)") }
}
@Test
fun parsesInsertWithoutColumnList() {
val statement = parseOne("INSERT INTO users VALUES (1, 'ada', 36)")
@@ -71,11 +167,20 @@ class ParserTest {
assertEquals(3, statement.rows.size)
}
@Test
fun parsesInsertValuesWithLiteralDefaultKeyword() {
val statement = parseOne("INSERT INTO t (a, b, c) VALUES (1, DEFAULT, 3)") as InsertStatement
assertEquals(3, statement.rows[0].size)
assertNotNull(statement.rows[0][0])
assertNull(statement.rows[0][1])
assertNotNull(statement.rows[0][2])
}
@Test
fun parsesSelectStar() {
val statement = parseOne("SELECT * FROM users") as SelectStatement
assertEquals(StarProjection, statement.projection)
assertEquals("users", statement.tableName)
assertEquals("users", tableName(statement))
assertNull(statement.where)
assertNull(statement.orderBy)
assertNull(statement.limit)
@@ -88,10 +193,90 @@ class ParserTest {
) as SelectStatement
val projection = statement.projection as ExpressionProjection
assertEquals("who", projection.items[0].alias)
assertEquals(true, statement.orderBy!!.descending)
assertEquals(true, statement.orderBy!!.keys.single().descending)
assertEquals(5, statement.limit)
}
@Test
fun parsesLimitFollowedByOffset() {
val statement = parseOne("SELECT id FROM users LIMIT 10 OFFSET 20") as SelectStatement
assertEquals(10, statement.limit)
assertEquals(20, statement.offset)
}
@Test
fun parsesOffsetFollowedByLimitInEitherOrder() {
val statement = parseOne("SELECT id FROM users OFFSET 20 LIMIT 10") as SelectStatement
assertEquals(10, statement.limit)
assertEquals(20, statement.offset)
}
@Test
fun parsesOffsetAloneWithoutLimit() {
val statement = parseOne("SELECT id FROM users OFFSET 5") as SelectStatement
assertNull(statement.limit)
assertEquals(5, statement.offset)
}
@Test
fun offsetDefaultsToNullWhenAbsent() {
val statement = parseOne("SELECT id FROM users LIMIT 5") as SelectStatement
assertNull(statement.offset)
}
@Test
fun rejectsARepeatedLimitOrOffsetClause() {
assertFailsWith<ParseException> { parse("SELECT id FROM users LIMIT 5 LIMIT 10") }
assertFailsWith<ParseException> { parse("SELECT id FROM users OFFSET 5 OFFSET 10") }
}
@Test
fun rejectsANonIntegerAfterOffset() {
assertFailsWith<ParseException> { parse("SELECT id FROM users OFFSET 'five'") }
}
@Test
fun parsesOrderByWithMultipleKeysAndIndependentDirections() {
val statement = parseOne("SELECT name FROM users ORDER BY dept ASC, age DESC, id") as SelectStatement
val keys = statement.orderBy!!.keys
assertEquals(3, keys.size)
assertEquals("dept", (keys[0].expression as ColumnExpression).name)
assertEquals(false, keys[0].descending)
assertEquals("age", (keys[1].expression as ColumnExpression).name)
assertEquals(true, keys[1].descending)
assertEquals("id", (keys[2].expression as ColumnExpression).name)
assertEquals(false, keys[2].descending)
}
@Test
fun parsesOrderByWithExplicitNullsFirstAndNullsLastPerKey() {
val statement = parseOne(
"SELECT name FROM users ORDER BY dept NULLS FIRST, age DESC NULLS LAST, id"
) as SelectStatement
val keys = statement.orderBy!!.keys
assertEquals(3, keys.size)
assertEquals(false, keys[0].descending)
assertEquals(true, keys[0].nullsFirst)
assertEquals(true, keys[1].descending)
assertEquals(false, keys[1].nullsFirst)
assertEquals(false, keys[2].descending)
assertNull(keys[2].nullsFirst)
}
@Test
fun nullsAndFirstAndLastRemainUsableAsOrdinaryColumnNamesOutsideTheNullsClause() {
val statement = parseOne("SELECT id FROM users ORDER BY nulls DESC") as SelectStatement
val key = statement.orderBy!!.keys.single()
assertEquals(ColumnExpression("nulls"), key.expression)
assertEquals(true, key.descending)
assertNull(key.nullsFirst)
}
@Test
fun nullsClauseWithoutFirstOrLastFailsToParse() {
assertFailsWith<ParseException> { parseOne("SELECT id FROM users ORDER BY age NULLS") }
}
@Test
fun parsesDeleteAndDropAndUpdate() {
assertTrue(parseOne("DELETE FROM t WHERE id = 1") is DeleteStatement)