diff --git a/src/main/kotlin/kestrel/catalog/Catalog.kt b/src/main/kotlin/kestrel/catalog/Catalog.kt index d95358d..998c211 100644 --- a/src/main/kotlin/kestrel/catalog/Catalog.kt +++ b/src/main/kotlin/kestrel/catalog/Catalog.kt @@ -1,32 +1,61 @@ package kestrel.catalog import kestrel.KestrelException +import kestrel.sql.ast.NextValueExpression +import kestrel.sql.ast.QueryStatement +import kestrel.sql.ast.TriggerEvent +import kestrel.sql.ast.TriggerTiming +import kestrel.storage.Collation import kestrel.storage.Row import kestrel.storage.Table +import kestrel.storage.Value class CatalogSnapshot( val tables: Map>>, - val indexDefinitions: List + val indexDefinitions: List, + val views: Map, + val triggers: Map, + /** + * A reference copy of the map, not a deep copy of each [Sequence] - see [Sequence]'s own doc + * for why that is exactly what makes ROLLBACK undo CREATE/DROP/ALTER SEQUENCE while leaving a + * NEXTVAL advance in place. + */ + val sequences: Map ) class Catalog { private val tables = linkedMapOf() private val indexes = linkedMapOf() + private val views = linkedMapOf() + private val triggers = linkedMapOf() + private val sequences = linkedMapOf() fun createTable(schema: TableSchema) { val key = schema.name.lowercase() if (tables.containsKey(key)) { throw KestrelException("table '${schema.name}' already exists") } + if (views.containsKey(key)) { + throw KestrelException("a view named '${schema.name}' already exists") + } tables[key] = Table(schema) } - fun dropTable(tableName: String) { + fun dropTable(tableName: String, cascade: Boolean = false) { val key = tableName.lowercase() - if (tables.remove(key) == null) { + if (!tables.containsKey(key)) { + if (views.containsKey(key)) { + throw KestrelException("'$tableName' is a view, use DROP VIEW") + } throw KestrelException("no such table: $tableName") } + dropDependentViews(tableName, cascade) + dropDependentForeignKeys(tableName, cascade) + tables.remove(key) indexes.entries.removeAll { it.value.definition.tableName.equals(tableName, ignoreCase = true) } + // A trigger without its table makes no sense to keep around - unlike a FOREIGN KEY + // reference, nothing else depends on it existing, so this always happens, CASCADE or not. + triggers.entries.removeAll { it.value.tableName.equals(tableName, ignoreCase = true) } } fun table(tableName: String): Table =