diff --git a/build.gradle.kts b/build.gradle.kts index 5fe6110..afd5397 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -33,6 +33,7 @@ xomrk { jvmMain { dependencies { compileOnly("com.intellij:annotations:9.0.4") + implementation("ru.landgrafhomyak.utility:highlevel-try-finally:0.5") } } } diff --git a/src/jvmMain/kotlin/ru/landgrafhomyak/db/jdbc_kotlin_extensions/PreparedStatementPrepareAndFetch.kt b/src/jvmMain/kotlin/ru/landgrafhomyak/db/jdbc_kotlin_extensions/PreparedStatementPrepareAndFetch.kt deleted file mode 100644 index 6a4a4bd..0000000 --- a/src/jvmMain/kotlin/ru/landgrafhomyak/db/jdbc_kotlin_extensions/PreparedStatementPrepareAndFetch.kt +++ /dev/null @@ -1,9 +0,0 @@ -package ru.landgrafhomyak.db.jdbc_kotlin_extensions - -import java.sql.PreparedStatement -import java.sql.ResultSet - -public interface PreparedStatementPrepareAndFetch { - public fun prepare(ps: PreparedStatement) - public fun mapOne(rs: ResultSet): R -} \ No newline at end of file diff --git a/src/jvmMain/kotlin/ru/landgrafhomyak/db/jdbc_kotlin_extensions/autoclose.kt b/src/jvmMain/kotlin/ru/landgrafhomyak/db/jdbc_kotlin_extensions/autoclose.kt new file mode 100644 index 0000000..17f47b4 --- /dev/null +++ b/src/jvmMain/kotlin/ru/landgrafhomyak/db/jdbc_kotlin_extensions/autoclose.kt @@ -0,0 +1,47 @@ +@file:JvmName("_AutocloseKt") +@file:OptIn(ExperimentalContracts::class) + +package ru.landgrafhomyak.db.jdbc_kotlin_extensions + +import org.intellij.lang.annotations.Language +import java.sql.Connection +import java.sql.PreparedStatement +import java.sql.ResultSet +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract +import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose1 +import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose2 + +public inline fun Connection.transaction(t: (Connection) -> R): R { + contract { + callsInPlace(t, InvocationKind.EXACTLY_ONCE) + } + + this.executeStatement("BEGIN TRANSACTION") + return safeAutoClose2( + onError = { this.executeStatement("ROLLBACK TRANSACTION") }, + onSuccess = { this.executeStatement("COMMIT TRANSACTION") }, + action = { t(this) } + ) +} + +public fun Connection.executeStatement(@Language("SQL") sql: String) { + this.prepareStatement(sql) { ps -> ps.execute() } +} + +public inline fun Connection.prepareStatement(@Language("SQL") sql: String, action: (PreparedStatement) -> R): R { + contract { + callsInPlace(action, InvocationKind.EXACTLY_ONCE) + } + @Suppress("SqlSourceToSinkFlow") + val ps = this.prepareStatement(sql) + return safeAutoClose1(finally = ps::close) { action(ps) } +} + +public inline fun PreparedStatement.executeQuery(action: (ResultSet) -> R): R { + contract { + callsInPlace(action, InvocationKind.EXACTLY_ONCE) + } + return this.executeQuery().use(action) +} diff --git a/src/jvmMain/kotlin/ru/landgrafhomyak/db/jdbc_kotlin_extensions/extensions.kt b/src/jvmMain/kotlin/ru/landgrafhomyak/db/jdbc_kotlin_extensions/extensions.kt deleted file mode 100644 index de2d39a..0000000 --- a/src/jvmMain/kotlin/ru/landgrafhomyak/db/jdbc_kotlin_extensions/extensions.kt +++ /dev/null @@ -1,113 +0,0 @@ -@file:JvmName("ExtensionsKt") -@file:Suppress("SqlSourceToSinkFlow") -@file:OptIn(ExperimentalContracts::class) - -package ru.landgrafhomyak.db.jdbc_kotlin_extensions - -import org.intellij.lang.annotations.Language -import java.sql.Connection -import java.sql.PreparedStatement -import java.sql.ResultSet -import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.InvocationKind -import kotlin.contracts.contract - -public fun > Connection.mapQuery(sql: String, action: PreparedStatementPrepareAndFetch, to: C): C { - this.prepareStatement(sql).use { ps -> - action.prepare(ps) - ps.executeQuery().use { rs -> - while (rs.next()) { - to.add(action.mapOne(rs)) - } - } - } - return to -} - -public fun Connection.mapQuery(sql: String, action: PreparedStatementPrepareAndFetch): List { - val out = ArrayList() - this.mapQuery(sql, action, out) - return out -} - -public inline fun Connection.transaction(t: (Connection) -> R): R { - contract { - callsInPlace(t, InvocationKind.EXACTLY_ONCE) - } - - this.prepareStatement("BEGIN TRANSACTION").use { ps -> ps.executeUpdate() } - val r: R - try { - r = t(this) - } catch (e1: Throwable) { - try { - this.prepareStatement("ROLLBACK TRANSACTION").use { ps -> ps.executeUpdate() } - } catch (e2: Throwable) { - e1.addSuppressed(e2) - } - throw e1 - } - this.prepareStatement("COMMIT TRANSACTION").use { ps -> ps.executeUpdate() } - return r -} - - -public fun Connection.executeStatement(@Language("SQL") sql: String) { - this.prepareStatement(sql) { ps -> ps.execute() } -} - - -public inline fun Connection.prepareStatement(@Language("SQL") sql: String, action: (PreparedStatement) -> R): R { - contract { - callsInPlace(action, InvocationKind.EXACTLY_ONCE) - } - this.prepareStatement(sql).use { ps -> return action(ps) } -} - -public inline fun > ResultSet.mapTo(to: C, transform: (rs: ResultSet) -> R): C { - while (this.next()) { - to.add(transform(this)) - } - return to -} - -public inline fun ResultSet.map(transform: (rs: ResultSet) -> R): List = - this.mapTo(ArrayList(), transform) - -public inline fun > ResultSet.mapToThenClose(to: C, transform: (rs: ResultSet) -> R): C = - this.use { rs -> rs.mapTo(to, transform) } - -public inline fun ResultSet.mapThenClose(transform: (rs: ResultSet) -> R): List = - this.use { rs -> rs.map(transform) } - -public inline fun PreparedStatement.executeQuery(action: (ResultSet) -> R): R { - contract { - callsInPlace(action, InvocationKind.EXACTLY_ONCE) - } - return this.executeQuery().use(action) -} - - -public fun PreparedStatement.setUByte(parameterIndex: Int, value: UByte): Unit = - this.setByte(parameterIndex, value.toByte()) - -public fun PreparedStatement.setUShort(parameterIndex: Int, value: UShort): Unit = - this.setShort(parameterIndex, value.toShort()) - -public fun PreparedStatement.setUInt(parameterIndex: Int, value: UInt): Unit = - this.setInt(parameterIndex, value.toInt()) - -public fun PreparedStatement.setULong(parameterIndex: Int, value: ULong): Unit = - this.setLong(parameterIndex, value.toLong()) - -public fun ResultSet.getUByte(columnIndex: Int): UByte = - this.getByte(columnIndex).toUByte() - -public fun ResultSet.getUShort(columnIndex: Int): UShort = - this.getShort(columnIndex).toUShort() - -public fun ResultSet.getUInt(columnIndex: Int): UInt = - this.getInt(columnIndex).toUInt() - -public fun ResultSet.getULong(columnIndex: Int): ULong = - this.getLong(columnIndex).toULong() \ No newline at end of file diff --git a/src/jvmMain/kotlin/ru/landgrafhomyak/db/jdbc_kotlin_extensions/map_resultset.kt b/src/jvmMain/kotlin/ru/landgrafhomyak/db/jdbc_kotlin_extensions/map_resultset.kt new file mode 100644 index 0000000..eeb4c0a --- /dev/null +++ b/src/jvmMain/kotlin/ru/landgrafhomyak/db/jdbc_kotlin_extensions/map_resultset.kt @@ -0,0 +1,22 @@ +@file:JvmName("_MapResultSetKt") +package ru.landgrafhomyak.db.jdbc_kotlin_extensions + +import java.sql.ResultSet +import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose1 + + +public inline fun > ResultSet.mapTo(to: C, transform: (rs: ResultSet) -> R): C { + while (this.next()) { + to.add(transform(this)) + } + return to +} + +public inline fun ResultSet.map(transform: (rs: ResultSet) -> R): List = + this.mapTo(ArrayList(), transform) + +public inline fun > ResultSet.mapToThenClose(to: C, transform: (rs: ResultSet) -> R): C = + safeAutoClose1(finally = this::close) { this.mapTo(to, transform) } + +public inline fun ResultSet.mapThenClose(transform: (rs: ResultSet) -> R): List = + safeAutoClose1(finally = this::close) { this.map(transform) } diff --git a/src/jvmMain/kotlin/ru/landgrafhomyak/db/jdbc_kotlin_extensions/unsigned.kt b/src/jvmMain/kotlin/ru/landgrafhomyak/db/jdbc_kotlin_extensions/unsigned.kt new file mode 100644 index 0000000..8794cbc --- /dev/null +++ b/src/jvmMain/kotlin/ru/landgrafhomyak/db/jdbc_kotlin_extensions/unsigned.kt @@ -0,0 +1,30 @@ +@file:JvmName("_UnsignedKt") + +package ru.landgrafhomyak.db.jdbc_kotlin_extensions + +import java.sql.PreparedStatement +import java.sql.ResultSet + +public fun PreparedStatement.setUByte(parameterIndex: Int, value: UByte): Unit = + this.setByte(parameterIndex, value.toByte()) + +public fun PreparedStatement.setUShort(parameterIndex: Int, value: UShort): Unit = + this.setShort(parameterIndex, value.toShort()) + +public fun PreparedStatement.setUInt(parameterIndex: Int, value: UInt): Unit = + this.setInt(parameterIndex, value.toInt()) + +public fun PreparedStatement.setULong(parameterIndex: Int, value: ULong): Unit = + this.setLong(parameterIndex, value.toLong()) + +public fun ResultSet.getUByte(columnIndex: Int): UByte = + this.getByte(columnIndex).toUByte() + +public fun ResultSet.getUShort(columnIndex: Int): UShort = + this.getShort(columnIndex).toUShort() + +public fun ResultSet.getUInt(columnIndex: Int): UInt = + this.getInt(columnIndex).toUInt() + +public fun ResultSet.getULong(columnIndex: Int): ULong = + this.getLong(columnIndex).toULong()