feat(engine): extend SelectRunner with describeFunctionArguments
This commit is contained in:
1 parent
842d0aa223
commit
02741c8ab5
3 files changed
+113
-4
No files matched your search
@@ -1630,11 +1630,37 @@ class SelectRunner(private val catalog: Catalog) {
|
||||
is ParameterExpression -> "\$${expression.index}"
|
||||
}
|
||||
|
||||
private fun describeFunctionArguments(expression: FunctionExpression): String {
|
||||
if (expression.star) return "*"
|
||||
val distinctPrefix = if (expression.distinct) "DISTINCT " else ""
|
||||
val value = expression.argument?.let { describeExpression(it) } ?: ""
|
||||
val separator = expression.separator ?: return "$distinctPrefix$value"
|
||||
val orderBy = if (expression.orderBy.isNotEmpty()) {
|
||||
" ORDER BY " + expression.orderBy.joinToString(", ") { key -> describeExpression(key.expression) + orderKeySuffix(key) }
|
||||
} else ""
|
||||
return "$distinctPrefix$value, ${describeExpression(separator)}$orderBy"
|
||||
}
|
||||
|
||||
private fun orderKeySuffix(key: OrderKey): String {
|
||||
val direction = if (key.descending) " DESC" else ""
|
||||
val nulls = when (key.nullsFirst) {
|
||||
true -> " NULLS FIRST"
|
||||
false -> " NULLS LAST"
|
||||
null -> ""
|
||||
}
|
||||
return direction + nulls
|
||||
}
|
||||
|
||||
private fun describeFilterClause(expression: FunctionExpression): String =
|
||||
expression.filter?.let { " FILTER (WHERE ${describeExpression(it)})" } ?: ""
|
||||
|
||||
private fun literalText(value: Value): String = when (value) {
|
||||
is Value.IntegerValue -> value.value.toString()
|
||||
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"
|
||||
}
|
||||
|
||||
@@ -1646,5 +1672,12 @@ class SelectRunner(private val catalog: Catalog) {
|
||||
val offset: Int
|
||||
)
|
||||
|
||||
private class HashKeys(val buildExpression: Expression, val probeExpression: Expression)
|
||||
private class HashKeys(val buildExpression: Expression, val probeExpression: Expression, val collation: Collation)
|
||||
|
||||
private companion object {
|
||||
const val MAX_RECURSION_ITERATIONS = 10_000
|
||||
|
||||
/** Kinds whose result actually depends on which rows fall inside the frame. */
|
||||
val FRAME_CAPABLE_KINDS = setOf(WindowFunctionKind.Aggregate, WindowFunctionKind.FirstValue, WindowFunctionKind.LastValue)
|
||||
}
|
||||
}
|
||||
@@ -6,20 +6,46 @@ sealed interface StatementResult
|
||||
|
||||
data class SelectResult(val columns: List<String>, val rows: List<List<Value>>) : StatementResult
|
||||
|
||||
data class InsertResult(val count: Int) : StatementResult
|
||||
data class InsertResult(val count: Int, val returning: SelectResult? = null) : StatementResult
|
||||
|
||||
data class UpdateResult(val count: Int) : StatementResult
|
||||
data class UpdateResult(val count: Int, val returning: SelectResult? = null) : StatementResult
|
||||
|
||||
data class DeleteResult(val count: Int) : StatementResult
|
||||
data class DeleteResult(val count: Int, val returning: SelectResult? = null) : StatementResult
|
||||
|
||||
data class CreateTableResult(val tableName: String) : StatementResult
|
||||
|
||||
data class DropTableResult(val tableName: String) : StatementResult
|
||||
|
||||
/** [tableNames] is every table actually truncated - the statement's own list plus, under CASCADE, every transitive FOREIGN KEY referencer added to it. */
|
||||
data class TruncateTableResult(val tableNames: List<String>) : StatementResult
|
||||
|
||||
data class MergeResult(
|
||||
val insertedCount: Int,
|
||||
val updatedCount: Int,
|
||||
val deletedCount: Int,
|
||||
val returning: SelectResult? = null
|
||||
) : StatementResult
|
||||
|
||||
data class AlterTableResult(val tableName: String) : StatementResult
|
||||
|
||||
data class CreateIndexResult(val indexName: String) : StatementResult
|
||||
|
||||
data class DropIndexResult(val indexName: String) : StatementResult
|
||||
|
||||
data class CreateViewResult(val viewName: String) : StatementResult
|
||||
|
||||
data class DropViewResult(val viewName: String) : StatementResult
|
||||
|
||||
data class CreateTriggerResult(val triggerName: String) : StatementResult
|
||||
|
||||
data class DropTriggerResult(val triggerName: String) : StatementResult
|
||||
|
||||
data class CreateSequenceResult(val sequenceName: String) : StatementResult
|
||||
|
||||
data class DropSequenceResult(val sequenceName: String) : StatementResult
|
||||
|
||||
data class AlterSequenceResult(val sequenceName: String) : StatementResult
|
||||
|
||||
data class ExplainResult(val plan: List<String>) : StatementResult
|
||||
|
||||
data object BeginResult : StatementResult
|
||||
@@ -27,3 +53,14 @@ data object BeginResult : StatementResult
|
||||
data object CommitResult : StatementResult
|
||||
|
||||
data object RollbackResult : StatementResult
|
||||
|
||||
data class SavepointResult(val name: String) : StatementResult
|
||||
|
||||
data class ReleaseSavepointResult(val name: String) : StatementResult
|
||||
|
||||
data class RollbackToSavepointResult(val name: String) : StatementResult
|
||||
|
||||
data class PrepareResult(val name: String) : StatementResult
|
||||
|
||||
/** [name] is `null` exactly when [all] is `true` (`DEALLOCATE ALL`), mirroring [kestrel.sql.ast.DeallocateStatement]. */
|
||||
data class DeallocateResult(val name: String?, val all: Boolean) : StatementResult
|
||||
@@ -0,0 +1,39 @@
|
||||
package kestrel.engine
|
||||
|
||||
import kestrel.KestrelException
|
||||
import kestrel.catalog.TableSchema
|
||||
import kestrel.sql.ast.ColumnExpression
|
||||
import kestrel.storage.Collation
|
||||
import kestrel.storage.Row
|
||||
import kestrel.storage.Value
|
||||
|
||||
/**
|
||||
* Binds `NEW`/`OLD` for the one row a firing trigger is currently processing, against [schema] -
|
||||
* always the trigger's OWN table (the one named in its `ON <table>`), never whatever table a
|
||||
* nested body statement happens to target. [newRow] is null for a DELETE trigger, [oldRow] is null
|
||||
* for an INSERT trigger; both are non-null only inside an UPDATE trigger.
|
||||
*/
|
||||
class TriggerRowContext(private val schema: TableSchema, private val newRow: Row?, private val oldRow: Row?) {
|
||||
fun matches(expression: ColumnExpression): Boolean {
|
||||
val qualifier = expression.qualifier ?: return false
|
||||
return (qualifier.equals("new", ignoreCase = true) && newRow != null) ||
|
||||
(qualifier.equals("old", ignoreCase = true) && oldRow != null)
|
||||
}
|
||||
|
||||
fun resolve(expression: ColumnExpression): Value {
|
||||
val row = if (expression.qualifier!!.equals("new", ignoreCase = true)) newRow!! else oldRow!!
|
||||
val index = schema.columnIndex(expression.name)
|
||||
?: throw KestrelException("no such column: ${expression.name}")
|
||||
return row.values[index]
|
||||
}
|
||||
|
||||
/** Column-existence check with no row required, for validating a reference before it is ever fired. */
|
||||
fun checkColumnExists(expression: ColumnExpression) {
|
||||
if (schema.columnIndex(expression.name) == null) {
|
||||
throw KestrelException("no such column: ${expression.name}")
|
||||
}
|
||||
}
|
||||
|
||||
/** `NEW.col`/`OLD.col` reads the same table's schema either way, so its declared COLLATE never depends on which side [expression] names. */
|
||||
fun declaredCollation(expression: ColumnExpression): Collation? = schema.column(expression.name)?.collation
|
||||
}
|
||||
Reference in new issue
Block a user