diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/Expression.kt b/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/Expression.kt index 274d911..697612d 100644 --- a/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/Expression.kt +++ b/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/Expression.kt @@ -8,5 +8,13 @@ public interface Expression, right: Expression ): Expression + + public fun isNull( + e: Expression<*, *, OwnerBuilderUserExtension>, + ): Expression + + public fun isNotNull( + e: Expression<*, *, OwnerBuilderUserExtension>, + ): Expression } } \ No newline at end of file diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/ddl/ModuleCreator.kt b/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/ddl/ModuleCreator.kt index cb777ea..2f9d31f 100644 --- a/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/ddl/ModuleCreator.kt +++ b/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/ddl/ModuleCreator.kt @@ -55,11 +55,17 @@ public interface ModuleCreator { public fun createSelect(initializer: CreateSelect): Select - public interface CreateInsert { - public fun createInsert(table: Table, creator: InsertCreator): QueryUserWrapper + public interface CreateInsertParam { + public fun createInsert(table: Table, creator: InsertCreator.InsertParams): QueryUserWrapper } - public fun createInsert(table: Table, initializer: CreateInsert): Insert + public fun createInsertParams(table: Table, initializer: CreateInsertParam): Insert.InsertParams + + public interface CreateInsertFromQuery { + public fun createInsert(table: Table, creator: InsertCreator.InsertFromQuery): QueryUserWrapper + } + + public fun createInsertFromQuery(table: Table, initializer: CreateInsertFromQuery): Insert.InsertFromQuery public interface CreateUpdate { public fun createUpdate(table: Table, creator: UpdateCreator): QueryUserWrapper diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/dml/Delete.kt b/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/dml/Delete.kt index fad98c8..d3da32c 100644 --- a/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/dml/Delete.kt +++ b/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/dml/Delete.kt @@ -1,5 +1,5 @@ package ru.landgrafhomyak.serdha.api.v0.dml -public interface Delete { +public interface Delete : _CommonQueryMethods.CanBeSubquery { public val userExtension: QueryUserExtension } \ No newline at end of file diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/dml/DeleteCreator.kt b/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/dml/DeleteCreator.kt index 684309b..61ee5b7 100644 --- a/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/dml/DeleteCreator.kt +++ b/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/dml/DeleteCreator.kt @@ -10,5 +10,5 @@ public interface DeleteCreator + public fun returning(s: (_CommonQueryMethods._Returning) -> Unit) } \ No newline at end of file diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/dml/Insert.kt b/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/dml/Insert.kt index e3019af..36a0cc2 100644 --- a/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/dml/Insert.kt +++ b/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/dml/Insert.kt @@ -1,5 +1,9 @@ package ru.landgrafhomyak.serdha.api.v0.dml -public interface Insert { - public val userExtension: UserExtension +public interface Insert { + public val userExtension: QueryUserExtension + + public interface InsertParams : Insert + + public interface InsertFromQuery : Insert } \ No newline at end of file diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/dml/InsertCreator.kt b/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/dml/InsertCreator.kt index a153016..f2a9f45 100644 --- a/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/dml/InsertCreator.kt +++ b/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/dml/InsertCreator.kt @@ -8,24 +8,66 @@ import ru.landgrafhomyak.serdha.api.v0.ddl.UniqueConstraint public interface InsertCreator : _CommonQueryMethods { public val insertedValues: SelectedTable - public fun > insert(column: Column, expression: Expression) + @Suppress("ClassName") + public interface _UpsertCreator { + public fun > oldColumnValue(c: Column): Expression - public fun > insertParam( - column: Column, - paramName: String = column.name - ): InputParam - - public fun > oldColumnValue(c: Column): Expression - - public interface UpsertCreator { public fun > updateColumn(c: Column, e: Expression) } - public fun onConflictUpdate(u: UniqueConstraint, c: UpsertCreator.() -> Unit) + public fun onConflictUpdate(u: UniqueConstraint, c: (_UpsertCreator) -> Unit) public fun onConflictThrow(u: UniqueConstraint) public fun onConflictIgnore(u: UniqueConstraint) - public val returning: _CommonQueryMethods._Returning -} \ No newline at end of file + public fun returningInserted(s: (_CommonQueryMethods._Returning) -> Unit) + + @Suppress("ClassName") + public interface _ReturningUpdated { + public fun > oldColumnValue(c: Column): Expression + } + + public fun returningUpdated(s: (_ReturningUpdated) -> Unit) + + public interface InsertParams : InsertCreator { + public class DataParam<@Suppress("unused") QueryUserExtension : Any> private constructor() + + public val dataExpressionBuilder: Expression.Builder> + + public fun > dataParam(name: String, type: DatabaseType): InputParam> + public fun > nullableDataParam(name: String, type: DatabaseType): InputParam> + + public fun > insertParam( + column: Column, + paramName: String = column.name + ): InputParam> + + public fun > insert(column: Column, expression: Expression>) + } + + public interface InsertFromQuery : InsertCreator { + public fun > insert(column: Column, expression: Expression) + } +} + + + + + + + + + + + + + + + + + + + + + diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/dml/Select.kt b/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/dml/Select.kt index 137ed52..854fade 100644 --- a/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/dml/Select.kt +++ b/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/dml/Select.kt @@ -1,6 +1,6 @@ package ru.landgrafhomyak.serdha.api.v0.dml -public interface Select { +public interface Select: _CommonQueryMethods.CanBeSubquery { public val userExtension: QueryUserExtension public enum class Order { diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/dml/Update.kt b/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/dml/Update.kt index 5aa6d20..05d308b 100644 --- a/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/dml/Update.kt +++ b/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/dml/Update.kt @@ -1,5 +1,5 @@ package ru.landgrafhomyak.serdha.api.v0.dml -public interface Update { +public interface Update : _CommonQueryMethods.CanBeSubquery { public val userExtension: QueryUserExtension } \ No newline at end of file diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/dml/UpdateCreator.kt b/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/dml/UpdateCreator.kt index f34d362..9e9c558 100644 --- a/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/dml/UpdateCreator.kt +++ b/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/dml/UpdateCreator.kt @@ -19,5 +19,5 @@ public interface UpdateCreator + public fun returning(s: (_CommonQueryMethods._Returning) -> Unit) } \ No newline at end of file diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/dml/_CommonQueryMethods.kt b/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/dml/_CommonQueryMethods.kt index 9231e4f..a067829 100644 --- a/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/dml/_CommonQueryMethods.kt +++ b/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/dml/_CommonQueryMethods.kt @@ -4,11 +4,20 @@ import ru.landgrafhomyak.serdha.api.v0.Expression import ru.landgrafhomyak.serdha.api.v0.ddl.Column import ru.landgrafhomyak.serdha.api.v0.ddl.ColumnType import ru.landgrafhomyak.serdha.api.v0.ddl.Table +import ru.landgrafhomyak.serdha.api.v0.runtime.ParametersSetter @Suppress("ClassName") public interface _CommonQueryMethods { public fun selectTable(t: Table): SelectedTable - public fun selectQuery(t: Select): SelectedTable + + public interface CanBeSubquery<@Suppress("unused") SelectedQueryUserExtension : Any> + + public interface SubqueryParametersSetter : ParametersSetter { + public operator fun > set(c: InputParam, value: Expression) + } + + public fun selectingQuery(q: CanBeSubquery, p: (SubqueryParametersSetter) -> Unit): SelectedTable + public fun > param(name: String, type: DatabaseType): InputParam public fun > nullableParam(name: String, type: DatabaseType): InputParam diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/runtime/SynchronizedDatabase.kt b/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/runtime/SynchronizedDatabase.kt index 7d0a7e4..00d2888 100644 --- a/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/runtime/SynchronizedDatabase.kt +++ b/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/runtime/SynchronizedDatabase.kt @@ -4,6 +4,7 @@ import ru.landgrafhomyak.serdha.api.v0.LowLevelApi import ru.landgrafhomyak.serdha.api.v0.ddl.Module import ru.landgrafhomyak.serdha.api.v0.dml.Delete import ru.landgrafhomyak.serdha.api.v0.dml.Insert +import ru.landgrafhomyak.serdha.api.v0.dml.InsertCreator import ru.landgrafhomyak.serdha.api.v0.dml.Select import ru.landgrafhomyak.serdha.api.v0.dml.Update @@ -14,19 +15,23 @@ public interface SynchronizedDatabase : Module _startAutoTransactedSelect(query: Select): _ParametersSetter> + public suspend fun _autoTransactedSelect(query: Select): _ParametersSetter> @Suppress("FunctionName") @LowLevelApi - public suspend fun _startAutoTransactedInsert(query: Insert): _ParametersSetter?> + public suspend fun _autoTransactedInsertParams(query: Insert.InsertParams): _ParametersSetter, _ResultSet>?>> @Suppress("FunctionName") @LowLevelApi - public suspend fun _startAutoTransactedUpdate(query: Update): _ParametersSetter?> + public suspend fun _autoTransactedInsertFromQuery(query: Insert.InsertFromQuery): _ParametersSetter>?> @Suppress("FunctionName") @LowLevelApi - public suspend fun _startAutoTransactedDelete(query: Delete): _ParametersSetter?> + public suspend fun _autoTransactedUpdate(query: Update): _ParametersSetter?> + + @Suppress("FunctionName") + @LowLevelApi + public suspend fun _autoTransactedDelete(query: Delete): _ParametersSetter?> public interface AtomicScript { public suspend fun executeTransaction(transaction: Transaction, context: C, args: A): R diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/runtime/Transaction.kt b/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/runtime/Transaction.kt index d76e4f3..ae77750 100644 --- a/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/runtime/Transaction.kt +++ b/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/runtime/Transaction.kt @@ -3,25 +3,30 @@ package ru.landgrafhomyak.serdha.api.v0.runtime import ru.landgrafhomyak.serdha.api.v0.LowLevelApi import ru.landgrafhomyak.serdha.api.v0.dml.Delete import ru.landgrafhomyak.serdha.api.v0.dml.Insert +import ru.landgrafhomyak.serdha.api.v0.dml.InsertCreator import ru.landgrafhomyak.serdha.api.v0.dml.Select import ru.landgrafhomyak.serdha.api.v0.dml.Update public interface Transaction { @Suppress("FunctionName") @LowLevelApi - public fun _select(compiledQuery: Select): _ParametersSetter> + public fun _select(compiledQuery: Select): _ParametersSetter> @Suppress("FunctionName") @LowLevelApi - public fun _insert(compiledQuery: Insert): _ParametersSetter?> + public fun _insertParams(compiledQuery: Insert.InsertParams): _ParametersSetter, _ResultSet>?>> @Suppress("FunctionName") @LowLevelApi - public fun _update(compiledQuery: Update): _ParametersSetter?> + public fun _insertFromQuery(compiledQuery: Insert.InsertFromQuery): _ParametersSetter>?> @Suppress("FunctionName") @LowLevelApi - public fun _delete(compiledQuery: Delete): _ParametersSetter?> + public fun _update(compiledQuery: Update): _ParametersSetter?> + + @Suppress("FunctionName") + @LowLevelApi + public fun _delete(compiledQuery: Delete): _ParametersSetter?> public suspend fun rollback() diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/runtime/_ParametersSetter.kt b/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/runtime/_ParametersSetter.kt index 3b8fa6a..393d8d0 100644 --- a/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/runtime/_ParametersSetter.kt +++ b/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/runtime/_ParametersSetter.kt @@ -10,4 +10,10 @@ public interface _ParametersSetter : Param @LowLevelApi public suspend fun _abort() + + @LowLevelApi + public interface _Multi : _ParametersSetter { + @LowLevelApi + public fun _next(): Boolean + } } \ No newline at end of file diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/runtime/_ResultSet.kt b/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/runtime/_ResultSet.kt index f5ae163..cea86d5 100644 --- a/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/runtime/_ResultSet.kt +++ b/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/runtime/_ResultSet.kt @@ -4,12 +4,16 @@ import ru.landgrafhomyak.serdha.api.v0.LowLevelApi @Suppress("ClassName") @LowLevelApi -public interface _ResultSet : Row { +public interface _ResultSet : Row { @Suppress("FunctionName") @LowLevelApi public suspend fun _next(): Boolean @Suppress("FunctionName") @LowLevelApi - public suspend fun _close() + public suspend fun _abort() + + @Suppress("FunctionName") + @LowLevelApi + public suspend fun _close(): Next } \ No newline at end of file diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/runtime/transaction_methods.kt b/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/runtime/transaction_methods.kt index cd8843e..7a23034 100644 --- a/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/runtime/transaction_methods.kt +++ b/src/commonMain/kotlin/ru/landgrafhomyak/serdha/api/v0/runtime/transaction_methods.kt @@ -8,6 +8,7 @@ import kotlin.jvm.JvmName import ru.landgrafhomyak.serdha.api.v0.LowLevelApi import ru.landgrafhomyak.serdha.api.v0.dml.Delete import ru.landgrafhomyak.serdha.api.v0.dml.Insert +import ru.landgrafhomyak.serdha.api.v0.dml.InsertCreator import ru.landgrafhomyak.serdha.api.v0.dml.Select import ru.landgrafhomyak.serdha.api.v0.dml.Update @@ -47,15 +48,15 @@ internal inline fun _safeAutoClose(finally: () -> Unit, action: () -> R): R public class QueryWithoutReturnError(message: String, public val query: Any) : Error(message) -@Suppress("FunctionName") +@Suppress("FunctionName", "DuplicatedCode") @PublishedApi @OptIn(LowLevelApi::class) internal suspend inline fun _wrapWithLambdas( compiledQuery: Any, - queryGetter: () -> _ParametersSetter?>, + queryGetter: () -> _ParametersSetter?>, parameters: (ParametersSetter) -> Unit, hasReturning: Boolean, - returning: (_ResultSet) -> Unit, + returning: (_ResultSet) -> Unit, queryWithoutErrorMessage: String ) { contract { @@ -76,7 +77,7 @@ internal suspend inline fun _wrapWithLambdas( return } - _safeAutoClose(finally = { rows._close() }) { + _safeAutoClose(onAbort = { rows._abort() }, onSuccess = { rows._close() }) { returning(rows) } } @@ -88,7 +89,7 @@ public class ResultNotSingleError : Error() @OptIn(LowLevelApi::class) internal suspend inline fun _wrapWithLambdasSingleOrNull( compiledQuery: Any, - queryGetter: () -> _ParametersSetter?>, + queryGetter: () -> _ParametersSetter?>, parameters: (ParametersSetter) -> Unit, hasReturning: Boolean, returning: (Row) -> R, @@ -121,7 +122,7 @@ internal suspend inline fun _wrapWithLambdasSingleOrNull( @OptIn(LowLevelApi::class) internal suspend inline fun _wrapWithLambdasIterate( compiledQuery: Any, - queryGetter: () -> _ParametersSetter?>, + queryGetter: () -> _ParametersSetter?>, parameters: (ParametersSetter) -> Unit, hasReturning: Boolean, returning: (Row) -> Unit, @@ -141,7 +142,7 @@ internal suspend inline fun _wrapWithLambdasIterate( @OptIn(LowLevelApi::class) internal suspend inline fun _wrapWithLambdasMap( compiledQuery: Any, - queryGetter: () -> _ParametersSetter?>, + queryGetter: () -> _ParametersSetter?>, parameters: (ParametersSetter) -> Unit, hasReturning: Boolean, returning: (Row) -> R, @@ -179,7 +180,7 @@ public suspend inline fun SynchronizedDatabase<*>.au } return _wrapWithLambdasSingleOrNull( compiledQuery = compiledQuery, - queryGetter = { this._startAutoTransactedSelect(compiledQuery) }, + queryGetter = { this._autoTransactedSelect(compiledQuery) }, parameters = parameters, hasReturning = true, returning = rowsConsumer, @@ -240,7 +241,7 @@ public suspend inline fun SynchronizedDatabase<*>.autoT } _wrapWithLambdasIterate( compiledQuery = compiledQuery, - queryGetter = { this._startAutoTransactedSelect(compiledQuery) }, + queryGetter = { this._autoTransactedSelect(compiledQuery) }, parameters = parameters, hasReturning = true, returning = rowsConsumer, @@ -281,7 +282,7 @@ public suspend inline fun SynchronizedDatabase<*>.au } return _wrapWithLambdasMap( compiledQuery = compiledQuery, - queryGetter = { this._startAutoTransactedSelect(compiledQuery) }, + queryGetter = { this._autoTransactedSelect(compiledQuery) }, parameters = parameters, hasReturning = true, returning = rowsConsumer, @@ -289,161 +290,508 @@ public suspend inline fun SynchronizedDatabase<*>.au ) } +@Suppress("FunctionName", "DuplicatedCode") +@PublishedApi +@OptIn(LowLevelApi::class) +internal suspend inline fun _wrapInsertParamsWithLambdas( + compiledQuery: Insert.InsertParams, + queryGetter: () -> _ParametersSetter, _ResultSet>?>>, + parameters: (ParametersSetter) -> Unit, + data: Iterable, + rowFormatter: (T, ParametersSetter>) -> Unit, + hasReturning: Boolean, + returningInserted: (_ResultSet) -> Unit, + returningUpdated: (_ResultSet) -> Unit, + queryWithoutErrorMessage: String +) { + contract { + callsInPlace(queryGetter, InvocationKind.EXACTLY_ONCE) + callsInPlace(parameters, InvocationKind.EXACTLY_ONCE) + callsInPlace(rowFormatter, InvocationKind.UNKNOWN) + callsInPlace(returningInserted, InvocationKind.AT_MOST_ONCE) + callsInPlace(returningUpdated, InvocationKind.AT_MOST_ONCE) + } + val paramsSetter = queryGetter() + + _safeAutoClose(onAbort = { paramsSetter._abort() }) { + parameters(paramsSetter) + } + + val insertableValues = paramsSetter._execute() + _safeAutoClose(onAbort = { insertableValues._abort() }) { + for (e in data) { + insertableValues._next() + rowFormatter(e, insertableValues) + } + } + + val insertedRows = insertableValues._execute() + if (insertedRows == null) { + if (hasReturning) + throw QueryWithoutReturnError(queryWithoutErrorMessage, compiledQuery) + return + } + + _safeAutoClose(onAbort = { insertedRows._abort() }) { + returningInserted(insertedRows) + } + val updatedRows = insertedRows._close() + _safeAutoClose(onAbort = { insertedRows._abort() }, onSuccess = { updatedRows._abort() }) { + returningInserted(insertedRows) + } +} @OptIn(LowLevelApi::class) -public suspend inline fun Transaction.insert( - compiledQuery: Insert, +public suspend inline fun Transaction.insertParams( + compiledQuery: Insert.InsertParams, + parameters: (ParametersSetter) -> Unit = {}, + data: Iterable, + rowFormatter: (T, ParametersSetter>) -> Unit, +) { + contract { + callsInPlace(parameters, InvocationKind.EXACTLY_ONCE) + callsInPlace(rowFormatter, InvocationKind.UNKNOWN) + } + _wrapInsertParamsWithLambdas( + compiledQuery = compiledQuery, + queryGetter = { this._insertParams(compiledQuery) }, + parameters = parameters, + data = data, + rowFormatter = rowFormatter, + hasReturning = false, + returningInserted = {}, + returningUpdated = {}, + queryWithoutErrorMessage = "" + ) +} + +@OptIn(LowLevelApi::class) +public suspend inline fun SynchronizedDatabase<*>.autoTransactedInsertParams( + compiledQuery: Insert.InsertParams, + parameters: (ParametersSetter) -> Unit = {}, + data: Iterable, + rowFormatter: (T, ParametersSetter>) -> Unit, +) { + contract { + callsInPlace(parameters, InvocationKind.EXACTLY_ONCE) + callsInPlace(rowFormatter, InvocationKind.UNKNOWN) + } + _wrapInsertParamsWithLambdas( + compiledQuery = compiledQuery, + queryGetter = { this._autoTransactedInsertParams(compiledQuery) }, + parameters = parameters, + data = data, + rowFormatter = rowFormatter, + hasReturning = false, + returningInserted = {}, + returningUpdated = {}, + queryWithoutErrorMessage = "" + ) +} + +@OptIn(LowLevelApi::class) +public suspend inline fun Transaction.insertParamsReturningIterate( + compiledQuery: Insert.InsertParams, + parameters: (ParametersSetter) -> Unit = {}, + data: Iterable, + rowFormatter: (T, ParametersSetter>) -> Unit, + insertedRows: (Row) -> Unit, + updatedRows: (Row) -> Unit, +) { + contract { + callsInPlace(parameters, InvocationKind.EXACTLY_ONCE) + callsInPlace(rowFormatter, InvocationKind.UNKNOWN) + } + _wrapInsertParamsWithLambdas( + compiledQuery = compiledQuery, + queryGetter = { this._insertParams(compiledQuery) }, + parameters = parameters, + data = data, + rowFormatter = rowFormatter, + hasReturning = true, + returningInserted = { rs -> while (rs._next()) insertedRows(rs) }, + returningUpdated = { rs -> while (rs._next()) updatedRows(rs) }, + queryWithoutErrorMessage = "" + ) +} + +@OptIn(LowLevelApi::class) +public suspend inline fun SynchronizedDatabase<*>.autoTransactedInsertParametersReturningIterate( + compiledQuery: Insert.InsertParams, + parameters: (ParametersSetter) -> Unit = {}, + data: Iterable, + rowFormatter: (T, ParametersSetter>) -> Unit, + insertedRows: (Row) -> Unit, + updatedRows: (Row) -> Unit, +) { + contract { + callsInPlace(parameters, InvocationKind.EXACTLY_ONCE) + callsInPlace(rowFormatter, InvocationKind.UNKNOWN) + } + _wrapInsertParamsWithLambdas( + compiledQuery = compiledQuery, + queryGetter = { this._autoTransactedInsertParams(compiledQuery) }, + parameters = parameters, + data = data, + rowFormatter = rowFormatter, + hasReturning = true, + returningInserted = { rs -> while (rs._next()) insertedRows(rs) }, + returningUpdated = { rs -> while (rs._next()) updatedRows(rs) }, + queryWithoutErrorMessage = "" + ) +} + + +@PublishedApi +internal object SingleIterable : Iterable, Iterator { + private var isReturned = false + + override fun iterator(): Iterator = this + + override fun hasNext(): Boolean = !this.isReturned + + override fun next(): Unit { + if (this.isReturned) throw IllegalStateException() + this.isReturned = true + } +} + +@OptIn(LowLevelApi::class) +public suspend inline fun Transaction.insertSingle( + compiledQuery: Insert.InsertParams, + parameters: (ParametersSetter) -> Unit = {}, + rowData: (ParametersSetter>) -> Unit, +) { + contract { + callsInPlace(parameters, InvocationKind.EXACTLY_ONCE) + callsInPlace(rowData, InvocationKind.EXACTLY_ONCE) + } + _wrapInsertParamsWithLambdas( + compiledQuery = compiledQuery, + queryGetter = { this._insertParams(compiledQuery) }, + parameters = parameters, + data = SingleIterable, + rowFormatter = { _, r -> rowData(r) }, + hasReturning = false, + returningInserted = {}, + returningUpdated = {}, + queryWithoutErrorMessage = "" + ) +} + +@OptIn(LowLevelApi::class) +public suspend inline fun SynchronizedDatabase<*>.autoTransactedInsertSingle( + compiledQuery: Insert.InsertParams, + parameters: (ParametersSetter) -> Unit = {}, + rowData: (ParametersSetter>) -> Unit, +) { + contract { + callsInPlace(parameters, InvocationKind.EXACTLY_ONCE) + callsInPlace(rowData, InvocationKind.EXACTLY_ONCE) + } + _wrapInsertParamsWithLambdas( + compiledQuery = compiledQuery, + queryGetter = { this._autoTransactedInsertParams(compiledQuery) }, + parameters = parameters, + data = SingleIterable, + rowFormatter = { _, r -> rowData(r) }, + hasReturning = false, + returningInserted = {}, + returningUpdated = {}, + queryWithoutErrorMessage = "" + ) +} + +@Suppress("DuplicatedCode") +@OptIn(LowLevelApi::class) +public suspend inline fun Transaction.insertSingleReturning( + compiledQuery: Insert.InsertParams, + parameters: (ParametersSetter) -> Unit = {}, + rowData: (ParametersSetter>) -> Unit = {}, + insertedRows: (Row) -> R, + updatedRows: (Row) -> R +): R { + contract { + callsInPlace(parameters, InvocationKind.EXACTLY_ONCE) + callsInPlace(rowData, InvocationKind.EXACTLY_ONCE) + callsInPlace(insertedRows, InvocationKind.AT_MOST_ONCE) + callsInPlace(updatedRows, InvocationKind.AT_MOST_ONCE) + } + var isReturned = false + var ret: R? = null + _wrapInsertParamsWithLambdas( + compiledQuery = compiledQuery, + queryGetter = { this._insertParams(compiledQuery) }, + parameters = parameters, + hasReturning = true, + data = SingleIterable, + rowFormatter = { _, r -> rowData(r) }, + returningInserted = returningInserted@{ rs -> + if (!rs._next()) return@returningInserted + isReturned = true + ret = insertedRows(rs) + if (rs._next()) throw ResultNotSingleError() + }, + returningUpdated = returningUpdated@{ rs -> + if (!rs._next()) return@returningUpdated + if (isReturned) throw ResultNotSingleError() + isReturned = true + ret = insertedRows(rs) + if (rs._next()) throw ResultNotSingleError() + }, + queryWithoutErrorMessage = "" + ) + if (!isReturned) + throw QueryWithoutReturnError("", compiledQuery) + @Suppress("UNCHECKED_CAST") + return ret as R +} + +@Suppress("DuplicatedCode") +@OptIn(LowLevelApi::class) +public suspend inline fun SynchronizedDatabase<*>.autoTransactedInsertSingleReturning( + compiledQuery: Insert.InsertParams, + parameters: (ParametersSetter) -> Unit = {}, + rowData: (ParametersSetter>) -> Unit = {}, + insertedRows: (Row) -> R, + updatedRows: (Row) -> R +): R { + contract { + callsInPlace(parameters, InvocationKind.EXACTLY_ONCE) + callsInPlace(rowData, InvocationKind.EXACTLY_ONCE) + callsInPlace(insertedRows, InvocationKind.AT_MOST_ONCE) + callsInPlace(updatedRows, InvocationKind.AT_MOST_ONCE) + } + var isReturned = false + var ret: R? = null + _wrapInsertParamsWithLambdas( + compiledQuery = compiledQuery, + queryGetter = { this._autoTransactedInsertParams(compiledQuery) }, + parameters = parameters, + hasReturning = true, + data = SingleIterable, + rowFormatter = { _, r -> rowData(r) }, + returningInserted = returningInserted@{ rs -> + if (!rs._next()) return@returningInserted + isReturned = true + ret = insertedRows(rs) + if (rs._next()) throw ResultNotSingleError() + }, + returningUpdated = returningUpdated@{ rs -> + if (!rs._next()) return@returningUpdated + if (isReturned) throw ResultNotSingleError() + isReturned = true + ret = insertedRows(rs) + if (rs._next()) throw ResultNotSingleError() + }, + queryWithoutErrorMessage = "" + ) + if (!isReturned) + throw QueryWithoutReturnError("", compiledQuery) + @Suppress("UNCHECKED_CAST") + return ret as R +} + +@Suppress("FunctionName", "DuplicatedCode") +@PublishedApi +@OptIn(LowLevelApi::class) +internal suspend inline fun _wrapInsertFromQueryWithLambdas( + compiledQuery: Insert.InsertFromQuery, + queryGetter: () -> _ParametersSetter>?>, + parameters: (ParametersSetter) -> Unit, + hasReturning: Boolean, + returningInserted: (_ResultSet) -> Unit, + returningUpdated: (_ResultSet) -> Unit, + queryWithoutErrorMessage: String +) { + contract { + callsInPlace(queryGetter, InvocationKind.EXACTLY_ONCE) + callsInPlace(parameters, InvocationKind.EXACTLY_ONCE) + callsInPlace(returningInserted, InvocationKind.AT_MOST_ONCE) + callsInPlace(returningUpdated, InvocationKind.AT_MOST_ONCE) + } + val paramsSetter = queryGetter() + + _safeAutoClose(onAbort = { paramsSetter._abort() }) { + parameters(paramsSetter) + } + + val insertedRows = paramsSetter._execute() + if (insertedRows == null) { + if (hasReturning) + throw QueryWithoutReturnError(queryWithoutErrorMessage, compiledQuery) + return + } + + _safeAutoClose(onAbort = { insertedRows._abort() }) { + returningInserted(insertedRows) + } + val updatedRows = insertedRows._close() + _safeAutoClose(onAbort = { insertedRows._abort() }, onSuccess = { updatedRows._abort() }) { + returningInserted(insertedRows) + } +} + +@OptIn(LowLevelApi::class) +public suspend inline fun Transaction.insertFromQuery( + compiledQuery: Insert.InsertFromQuery, parameters: (ParametersSetter) -> Unit = {}, ) { contract { callsInPlace(parameters, InvocationKind.EXACTLY_ONCE) } - _wrapWithLambdas( + _wrapInsertFromQueryWithLambdas( compiledQuery = compiledQuery, - queryGetter = { this._insert(compiledQuery) }, + queryGetter = { this._insertFromQuery(compiledQuery) }, parameters = parameters, hasReturning = false, - returning = {}, - "" + returningInserted = {}, + returningUpdated = {}, + queryWithoutErrorMessage = "" ) } @OptIn(LowLevelApi::class) -public suspend inline fun SynchronizedDatabase<*>.autoTransactedInsert( - compiledQuery: Insert, +public suspend inline fun SynchronizedDatabase<*>.autoTransactedInsertFromQuery( + compiledQuery: Insert.InsertFromQuery, parameters: (ParametersSetter) -> Unit = {}, ) { contract { callsInPlace(parameters, InvocationKind.EXACTLY_ONCE) } - _wrapWithLambdas( + _wrapInsertFromQueryWithLambdas( compiledQuery = compiledQuery, - queryGetter = { this._startAutoTransactedInsert(compiledQuery) }, + queryGetter = { this._autoTransactedInsertFromQuery(compiledQuery) }, parameters = parameters, hasReturning = false, - returning = {}, - "" + returningInserted = {}, + returningUpdated = {}, + queryWithoutErrorMessage = "" ) } @OptIn(LowLevelApi::class) -public suspend inline fun Transaction.insertReturningSingleOrNull( - compiledQuery: Insert, +public suspend inline fun Transaction.insertFromQueryReturningIterate( + compiledQuery: Insert.InsertFromQuery, parameters: (ParametersSetter) -> Unit = {}, - rowsConsumer: (Row) -> R + insertedRows: (Row) -> Unit, + updatedRows: (Row) -> Unit +) { + contract { + callsInPlace(parameters, InvocationKind.EXACTLY_ONCE) + callsInPlace(insertedRows, InvocationKind.UNKNOWN) + callsInPlace(updatedRows, InvocationKind.UNKNOWN) + } + _wrapInsertFromQueryWithLambdas( + compiledQuery = compiledQuery, + queryGetter = { this._insertFromQuery(compiledQuery) }, + parameters = parameters, + hasReturning = true, + returningInserted = { rs -> while (rs._next()) insertedRows(rs) }, + returningUpdated = { rs -> while (rs._next()) updatedRows(rs) }, + queryWithoutErrorMessage = "" + ) +} + +@OptIn(LowLevelApi::class) +public suspend inline fun SynchronizedDatabase<*>.autoTransactedInsertFromQueryReturningIterate( + compiledQuery: Insert.InsertFromQuery, + parameters: (ParametersSetter) -> Unit = {}, + insertedRows: (Row) -> Unit, + updatedRows: (Row) -> Unit +) { + contract { + callsInPlace(parameters, InvocationKind.EXACTLY_ONCE) + callsInPlace(insertedRows, InvocationKind.UNKNOWN) + callsInPlace(updatedRows, InvocationKind.UNKNOWN) + } + _wrapInsertFromQueryWithLambdas( + compiledQuery = compiledQuery, + queryGetter = { this._autoTransactedInsertFromQuery(compiledQuery) }, + parameters = parameters, + hasReturning = true, + returningInserted = { rs -> while (rs._next()) insertedRows(rs) }, + returningUpdated = { rs -> while (rs._next()) updatedRows(rs) }, + queryWithoutErrorMessage = "" + ) +} + +@OptIn(LowLevelApi::class) +public suspend inline fun Transaction.insertFromQueryReturningSingleOrNull( + compiledQuery: Insert.InsertFromQuery, + parameters: (ParametersSetter) -> Unit = {}, + insertedRows: (Row) -> R, + updatedRows: (Row) -> R ): R? { contract { callsInPlace(parameters, InvocationKind.EXACTLY_ONCE) - callsInPlace(rowsConsumer, InvocationKind.AT_MOST_ONCE) + callsInPlace(insertedRows, InvocationKind.AT_MOST_ONCE) + callsInPlace(updatedRows, InvocationKind.AT_MOST_ONCE) } - return _wrapWithLambdasSingleOrNull( + var isReturned = false + var ret: R? = null + _wrapInsertFromQueryWithLambdas( compiledQuery = compiledQuery, - queryGetter = { this._insert(compiledQuery) }, + queryGetter = { this._insertFromQuery(compiledQuery) }, parameters = parameters, hasReturning = true, - returning = rowsConsumer, - "" + returningInserted = returningInserted@{ rs -> + if (!rs._next()) return@returningInserted + isReturned = true + ret = insertedRows(rs) + if (rs._next()) throw ResultNotSingleError() + }, + returningUpdated = returningUpdated@{ rs -> + if (!rs._next()) return@returningUpdated + if (isReturned) throw ResultNotSingleError() + isReturned = true + ret = insertedRows(rs) + if (rs._next()) throw ResultNotSingleError() + }, + queryWithoutErrorMessage = "" ) + if (!isReturned) + return null + return ret } @OptIn(LowLevelApi::class) -public suspend inline fun SynchronizedDatabase<*>.autoTransactedInsertReturningSingleOrNull( - compiledQuery: Insert, +public suspend inline fun SynchronizedDatabase<*>.autoTransactedInsertFromQueryReturningSingleOrNull( + compiledQuery: Insert.InsertFromQuery, parameters: (ParametersSetter) -> Unit = {}, - rowsConsumer: (Row) -> R + insertedRows: (Row) -> R, + updatedRows: (Row) -> R ): R? { contract { callsInPlace(parameters, InvocationKind.EXACTLY_ONCE) - callsInPlace(rowsConsumer, InvocationKind.AT_MOST_ONCE) + callsInPlace(insertedRows, InvocationKind.AT_MOST_ONCE) + callsInPlace(updatedRows, InvocationKind.AT_MOST_ONCE) } - return _wrapWithLambdasSingleOrNull( + var isReturned = false + var ret: R? = null + _wrapInsertFromQueryWithLambdas( compiledQuery = compiledQuery, - queryGetter = { this._startAutoTransactedInsert(compiledQuery) }, + queryGetter = { this._autoTransactedInsertFromQuery(compiledQuery) }, parameters = parameters, hasReturning = true, - returning = rowsConsumer, - "" - ) -} - -@OptIn(LowLevelApi::class) -public suspend inline fun Transaction.insertReturningIterate( - compiledQuery: Insert, - parameters: (ParametersSetter) -> Unit, - rowsConsumer: (Row) -> Unit -) { - contract { - callsInPlace(parameters, InvocationKind.EXACTLY_ONCE) - callsInPlace(rowsConsumer, InvocationKind.UNKNOWN) - } - _wrapWithLambdasIterate( - compiledQuery = compiledQuery, - queryGetter = { this._insert(compiledQuery) }, - parameters = parameters, - hasReturning = true, - returning = rowsConsumer, - "" - ) -} - -@OptIn(LowLevelApi::class) -public suspend inline fun SynchronizedDatabase<*>.autoTransactedInsertReturningIterate( - compiledQuery: Insert, - parameters: (ParametersSetter) -> Unit, - rowsConsumer: (Row) -> Unit -) { - contract { - callsInPlace(parameters, InvocationKind.EXACTLY_ONCE) - callsInPlace(rowsConsumer, InvocationKind.UNKNOWN) - } - _wrapWithLambdasIterate( - compiledQuery = compiledQuery, - queryGetter = { this._startAutoTransactedInsert(compiledQuery) }, - parameters = parameters, - hasReturning = true, - returning = rowsConsumer, - "" - ) -} - -@OptIn(LowLevelApi::class) -public suspend inline fun Transaction.insertReturningMap( - compiledQuery: Insert, - parameters: (ParametersSetter) -> Unit, - rowsConsumer: (Row) -> R -): List { - contract { - callsInPlace(parameters, InvocationKind.EXACTLY_ONCE) - callsInPlace(rowsConsumer, InvocationKind.UNKNOWN) - } - return _wrapWithLambdasMap( - compiledQuery = compiledQuery, - queryGetter = { this._insert(compiledQuery) }, - parameters = parameters, - hasReturning = true, - returning = rowsConsumer, - "" - ) -} - -@OptIn(LowLevelApi::class) -public suspend inline fun SynchronizedDatabase<*>.autoTransactedInsertReturningMap( - compiledQuery: Insert, - parameters: (ParametersSetter) -> Unit, - rowsConsumer: (Row) -> R -): List { - contract { - callsInPlace(parameters, InvocationKind.EXACTLY_ONCE) - callsInPlace(rowsConsumer, InvocationKind.UNKNOWN) - } - return _wrapWithLambdasMap( - compiledQuery = compiledQuery, - queryGetter = { this._startAutoTransactedInsert(compiledQuery) }, - parameters = parameters, - hasReturning = true, - returning = rowsConsumer, - "" + returningInserted = returningInserted@{ rs -> + if (!rs._next()) return@returningInserted + isReturned = true + ret = insertedRows(rs) + if (rs._next()) throw ResultNotSingleError() + }, + returningUpdated = returningUpdated@{ rs -> + if (!rs._next()) return@returningUpdated + if (isReturned) throw ResultNotSingleError() + isReturned = true + ret = insertedRows(rs) + if (rs._next()) throw ResultNotSingleError() + }, + queryWithoutErrorMessage = "" ) + if (!isReturned) + return null + return ret } @OptIn(LowLevelApi::class) @@ -474,7 +822,7 @@ public suspend inline fun SynchronizedDatabase<*>.autoT } _wrapWithLambdas( compiledQuery = compiledQuery, - queryGetter = { this._startAutoTransactedUpdate(compiledQuery) }, + queryGetter = { this._autoTransactedUpdate(compiledQuery) }, parameters = parameters, hasReturning = false, returning = {}, @@ -515,7 +863,7 @@ public suspend inline fun SynchronizedDatabase<*>.au } return _wrapWithLambdasSingleOrNull( compiledQuery = compiledQuery, - queryGetter = { this._startAutoTransactedUpdate(compiledQuery) }, + queryGetter = { this._autoTransactedUpdate(compiledQuery) }, parameters = parameters, hasReturning = true, returning = rowsConsumer, @@ -555,7 +903,7 @@ public suspend inline fun SynchronizedDatabase<*>.autoT } _wrapWithLambdasIterate( compiledQuery = compiledQuery, - queryGetter = { this._startAutoTransactedUpdate(compiledQuery) }, + queryGetter = { this._autoTransactedUpdate(compiledQuery) }, parameters = parameters, hasReturning = true, returning = rowsConsumer, @@ -595,7 +943,7 @@ public suspend inline fun SynchronizedDatabase<*>.au } return _wrapWithLambdasMap( compiledQuery = compiledQuery, - queryGetter = { this._startAutoTransactedUpdate(compiledQuery) }, + queryGetter = { this._autoTransactedUpdate(compiledQuery) }, parameters = parameters, hasReturning = true, returning = rowsConsumer, @@ -631,7 +979,7 @@ public suspend inline fun SynchronizedDatabase<*>.autoT } _wrapWithLambdas( compiledQuery = compiledQuery, - queryGetter = { this._startAutoTransactedDelete(compiledQuery) }, + queryGetter = { this._autoTransactedDelete(compiledQuery) }, parameters = parameters, hasReturning = false, returning = {}, @@ -672,7 +1020,7 @@ public suspend inline fun SynchronizedDatabase<*>.au } return _wrapWithLambdasSingleOrNull( compiledQuery = compiledQuery, - queryGetter = { this._startAutoTransactedDelete(compiledQuery) }, + queryGetter = { this._autoTransactedDelete(compiledQuery) }, parameters = parameters, hasReturning = true, returning = rowsConsumer, @@ -712,7 +1060,7 @@ public suspend inline fun SynchronizedDatabase<*>.autoT } _wrapWithLambdasIterate( compiledQuery = compiledQuery, - queryGetter = { this._startAutoTransactedDelete(compiledQuery) }, + queryGetter = { this._autoTransactedDelete(compiledQuery) }, parameters = parameters, hasReturning = true, returning = rowsConsumer, @@ -752,7 +1100,7 @@ public suspend inline fun SynchronizedDatabase<*>.au } return _wrapWithLambdasMap( compiledQuery = compiledQuery, - queryGetter = { this._startAutoTransactedDelete(compiledQuery) }, + queryGetter = { this._autoTransactedDelete(compiledQuery) }, parameters = parameters, hasReturning = true, returning = rowsConsumer,