From f651e85f06424a8294a030c158b8729dfaf03e89 Mon Sep 17 00:00:00 2001 From: tamimattafi <35191844+tamimattafi@users.noreply.github.com> Date: Fri, 19 Apr 2024 01:16:39 +0300 Subject: [PATCH] fix: foreign key behaviour inside a transaction (#13) fix: foreign key behaviour inside a transaction Close #9 --- .../sql/generator/dao/DaoGenerator.kt | 12 ++++ .../generator/database/DatabaseGenerator.kt | 68 ++++++++++--------- .../sql/generator/queries/QueriesGenerator.kt | 16 +++-- .../kabin/compiler/sql/syntax/SQLSyntax.kt | 1 + .../compiler/sql/utils/poet/PoetConstants.kt | 1 - .../sql/utils/sql/index/SQLForeignKeyUtils.kt | 5 +- .../KabinDatabaseConfiguration.kt | 18 ++--- .../configuration/KabinExtendedConfig.kt | 6 ++ .../core/driver/DriverFactory.android.kt | 4 +- .../attafitamim/kabin/core/dao/KabinDao.kt | 11 +-- .../kabin/core/dao/KabinSuspendingQueries.kt | 64 +++++++++++++++++ .../core/dao/KabinSuspendingTransactor.kt | 17 ----- .../kabin/core/database/KabinBaseDatabase.kt | 21 ++++++ .../kabin/core/database/KabinDatabase.kt | 2 +- .../database/KabinDatabaseConfiguration.kt | 3 - .../kabin/core/database/KabinSqlSchema.kt | 14 ++-- .../KabinDatabaseConfiguration.kt | 5 ++ .../configuration/KabinExtendedConfig.kt | 6 ++ .../kabin/core/driver/DriverFactory.kt | 2 +- .../kabin/core/utils/KabinUtils.kt | 60 ++++++++++++++++ .../database/KabinDatabaseConfiguration.kt | 3 - .../KabinDatabaseConfiguration.kt | 5 ++ .../configuration/KabinExtendedConfig.kt | 6 ++ .../kabin/core/driver/DriverFactory.js.kt | 2 +- .../KabinDatabaseConfiguration.kt | 6 +- .../configuration/KabinExtendedConfig.kt | 6 ++ .../kabin/core/driver/DriverFactory.jvm.kt | 9 +-- .../database/KabinDatabaseConfiguration.kt | 24 ------- .../KabinDatabaseConfiguration.kt | 56 +++++++++++++++ .../configuration/KabinExtendedConfig.kt | 15 ++++ .../kabin/core/driver/DriverFactory.native.kt | 2 +- .../kabin/sample/android/MainActivity.kt | 2 +- .../com/attafitamim/kabin/local/Playground.kt | 4 +- .../kabin/local/database/SampleDatabase.kt | 2 +- .../local/entities/school/StudentEntity.kt | 13 +++- .../kotlin/com/attafitamim/kabin/main.kt | 2 +- 36 files changed, 365 insertions(+), 128 deletions(-) rename library/core/src/androidMain/kotlin/com/attafitamim/kabin/core/database/{ => configuration}/KabinDatabaseConfiguration.kt (57%) create mode 100644 library/core/src/androidMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinExtendedConfig.kt create mode 100644 library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/dao/KabinSuspendingQueries.kt delete mode 100644 library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/dao/KabinSuspendingTransactor.kt create mode 100644 library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/database/KabinBaseDatabase.kt delete mode 100644 library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/database/KabinDatabaseConfiguration.kt create mode 100644 library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinDatabaseConfiguration.kt create mode 100644 library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinExtendedConfig.kt create mode 100644 library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/utils/KabinUtils.kt delete mode 100644 library/core/src/jsMain/kotlin/com/attafitamim/kabin/core/database/KabinDatabaseConfiguration.kt create mode 100644 library/core/src/jsMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinDatabaseConfiguration.kt create mode 100644 library/core/src/jsMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinExtendedConfig.kt rename library/core/src/jvmMain/kotlin/com/attafitamim/kabin/core/database/{ => configuration}/KabinDatabaseConfiguration.kt (68%) create mode 100644 library/core/src/jvmMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinExtendedConfig.kt delete mode 100644 library/core/src/nativeMain/kotlin/com/attafitamim/kabin/core/database/KabinDatabaseConfiguration.kt create mode 100644 library/core/src/nativeMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinDatabaseConfiguration.kt create mode 100644 library/core/src/nativeMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinExtendedConfig.kt diff --git a/library/compiler/src/jvmMain/kotlin/com/attafitamim/kabin/compiler/sql/generator/dao/DaoGenerator.kt b/library/compiler/src/jvmMain/kotlin/com/attafitamim/kabin/compiler/sql/generator/dao/DaoGenerator.kt index 39c55e6..235118b 100644 --- a/library/compiler/src/jvmMain/kotlin/com/attafitamim/kabin/compiler/sql/generator/dao/DaoGenerator.kt +++ b/library/compiler/src/jvmMain/kotlin/com/attafitamim/kabin/compiler/sql/generator/dao/DaoGenerator.kt @@ -26,6 +26,7 @@ import com.attafitamim.kabin.compiler.sql.utils.sql.dao.getParameterReferences import com.attafitamim.kabin.compiler.sql.utils.sql.dao.getSQLQuery import com.attafitamim.kabin.compiler.sql.utils.sql.dao.getSelectSQLQuery import com.attafitamim.kabin.core.dao.KabinDao +import com.attafitamim.kabin.core.database.configuration.KabinExtendedConfig import com.attafitamim.kabin.processor.ksp.options.KabinOptions import com.attafitamim.kabin.processor.utils.throwException import com.attafitamim.kabin.specs.column.ColumnSpec @@ -47,6 +48,7 @@ import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy import com.squareup.kotlinpoet.PropertySpec import com.squareup.kotlinpoet.TypeSpec import com.squareup.kotlinpoet.asClassName +import com.squareup.kotlinpoet.asTypeName import com.squareup.kotlinpoet.ksp.toClassName import com.squareup.kotlinpoet.ksp.toTypeName @@ -57,6 +59,8 @@ class DaoGenerator( ) { private val daoQueriesPropertyName = KabinDao<*>::queries.name + private val daoConfigPropertyName = KabinDao<*>::configuration.name + private val daoConfigPropertyType = KabinDao<*>::configuration.returnType.asTypeName() fun generate(daoSpec: DaoSpec): Result { val daoFilePackage = daoSpec.declaration.packageName.asString() @@ -124,6 +128,7 @@ class DaoGenerator( val constructorBuilder = FunSpec.constructorBuilder() .addParameter(daoQueriesPropertyName, daoQueriesClassName) + .addParameter(daoConfigPropertyName, daoConfigPropertyType) val daoQueriesPropertySpec = PropertySpec.builder( daoQueriesPropertyName, @@ -131,6 +136,12 @@ class DaoGenerator( KModifier.OVERRIDE ).initializer(daoQueriesPropertyName).build() + val daoConfigPropertySpec = PropertySpec.builder( + daoConfigPropertyName, + daoConfigPropertyType, + KModifier.OVERRIDE + ).initializer(daoConfigPropertyName).build() + adapters.forEach { adapter -> val propertyName = adapter.getPropertyName() val adapterType = ColumnAdapter::class.asClassName() @@ -153,6 +164,7 @@ class DaoGenerator( classBuilder .primaryConstructor(constructorBuilder.build()) .addProperty(daoQueriesPropertySpec) + .addProperty(daoConfigPropertySpec) codeGenerator.writeType( className, diff --git a/library/compiler/src/jvmMain/kotlin/com/attafitamim/kabin/compiler/sql/generator/database/DatabaseGenerator.kt b/library/compiler/src/jvmMain/kotlin/com/attafitamim/kabin/compiler/sql/generator/database/DatabaseGenerator.kt index da91659..5550af7 100644 --- a/library/compiler/src/jvmMain/kotlin/com/attafitamim/kabin/compiler/sql/generator/database/DatabaseGenerator.kt +++ b/library/compiler/src/jvmMain/kotlin/com/attafitamim/kabin/compiler/sql/generator/database/DatabaseGenerator.kt @@ -2,7 +2,6 @@ package com.attafitamim.kabin.compiler.sql.generator.database import app.cash.sqldelight.ColumnAdapter import app.cash.sqldelight.db.QueryResult -import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.db.SqlSchema import com.attafitamim.kabin.compiler.sql.generator.dao.DaoGenerator import com.attafitamim.kabin.compiler.sql.generator.mapper.MapperGenerator @@ -10,7 +9,6 @@ import com.attafitamim.kabin.compiler.sql.generator.queries.QueriesGenerator import com.attafitamim.kabin.compiler.sql.generator.references.ColumnAdapterReference import com.attafitamim.kabin.compiler.sql.generator.references.MapperReference import com.attafitamim.kabin.compiler.sql.generator.tables.TableGenerator -import com.attafitamim.kabin.compiler.sql.utils.poet.DRIVER_NAME import com.attafitamim.kabin.compiler.sql.utils.poet.SCHEMA_CREATOR_NAME import com.attafitamim.kabin.compiler.sql.utils.poet.SCHEMA_NAME import com.attafitamim.kabin.compiler.sql.utils.poet.asPropertyName @@ -29,8 +27,7 @@ import com.attafitamim.kabin.compiler.sql.utils.spec.getDatabaseClassName import com.attafitamim.kabin.compiler.sql.utils.spec.getQueryFunctionName import com.attafitamim.kabin.compiler.sql.utils.spec.mapperResultByReferences import com.attafitamim.kabin.compiler.sql.utils.spec.mapperSpecsByReferences -import com.attafitamim.kabin.core.database.KabinDatabase -import com.attafitamim.kabin.core.database.KabinDatabaseConfiguration +import com.attafitamim.kabin.core.database.KabinBaseDatabase import com.attafitamim.kabin.core.database.KabinSqlSchema import com.attafitamim.kabin.core.table.KabinMapper import com.attafitamim.kabin.processor.ksp.options.KabinOptions @@ -42,13 +39,13 @@ import com.google.devtools.ksp.symbol.ClassKind import com.squareup.kotlinpoet.ClassName import com.squareup.kotlinpoet.FileSpec import com.squareup.kotlinpoet.FunSpec -import com.squareup.kotlinpoet.Import import com.squareup.kotlinpoet.KModifier import com.squareup.kotlinpoet.ParameterSpec import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy import com.squareup.kotlinpoet.PropertySpec import com.squareup.kotlinpoet.TypeSpec import com.squareup.kotlinpoet.asClassName +import com.squareup.kotlinpoet.asTypeName import com.squareup.kotlinpoet.ksp.toClassName import kotlin.reflect.KClass import kotlin.reflect.full.primaryConstructor @@ -116,28 +113,36 @@ class DatabaseGenerator( requiredMappers: List ) { val className = databaseSpec.getDatabaseClassName(options) - val superInterface = KabinDatabase::class.asClassName() + val superClass = KabinBaseDatabase::class.asClassName() val databaseInterface = databaseSpec.declaration.toClassName() val classBuilder = TypeSpec.classBuilder(className) - .addSuperinterface(superInterface) + .superclass(superClass) .addSuperinterface(databaseInterface) .addModifiers(KModifier.PRIVATE) - val driverName = DRIVER_NAME - val driverType = SqlDriver::class.asClassName() - val constructorBuilder = FunSpec.constructorBuilder() - .addParameter(driverName, SqlDriver::class.asClassName()) + val driverName = KabinBaseDatabase::driver.name + val driverType = KabinBaseDatabase::driver.returnType.asTypeName() + val driverParameter = ParameterSpec.builder( + driverName, + driverType + ) - classBuilder.primaryConstructor(constructorBuilder.build()) + val configurationName = KabinBaseDatabase::configuration.name + val configurationType = KabinBaseDatabase::configuration.returnType.asTypeName() + val configurationParameter = ParameterSpec.builder( + configurationName, + configurationType + ) - val driverProperty = PropertySpec.builder( - driverName, - SqlDriver::class.asClassName(), - KModifier.PRIVATE - ).initializer(driverName) + val primaryConstructor = requireNotNull(KabinBaseDatabase::class.primaryConstructor) + val primaryConstructorBuilder = FunSpec.constructorBuilder() + primaryConstructor.parameters.forEach { kParameter -> + primaryConstructorBuilder.addParameter(kParameter.buildSpec().build()) + classBuilder.addSuperclassConstructorParameter(requireNotNull(kParameter.name)) + } - classBuilder.addProperty(driverProperty.build()) + classBuilder.primaryConstructor(primaryConstructorBuilder.build()) val typeConvertersMap = databaseSpec.typeConverters?.converterSpecsByReferences() requiredAdapters.forEach { adapter -> @@ -238,6 +243,7 @@ class DatabaseGenerator( val parameters = ArrayList() val queryClassName = databaseDaoGetterSpec.daoSpec.getQueryFunctionName(options) parameters.add(queryClassName.asPropertyName()) + parameters.add(configurationName) generatedDao.adapters.forEach { adapter -> parameters.add(adapter.getPropertyName()) @@ -258,13 +264,6 @@ class DatabaseGenerator( val schemaObject = createSchemaObjectSpec(objectClassName, generatedTables) classBuilder.addType(schemaObject) - - val configurationType = KabinDatabaseConfiguration::class.asClassName() - val configurationParameterName = configurationType.asPropertyName() - val configurationParameter = ParameterSpec.builder( - configurationParameterName, - configurationType - ) val migrationsParameter = KabinSqlSchema::migrations .parameterBuildSpec().defaultValue("emptyList()") @@ -293,14 +292,17 @@ class DatabaseGenerator( .addParameter(migrationsParameter.build()) .addParameter(migrationStrategyParameter.build()) .addParameter(versionParameter.build()) + .addParameter(configurationParameter.build()) .addStatement("return·%T($schemaConstructorParametersCall)", objectClassName) .build() - val newInstanceExtension = FunSpec.builder(Class<*>::newInstance.name) + val newInstanceName = Class<*>::newInstance.name + val newInstanceExtension = FunSpec.builder(newInstanceName) .receiver(databaseKClassType) .returns(databaseInterface) - .addParameter(driverName, driverType) - .addStatement("return·%T($driverName)", className) + .addParameter(driverParameter.build()) + .addParameter(configurationParameter.build()) + .addStatement("return·%T($driverName, $configurationName)", className) .build() val schemaParameterName = SCHEMA_NAME.asPropertyName() @@ -308,13 +310,13 @@ class DatabaseGenerator( .receiver(databaseKClassType) .returns(databaseInterface) .addModifiers() - .addParameter(configurationParameter.build()) .addParameter(migrationsParameter.build()) .addParameter(migrationStrategyParameter.build()) .addParameter(versionParameter.build()) + .addParameter(configurationParameter.build()) .addStatement("val·$schemaParameterName·=·$schemaExtensionName($schemaConstructorParametersCall)") - .addStatement("val·$driverName·=·$configurationParameterName.createDriver($schemaParameterName)") - .addStatement("return·%T($driverName)", className) + .addStatement("val·$driverName·=·$configurationName.createDriver($schemaParameterName)") + .addStatement("return·$newInstanceName($driverName, $configurationName)") .build() val fileSpec = FileSpec.builder(className) @@ -389,8 +391,8 @@ class DatabaseGenerator( private fun TypeSpec.Builder.addTableActions( generatedTables: List ) { - val driverName = DRIVER_NAME - val clearFunctionBuilder = KabinDatabase::clearTables.buildSpec() + val driverName = KabinBaseDatabase::driver.name + val clearFunctionBuilder = KabinBaseDatabase::clearTables.buildSpec() .addModifiers(KModifier.OVERRIDE) generatedTables.forEach { generatedTable -> diff --git a/library/compiler/src/jvmMain/kotlin/com/attafitamim/kabin/compiler/sql/generator/queries/QueriesGenerator.kt b/library/compiler/src/jvmMain/kotlin/com/attafitamim/kabin/compiler/sql/generator/queries/QueriesGenerator.kt index d85dd0e..3f6dc34 100644 --- a/library/compiler/src/jvmMain/kotlin/com/attafitamim/kabin/compiler/sql/generator/queries/QueriesGenerator.kt +++ b/library/compiler/src/jvmMain/kotlin/com/attafitamim/kabin/compiler/sql/generator/queries/QueriesGenerator.kt @@ -11,7 +11,6 @@ import com.attafitamim.kabin.compiler.sql.generator.references.ColumnAdapterRefe import com.attafitamim.kabin.compiler.sql.generator.references.FunctionReference import com.attafitamim.kabin.compiler.sql.generator.references.MapperReference import com.attafitamim.kabin.compiler.sql.syntax.SQLQuery -import com.attafitamim.kabin.compiler.sql.utils.poet.DRIVER_NAME import com.attafitamim.kabin.compiler.sql.utils.poet.SYMBOL_ACCESS_SIGN import com.attafitamim.kabin.compiler.sql.utils.poet.asSpecs import com.attafitamim.kabin.compiler.sql.utils.poet.buildSpec @@ -40,7 +39,8 @@ import com.attafitamim.kabin.compiler.sql.utils.sql.dao.getSQLQuery import com.attafitamim.kabin.compiler.sql.utils.sql.dao.getSelectSQLQuery import com.attafitamim.kabin.compiler.sql.utils.sql.entity.getFlatColumns import com.attafitamim.kabin.compiler.sql.utils.sql.sqlType -import com.attafitamim.kabin.core.dao.KabinSuspendingTransactor +import com.attafitamim.kabin.core.dao.KabinSuspendingQueries +import com.attafitamim.kabin.core.database.KabinBaseDatabase import com.attafitamim.kabin.core.table.KabinMapper import com.attafitamim.kabin.processor.ksp.options.KabinOptions import com.attafitamim.kabin.processor.utils.throwException @@ -80,11 +80,11 @@ class QueriesGenerator( fun generate(daoSpec: DaoSpec): Result { val className = daoSpec.getQueryFunctionName(options) - val superClassName = KabinSuspendingTransactor::class.asClassName() + val superClassName = KabinSuspendingQueries::class.asClassName() val classBuilder = TypeSpec.classBuilder(className) .superclass(superClassName) - .addSuperclassConstructorParameter(DRIVER_NAME) + .addSuperclassConstructorParameter(KabinBaseDatabase::driver.name) val adapters = HashSet() val mappers = HashSet() @@ -118,8 +118,10 @@ class QueriesGenerator( } } - val constructorBuilder = FunSpec.constructorBuilder() - .addParameter(DRIVER_NAME, SqlDriver::class.asClassName()) + val constructorBuilder = FunSpec.constructorBuilder().addParameter( + KabinBaseDatabase::driver.name, + KabinBaseDatabase::driver.returnType.asTypeName() + ) adapters.forEach { adapter -> val propertyName = adapter.getPropertyName() @@ -857,7 +859,7 @@ class QueriesGenerator( return@apply } - val driverName = DRIVER_NAME + val driverName = KabinBaseDatabase::driver.name addStatement("$driverName.$listenerMethod(") keys.forEach { key -> addStatement("%S,", key) diff --git a/library/compiler/src/jvmMain/kotlin/com/attafitamim/kabin/compiler/sql/syntax/SQLSyntax.kt b/library/compiler/src/jvmMain/kotlin/com/attafitamim/kabin/compiler/sql/syntax/SQLSyntax.kt index d510826..93fd163 100644 --- a/library/compiler/src/jvmMain/kotlin/com/attafitamim/kabin/compiler/sql/syntax/SQLSyntax.kt +++ b/library/compiler/src/jvmMain/kotlin/com/attafitamim/kabin/compiler/sql/syntax/SQLSyntax.kt @@ -40,6 +40,7 @@ object SQLSyntax { val SQLBuilder.DEFERRABLE get() = append("DEFERRABLE") val SQLBuilder.INITIALLY get() = append("INITIALLY") val SQLBuilder.DEFERRED get() = append("DEFERRED") + val SQLBuilder.IMMEDIATE get() = append("IMMEDIATE") val SQLBuilder.ASC get() = append("ASC") val SQLBuilder.DESC get() = append("DESC") val SQLBuilder.ROLLBACK get() = append("ROLLBACK") diff --git a/library/compiler/src/jvmMain/kotlin/com/attafitamim/kabin/compiler/sql/utils/poet/PoetConstants.kt b/library/compiler/src/jvmMain/kotlin/com/attafitamim/kabin/compiler/sql/utils/poet/PoetConstants.kt index 6d780c5..6ec7940 100644 --- a/library/compiler/src/jvmMain/kotlin/com/attafitamim/kabin/compiler/sql/utils/poet/PoetConstants.kt +++ b/library/compiler/src/jvmMain/kotlin/com/attafitamim/kabin/compiler/sql/utils/poet/PoetConstants.kt @@ -1,6 +1,5 @@ package com.attafitamim.kabin.compiler.sql.utils.poet -const val DRIVER_NAME = "driver" const val SCHEMA_CREATOR_NAME = "createSchema" const val SCHEMA_NAME = "Schema" diff --git a/library/compiler/src/jvmMain/kotlin/com/attafitamim/kabin/compiler/sql/utils/sql/index/SQLForeignKeyUtils.kt b/library/compiler/src/jvmMain/kotlin/com/attafitamim/kabin/compiler/sql/utils/sql/index/SQLForeignKeyUtils.kt index 877a984..a24483b 100644 --- a/library/compiler/src/jvmMain/kotlin/com/attafitamim/kabin/compiler/sql/utils/sql/index/SQLForeignKeyUtils.kt +++ b/library/compiler/src/jvmMain/kotlin/com/attafitamim/kabin/compiler/sql/utils/sql/index/SQLForeignKeyUtils.kt @@ -8,6 +8,7 @@ import com.attafitamim.kabin.compiler.sql.syntax.SQLSyntax.DEFERRABLE import com.attafitamim.kabin.compiler.sql.syntax.SQLSyntax.DEFERRED import com.attafitamim.kabin.compiler.sql.syntax.SQLSyntax.DELETE import com.attafitamim.kabin.compiler.sql.syntax.SQLSyntax.FOREIGN_KEY +import com.attafitamim.kabin.compiler.sql.syntax.SQLSyntax.IMMEDIATE import com.attafitamim.kabin.compiler.sql.syntax.SQLSyntax.INITIALLY import com.attafitamim.kabin.compiler.sql.syntax.SQLSyntax.NO_ACTION import com.attafitamim.kabin.compiler.sql.syntax.SQLSyntax.NULL @@ -46,9 +47,7 @@ fun SQLBuilder.appendForeignKeyDefinition( ON; UPDATE(foreignKeySpec.onUpdate) ON; DELETE(foreignKeySpec.onDelete) - if (foreignKeySpec.deferred) { - DEFERRABLE; INITIALLY; DEFERRED - } + DEFERRABLE; INITIALLY; if (foreignKeySpec.deferred) DEFERRED else IMMEDIATE } operator fun SQLBuilder.invoke(action: ForeignKey.Action?) = when (action) { diff --git a/library/core/src/androidMain/kotlin/com/attafitamim/kabin/core/database/KabinDatabaseConfiguration.kt b/library/core/src/androidMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinDatabaseConfiguration.kt similarity index 57% rename from library/core/src/androidMain/kotlin/com/attafitamim/kabin/core/database/KabinDatabaseConfiguration.kt rename to library/core/src/androidMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinDatabaseConfiguration.kt index f2ff0d0..f5b73cc 100644 --- a/library/core/src/androidMain/kotlin/com/attafitamim/kabin/core/database/KabinDatabaseConfiguration.kt +++ b/library/core/src/androidMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinDatabaseConfiguration.kt @@ -1,4 +1,4 @@ -package com.attafitamim.kabin.core.database +package com.attafitamim.kabin.core.database.configuration import android.content.Context import androidx.sqlite.db.SupportSQLiteDatabase @@ -7,18 +7,18 @@ private const val DEFAULT_CACHE_SIZE = 20 typealias OpenCallback = (db: SupportSQLiteDatabase) -> Unit -private fun createDefaultCallback( - foreignKeyConstraintsEnabled: Boolean -): OpenCallback = { db -> - db.setForeignKeyConstraintsEnabled(foreignKeyConstraintsEnabled) -} - actual class KabinDatabaseConfiguration( val context: Context, val name: String? = null, val cacheSize: Int = DEFAULT_CACHE_SIZE, val useNoBackupDirectory: Boolean = false, val windowSizeBytes: Long? = null, - val foreignKeyConstraintsEnabled: Boolean = true, - val onOpen: OpenCallback? = createDefaultCallback(foreignKeyConstraintsEnabled) + actual val extendedConfig: KabinExtendedConfig = KabinExtendedConfig(), + val onOpen: OpenCallback? = createDefaultCallback(extendedConfig) ) + +private fun createDefaultCallback( + constraintsConfiguration: KabinExtendedConfig +): OpenCallback = { db -> + db.setForeignKeyConstraintsEnabled(constraintsConfiguration.foreignKeyConstraintsEnabled) +} diff --git a/library/core/src/androidMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinExtendedConfig.kt b/library/core/src/androidMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinExtendedConfig.kt new file mode 100644 index 0000000..e73bf82 --- /dev/null +++ b/library/core/src/androidMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinExtendedConfig.kt @@ -0,0 +1,6 @@ +package com.attafitamim.kabin.core.database.configuration + +actual class KabinExtendedConfig( + actual val foreignKeyConstraintsEnabled: Boolean = true, + actual val deferForeignKeysInsideTransaction: Boolean = true +) diff --git a/library/core/src/androidMain/kotlin/com/attafitamim/kabin/core/driver/DriverFactory.android.kt b/library/core/src/androidMain/kotlin/com/attafitamim/kabin/core/driver/DriverFactory.android.kt index ae0592f..23b6b56 100644 --- a/library/core/src/androidMain/kotlin/com/attafitamim/kabin/core/driver/DriverFactory.android.kt +++ b/library/core/src/androidMain/kotlin/com/attafitamim/kabin/core/driver/DriverFactory.android.kt @@ -6,8 +6,8 @@ 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 -import com.attafitamim.kabin.core.database.KabinDatabaseConfiguration -import com.attafitamim.kabin.core.database.OpenCallback +import com.attafitamim.kabin.core.database.configuration.KabinDatabaseConfiguration +import com.attafitamim.kabin.core.database.configuration.OpenCallback private fun createCallback( schema: SqlSchema>, diff --git a/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/dao/KabinDao.kt b/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/dao/KabinDao.kt index 3ff2770..745eb44 100644 --- a/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/dao/KabinDao.kt +++ b/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/dao/KabinDao.kt @@ -2,37 +2,40 @@ package com.attafitamim.kabin.core.dao import app.cash.sqldelight.ExecutableQuery import app.cash.sqldelight.Query -import app.cash.sqldelight.SuspendingTransacter import app.cash.sqldelight.SuspendingTransactionWithReturn import app.cash.sqldelight.SuspendingTransactionWithoutReturn import app.cash.sqldelight.coroutines.asFlow import app.cash.sqldelight.coroutines.mapToList import app.cash.sqldelight.coroutines.mapToOne import app.cash.sqldelight.coroutines.mapToOneOrNull +import com.attafitamim.kabin.core.database.configuration.KabinDatabaseConfiguration import com.attafitamim.kabin.core.utils.IO import com.attafitamim.kabin.core.utils.awaitAll import com.attafitamim.kabin.core.utils.awaitFirst import com.attafitamim.kabin.core.utils.awaitFirstOrNull +import com.attafitamim.kabin.core.utils.safeTransaction +import com.attafitamim.kabin.core.utils.safeTransactionWithResult import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.withContext -interface KabinDao { +interface KabinDao { val queries: T + val configuration: KabinDatabaseConfiguration suspend fun transaction( body: suspend SuspendingTransactionWithoutReturn.() -> Unit ) = withContextIO { - queries.transaction(body = body) + queries.safeTransaction(configuration, body = body) } suspend fun transactionWithResult( body: suspend SuspendingTransactionWithReturn.() -> R ): R = withContextIO { - queries.transactionWithResult(bodyWithReturn = body) + queries.safeTransactionWithResult(configuration, body = body) } suspend fun ExecutableQuery.awaitAsListIO(): List = withContextIO { diff --git a/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/dao/KabinSuspendingQueries.kt b/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/dao/KabinSuspendingQueries.kt new file mode 100644 index 0000000..36a8be1 --- /dev/null +++ b/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/dao/KabinSuspendingQueries.kt @@ -0,0 +1,64 @@ +package com.attafitamim.kabin.core.dao + +import app.cash.sqldelight.SuspendingTransacterImpl +import app.cash.sqldelight.db.SqlDriver + +open class KabinSuspendingQueries(driver: SqlDriver) : SuspendingTransacterImpl(driver) { + + fun createNullableArguments(count: Int?): String { + if (count == null) return "()" + return createArguments(count) + } + + fun createNullableParameter(parameter: Any?): String { + if (parameter == null) return "NULL" + 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 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 + } + } + } diff --git a/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/dao/KabinSuspendingTransactor.kt b/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/dao/KabinSuspendingTransactor.kt deleted file mode 100644 index e9d4071..0000000 --- a/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/dao/KabinSuspendingTransactor.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.attafitamim.kabin.core.dao - -import app.cash.sqldelight.SuspendingTransacterImpl -import app.cash.sqldelight.db.SqlDriver - -abstract class KabinSuspendingTransactor(driver: SqlDriver) : SuspendingTransacterImpl(driver) { - - fun createNullableArguments(count: Int?): String { - if (count == null) return "()" - return createArguments(count) - } - - fun createNullableParameter(parameter: Any?): String { - if (parameter == null) return "NULL" - return parameter.toString() - } -} diff --git a/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/database/KabinBaseDatabase.kt b/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/database/KabinBaseDatabase.kt new file mode 100644 index 0000000..96dee28 --- /dev/null +++ b/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/database/KabinBaseDatabase.kt @@ -0,0 +1,21 @@ +package com.attafitamim.kabin.core.database + +import app.cash.sqldelight.db.SqlDriver +import com.attafitamim.kabin.core.dao.KabinSuspendingQueries +import com.attafitamim.kabin.core.database.configuration.KabinDatabaseConfiguration +import com.attafitamim.kabin.core.utils.safeGlobalTransaction + +abstract class KabinBaseDatabase( + val driver: SqlDriver, + val configuration: KabinDatabaseConfiguration +) : KabinDatabase { + + private val queries = KabinSuspendingQueries(driver) + abstract suspend fun clearTables() + + final override suspend fun clear() { + queries.safeGlobalTransaction(configuration) { + clearTables() + }.await() + } +} \ No newline at end of file diff --git a/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/database/KabinDatabase.kt b/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/database/KabinDatabase.kt index 1c92d97..52cea05 100644 --- a/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/database/KabinDatabase.kt +++ b/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/database/KabinDatabase.kt @@ -1,5 +1,5 @@ package com.attafitamim.kabin.core.database interface KabinDatabase { - suspend fun clearTables() + suspend fun clear() } diff --git a/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/database/KabinDatabaseConfiguration.kt b/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/database/KabinDatabaseConfiguration.kt deleted file mode 100644 index 1d5881d..0000000 --- a/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/database/KabinDatabaseConfiguration.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.attafitamim.kabin.core.database - -expect class KabinDatabaseConfiguration \ No newline at end of file diff --git a/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/database/KabinSqlSchema.kt b/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/database/KabinSqlSchema.kt index f2eced8..c6b4cc2 100644 --- a/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/database/KabinSqlSchema.kt +++ b/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/database/KabinSqlSchema.kt @@ -4,29 +4,33 @@ import app.cash.sqldelight.db.AfterVersion import app.cash.sqldelight.db.QueryResult import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.db.SqlSchema +import com.attafitamim.kabin.core.database.configuration.KabinDatabaseConfiguration import com.attafitamim.kabin.core.exceptions.SqlMigrationMissing import com.attafitamim.kabin.core.migration.KabinMigrationStrategy import com.attafitamim.kabin.core.migration.Migration +import com.attafitamim.kabin.core.utils.safeGlobalTransaction abstract class KabinSqlSchema( val migrations: List, val migrationStrategy: KabinMigrationStrategy, - override val version: Long + override val version: Long, + val configuration: KabinDatabaseConfiguration ) : SqlSchema> { abstract suspend fun dropTables(driver: SqlDriver) abstract suspend fun createTables(driver: SqlDriver) - override fun create(driver: SqlDriver): QueryResult.AsyncValue = QueryResult.AsyncValue { - createTables(driver) - } + override fun create(driver: SqlDriver): QueryResult.AsyncValue = + driver.safeGlobalTransaction(configuration) { + createTables(driver) + } override fun migrate( driver: SqlDriver, oldVersion: Long, newVersion: Long, vararg callbacks: AfterVersion - ): QueryResult.AsyncValue = QueryResult.AsyncValue { + ): QueryResult.AsyncValue = driver.safeGlobalTransaction(configuration) { when { oldVersion == newVersion -> callbacks.notifyAll(driver) oldVersion > newVersion -> handleMissingMigration(oldVersion, newVersion, driver, callbacks) diff --git a/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinDatabaseConfiguration.kt b/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinDatabaseConfiguration.kt new file mode 100644 index 0000000..8bd66c5 --- /dev/null +++ b/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinDatabaseConfiguration.kt @@ -0,0 +1,5 @@ +package com.attafitamim.kabin.core.database.configuration + +expect class KabinDatabaseConfiguration { + val extendedConfig: KabinExtendedConfig +} \ No newline at end of file diff --git a/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinExtendedConfig.kt b/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinExtendedConfig.kt new file mode 100644 index 0000000..4536cb0 --- /dev/null +++ b/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinExtendedConfig.kt @@ -0,0 +1,6 @@ +package com.attafitamim.kabin.core.database.configuration + +expect class KabinExtendedConfig { + val foreignKeyConstraintsEnabled: Boolean + val deferForeignKeysInsideTransaction: Boolean +} \ No newline at end of file diff --git a/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/driver/DriverFactory.kt b/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/driver/DriverFactory.kt index d06bbf1..b26c36c 100644 --- a/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/driver/DriverFactory.kt +++ b/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/driver/DriverFactory.kt @@ -3,7 +3,7 @@ package com.attafitamim.kabin.core.driver import app.cash.sqldelight.db.QueryResult import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.db.SqlSchema -import com.attafitamim.kabin.core.database.KabinDatabaseConfiguration +import com.attafitamim.kabin.core.database.configuration.KabinDatabaseConfiguration expect fun KabinDatabaseConfiguration.createDriver( schema: SqlSchema>, diff --git a/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/utils/KabinUtils.kt b/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/utils/KabinUtils.kt new file mode 100644 index 0000000..f640ca5 --- /dev/null +++ b/library/core/src/commonMain/kotlin/com/attafitamim/kabin/core/utils/KabinUtils.kt @@ -0,0 +1,60 @@ +package com.attafitamim.kabin.core.utils + +import app.cash.sqldelight.SuspendingTransactionWithReturn +import app.cash.sqldelight.SuspendingTransactionWithoutReturn +import app.cash.sqldelight.TransactionCallbacks +import app.cash.sqldelight.db.QueryResult +import app.cash.sqldelight.db.SqlDriver +import com.attafitamim.kabin.core.dao.KabinSuspendingQueries +import com.attafitamim.kabin.core.database.configuration.KabinDatabaseConfiguration + +suspend inline fun KabinSuspendingQueries.safeTransaction( + configuration: KabinDatabaseConfiguration, + crossinline body: suspend SuspendingTransactionWithoutReturn.() -> Unit +) = transaction { + tryDifferForeignKeys(configuration) + body() +} + +suspend inline fun KabinSuspendingQueries.safeTransactionWithResult( + configuration: KabinDatabaseConfiguration, + crossinline body: suspend SuspendingTransactionWithReturn.() -> T +): T = transactionWithResult { + tryDifferForeignKeys(configuration) + body() +} + +inline fun SqlDriver.safeGlobalTransaction( + configuration: KabinDatabaseConfiguration, + noinline body: suspend TransactionCallbacks.() -> Unit +): QueryResult.AsyncValue = KabinSuspendingQueries(this) + .safeGlobalTransaction(configuration, body) + +inline fun KabinSuspendingQueries.safeGlobalTransaction( + configuration: KabinDatabaseConfiguration, + noinline body: suspend TransactionCallbacks.() -> Unit +) = QueryResult.AsyncValue { + try { + tryToggleForeignKeys(configuration, enabled = false) + safeTransaction(configuration, body = body) + } finally { + tryToggleForeignKeys(configuration, enabled = true) + } +} + +suspend fun KabinSuspendingQueries.tryToggleForeignKeys( + configuration: KabinDatabaseConfiguration, + enabled: Boolean +) { + if (configuration.extendedConfig.foreignKeyConstraintsEnabled) { + toggleForeignKeys(enabled) + } +} + +suspend fun KabinSuspendingQueries.tryDifferForeignKeys( + configuration: KabinDatabaseConfiguration +) = with(configuration.extendedConfig) { + if (foreignKeyConstraintsEnabled && deferForeignKeysInsideTransaction) { + deferForeignKeys() + } +} \ No newline at end of file diff --git a/library/core/src/jsMain/kotlin/com/attafitamim/kabin/core/database/KabinDatabaseConfiguration.kt b/library/core/src/jsMain/kotlin/com/attafitamim/kabin/core/database/KabinDatabaseConfiguration.kt deleted file mode 100644 index f3e6f33..0000000 --- a/library/core/src/jsMain/kotlin/com/attafitamim/kabin/core/database/KabinDatabaseConfiguration.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.attafitamim.kabin.core.database - -actual class KabinDatabaseConfiguration \ No newline at end of file diff --git a/library/core/src/jsMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinDatabaseConfiguration.kt b/library/core/src/jsMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinDatabaseConfiguration.kt new file mode 100644 index 0000000..91f3cb0 --- /dev/null +++ b/library/core/src/jsMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinDatabaseConfiguration.kt @@ -0,0 +1,5 @@ +package com.attafitamim.kabin.core.database.configuration + +actual class KabinDatabaseConfiguration( + actual val extendedConfig: KabinExtendedConfig = KabinExtendedConfig() +) diff --git a/library/core/src/jsMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinExtendedConfig.kt b/library/core/src/jsMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinExtendedConfig.kt new file mode 100644 index 0000000..e73bf82 --- /dev/null +++ b/library/core/src/jsMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinExtendedConfig.kt @@ -0,0 +1,6 @@ +package com.attafitamim.kabin.core.database.configuration + +actual class KabinExtendedConfig( + actual val foreignKeyConstraintsEnabled: Boolean = true, + actual val deferForeignKeysInsideTransaction: Boolean = true +) diff --git a/library/core/src/jsMain/kotlin/com/attafitamim/kabin/core/driver/DriverFactory.js.kt b/library/core/src/jsMain/kotlin/com/attafitamim/kabin/core/driver/DriverFactory.js.kt index 8d64094..98d761d 100644 --- a/library/core/src/jsMain/kotlin/com/attafitamim/kabin/core/driver/DriverFactory.js.kt +++ b/library/core/src/jsMain/kotlin/com/attafitamim/kabin/core/driver/DriverFactory.js.kt @@ -4,7 +4,7 @@ import app.cash.sqldelight.db.QueryResult import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.db.SqlSchema import app.cash.sqldelight.driver.worker.WebWorkerDriver -import com.attafitamim.kabin.core.database.KabinDatabaseConfiguration +import com.attafitamim.kabin.core.database.configuration.KabinDatabaseConfiguration import org.w3c.dom.Worker private const val WORKER_CODE = """new URL("@cashapp/sqldelight-sqljs-worker/sqljs.worker.js", import.meta.url)""" diff --git a/library/core/src/jvmMain/kotlin/com/attafitamim/kabin/core/database/KabinDatabaseConfiguration.kt b/library/core/src/jvmMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinDatabaseConfiguration.kt similarity index 68% rename from library/core/src/jvmMain/kotlin/com/attafitamim/kabin/core/database/KabinDatabaseConfiguration.kt rename to library/core/src/jvmMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinDatabaseConfiguration.kt index 139d03b..e5936d3 100644 --- a/library/core/src/jvmMain/kotlin/com/attafitamim/kabin/core/database/KabinDatabaseConfiguration.kt +++ b/library/core/src/jvmMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinDatabaseConfiguration.kt @@ -1,4 +1,4 @@ -package com.attafitamim.kabin.core.database +package com.attafitamim.kabin.core.database.configuration import app.cash.sqldelight.db.AfterVersion import java.util.Properties @@ -8,5 +8,5 @@ actual class KabinDatabaseConfiguration( val properties: Properties = Properties(), val migrateEmptySchema: Boolean = false, val callbacks: Array = emptyArray(), - val foreignKeyConstraintsEnabled: Boolean = true -) + actual val extendedConfig: KabinExtendedConfig = KabinExtendedConfig() +) \ No newline at end of file diff --git a/library/core/src/jvmMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinExtendedConfig.kt b/library/core/src/jvmMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinExtendedConfig.kt new file mode 100644 index 0000000..e73bf82 --- /dev/null +++ b/library/core/src/jvmMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinExtendedConfig.kt @@ -0,0 +1,6 @@ +package com.attafitamim.kabin.core.database.configuration + +actual class KabinExtendedConfig( + actual val foreignKeyConstraintsEnabled: Boolean = true, + actual val deferForeignKeysInsideTransaction: Boolean = true +) diff --git a/library/core/src/jvmMain/kotlin/com/attafitamim/kabin/core/driver/DriverFactory.jvm.kt b/library/core/src/jvmMain/kotlin/com/attafitamim/kabin/core/driver/DriverFactory.jvm.kt index aa97e21..b0aabeb 100644 --- a/library/core/src/jvmMain/kotlin/com/attafitamim/kabin/core/driver/DriverFactory.jvm.kt +++ b/library/core/src/jvmMain/kotlin/com/attafitamim/kabin/core/driver/DriverFactory.jvm.kt @@ -5,22 +5,23 @@ import app.cash.sqldelight.db.QueryResult import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.db.SqlSchema import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver -import com.attafitamim.kabin.core.database.KabinDatabaseConfiguration +import com.attafitamim.kabin.core.database.configuration.KabinDatabaseConfiguration +import com.attafitamim.kabin.core.database.configuration.KabinExtendedConfig import java.util.Properties private const val FOREIGN_KEY_FLAG = "foreign_keys" private fun Properties.appendFlags( - foreignKeyConstraintsEnabled: Boolean + extendedConfig: KabinExtendedConfig ) = apply { - put(FOREIGN_KEY_FLAG, foreignKeyConstraintsEnabled) + put(FOREIGN_KEY_FLAG, extendedConfig.foreignKeyConstraintsEnabled) } actual fun KabinDatabaseConfiguration.createDriver( schema: SqlSchema>, ): SqlDriver = JdbcSqliteDriver( url, - properties.appendFlags(foreignKeyConstraintsEnabled), + properties.appendFlags(extendedConfig), schema.synchronous(), migrateEmptySchema, *callbacks diff --git a/library/core/src/nativeMain/kotlin/com/attafitamim/kabin/core/database/KabinDatabaseConfiguration.kt b/library/core/src/nativeMain/kotlin/com/attafitamim/kabin/core/database/KabinDatabaseConfiguration.kt deleted file mode 100644 index b39f5c9..0000000 --- a/library/core/src/nativeMain/kotlin/com/attafitamim/kabin/core/database/KabinDatabaseConfiguration.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.attafitamim.kabin.core.database - -import app.cash.sqldelight.db.AfterVersion -import co.touchlab.sqliter.DatabaseConfiguration - -typealias OnConfiguration = (DatabaseConfiguration) -> DatabaseConfiguration - -private fun createConfiguration( - foreignKeyConstraintsEnabled: Boolean -): OnConfiguration = { config -> - val extendedConfig = DatabaseConfiguration.Extended( - foreignKeyConstraints = foreignKeyConstraintsEnabled - ) - - config.copy(extendedConfig = extendedConfig) -} - -actual class KabinDatabaseConfiguration( - val name: String, - val maxReaderConnections: Int = 1, - val callbacks: Array = emptyArray(), - val foreignKeyConstraintsEnabled: Boolean = true, - val onConfiguration: OnConfiguration = createConfiguration(foreignKeyConstraintsEnabled) -) \ No newline at end of file diff --git a/library/core/src/nativeMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinDatabaseConfiguration.kt b/library/core/src/nativeMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinDatabaseConfiguration.kt new file mode 100644 index 0000000..1fdc399 --- /dev/null +++ b/library/core/src/nativeMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinDatabaseConfiguration.kt @@ -0,0 +1,56 @@ +package com.attafitamim.kabin.core.database.configuration + +import app.cash.sqldelight.db.AfterVersion +import co.touchlab.sqliter.DatabaseConfiguration +import co.touchlab.sqliter.JournalMode + +typealias OnConfiguration = (DatabaseConfiguration) -> DatabaseConfiguration + +actual class KabinDatabaseConfiguration( + val name: String, + val maxReaderConnections: Int = 1, + val callbacks: Array = emptyArray(), + actual val extendedConfig: KabinExtendedConfig = KabinExtendedConfig(), + val loggingConfig: DatabaseConfiguration.Logging = DatabaseConfiguration.Logging(), + val lifecycleConfig: DatabaseConfiguration.Lifecycle = DatabaseConfiguration.Lifecycle(), + val encryptionConfig: DatabaseConfiguration.Encryption = DatabaseConfiguration.Encryption(), + val inMemory: Boolean = false, + val journalMode: JournalMode = JournalMode.WAL, + val onConfiguration: OnConfiguration = createConfiguration( + extendedConfig, + loggingConfig, + lifecycleConfig, + encryptionConfig, + inMemory, + journalMode + ) +) + +private fun createConfiguration( + extendedConfig: KabinExtendedConfig, + loggingConfig: DatabaseConfiguration.Logging, + lifecycleConfig: DatabaseConfiguration.Lifecycle, + encryptionConfig: DatabaseConfiguration.Encryption, + inMemory: Boolean, + journalMode: JournalMode, +): OnConfiguration = { config -> + val newExtendedConfig = config.extendedConfig.copy( + foreignKeyConstraints = extendedConfig.foreignKeyConstraintsEnabled, + busyTimeout = extendedConfig.busyTimeout, + pageSize = extendedConfig.pageSize, + basePath = extendedConfig.basePath, + synchronousFlag = extendedConfig.synchronousFlag, + recursiveTriggers = extendedConfig.recursiveTriggers, + lookasideSlotSize = extendedConfig.lookasideSlotSize, + lookasideSlotCount = extendedConfig.lookasideSlotCount, + ) + + config.copy( + extendedConfig = newExtendedConfig, + loggingConfig = loggingConfig, + lifecycleConfig = lifecycleConfig, + encryptionConfig = encryptionConfig, + inMemory = inMemory, + journalMode = journalMode + ) +} \ No newline at end of file diff --git a/library/core/src/nativeMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinExtendedConfig.kt b/library/core/src/nativeMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinExtendedConfig.kt new file mode 100644 index 0000000..f7e06da --- /dev/null +++ b/library/core/src/nativeMain/kotlin/com/attafitamim/kabin/core/database/configuration/KabinExtendedConfig.kt @@ -0,0 +1,15 @@ +package com.attafitamim.kabin.core.database.configuration + +import co.touchlab.sqliter.SynchronousFlag + +actual class KabinExtendedConfig( + actual val foreignKeyConstraintsEnabled: Boolean = true, + actual val deferForeignKeysInsideTransaction: Boolean = true, + val busyTimeout: Int = 5000, + val pageSize: Int? = null, + val basePath: String? = null, + val synchronousFlag: SynchronousFlag? = null, + val recursiveTriggers: Boolean = false, + val lookasideSlotSize: Int = -1, + val lookasideSlotCount: Int = -1, +) \ No newline at end of file diff --git a/library/core/src/nativeMain/kotlin/com/attafitamim/kabin/core/driver/DriverFactory.native.kt b/library/core/src/nativeMain/kotlin/com/attafitamim/kabin/core/driver/DriverFactory.native.kt index 5807726..827380b 100644 --- a/library/core/src/nativeMain/kotlin/com/attafitamim/kabin/core/driver/DriverFactory.native.kt +++ b/library/core/src/nativeMain/kotlin/com/attafitamim/kabin/core/driver/DriverFactory.native.kt @@ -5,7 +5,7 @@ import app.cash.sqldelight.db.QueryResult import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.db.SqlSchema import app.cash.sqldelight.driver.native.NativeSqliteDriver -import com.attafitamim.kabin.core.database.KabinDatabaseConfiguration +import com.attafitamim.kabin.core.database.configuration.KabinDatabaseConfiguration actual fun KabinDatabaseConfiguration.createDriver( schema: SqlSchema>, diff --git a/sample/androidApp/src/main/java/com/attafitamim/kabin/sample/android/MainActivity.kt b/sample/androidApp/src/main/java/com/attafitamim/kabin/sample/android/MainActivity.kt index 34c5dd8..213dfda 100644 --- a/sample/androidApp/src/main/java/com/attafitamim/kabin/sample/android/MainActivity.kt +++ b/sample/androidApp/src/main/java/com/attafitamim/kabin/sample/android/MainActivity.kt @@ -2,7 +2,7 @@ package com.attafitamim.kabin.sample.android import android.os.Bundle import androidx.appcompat.app.AppCompatActivity -import com.attafitamim.kabin.core.database.KabinDatabaseConfiguration +import com.attafitamim.kabin.core.database.configuration.KabinDatabaseConfiguration import com.attafitamim.kabin.local.Playground class MainActivity : AppCompatActivity() { diff --git a/sample/shared/src/commonMain/kotlin/com/attafitamim/kabin/local/Playground.kt b/sample/shared/src/commonMain/kotlin/com/attafitamim/kabin/local/Playground.kt index 7ebebaa..2e1417b 100644 --- a/sample/shared/src/commonMain/kotlin/com/attafitamim/kabin/local/Playground.kt +++ b/sample/shared/src/commonMain/kotlin/com/attafitamim/kabin/local/Playground.kt @@ -1,6 +1,6 @@ package com.attafitamim.kabin.local -import com.attafitamim.kabin.core.database.KabinDatabaseConfiguration +import com.attafitamim.kabin.core.database.configuration.KabinDatabaseConfiguration import com.attafitamim.kabin.core.migration.KabinMigrationStrategy import com.attafitamim.kabin.local.dao.UserCompoundsDao import com.attafitamim.kabin.local.dao.UserDao @@ -23,7 +23,7 @@ class Playground( val scope = CoroutineScope(Dispatchers.Default) private val database: SampleDatabase by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { SampleDatabase::class.newInstance( - configuration, + configuration = configuration, migrations = emptyList(), migrationStrategy = KabinMigrationStrategy.DESTRUCTIVE ) diff --git a/sample/shared/src/commonMain/kotlin/com/attafitamim/kabin/local/database/SampleDatabase.kt b/sample/shared/src/commonMain/kotlin/com/attafitamim/kabin/local/database/SampleDatabase.kt index b4b10b3..772322e 100644 --- a/sample/shared/src/commonMain/kotlin/com/attafitamim/kabin/local/database/SampleDatabase.kt +++ b/sample/shared/src/commonMain/kotlin/com/attafitamim/kabin/local/database/SampleDatabase.kt @@ -30,7 +30,7 @@ import com.attafitamim.kabin.local.entities.school.StudentEntity SchoolStudentJunction::class, BackPackEntity::class ], - version = 25 + version = 26 ) @TypeConverters( IntStringAdapter::class, diff --git a/sample/shared/src/commonMain/kotlin/com/attafitamim/kabin/local/entities/school/StudentEntity.kt b/sample/shared/src/commonMain/kotlin/com/attafitamim/kabin/local/entities/school/StudentEntity.kt index f0d1554..960b11c 100644 --- a/sample/shared/src/commonMain/kotlin/com/attafitamim/kabin/local/entities/school/StudentEntity.kt +++ b/sample/shared/src/commonMain/kotlin/com/attafitamim/kabin/local/entities/school/StudentEntity.kt @@ -1,9 +1,20 @@ package com.attafitamim.kabin.local.entities.school import com.attafitamim.kabin.annotations.Entity +import com.attafitamim.kabin.annotations.ForeignKey import com.attafitamim.kabin.annotations.PrimaryKey -@Entity +@Entity( + foreignKeys = [ + ForeignKey( + SchoolEntity::class, + parentColumns = ["identity"], + childColumns = ["schoolId"], + onDelete = ForeignKey.Action.CASCADE, + onUpdate = ForeignKey.Action.CASCADE + ) + ] +) data class StudentEntity( @PrimaryKey val id: String, diff --git a/sample/shared/src/jvmMain/kotlin/com/attafitamim/kabin/main.kt b/sample/shared/src/jvmMain/kotlin/com/attafitamim/kabin/main.kt index 531b409..1f237c2 100644 --- a/sample/shared/src/jvmMain/kotlin/com/attafitamim/kabin/main.kt +++ b/sample/shared/src/jvmMain/kotlin/com/attafitamim/kabin/main.kt @@ -1,7 +1,7 @@ package com.attafitamim.kabin import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver -import com.attafitamim.kabin.core.database.KabinDatabaseConfiguration +import com.attafitamim.kabin.core.database.configuration.KabinDatabaseConfiguration import com.attafitamim.kabin.local.Playground import kotlinx.coroutines.isActive