From b757b4c589056d9d22c9e22e006aa1b9696088e9 Mon Sep 17 00:00:00 2001 From: Andrew Golovashevich Date: Mon, 19 Aug 2024 05:23:00 +0300 Subject: [PATCH 1/3] [history/cw-adjutant] Namespaces and table names bound to JDBC SQLite connection --- .../db/raw_sql_skeleton/namespaces_sqlite.kt | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 src/jvmMain/kotlin/ru/landgrafhomyak/db/raw_sql_skeleton/namespaces_sqlite.kt diff --git a/src/jvmMain/kotlin/ru/landgrafhomyak/db/raw_sql_skeleton/namespaces_sqlite.kt b/src/jvmMain/kotlin/ru/landgrafhomyak/db/raw_sql_skeleton/namespaces_sqlite.kt new file mode 100644 index 0000000..dfcc9ae --- /dev/null +++ b/src/jvmMain/kotlin/ru/landgrafhomyak/db/raw_sql_skeleton/namespaces_sqlite.kt @@ -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( + private val continuation: Continuation, + private val task: (connection: Connection) -> R, + ) : Callable { + 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 transaction(body: (connection: Connection) -> R): R = this.raw { connection -> + return@raw connection.transaction(body) + } + + public suspend fun 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, +) { + private val usedNames = HashSet() + + 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 +} From fc29095e2b9fb77d022ddca62f09ac0054e94a1d Mon Sep 17 00:00:00 2001 From: Andrew Golovashevich Date: Fri, 23 Aug 2024 13:30:00 +0300 Subject: [PATCH 2/3] [history/cw-adjutant] Table now can be converted to SQL string --- .../db/raw_sql_skeleton/namespaces_sqlite.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/jvmMain/kotlin/ru/landgrafhomyak/db/raw_sql_skeleton/namespaces_sqlite.kt b/src/jvmMain/kotlin/ru/landgrafhomyak/db/raw_sql_skeleton/namespaces_sqlite.kt index dfcc9ae..eec8efb 100644 --- a/src/jvmMain/kotlin/ru/landgrafhomyak/db/raw_sql_skeleton/namespaces_sqlite.kt +++ b/src/jvmMain/kotlin/ru/landgrafhomyak/db/raw_sql_skeleton/namespaces_sqlite.kt @@ -91,12 +91,12 @@ public class SqliteNamespace( return SqliteTableName(this.formatTableName(name)) } - - private fun formatTableName(name: String) = this.path.joinToString(prefix = "\"::", separator = "::", postfix = "::${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 +public class SqliteTableName(private val name: String) { + public val asSqlReference: String = "\"${this.name}\"" + public val asSqlString: String = "\'${this.name}\'" + override fun toString(): String = this.asSqlReference } From cf5ba5595056ebbcddd379e483596b04be30c875 Mon Sep 17 00:00:00 2001 From: Andrew Golovashevich Date: Sun, 23 Mar 2025 22:32:11 +0300 Subject: [PATCH 3/3] Cleanup classes from JDBC --- .../db/raw_sql_skeleton/Namespace.kt | 24 +++++++++++++++++++ .../db/raw_sql_skeleton/TableName.kt | 7 ++++++ 2 files changed, 31 insertions(+) create mode 100644 src/commonMain/kotlin/ru/landgrafhomyak/db/raw_sql_skeleton/Namespace.kt create mode 100644 src/commonMain/kotlin/ru/landgrafhomyak/db/raw_sql_skeleton/TableName.kt diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/db/raw_sql_skeleton/Namespace.kt b/src/commonMain/kotlin/ru/landgrafhomyak/db/raw_sql_skeleton/Namespace.kt new file mode 100644 index 0000000..e50d923 --- /dev/null +++ b/src/commonMain/kotlin/ru/landgrafhomyak/db/raw_sql_skeleton/Namespace.kt @@ -0,0 +1,24 @@ +package ru.landgrafhomyak.db.raw_sql_skeleton + +public class Namespace( + private val path: Array +) { + private val usedNames = HashSet() + + public fun subNamespace(name: String): Namespace { + if (name in this.usedNames) + throw IllegalArgumentException("Name is already used: $name") + this.usedNames.add(name) + return Namespace(this.path + name) + } + + public fun table(name: String): TableName { + if (name in this.usedNames) + throw IllegalArgumentException("Name is already used: $name") + this.usedNames.add(name) + + return TableName(this.formatTableName(name)) + } + + private fun formatTableName(name: String) = this.path.joinToString(prefix = "::", separator = "::", postfix = "::${name}") +} \ No newline at end of file diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/db/raw_sql_skeleton/TableName.kt b/src/commonMain/kotlin/ru/landgrafhomyak/db/raw_sql_skeleton/TableName.kt new file mode 100644 index 0000000..a11abed --- /dev/null +++ b/src/commonMain/kotlin/ru/landgrafhomyak/db/raw_sql_skeleton/TableName.kt @@ -0,0 +1,7 @@ +package ru.landgrafhomyak.db.raw_sql_skeleton + +public class TableName(private val name: String) { + public val asSqlReference: String = "\"${this.name}\"" + public val asSqlString: String = "\'${this.name}\'" + override fun toString(): String = this.asSqlReference +} \ No newline at end of file