[history/cw-adjutant] Namespaces and table names bound to JDBC SQLite connection
This commit is contained in:
commit
b757b4c589
@ -0,0 +1,102 @@
|
||||
@file:JvmName("NamespacesSqliteKt")
|
||||
|
||||
package ru.landgrafhomyak.db.raw_sql_skeleton
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import java.sql.Connection
|
||||
import java.sql.DriverManager
|
||||
import java.util.concurrent.Callable
|
||||
import java.util.concurrent.Executors.newFixedThreadPool
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.resumeWithException
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
|
||||
public class SqliteConnection private constructor(
|
||||
internal val jdbcConnection: Connection,
|
||||
) {
|
||||
private val executor = newFixedThreadPool(1)
|
||||
|
||||
private val mutex = Mutex()
|
||||
|
||||
private inner class Task<R>(
|
||||
private val continuation: Continuation<R>,
|
||||
private val task: (connection: Connection) -> R,
|
||||
) : Callable<Unit> {
|
||||
override fun call() {
|
||||
try {
|
||||
this.continuation.resume(this.task(this@SqliteConnection.jdbcConnection))
|
||||
} catch (e: InterruptedException) {
|
||||
this.continuation.resumeWithException(RuntimeException("runner interrupted"))
|
||||
throw e
|
||||
} catch (e: Throwable) {
|
||||
this.continuation.resumeWithException(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public suspend fun <R> transaction(body: (connection: Connection) -> R): R = this.raw { connection ->
|
||||
return@raw connection.transaction(body)
|
||||
}
|
||||
|
||||
public suspend fun <R> raw(action: (Connection) -> R): R = this.mutex.withLock {
|
||||
return@withLock suspendCoroutine { continuation ->
|
||||
this.executor.submit(this.Task(continuation, action))
|
||||
}
|
||||
}
|
||||
|
||||
public val rootNamespace: SqliteNamespace = SqliteNamespace(this, emptyArray())
|
||||
|
||||
public companion object {
|
||||
@JvmStatic
|
||||
public suspend fun wrap7prepare(rawConnection: Connection): SqliteConnection {
|
||||
val w = SqliteConnection(rawConnection)
|
||||
w.raw { connection ->
|
||||
connection.autoCommit = false
|
||||
connection.prepareStatement("ROLLBACK TRANSACTION") { ps -> ps.execute() }
|
||||
connection.prepareStatement("PRAGMA foreign_keys=TRUE") { ps -> ps.execute() }
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
public suspend fun connect(url: String): SqliteConnection = this.wrap7prepare(DriverManager.getConnection(url))
|
||||
|
||||
@JvmStatic
|
||||
public fun connectBlocking(url: String): SqliteConnection = runBlocking { this@Companion.connect(url) }
|
||||
}
|
||||
}
|
||||
|
||||
public class SqliteNamespace(
|
||||
private val owner: SqliteConnection,
|
||||
private val path: Array<String>,
|
||||
) {
|
||||
private val usedNames = HashSet<String>()
|
||||
|
||||
public fun subNamespace(name: String): SqliteNamespace {
|
||||
if (name in this.usedNames)
|
||||
throw IllegalArgumentException("Name is already used: $name")
|
||||
this.usedNames.add(name)
|
||||
return SqliteNamespace(this.owner, this.path + name)
|
||||
}
|
||||
|
||||
public fun table(name: String): SqliteTableName {
|
||||
if (name in this.usedNames)
|
||||
throw IllegalArgumentException("Name is already used: $name")
|
||||
this.usedNames.add(name)
|
||||
|
||||
return SqliteTableName(this.formatTableName(name))
|
||||
}
|
||||
|
||||
|
||||
private fun formatTableName(name: String) = this.path.joinToString(prefix = "\"::", separator = "::", postfix = "::${name}\"")
|
||||
}
|
||||
|
||||
|
||||
@JvmInline
|
||||
public value class SqliteTableName(private val value: String) {
|
||||
override fun toString(): String = this.value
|
||||
}
|
Loading…
Reference in New Issue
Block a user