Skip to content

Commit

Permalink
fix: issues with foreign key consistency accross different targets (#19)
Browse files Browse the repository at this point in the history
  • Loading branch information
tamimattafi authored May 17, 2024
1 parent 408732c commit 8df0234
Show file tree
Hide file tree
Showing 20 changed files with 191 additions and 226 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import org.gradle.api.publish.maven.MavenPomScm

class PublishConventions : Plugin<Project> {

private val version = "0.1.0-alpha07"
private val version = "0.1.0-alpha08"
private val group = "com.attafitamim.kabin"

override fun apply(project: Project) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ class DatabaseGenerator(
.addParameter(versionParameter.build())
.addParameter(configurationParameter.build())
.addStatement("val·$schemaParameterName·=·$schemaExtensionName($schemaConstructorParametersCall)")
.addStatement("val·$driverName·=·$configurationName.createDriver($schemaParameterName)")
.addStatement("val·$driverName·=·$configurationName.createSqlDriver($schemaParameterName)")
.addStatement("return·$newInstanceName($driverName, $configurationName)")
.build()

Expand All @@ -324,7 +324,6 @@ class DatabaseGenerator(
.addFunction(newInstanceExtension)
.addFunction(newInstanceFullExtension)
.addType(classBuilder.build())
.addImport("com.attafitamim.kabin.core.driver", "createDriver")
.build()

codeGenerator.writeFile(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ fun FunSpec.Builder.addDriverExecutionCode(
sql: String,
parametersSize: Int = 0
) = apply {
val logic = "driver.execute($identifier,·%P,·$parametersSize).await()"
val logic = "driver.execute($identifier,·%P,·$parametersSize)"
addStatement(logic, sql)
}

Expand Down Expand Up @@ -84,8 +84,6 @@ fun FunSpec.Builder.addDriverRawQueryCode(

if (function == "executeQuery") {
codeBlockBuilder.addStatement("return result")
} else {
codeBlockBuilder.add(".await()")
}

addCode(codeBlockBuilder.build())
Expand Down Expand Up @@ -178,10 +176,6 @@ fun FunSpec.Builder.addDriverQueryCode(
codeBlockBuilder.binderCode()
}

if (function == EXECUTE_FUNCTION) {
codeBlockBuilder.addStatement(".await()")
}

if (query.mutatedKeys.isNotEmpty()) {
codeBlockBuilder.beginControlFlow("notifyQueries($identifier) { emit ->")

Expand Down Expand Up @@ -232,10 +226,6 @@ fun FunSpec.Builder.addDriverQueryCode(
codeBlockBuilder.binderCode()
}

if (function == EXECUTE_FUNCTION) {
codeBlockBuilder.addStatement(".await()")
}

if (query.mutatedKeys.isNotEmpty()) {
codeBlockBuilder.beginControlFlow("notifyQueries($identifier) { emit ->")

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.attafitamim.kabin.core.database.configuration

import android.content.Context
import app.cash.sqldelight.async.coroutines.synchronous
import app.cash.sqldelight.db.QueryResult
import app.cash.sqldelight.db.SqlDriver
import app.cash.sqldelight.db.SqlSchema
import app.cash.sqldelight.driver.android.AndroidSqliteDriver

private const val DEFAULT_CACHE_SIZE = 20

actual class KabinDatabaseConfiguration(
val context: Context,
val name: String? = null,
val cacheSize: Int = DEFAULT_CACHE_SIZE,
val useNoBackupDirectory: Boolean = false,
val windowSizeBytes: Long? = null,
actual val extendedConfig: KabinExtendedConfig = KabinExtendedConfig()
) {

private fun createCallback(
schema: SqlSchema<QueryResult.Value<Unit>>
) = object : AndroidSqliteDriver.Callback(schema) {}

actual fun createSqlDriver(
schema: SqlSchema<QueryResult.AsyncValue<Unit>>,
): SqlDriver {
val synchronousSchema = schema.synchronous()
return AndroidSqliteDriver(
synchronousSchema,
context,
name,
cacheSize = cacheSize,
useNoBackupDirectory = useNoBackupDirectory,
windowSizeBytes = windowSizeBytes,
callback = createCallback(synchronousSchema)
).configure(this)
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package com.attafitamim.kabin.core.dao

import app.cash.sqldelight.SuspendingTransacterImpl
import app.cash.sqldelight.db.SqlDriver
import com.attafitamim.kabin.core.database.configuration.KabinDatabaseConfiguration
import com.attafitamim.kabin.core.utils.tryDifferForeignKeys
import com.attafitamim.kabin.core.utils.tryToggleForeignKeys

open class KabinSuspendingQueries(driver: SqlDriver) : SuspendingTransacterImpl(driver) {

Expand All @@ -15,50 +18,13 @@ open class KabinSuspendingQueries(driver: SqlDriver) : SuspendingTransacterImpl(
return parameter.toString()
}

/**
* Used to delay enforcement of all foreign key constraints until the outermost transaction
* is committed. Should be called only from inside a transaction.
*
* @see [defer_foreign_keys](https://sqlite.org/pragma.html#pragma_defer_foreign_keys)
*/
suspend fun deferForeignKeys(enabled: Boolean = true) {
executePragma(DEFER_FOREIGN_KEYS_PRAGMA, enabled.toPragmaValue())
}

/**
* Used to enable or disable foreign keys.
* Should be called only from outside a transaction.
*
* @see [foreign_keys](https://sqlite.org/pragma.html#pragma_foreign_keys)
*/
suspend fun toggleForeignKeys(enabled: Boolean) {
executePragma(FOREIGN_KEYS_PRAGMA, enabled.toPragmaValue())
}

suspend fun executePragma(name: String, value: String) {
val sqlPragma = "PRAGMA $name = $value"
executeSQL(sqlPragma)
}
suspend fun tryToggleForeignKeys(
configuration: KabinDatabaseConfiguration,
enabled: Boolean
) = driver.tryToggleForeignKeys(configuration, enabled)

suspend fun executeSQL(sql: String) {
driver.execute(
identifier = null,
sql = sql,
parameters = 0,
binders = null
).await()
}

private companion object {
const val DEFER_FOREIGN_KEYS_PRAGMA = "defer_foreign_keys"
const val FOREIGN_KEYS_PRAGMA = "foreign_keys"

const val PRAGMA_TRUE = "TRUE"
const val PRAGMA_FALSE = "FALSE"

fun Boolean.toPragmaValue() = when (this) {
true -> PRAGMA_TRUE
false -> PRAGMA_FALSE
}
}
}
suspend fun tryDifferForeignKeys(
configuration: KabinDatabaseConfiguration,
enabled: Boolean
) = driver.tryDifferForeignKeys(configuration, enabled)
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
package com.attafitamim.kabin.core.database.configuration

import app.cash.sqldelight.db.QueryResult
import app.cash.sqldelight.db.SqlDriver
import app.cash.sqldelight.db.SqlSchema
import com.attafitamim.kabin.core.utils.toggleForeignKeys

expect class KabinDatabaseConfiguration {
val extendedConfig: KabinExtendedConfig
}

fun createSqlDriver(
schema: SqlSchema<QueryResult.AsyncValue<Unit>>,
): SqlDriver
}

fun SqlDriver.configure(configuration: KabinDatabaseConfiguration) = apply {
toggleForeignKeys(configuration.extendedConfig.foreignKeyConstraintsEnabled)
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,35 +8,43 @@ import app.cash.sqldelight.db.SqlDriver
import com.attafitamim.kabin.core.dao.KabinSuspendingQueries
import com.attafitamim.kabin.core.database.configuration.KabinDatabaseConfiguration

private const val DEFER_FOREIGN_KEYS_PRAGMA = "defer_foreign_keys"
private const val FOREIGN_KEYS_PRAGMA = "foreign_keys"

private const val PRAGMA_ENABLED = "ON"
private const val PRAGMA_DISABLED = "OFF"

fun Boolean.toPragmaValue() = when (this) {
true -> PRAGMA_ENABLED
false -> PRAGMA_DISABLED
}

suspend inline fun KabinSuspendingQueries.safeTransaction(
configuration: KabinDatabaseConfiguration,
crossinline body: suspend SuspendingTransactionWithoutReturn.() -> Unit
) = transaction {
tryDifferForeignKeys(configuration)
tryDifferForeignKeys(configuration, enabled = true)
body()
}

suspend inline fun <T> KabinSuspendingQueries.safeTransactionWithResult(
configuration: KabinDatabaseConfiguration,
crossinline body: suspend SuspendingTransactionWithReturn<T>.() -> T
): T = transactionWithResult {
tryDifferForeignKeys(configuration)
tryDifferForeignKeys(configuration, enabled = true)
body()
}

inline fun SqlDriver.safeGlobalQuery(
configuration: KabinDatabaseConfiguration,
noinline body: suspend () -> Unit
): QueryResult.AsyncValue<Unit> = with(KabinSuspendingQueries(this)) {
QueryResult.AsyncValue {
try {
tryToggleForeignKeys(configuration, enabled = false)
tryDifferForeignKeys(configuration, enabled = true)
body()
} finally {
tryToggleForeignKeys(configuration, enabled = true)
tryDifferForeignKeys(configuration, enabled = false)
}
): QueryResult.AsyncValue<Unit> = QueryResult.AsyncValue {
try {
tryToggleForeignKeys(configuration, enabled = false)
tryDifferForeignKeys(configuration, enabled = true)
body()
} finally {
tryToggleForeignKeys(configuration, enabled = true)
}
}

Expand All @@ -50,7 +58,27 @@ suspend inline fun KabinSuspendingQueries.safeGlobalTransaction(
tryToggleForeignKeys(configuration, enabled = true)
}

suspend fun KabinSuspendingQueries.tryToggleForeignKeys(
/**
* Used to delay enforcement of all foreign key constraints until the outermost transaction
* is committed. Should be called only from inside a transaction.
*
* @see [defer_foreign_keys](https://sqlite.org/pragma.html#pragma_defer_foreign_keys)
*/
fun SqlDriver.deferForeignKeys(enabled: Boolean) {
executePragma(DEFER_FOREIGN_KEYS_PRAGMA, enabled.toPragmaValue())
}

/**
* Used to enable or disable foreign keys.
* Should be called only from outside a transaction.
*
* @see [foreign_keys](https://sqlite.org/pragma.html#pragma_foreign_keys)
*/
fun SqlDriver.toggleForeignKeys(enabled: Boolean) {
executePragma(FOREIGN_KEYS_PRAGMA, enabled.toPragmaValue())
}

fun SqlDriver.tryToggleForeignKeys(
configuration: KabinDatabaseConfiguration,
enabled: Boolean
) {
Expand All @@ -59,11 +87,25 @@ suspend fun KabinSuspendingQueries.tryToggleForeignKeys(
}
}

suspend fun KabinSuspendingQueries.tryDifferForeignKeys(
fun SqlDriver.tryDifferForeignKeys(
configuration: KabinDatabaseConfiguration,
enabled: Boolean = true
enabled: Boolean
) = with(configuration.extendedConfig) {
if (foreignKeyConstraintsEnabled && deferForeignKeysInsideTransaction) {
deferForeignKeys(enabled)
}
}

fun SqlDriver.executePragma(name: String, value: String) {
val sqlPragma = "PRAGMA $name = $value;"
executeSQL(sqlPragma)
}

fun SqlDriver.executeSQL(sql: String) {
execute(
identifier = null,
sql = sql,
parameters = 0,
binders = null
)
}
Loading

0 comments on commit 8df0234

Please sign in to comment.