feat(cli): extend Repl with printNames and tableNames

This commit is contained in:
dualfroz committed 2026-09-01 16:19:12 +02:00
1 parent 9387689ade
commit aedd88ca70
2 files changed
+102 -15

No files matched your search

+100 -15
View File
@@ -319,12 +319,34 @@ class Repl(
}
is CreateTableResult -> output.appendLine("Table ${result.tableName} created")
is DropTableResult -> output.appendLine("Table ${result.tableName} dropped")
is TruncateTableResult -> output.appendLine("Table(s) ${result.tableNames.joinToString(", ")} truncated")
is MergeResult -> {
output.appendLine(
"${rowsWord(result.insertedCount)} inserted, ${rowsWord(result.updatedCount)} updated, " +
"${rowsWord(result.deletedCount)} deleted"
)
result.returning?.let { output.appendLine(TableRenderer.render(it)) }
}
is AlterTableResult -> output.appendLine("Table ${result.tableName} altered")
is CreateIndexResult -> output.appendLine("Index ${result.indexName} created")
is DropIndexResult -> output.appendLine("Index ${result.indexName} dropped")
is CreateViewResult -> output.appendLine("View ${result.viewName} created")
is DropViewResult -> output.appendLine("View ${result.viewName} dropped")
is CreateTriggerResult -> output.appendLine("Trigger ${result.triggerName} created")
is DropTriggerResult -> output.appendLine("Trigger ${result.triggerName} dropped")
is CreateSequenceResult -> output.appendLine("Sequence ${result.sequenceName} created")
is DropSequenceResult -> output.appendLine("Sequence ${result.sequenceName} dropped")
is AlterSequenceResult -> output.appendLine("Sequence ${result.sequenceName} altered")
is ExplainResult -> result.plan.forEach { output.appendLine(it) }
BeginResult -> output.appendLine("Transaction started")
CommitResult -> output.appendLine("Transaction committed")
RollbackResult -> output.appendLine("Transaction rolled back")
is SavepointResult -> output.appendLine("Savepoint ${result.name} created")
is ReleaseSavepointResult -> output.appendLine("Savepoint ${result.name} released")
is RollbackToSavepointResult -> output.appendLine("Rolled back to savepoint ${result.name}")
is PrepareResult -> output.appendLine("Prepared statement ${result.name} created")
is DeallocateResult ->
output.appendLine(if (result.all) "All prepared statements deallocated" else "Prepared statement ${result.name} deallocated")
}
}
@@ -332,14 +354,15 @@ class Repl(
val parts = command.split(Regex("\\s+"), limit = 2)
when (parts[0]) {
".exit", ".quit" -> return true
".tables" -> {
val names = catalog.tableNames
if (names.isEmpty()) output.appendLine("(no tables)") else names.forEach { output.appendLine(it) }
}
".indexes" -> {
val names = catalog.indexNames
if (names.isEmpty()) output.appendLine("(no indexes)") else names.forEach { output.appendLine(it) }
}
".tables" -> printNames(tableNames(), "(no tables)")
".indexes" -> printNames(indexNames(), "(no indexes)")
".views" -> printNames(viewNames(), "(no views)")
".triggers" -> printNames(triggerNames(), "(no triggers)")
".sequences" -> printNames(sequenceNames(), "(no sequences)")
".prepared" -> printNames(
diskExecutor?.preparedStatementNames ?: executor.preparedStatementNames,
"(no prepared statements)"
)
".schema" -> {
if (parts.size < 2 || parts[1].isBlank()) {
printError("usage: .schema <table>")
@@ -352,25 +375,87 @@ class Repl(
return false
}
private fun printNames(names: List<String>, emptyMessage: String) {
if (names.isEmpty()) output.appendLine(emptyMessage) else names.forEach { output.appendLine(it) }
}
private fun tableNames(): List<String> = diskCatalog?.tableNames ?: catalog.tableNames
private fun viewNames(): List<String> = diskCatalog?.viewNames ?: catalog.viewNames
private fun triggerNames(): List<String> = diskCatalog?.triggerNames ?: catalog.triggerNames
private fun sequenceNames(): List<String> = diskCatalog?.sequenceNames ?: catalog.sequenceNames
private fun indexNames(): List<String> {
val disk = diskCatalog ?: return catalog.indexNames
return disk.tableNames.flatMap { disk.table(it).secondaryIndexes.map { index -> index.name } }
}
/** Every `CREATE [UNIQUE] INDEX ...;` line for [tableName], one engine or the other - never both. */
private fun indexLines(tableName: String): List<String> {
val disk = diskCatalog
if (disk != null) {
val table = disk.tableOrNull(tableName) ?: return emptyList()
return table.secondaryIndexes.map { index ->
val uniqueKeyword = if (index.unique) "UNIQUE " else ""
"CREATE ${uniqueKeyword}INDEX ${index.name} ON $tableName (${index.columnNames.joinToString(", ")});"
}
}
return catalog.indexDefinitions()
.filter { it.tableName.equals(tableName, ignoreCase = true) }
.map { index ->
val uniqueKeyword = if (index.unique) "UNIQUE " else ""
"CREATE ${uniqueKeyword}INDEX ${index.name} ON $tableName (${index.columnDisplayName});"
}
}
/** Every `CREATE TRIGGER ...` block for [tableName], one engine or the other - never both. */
private fun triggerLines(tableName: String): List<String> {
val definitions = diskCatalog?.triggerDefinitions() ?: catalog.triggerDefinitions()
return definitions
.filter { it.tableName.equals(tableName, ignoreCase = true) }
.map { SqlWriter.triggerText(it) }
}
private fun printSchema(tableName: String) {
val table = catalog.tableOrNull(tableName)
if (table == null) {
val schema = diskCatalog?.tableOrNull(tableName)?.schema
?: diskCatalog?.temporaryTableOrNull(tableName)?.schema
?: catalog.tableOrNull(tableName)?.schema
if (schema == null) {
printError("no such table: $tableName")
return
}
val schema = table.schema
val builder = StringBuilder()
builder.appendLine("CREATE TABLE ${schema.name} (")
schema.columns.forEachIndexed { index, column ->
val lines = mutableListOf<String>()
schema.columns.forEach { column ->
val constraints = buildString {
if (column.primaryKey) append(" PRIMARY KEY")
if (column.notNull && !column.primaryKey) append(" NOT NULL")
column.default?.let { append(" DEFAULT ${SqlWriter.expressionText(it)}") }
}
val comma = if (index < schema.columns.size - 1) "," else ""
builder.appendLine(" ${column.name} ${column.type.displayName}$constraints$comma")
lines.add(" ${column.name} ${column.type.displayWith(column.precision, column.scale)}$constraints")
}
builder.append(");")
if (schema.compositePrimaryKey.isNotEmpty()) {
lines.add(" PRIMARY KEY (${schema.compositePrimaryKey.joinToString(", ")})")
}
schema.foreignKeys.forEach { fk ->
val onDelete = if (fk.onDelete != ForeignKeyAction.NoAction) " ON DELETE ${fk.onDelete.sqlKeyword}" else ""
val onUpdate = if (fk.onUpdate != ForeignKeyAction.NoAction) " ON UPDATE ${fk.onUpdate.sqlKeyword}" else ""
lines.add(
" FOREIGN KEY (${fk.columns.joinToString(", ")}) REFERENCES ${fk.referencedTable} " +
"(${fk.referencedColumns.joinToString(", ")})$onDelete$onUpdate"
)
}
schema.checks.forEach { check ->
lines.add(" CHECK (${SqlWriter.expressionText(check.expression)})")
}
schema.uniqueConstraints.forEach { unique ->
lines.add(" UNIQUE (${unique.columns.joinToString(", ")})")
}
builder.append(lines.joinToString(",\n"))
builder.append("\n);")
output.appendLine(builder.toString())
indexLines(schema.name).forEach { output.appendLine(it) }
triggerLines(schema.name).forEach { output.appendLine(it) }
}
private fun printError(message: String?) {
@@ -31,6 +31,8 @@ object TableRenderer {
is Value.RealValue -> value.value.toString()
is Value.TextValue -> value.value
is Value.BooleanValue -> value.value.toString()
is Value.DecimalValue -> value.value.toPlainString()
is Value.DateValue -> value.value.toString()
is Value.NullValue -> "NULL"
}