Skip to content

Commit e475a08

Browse files
authored
Consolidated run-raw APIs, add specialized runBoth Capability (#24)
* Cleanup API, add special streamBoth/runBoth * Bump to 4.3.0
1 parent 1e394a3 commit e475a08

50 files changed

Lines changed: 311 additions & 128 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

build-logic/src/main/kotlin/conventions.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ repositories {
77
mavenLocal()
88
}
99

10-
val controllerVersion = "4.2.0"
10+
val controllerVersion = "4.3.0"
1111
val pluginVersion = "2.0.0.PL"
1212

1313
extra["controllerVersion"] = controllerVersion

controller-android/build.gradle.kts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ android {
3535
}
3636

3737
kotlin {
38+
compilerOptions {
39+
optIn.addAll(
40+
"io.exoquery.controller.TerpalSqlInternal"
41+
)
42+
}
43+
3844
if (HostManager.hostIsLinux || !project.hasProperty("isCI")) {
3945

4046
androidTarget {

controller-android/src/androidMain/kotlin/io/exoquery/controller/android/AndroidDatabaseController.kt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ class AndroidDatabaseController internal constructor(
230230

231231
protected fun wrap(stmt: SupportSQLiteStatement) = AndroidxStatementWrapper(stmt)
232232

233+
@OptIn(TerpalSqlInternal::class)
233234
suspend fun runActionScoped(sql: String, options: UnusedOpts, params: List<StatementParam<*>>): Long =
234235
withConnection(options) {
235236
val conn = localConnection()
@@ -376,10 +377,6 @@ class AndroidDatabaseController internal constructor(
376377
override suspend fun <T> stream(query: ControllerBatchActionReturning<T>, options: UnusedOpts): Flow<T> =
377378
throw IllegalArgumentException("Batch Queries are not supported in NativeContext.")
378379

379-
fun runRaw(sql: String, options: UnusedOpts = UnusedOpts) = runBlocking {
380-
sql.split(";").forEach { if (it.trim().isNotEmpty()) runActionScoped(it, options, emptyList()) }
381-
}
382-
383380
override fun close() =
384381
this.pool.finalize()
385382
}

controller-core/build.gradle.kts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ plugins {
1212
version = extra["controllerVersion"].toString()
1313

1414
kotlin {
15+
compilerOptions {
16+
optIn.addAll(
17+
"io.exoquery.controller.TerpalSqlInternal"
18+
)
19+
}
20+
1521
jvmToolchain(17)
1622
jvm {
1723
}

controller-core/src/commonMain/kotlin/io/exoquery/controller/Annotations.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,10 @@ package io.exoquery.controller
22

33
@RequiresOptIn(message = "This is internal Terpal-SQL API and may change in the future.")
44
@Retention(AnnotationRetention.BINARY)
5-
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
5+
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.FIELD)
66
annotation class TerpalSqlInternal
7+
8+
@RequiresOptIn(message = "This is an unsafe raw-SQL API. Injections are possible.")
9+
@Retention(AnnotationRetention.BINARY)
10+
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
11+
annotation class TerpalSqlUnsafe

controller-core/src/commonMain/kotlin/io/exoquery/controller/Controller.kt

Lines changed: 45 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import org.intellij.lang.annotations.Language
1414
import kotlin.coroutines.AbstractCoroutineContextElement
1515
import kotlin.experimental.ExperimentalTypeInference
1616

17+
@TerpalSqlInternal
1718
class CoroutineSession<Session>(val session: Session, val sessionKey: CoroutineContext.Key<CoroutineSession<Session>>) : AbstractCoroutineContextElement(sessionKey) {
1819
override fun toString() = "CoroutineSession($sessionKey)"
1920
}
@@ -22,7 +23,7 @@ class TerpalException(msg: String, cause: Throwable?): Exception(msg, cause) {
2223
constructor(msg: String): this(msg, null)
2324
}
2425

25-
@OptIn(TerpalSqlInternal::class)
26+
@TerpalSqlInternal
2627
interface EncodingConfig<Session, Stmt, ResultRow> {
2728
val additionalEncoders: Set<SqlEncoder<Session, Stmt, out Any>>
2829
val additionalDecoders: Set<SqlDecoder<Session, ResultRow, out Any>>
@@ -33,7 +34,7 @@ interface EncodingConfig<Session, Stmt, ResultRow> {
3334
val debugMode: Boolean
3435
}
3536

36-
@OptIn(TerpalSqlInternal::class)
37+
@TerpalSqlInternal
3738
interface WithEncoding<Session, Stmt, ResultRow> {
3839
val encodingConfig: EncodingConfig<Session, Stmt, ResultRow>
3940
val startingStatementIndex: StartingIndex get() = StartingIndex.Zero // default for JDBC is 1 so this needs to be overrideable
@@ -85,22 +86,34 @@ interface WithEncoding<Session, Stmt, ResultRow> {
8586
}
8687
}
8788

88-
@OptIn(TerpalSqlInternal::class)
89+
@TerpalSqlInternal
8990
interface RequiresSession<Session, Stmt, ExecutionOpts> {
9091

9192
// Methods that implementors need to provide
9293
val sessionKey: CoroutineContext.Key<CoroutineSession<Session>>
94+
95+
@TerpalSqlInternal
9396
abstract suspend fun newSession(executionOptions: ExecutionOpts): Session
97+
98+
@TerpalSqlInternal
9499
abstract suspend fun closeSession(session: Session): Unit
100+
101+
@TerpalSqlInternal
95102
abstract suspend fun isClosedSession(session: Session): Boolean
103+
104+
@TerpalSqlInternal
96105
suspend fun <R> accessStmt(sql: String, conn: Session, block: suspend (Stmt) -> R): R
106+
107+
@TerpalSqlInternal
97108
suspend fun <R> accessStmtReturning(sql: String, conn: Session, options: ExecutionOpts, returningColumns: List<String>, block: suspend (Stmt) -> R): R
98109

110+
@TerpalSqlInternal
99111
suspend fun CoroutineContext.hasOpenConnection(): Boolean {
100112
val session = get(sessionKey)?.session
101113
return session != null && !isClosedSession(session)
102114
}
103115

116+
@TerpalSqlInternal
104117
suspend fun <T> withConnection(executionOptions: ExecutionOpts, block: suspend CoroutineScope.() -> T): T {
105118
return if (coroutineContext.hasOpenConnection()) {
106119
withContext(coroutineContext + Dispatchers.IO) { block() }
@@ -115,7 +128,7 @@ interface RequiresSession<Session, Stmt, ExecutionOpts> {
115128
}
116129
}
117130

118-
131+
@TerpalSqlInternal
119132
suspend fun localConnection() =
120133
coroutineContext.get(sessionKey)?.session ?: error("No connection detected in withConnection scope. This should be impossible.")
121134

@@ -140,9 +153,11 @@ interface RequiresSession<Session, Stmt, ExecutionOpts> {
140153
}
141154
}
142155

156+
@TerpalSqlInternal
143157
interface RequiresTransactionality<Session, Stmt, ExecutionOpts>: RequiresSession<Session, Stmt, ExecutionOpts> {
144158
abstract suspend fun <T> runTransactionally(block: suspend CoroutineScope.() -> T): T
145159

160+
@TerpalSqlInternal
146161
suspend fun <T> withTransactionScope(executionOptions: ExecutionOpts, block: suspend CoroutineScope.() -> T): T {
147162
val existingTransaction = coroutineContext[CoroutineTransaction]
148163

@@ -159,48 +174,46 @@ interface RequiresTransactionality<Session, Stmt, ExecutionOpts>: RequiresSessio
159174
}
160175
}
161176

162-
@OptIn(TerpalSqlInternal::class)
163177
interface ControllerVerbs<ExecutionOpts> {
164178
fun DefaultOpts(): ExecutionOpts
165179

166-
suspend fun <T> stream(query: ControllerQuery<T>, options: ExecutionOpts): Flow<T>
167-
suspend fun <T> stream(query: ControllerBatchActionReturning<T>, options: ExecutionOpts): Flow<T>
168-
suspend fun <T> stream(query: ControllerActionReturning<T>, options: ExecutionOpts): Flow<T>
169-
suspend fun <T> run(query: ControllerQuery<T>, options: ExecutionOpts): List<T>
170-
suspend fun run(query: ControllerAction, options: ExecutionOpts): Long
171-
suspend fun run(query: ControllerBatchAction, options: ExecutionOpts): List<Long>
172-
suspend fun <T> run(query: ControllerActionReturning<T>, options: ExecutionOpts): T
173-
suspend fun <T> run(query: ControllerBatchActionReturning<T>, options: ExecutionOpts): List<T>
174-
175-
suspend fun <T> runRaw(query: ControllerQuery<T>, options: ExecutionOpts): List<List<Pair<String, String?>>>
176-
177-
suspend fun <T> stream(query: ControllerQuery<T>): Flow<T> = stream(query, DefaultOpts())
178-
suspend fun <T> stream(query: ControllerBatchActionReturning<T>): Flow<T> = stream(query, DefaultOpts())
179-
suspend fun <T> stream(query: ControllerActionReturning<T>): Flow<T> = stream(query, DefaultOpts())
180-
suspend fun <T> run(query: ControllerQuery<T>): List<T> = run(query, DefaultOpts())
181-
suspend fun run(query: ControllerAction): Long = run(query, DefaultOpts())
182-
suspend fun run(query: ControllerBatchAction): List<Long> = run(query, DefaultOpts())
183-
suspend fun <T> run(query: ControllerActionReturning<T>): T = run(query, DefaultOpts())
184-
suspend fun <T> run(query: ControllerBatchActionReturning<T>): List<T> = run(query, DefaultOpts())
185-
186-
suspend fun <T> runRaw(query: ControllerQuery<T>): List<List<Pair<String, String?>>> = runRaw(query, DefaultOpts())
180+
@TerpalSqlInternal suspend fun <T> stream(query: ControllerQuery<T>, options: ExecutionOpts): Flow<T>
181+
@TerpalSqlInternal suspend fun <T> stream(query: ControllerBatchActionReturning<T>, options: ExecutionOpts): Flow<T>
182+
@TerpalSqlInternal suspend fun <T> stream(query: ControllerActionReturning<T>, options: ExecutionOpts): Flow<T>
183+
@TerpalSqlInternal suspend fun <T> run(query: ControllerQuery<T>, options: ExecutionOpts): List<T>
184+
@TerpalSqlInternal suspend fun run(query: ControllerAction, options: ExecutionOpts): Long
185+
@TerpalSqlInternal suspend fun run(query: ControllerBatchAction, options: ExecutionOpts): List<Long>
186+
@TerpalSqlInternal suspend fun <T> run(query: ControllerActionReturning<T>, options: ExecutionOpts): T
187+
@TerpalSqlInternal suspend fun <T> run(query: ControllerBatchActionReturning<T>, options: ExecutionOpts): List<T>
188+
189+
@TerpalSqlInternal suspend fun <T> stream(query: ControllerQuery<T>): Flow<T> = stream(query, DefaultOpts())
190+
@TerpalSqlInternal suspend fun <T> stream(query: ControllerBatchActionReturning<T>): Flow<T> = stream(query, DefaultOpts())
191+
@TerpalSqlInternal suspend fun <T> stream(query: ControllerActionReturning<T>): Flow<T> = stream(query, DefaultOpts())
192+
@TerpalSqlInternal suspend fun <T> run(query: ControllerQuery<T>): List<T> = run(query, DefaultOpts())
193+
@TerpalSqlInternal suspend fun run(query: ControllerAction): Long = run(query, DefaultOpts())
194+
@TerpalSqlInternal suspend fun run(query: ControllerBatchAction): List<Long> = run(query, DefaultOpts())
195+
@TerpalSqlInternal suspend fun <T> run(query: ControllerActionReturning<T>): T = run(query, DefaultOpts())
196+
@TerpalSqlInternal suspend fun <T> run(query: ControllerBatchActionReturning<T>): List<T> = run(query, DefaultOpts())
197+
198+
@TerpalSqlInternal suspend fun <T> runRaw(query: ControllerQuery<T>, options: ExecutionOpts): List<List<Pair<String, String?>>>
199+
@TerpalSqlInternal suspend fun <T> runRaw(query: ControllerQuery<T>): List<List<Pair<String, String?>>> = runRaw(query, DefaultOpts())
187200
}
188201

189202
/**
190203
* This is the base class of all Terpal Drivers (not to be confused with JDBC drivers or similar).
191204
* This is a minimal set of semantics needed to support Sql-interpolated query executions. This makes minimal assumptions
192205
* about how the context functionality is composed. Typically implementations will want to start with ContextBase or ContextCannonical.
193206
*/
194-
@OptIn(TerpalSqlInternal::class)
195207
interface Controller<ExecutionOpts>: ControllerVerbs<ExecutionOpts> {
196208

197209
}
198210

199-
@OptIn(TerpalSqlInternal::class)
200-
suspend fun Controller<*>.runActions(@Language("SQL") actions: String): List<Long> =
211+
@TerpalSqlUnsafe
212+
suspend fun Controller<*>.runActionsUnsafe(@Language("SQL") actions: String): Unit {
201213
actions.split(";").map { it.trim() }.filter { it.isNotEmpty() }.map { run(ControllerAction(it, listOf()), DefaultOpts()) }
214+
}
215+
202216

203-
@OptIn(TerpalSqlInternal::class)
204217
interface ControllerTransactional<Session, Stmt, ExecutionOpts>: Controller<ExecutionOpts>, RequiresSession<Session, Stmt, ExecutionOpts>, RequiresTransactionality<Session, Stmt, ExecutionOpts> {
205218
fun showStats(): String = ""
206219
}
@@ -210,14 +223,15 @@ interface ControllerTransactional<Session, Stmt, ExecutionOpts>: Controller<Exec
210223
* and encoders. The assumption here is that the same Session/Row/Result types used in the encoders are used in the session-handling.
211224
* If this is not the case use ContextBase.
212225
*/
213-
@OptIn(TerpalSqlInternal::class)
226+
@TerpalSqlInternal
214227
interface ControllerCanonical<Session, Stmt, ResultRow, ExecutionOpts>: ControllerTransactional<Session, Stmt, ExecutionOpts>, RequiresSession<Session, Stmt, ExecutionOpts>, RequiresTransactionality<Session, Stmt, ExecutionOpts>, WithEncoding<Session, Stmt, ResultRow>
215228

216229
suspend fun <T> ControllerQuery<T>.runOn(ctx: Controller<*>) = ctx.run(this)
217230
suspend fun <T> ControllerQuery<T>.streamOn(ctx: Controller<*>) = ctx.stream(this)
218231
suspend fun <T> ControllerQuery<T>.runRawOn(ctx: Controller<*>) = ctx.runRaw(this)
219232
suspend fun ControllerAction.runOn(ctx: Controller<*>) = ctx.run(this)
220233
suspend fun <T> ControllerActionReturning<T>.runOn(ctx: Controller<*>) = ctx.run(this)
234+
suspend fun <T> ControllerActionReturning<T>.streamOn(ctx: Controller<*>) = ctx.stream(this)
221235
suspend fun ControllerBatchAction.runOn(ctx: Controller<*>) = ctx.run(this)
222236
suspend fun <T> ControllerBatchActionReturning<T>.runOn(ctx: Controller<*>) = ctx.run(this)
223237
suspend fun <T> ControllerBatchActionReturning<T>.streamOn(ctx: Controller<*>) = ctx.stream(this)

controller-jdbc/build.gradle.kts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ tasks.withType<Test>().configureEach {
3838
}
3939

4040
kotlin {
41+
compilerOptions {
42+
optIn.addAll(
43+
"io.exoquery.controller.TerpalSqlInternal"
44+
)
45+
}
46+
4147
jvmToolchain(17)
4248
jvm {
4349
}

0 commit comments

Comments
 (0)