-
Notifications
You must be signed in to change notification settings - Fork 515
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for async drivers (#3168)
* Add async codegen and runtime Add async drivers for R2DBC and sqljs workers * Split sync and async runtime modules * Generate suspend functions for queries Update async runtime to use suspend functions Add Closeable to async runtime Update sqljs worker and r2dbc drivers * Add runtime module tasks to gradle plugin tests * Run spotless * Fix up broken async codegen tests * Pull async gen flag from SqlDelightFile * Add missing prop in intellij tests * Update async mysql integration test Add other async codegen compiler tests * Add async runtime tests Remove async extensions from coroutines-extensions (to revisit later) * Fix spotless errors * Fix broken AsyncTest * Fix js worker test Fix sample builds with new runtime modules * Skip js web worker tests on node * Add driver-async-test Add some brief documentation on implementing async drivers * Disable mingwX86 on driver-async-test * Run spotless (again) * Apply suggestions from code review Co-authored-by: Alec Strong <AlecStrong@users.noreply.github.com> * Remove mingwX86 from runtime-async entirely * Clean things up Add RuntimeTypes class for codegen types Add default async runtime types Remove suspend modifier from generated functions that return an `AsyncQuery` * Make Closeable.close() suspendable * Add missing suspend modifier * Clean up driver-async-test to handle suspend funs * Handle even more suspending test teardowns * Run spotless * Fix one last close function * Fix up and enable remaining js worker tests * Remove driver-async-test Run spotless again again again * Apply suggestions from code review Co-authored-by: Alec Strong <AlecStrong@users.noreply.github.com> * Clean up async runtime Disable async codegen for HSQL Remove unused functions in R2DBC driver Move LogAsyncSqlDriver to tests * Delete async runtime tests Co-authored-by: Alec Strong <AlecStrong@users.noreply.github.com>
- Loading branch information
Showing
110 changed files
with
2,261 additions
and
132 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
plugins { | ||
alias(deps.plugins.kotlin.jvm) | ||
alias(deps.plugins.publish) | ||
alias(deps.plugins.dokka) | ||
} | ||
|
||
archivesBaseName = 'sqldelight-r2dbc-driver' | ||
|
||
dependencies { | ||
api project(':runtime:runtime-async') | ||
implementation deps.r2dbc | ||
implementation deps.kotlin.coroutines.core | ||
implementation deps.kotlin.coroutines.reactive | ||
} | ||
|
||
apply from: "$rootDir/gradle/gradle-mvn-push.gradle" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
POM_ARTIFACT_ID=r2dbc-driver | ||
POM_NAME=SQLDelight R2DBC Driver | ||
POM_DESCRIPTION=R2DBC driver for SQLDelight | ||
POM_PACKAGING=jar |
159 changes: 159 additions & 0 deletions
159
drivers/r2dbc-driver/src/main/kotlin/app/cash/sqldelight/driver/r2dbc/R2dbcDriver.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
package app.cash.sqldelight.driver.r2dbc | ||
|
||
import app.cash.sqldelight.async.AsyncQuery | ||
import app.cash.sqldelight.async.AsyncTransacter | ||
import app.cash.sqldelight.async.db.AsyncSqlCursor | ||
import app.cash.sqldelight.async.db.AsyncSqlDriver | ||
import app.cash.sqldelight.async.db.AsyncSqlPreparedStatement | ||
import io.r2dbc.spi.Connection | ||
import io.r2dbc.spi.Statement | ||
import kotlinx.coroutines.reactive.awaitLast | ||
import kotlinx.coroutines.reactive.awaitSingle | ||
|
||
class R2dbcDriver(private val connection: Connection) : AsyncSqlDriver { | ||
override suspend fun <R> executeQuery( | ||
identifier: Int?, | ||
sql: String, | ||
mapper: (AsyncSqlCursor) -> R, | ||
parameters: Int, | ||
binders: (AsyncSqlPreparedStatement.() -> Unit)? | ||
): R { | ||
val prepared = connection.createStatement(sql).also { statement -> | ||
R2dbcPreparedStatement(statement).apply { if (binders != null) this.binders() } | ||
} | ||
val result = prepared.execute().awaitSingle() | ||
|
||
val rowSet = mutableListOf<Map<Int, Any?>>() | ||
result.map { row, rowMetadata -> | ||
rowSet.add(rowMetadata.columnMetadatas.mapIndexed { index, _ -> index to row.get(index) }.toMap()) | ||
}.awaitLast() | ||
|
||
return mapper(R2dbcCursor(rowSet)) | ||
} | ||
|
||
override suspend fun execute( | ||
identifier: Int?, | ||
sql: String, | ||
parameters: Int, | ||
binders: (AsyncSqlPreparedStatement.() -> Unit)? | ||
): Long { | ||
val prepared = connection.createStatement(sql).also { statement -> | ||
R2dbcPreparedStatement(statement).apply { if (binders != null) this.binders() } | ||
} | ||
|
||
val result = prepared.execute().awaitSingle() | ||
return result.rowsUpdated.awaitSingle() | ||
} | ||
|
||
private val transactions = ThreadLocal<Transaction>() | ||
private var transaction: Transaction? | ||
get() = transactions.get() | ||
set(value) { | ||
transactions.set(value) | ||
} | ||
|
||
override suspend fun newTransaction(): AsyncTransacter.Transaction { | ||
val enclosing = transaction | ||
val transaction = Transaction(enclosing, connection) | ||
connection.beginTransaction().awaitSingle() | ||
|
||
return transaction | ||
} | ||
|
||
override fun currentTransaction(): AsyncTransacter.Transaction? = transaction | ||
|
||
override fun addListener(listener: AsyncQuery.Listener, queryKeys: Array<String>) = Unit | ||
override fun removeListener(listener: AsyncQuery.Listener, queryKeys: Array<String>) = Unit | ||
override fun notifyListeners(queryKeys: Array<String>) = Unit | ||
|
||
override suspend fun close() { | ||
connection.close().awaitSingle() | ||
} | ||
|
||
private class Transaction( | ||
override val enclosingTransaction: AsyncTransacter.Transaction?, | ||
private val connection: Connection | ||
) : AsyncTransacter.Transaction() { | ||
override suspend fun endTransaction(successful: Boolean) { | ||
if (enclosingTransaction == null) { | ||
if (successful) connection.commitTransaction().awaitSingle() | ||
else connection.rollbackTransaction().awaitSingle() | ||
} | ||
} | ||
} | ||
} | ||
|
||
open class R2dbcPreparedStatement(private val statement: Statement) : AsyncSqlPreparedStatement { | ||
override fun bindBytes(index: Int, bytes: ByteArray?) { | ||
if (bytes == null) { | ||
statement.bindNull(index - 1, ByteArray::class.java) | ||
} else { | ||
statement.bind(index - 1, bytes) | ||
} | ||
} | ||
|
||
override fun bindLong(index: Int, long: Long?) { | ||
if (long == null) { | ||
statement.bindNull(index - 1, Long::class.java) | ||
} else { | ||
statement.bind(index - 1, long) | ||
} | ||
} | ||
|
||
override fun bindDouble(index: Int, double: Double?) { | ||
if (double == null) { | ||
statement.bindNull(index - 1, Double::class.java) | ||
} else { | ||
statement.bind(index - 1, double) | ||
} | ||
} | ||
|
||
override fun bindString(index: Int, string: String?) { | ||
if (string == null) { | ||
statement.bindNull(index - 1, String::class.java) | ||
} else { | ||
statement.bind(index - 1, string) | ||
} | ||
} | ||
|
||
override fun bindBoolean(index: Int, boolean: Boolean?) { | ||
if (boolean == null) { | ||
statement.bindNull(index - 1, Boolean::class.java) | ||
} else { | ||
statement.bind(index - 1, boolean) | ||
} | ||
} | ||
|
||
fun bindObject(index: Int, any: Any?) { | ||
if (any == null) { | ||
statement.bindNull(index - 1, Any::class.java) | ||
} else { | ||
statement.bind(index - 1, any) | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* TODO: Write a better async cursor API | ||
*/ | ||
open class R2dbcCursor(val rowSet: List<Map<Int, Any?>>) : AsyncSqlCursor { | ||
var row = -1 | ||
private set | ||
|
||
override fun next(): Boolean = ++row < rowSet.size | ||
|
||
override fun getString(index: Int): String? = rowSet[row][index] as String? | ||
|
||
override fun getLong(index: Int): Long? = (rowSet[row][index] as Number?)?.toLong() | ||
|
||
override fun getBytes(index: Int): ByteArray? = rowSet[row][index] as ByteArray? | ||
|
||
override fun getDouble(index: Int): Double? = rowSet[row][index] as Double? | ||
|
||
override fun getBoolean(index: Int): Boolean? = rowSet[row][index] as Boolean? | ||
|
||
inline fun <reified T : Any> getObject(index: Int): T? = rowSet[row][index] as T? | ||
|
||
@Suppress("UNCHECKED_CAST") | ||
fun <T> getArray(index: Int): Array<T>? = rowSet[row][index] as Array<T>? | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,21 @@ | ||
const path = require("path"); | ||
const abs = path.resolve("../../node_modules/sql.js/dist/sql-wasm.wasm") | ||
const dist = path.resolve("../../node_modules/sql.js/dist/") | ||
const wasm = path.join(dist, "sql-wasm.wasm") | ||
const worker = path.join(dist, "worker.sql-wasm.js") | ||
|
||
config.files.push({ | ||
pattern: abs, | ||
pattern: wasm, | ||
served: true, | ||
watched: false, | ||
included: false, | ||
nocache: false, | ||
}, { | ||
pattern: worker, | ||
served: true, | ||
watched: false, | ||
included: false, | ||
nocache: false, | ||
}); | ||
|
||
config.proxies["/sql-wasm.wasm"] = `/absolute${abs}` | ||
config.proxies["/sql-wasm.wasm"] = `/absolute${wasm}` | ||
config.proxies["/worker.sql-wasm.js"] = `/absolute${worker}` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.