From e9377b76407af2d6432495b78058f2e82a861805 Mon Sep 17 00:00:00 2001 From: Andrew Golovashevich Date: Mon, 24 Feb 2025 02:15:03 +0300 Subject: [PATCH] Functions to safe IO with queries --- .../user_commons/executors/RowConsumer0.kt | 7 + .../user_commons/executors/RowConsumer1.kt | 7 + .../user_commons/executors/RowProducer0.kt | 7 + .../user_commons/executors/RowProducer1.kt | 7 + .../user_commons/executors/RowProducer2.kt | 7 + .../user_commons/executors/_autoclose.kt | 29 +++ .../serdha0/user_commons/executors/row2row.kt | 168 ++++++++++++++ .../user_commons/executors/row2table.kt | 212 ++++++++++++++++++ .../user_commons/executors/row2void.kt | 32 +++ .../user_commons/executors/table2void.kt | 109 +++++++++ 10 files changed, 585 insertions(+) create mode 100644 src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/RowConsumer0.kt create mode 100644 src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/RowConsumer1.kt create mode 100644 src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/RowProducer0.kt create mode 100644 src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/RowProducer1.kt create mode 100644 src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/RowProducer2.kt create mode 100644 src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/_autoclose.kt create mode 100644 src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/row2row.kt create mode 100644 src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/row2table.kt create mode 100644 src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/row2void.kt create mode 100644 src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/table2void.kt diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/RowConsumer0.kt b/src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/RowConsumer0.kt new file mode 100644 index 0000000..6baba1d --- /dev/null +++ b/src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/RowConsumer0.kt @@ -0,0 +1,7 @@ +package ru.landgrafhomyak.db.serdha0.user_commons.executors + +import ru.landgrafhomyak.db.serdha0.api.runtime.OutputRow + +public fun interface RowConsumer0 { + public fun transformRow(row: OutputRow): R +} \ No newline at end of file diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/RowConsumer1.kt b/src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/RowConsumer1.kt new file mode 100644 index 0000000..2c54ae0 --- /dev/null +++ b/src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/RowConsumer1.kt @@ -0,0 +1,7 @@ +package ru.landgrafhomyak.db.serdha0.user_commons.executors + +import ru.landgrafhomyak.db.serdha0.api.runtime.OutputRow + +public fun interface RowConsumer1 { + public fun transformRow(row: OutputRow, index: Int): R +} \ No newline at end of file diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/RowProducer0.kt b/src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/RowProducer0.kt new file mode 100644 index 0000000..846080a --- /dev/null +++ b/src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/RowProducer0.kt @@ -0,0 +1,7 @@ +package ru.landgrafhomyak.db.serdha0.user_commons.executors + +import ru.landgrafhomyak.db.serdha0.api.runtime.InputRow + +public fun interface RowProducer0 { + public fun initializeRow(row: InputRow) +} \ No newline at end of file diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/RowProducer1.kt b/src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/RowProducer1.kt new file mode 100644 index 0000000..3febf2e --- /dev/null +++ b/src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/RowProducer1.kt @@ -0,0 +1,7 @@ +package ru.landgrafhomyak.db.serdha0.user_commons.executors + +import ru.landgrafhomyak.db.serdha0.api.runtime.InputRow + +public fun interface RowProducer1 { + public fun transformToRow(row: InputRow, value: E) +} \ No newline at end of file diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/RowProducer2.kt b/src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/RowProducer2.kt new file mode 100644 index 0000000..2caeb97 --- /dev/null +++ b/src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/RowProducer2.kt @@ -0,0 +1,7 @@ +package ru.landgrafhomyak.db.serdha0.user_commons.executors + +import ru.landgrafhomyak.db.serdha0.api.runtime.InputRow + +public fun interface RowProducer2 { + public fun transformToRow(row: InputRow, index: Int, value: E) +} \ No newline at end of file diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/_autoclose.kt b/src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/_autoclose.kt new file mode 100644 index 0000000..428b4bc --- /dev/null +++ b/src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/_autoclose.kt @@ -0,0 +1,29 @@ +@file:JvmName("_AutocloseKt") + +package ru.landgrafhomyak.db.serdha0.user_commons.executors + +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract +import kotlin.jvm.JvmName + + +@Suppress("FunctionName") +@PublishedApi +internal inline fun _safeAutoClose(onAbort: () -> Unit = {}, action: () -> R): R { + contract { + callsInPlace(action, InvocationKind.EXACTLY_ONCE) + callsInPlace(onAbort, InvocationKind.AT_MOST_ONCE) + } + val ret: R + try { + ret = action() + } catch (e1: Throwable) { + try { + onAbort() + } catch (e2: Throwable) { + e1.addSuppressed(e2) + } + throw e1 + } + return ret +} \ No newline at end of file diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/row2row.kt b/src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/row2row.kt new file mode 100644 index 0000000..816f6a0 --- /dev/null +++ b/src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/row2row.kt @@ -0,0 +1,168 @@ +@file:OptIn(LowLevelApi::class) +@file:JvmName("Row2Void") +@file:Suppress("DuplicatedCode") + +package ru.landgrafhomyak.db.serdha0.user_commons.executors + +import kotlin.jvm.JvmName +import ru.landgrafhomyak.db.serdha0.api.LowLevelApi +import ru.landgrafhomyak.db.serdha0.api.queries._Query +import ru.landgrafhomyak.db.serdha0.api.runtime.Transaction + +public class ExpectedOneRowException : Error("Expected at least one row, but table is empty") +public class TooManyRowsException : Error("Expected exactly one row, but got more") + +public suspend inline fun Transaction.selectExactlyOneOrError( + compiledQuery: _Query.Params2Table, + params: RowProducer0, + result: RowConsumer0 +): R { + val iRow = this._executeQuery(compiledQuery) + _safeAutoClose(onAbort = { iRow._abort() }) { + params.initializeRow(iRow) + } + val oRow = iRow._finish() + val ret: R + _safeAutoClose(onAbort = { oRow._abort() }) { + if (!oRow._next()) + throw ExpectedOneRowException() + ret = result.transformRow(oRow) + if (oRow._next()) + throw TooManyRowsException() + } + oRow._finish() + return ret +} + +public suspend inline fun Transaction.selectExactlyOneOrError( + compiledQuery: _Query.Void2Table, + result: RowConsumer0 +): R { + val oRow = this._executeQuery(compiledQuery) + val ret: R + _safeAutoClose(onAbort = { oRow._abort() }) { + if (!oRow._next()) + throw ExpectedOneRowException() + ret = result.transformRow(oRow) + if (oRow._next()) + throw TooManyRowsException() + } + oRow._finish() + return ret +} + +public suspend inline fun Transaction.selectExactlyOneOrNull( + compiledQuery: _Query.Params2Table, + params: RowProducer0, + result: RowConsumer0 +): R? { + val iRow = this._executeQuery(compiledQuery) + _safeAutoClose(onAbort = { iRow._abort() }) { + params.initializeRow(iRow) + } + val oRow = iRow._finish() + val ret: R? + _safeAutoClose(onAbort = { oRow._abort() }) { + if (!oRow._next()) { + ret = null + return@_safeAutoClose + } + ret = result.transformRow(oRow) + if (oRow._next()) + throw TooManyRowsException() + } + oRow._finish() + return ret +} + +public suspend inline fun Transaction.selectExactlyOneOrNull( + compiledQuery: _Query.Void2Table, + result: RowConsumer0 +): R? { + val oRow = this._executeQuery(compiledQuery) + val ret: R? + _safeAutoClose(onAbort = { oRow._abort() }) { + if (!oRow._next()) { + ret = null + return@_safeAutoClose + } + ret = result.transformRow(oRow) + if (oRow._next()) + throw TooManyRowsException() + } + oRow._finish() + return ret +} +public suspend inline fun Transaction.selectFirstOrError( + compiledQuery: _Query.Params2Table, + params: RowProducer0, + result: RowConsumer0 +): R { + val iRow = this._executeQuery(compiledQuery) + _safeAutoClose(onAbort = { iRow._abort() }) { + params.initializeRow(iRow) + } + val oRow = iRow._finish() + val ret: R + _safeAutoClose(onAbort = { oRow._abort() }) { + if (!oRow._next()) + throw ExpectedOneRowException() + ret = result.transformRow(oRow) + } + oRow._finish() + return ret +} + +public suspend inline fun Transaction.selectFirstOrError( + compiledQuery: _Query.Void2Table, + result: RowConsumer0 +): R { + val oRow = this._executeQuery(compiledQuery) + val ret: R + _safeAutoClose(onAbort = { oRow._abort() }) { + if (!oRow._next()) + throw ExpectedOneRowException() + ret = result.transformRow(oRow) + } + oRow._finish() + return ret +} + +public suspend inline fun Transaction.selectFirstOrNull( + compiledQuery: _Query.Params2Table, + params: RowProducer0, + result: RowConsumer0 +): R? { + val iRow = this._executeQuery(compiledQuery) + _safeAutoClose(onAbort = { iRow._abort() }) { + params.initializeRow(iRow) + } + val oRow = iRow._finish() + val ret: R? + _safeAutoClose(onAbort = { oRow._abort() }) { + if (!oRow._next()) { + ret = null + return@_safeAutoClose + } + ret = result.transformRow(oRow) + } + oRow._finish() + return ret +} + +public suspend inline fun Transaction.selectFirstOrNull( + compiledQuery: _Query.Void2Table, + result: RowConsumer0 +): R? { + val oRow = this._executeQuery(compiledQuery) + val ret: R? + _safeAutoClose(onAbort = { oRow._abort() }) { + if (!oRow._next()) { + ret = null + return@_safeAutoClose + } + ret = result.transformRow(oRow) + } + oRow._finish() + return ret +} \ No newline at end of file diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/row2table.kt b/src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/row2table.kt new file mode 100644 index 0000000..8a9a883 --- /dev/null +++ b/src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/row2table.kt @@ -0,0 +1,212 @@ +@file:OptIn(LowLevelApi::class) +@file:JvmName("Row2Void") +@file:Suppress("DuplicatedCode") + +package ru.landgrafhomyak.db.serdha0.user_commons.executors + +import kotlin.jvm.JvmName +import ru.landgrafhomyak.db.serdha0.api.LowLevelApi +import ru.landgrafhomyak.db.serdha0.api.queries._Query +import ru.landgrafhomyak.db.serdha0.api.runtime.Transaction + +public suspend inline fun Transaction.select( + compiledQuery: _Query.Params2Table, + params: RowProducer0, + transform: RowConsumer0 +) { + val iRow = this._executeQuery(compiledQuery) + _safeAutoClose(onAbort = { iRow._abort() }) { + params.initializeRow(iRow) + } + val oRow = iRow._finish() + _safeAutoClose(onAbort = { oRow._abort() }) { + while (oRow._next()) + transform.transformRow(oRow) + } + oRow._finish() +} + +public suspend inline fun Transaction.select( + compiledQuery: _Query.Void2Table, + transform: RowConsumer0 +) { + val oRow = this._executeQuery(compiledQuery) + _safeAutoClose(onAbort = { oRow._abort() }) { + while (oRow._next()) + transform.transformRow(oRow) + } + oRow._finish() +} + +public suspend inline fun Transaction.mapRows( + compiledQuery: _Query.Params2Table, + params: RowProducer0, + transform: RowConsumer0 +): List { + val iRow = this._executeQuery(compiledQuery) + _safeAutoClose(onAbort = { iRow._abort() }) { + params.initializeRow(iRow) + } + val oRow = iRow._finish() + val out = ArrayList() + _safeAutoClose(onAbort = { oRow._abort() }) { + while (oRow._next()) + out.add(transform.transformRow(oRow)) + } + oRow._finish() + return out +} + +public suspend inline fun Transaction.mapRows( + compiledQuery: _Query.Void2Table, + transform: RowConsumer0 +): List { + val oRow = this._executeQuery(compiledQuery) + val out = ArrayList() + _safeAutoClose(onAbort = { oRow._abort() }) { + while (oRow._next()) + out.add(transform.transformRow(oRow)) + } + oRow._finish() + return out +} + +public suspend inline fun Transaction.mapRowsIndexed( + compiledQuery: _Query.Params2Table, + params: RowProducer0, + firstIndex: Int = 0, + transform: RowConsumer1 +): List { + val iRow = this._executeQuery(compiledQuery) + _safeAutoClose(onAbort = { iRow._abort() }) { + params.initializeRow(iRow) + } + val oRow = iRow._finish() + val out = ArrayList() + _safeAutoClose(onAbort = { oRow._abort() }) { + var i = firstIndex + while (oRow._next()) + out.add(transform.transformRow(oRow, i++)) + } + oRow._finish() + return out +} + +public suspend inline fun Transaction.mapRowsIndexed( + compiledQuery: _Query.Void2Table, + firstIndex: Int = 0, + transform: RowConsumer1 +): List { + val oRow = this._executeQuery(compiledQuery) + val out = ArrayList() + _safeAutoClose(onAbort = { oRow._abort() }) { + var i = firstIndex + while (oRow._next()) + out.add(transform.transformRow(oRow, i++)) + } + oRow._finish() + return out +} + +public suspend inline fun > Transaction.mapRowsTo( + compiledQuery: _Query.Params2Table, + params: RowProducer0, + dst: C, + transform: RowConsumer0 +): C { + val iRow = this._executeQuery(compiledQuery) + _safeAutoClose(onAbort = { iRow._abort() }) { + params.initializeRow(iRow) + } + val oRow = iRow._finish() + _safeAutoClose(onAbort = { oRow._abort() }) { + while (oRow._next()) + dst.add(transform.transformRow(oRow)) + } + oRow._finish() + return dst +} + +public suspend inline fun > Transaction.mapRowsTo( + compiledQuery: _Query.Void2Table, + dst: C, + transform: RowConsumer0 +): C { + val oRow = this._executeQuery(compiledQuery) + _safeAutoClose(onAbort = { oRow._abort() }) { + while (oRow._next()) + dst.add(transform.transformRow(oRow)) + } + oRow._finish() + return dst +} + +public suspend inline fun > Transaction.mapRowsIndexedTo( + compiledQuery: _Query.Params2Table, + params: RowProducer0, + dst: C, firstIndex: Int = 0, + transform: RowConsumer1 +): C { + val iRow = this._executeQuery(compiledQuery) + _safeAutoClose(onAbort = { iRow._abort() }) { + params.initializeRow(iRow) + } + val oRow = iRow._finish() + _safeAutoClose(onAbort = { oRow._abort() }) { + var i = firstIndex + while (oRow._next()) + dst.add(transform.transformRow(oRow, i++)) + } + oRow._finish() + return dst +} + +public suspend inline fun > Transaction.mapRowsIndexedTo( + compiledQuery: _Query.Void2Table, + dst: C, firstIndex: Int = 0, + transform: RowConsumer1 +): C { + val oRow = this._executeQuery(compiledQuery) + _safeAutoClose(onAbort = { oRow._abort() }) { + var i = firstIndex + while (oRow._next()) + dst.add(transform.transformRow(oRow, i++)) + } + oRow._finish() + return dst +} + +public suspend inline fun Transaction.mapRowsTo( + compiledQuery: _Query.Params2Table, + params: RowProducer0, + dst: Array, dstOffset: Int = 0, + transform: RowConsumer0 +): Array { + val iRow = this._executeQuery(compiledQuery) + _safeAutoClose(onAbort = { iRow._abort() }) { + params.initializeRow(iRow) + } + val oRow = iRow._finish() + _safeAutoClose(onAbort = { oRow._abort() }) { + var i = dstOffset + while (oRow._next()) + dst[i++] = transform.transformRow(oRow) + } + oRow._finish() + return dst +} + +public suspend inline fun Transaction.mapRowsTo( + compiledQuery: _Query.Void2Table, + dst: Array, dstOffset: Int = 0, + transform: RowConsumer0 +): Array { + val oRow = this._executeQuery(compiledQuery) + _safeAutoClose(onAbort = { oRow._abort() }) { + var i = dstOffset + while (oRow._next()) + dst[i++] = transform.transformRow(oRow) + } + oRow._finish() + return dst +} \ No newline at end of file diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/row2void.kt b/src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/row2void.kt new file mode 100644 index 0000000..4daf8b3 --- /dev/null +++ b/src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/row2void.kt @@ -0,0 +1,32 @@ +@file:OptIn(LowLevelApi::class) +@file:JvmName("Row2Void") +@file:Suppress("DuplicatedCode") + +package ru.landgrafhomyak.db.serdha0.user_commons.executors + +import kotlin.jvm.JvmName +import ru.landgrafhomyak.db.serdha0.api.LowLevelApi +import ru.landgrafhomyak.db.serdha0.api.queries._Query +import ru.landgrafhomyak.db.serdha0.api.runtime.Transaction + +public suspend inline fun Transaction.executeWithParams( + compiledQuery: _Query.Params2Void, + params: RowProducer0 +) { + val iRow = this._executeQuery(compiledQuery) + _safeAutoClose(onAbort = { iRow._abort() }) { + params.initializeRow(iRow) + } + iRow._finish() +} + +public suspend inline fun Transaction.executeWithParams( + compiledQuery: _Query.Params2Table, + params: RowProducer0 +) { + val iRow = this._executeQuery(compiledQuery) + _safeAutoClose(onAbort = { iRow._abort() }) { + params.initializeRow(iRow) + } + iRow._finish()._finish() +} \ No newline at end of file diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/table2void.kt b/src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/table2void.kt new file mode 100644 index 0000000..af332b8 --- /dev/null +++ b/src/commonMain/kotlin/ru/landgrafhomyak/db/serdha0/user_commons/executors/table2void.kt @@ -0,0 +1,109 @@ +@file:OptIn(LowLevelApi::class) +@file:JvmName("Rows2VoidKt") +@file:Suppress("DuplicatedCode") + +package ru.landgrafhomyak.db.serdha0.user_commons.executors + +import kotlin.jvm.JvmName +import ru.landgrafhomyak.db.serdha0.api.LowLevelApi +import ru.landgrafhomyak.db.serdha0.api.queries._Query +import ru.landgrafhomyak.db.serdha0.api.runtime.Transaction + +public suspend inline fun Transaction.mapToRows( + compiledQuery: _Query.Table2Void, + collection: Iterable, + transform: RowProducer1 +) { + val iRow = this._executeQuery(compiledQuery) + _safeAutoClose(onAbort = { iRow._abort() }) { + for (elem in collection) { + iRow._next() + transform.transformToRow(iRow, elem) + } + } + iRow._finish() +} + +public suspend inline fun Transaction.mapToRowsIndexed( + compiledQuery: _Query.Table2Void, + collection: Iterable, + firstIndex: Int = 0, + transform: RowProducer2 +) { + val iRow = this._executeQuery(compiledQuery) + var index = firstIndex + _safeAutoClose(onAbort = { iRow._abort() }) { + for (elem in collection) { + iRow._next() + transform.transformToRow(iRow, index, elem) + index++ + } + } + iRow._finish() +} + +public suspend inline fun Transaction.mapToRows( + compiledQuery: _Query.Table2Void, + sequence: Sequence, + transform: RowProducer1 +) { + val iRow = this._executeQuery(compiledQuery) + _safeAutoClose(onAbort = { iRow._abort() }) { + for (elem in sequence) { + iRow._next() + transform.transformToRow(iRow, elem) + } + } + iRow._finish() +} + +public suspend inline fun Transaction.mapToRowsIndexed( + compiledQuery: _Query.Table2Void, + sequence: Sequence, + firstIndex: Int = 0, + transform: RowProducer2 +) { + val iRow = this._executeQuery(compiledQuery) + var index = firstIndex + _safeAutoClose(onAbort = { iRow._abort() }) { + for (elem in sequence) { + iRow._next() + transform.transformToRow(iRow, index, elem) + index++ + } + } + iRow._finish() +} + +public suspend inline fun Transaction.mapToRows( + compiledQuery: _Query.Table2Void, + sequence: Array, + transform: RowProducer1 +) { + val iRow = this._executeQuery(compiledQuery) + _safeAutoClose(onAbort = { iRow._abort() }) { + for (elem in sequence) { + iRow._next() + transform.transformToRow(iRow, elem) + } + } + iRow._finish() +} + +public suspend inline fun Transaction.mapToRowsIndexed( + compiledQuery: _Query.Table2Void, + sequence: Array, + firstIndex: Int = 0, + transform: RowProducer2 +) { + val iRow = this._executeQuery(compiledQuery) + var index = firstIndex + _safeAutoClose(onAbort = { iRow._abort() }) { + for (elem in sequence) { + iRow._next() + transform.transformToRow(iRow, index, elem) + index++ + } + } + iRow._finish() +} \ No newline at end of file