From 2a0c532de35b2dffd002a86c4f837b5594f1eaf0 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Tue, 1 Sep 2026 18:50:18 +0200 Subject: [PATCH] feat(engine): extend Executor with buildInsertRow --- src/main/kotlin/kestrel/engine/Executor.kt | 38 +++++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/kestrel/engine/Executor.kt b/src/main/kotlin/kestrel/engine/Executor.kt index a67d2a7..5c566d2 100644 --- a/src/main/kotlin/kestrel/engine/Executor.kt +++ b/src/main/kotlin/kestrel/engine/Executor.kt @@ -1303,13 +1303,41 @@ class Executor(private val catalog: Catalog) : SequenceAccess { ForeignKeyEnforcer.validateRow(catalog, schema, row.values, uncommittedSiblings = newRows) enforceChecks(schema, row) } - val values = MutableList(schema.columns.size) { Value.NullValue } - for ((position, expression) in rawRow.withIndex()) { - val columnIndex = targetIndices[position] + enforcePrimaryKeyUniqueness(schema, table.rows + newRows) + enforceUniqueConstraints(schema, table.rows + newRows) + enforceUniqueIndexes(schema, table.rows + newRows) + table.rows.addAll(newRows) + catalog.reindexTable(schema.name) + fireRowTriggers(schema, TriggerTiming.After, TriggerEvent.Insert, newRows.map { null to it }) + val returning = statement.returning?.let { evaluateReturning(schema, newRows, it) } + return InsertResult(newRows.size, returning) + } + return executeInsertWithConflictHandling(schema, table, newRows, onConflict, statement.returning, triggerContext) + } + + private fun buildInsertRow( + schema: TableSchema, + targetIndices: List, + rawRow: List, + evaluator: Evaluator + ): Row { + if (rawRow.size != targetIndices.size) { + throw KestrelException( + "table ${schema.name} expects ${targetIndices.size} values but ${rawRow.size} were supplied" + ) + } + val values = MutableList(schema.columns.size) { Value.NullValue } + val touched = BooleanArray(schema.columns.size) + for ((position, expression) in rawRow.withIndex()) { + val columnIndex = targetIndices[position] + // A literal DEFAULT in the VALUES list is left untouched here, exactly like a + // column missing from an explicit INSERT column list - both fall through to the + // same default-filling loop below, matching UPDATE ... SET col = DEFAULT semantics. + if (expression != null) { + rejectGeneratedAssignment(schema.columns[columnIndex], explicitValueGiven = true) values[columnIndex] = coerceForColumn(evaluator.evaluate(expression), schema.columns[columnIndex]) + touched[columnIndex] = true } - validateNotNull(schema, values) - Row(values) } enforcePrimaryKeyUniqueness(schema, table.rows + newRows) table.rows.addAll(newRows)