From c46a6dfcd5c2fe300c1ddc42c2ea62ccb3301845 Mon Sep 17 00:00:00 2001 From: Florent Champigny Date: Fri, 30 Oct 2026 23:11:31 +0100 Subject: [PATCH 1/6] fix: [DATABASE] Link the query logs to their database by its id --- .../database/core/FloconDatabasePlugin.kt | 4 +- .../database/core/FloconDatabasePlugin.kt | 10 +- .../database/core/FloconDatabasePluginImpl.kt | 8 +- .../fromdevice/sql/DatabaseQueryLogModel.kt | 2 +- .../database/core/FloconDatabasePluginTest.kt | 23 +- .../room/extensions/RoomBuilderExt.kt | 2 +- .../database/room/FloconRoomDatabaseModel.kt | 2 +- .../room/extensions/RoomBuilderExt.kt | 4 +- .../database/room/FloconRoomDatabaseModel.kt | 6 +- .../room3/FloconRoom3DatabaseModel.kt | 2 +- .../room3/FloconRoom3DatabaseModel.kt | 6 +- .../flocon/myapplication/multi/Databases.kt | 3 + .../myapplication/multi/room2/CatDatabase.kt | 4 +- .../kotlin/com/flocon/sample/desktop/Main.kt | 4 +- .../kotlin/com/flocon/sample/ios/Databases.kt | 4 +- .../com/flocon/sample/wasm/Databases.kt | 4 +- .../90.json | 2465 +++++++++++++++++ .../flocondesktop/common/db/AppDatabase.kt | 2 +- .../DatabaseQueryLogPanelViewModel.kt | 2 +- .../database/DatabaseQueryLogsViewModel.kt | 10 +- .../features/database/DatabaseViewModel.kt | 4 - .../database/model/DatabaseTabState.kt | 1 - .../features/database/view/DatabaseTab.kt | 2 +- .../database/view/logs/DatabaseQueryLogs.kt | 6 +- .../datasource/LocalDatabaseDataSource.kt | 8 +- .../repository/DatabaseRepositoryImpl.kt | 16 +- .../datasource/LocalDatabaseDataSourceRoom.kt | 40 +- .../database/models/DatabaseQueryLogEntity.kt | 4 +- .../datasource/TimelineQueryBuilder.kt | 6 +- .../QueryDatabaseRemoteDataSourceImpl.kt | 2 +- .../database/models/DatabaseQueryLogModel.kt | 2 +- .../QueryDatabaseRemoteDataSourceImplTest.kt | 66 + .../models/DatabaseQueryLogDomainModel.kt | 2 +- .../database/repository/DatabaseRepository.kt | 8 +- .../usecase/CountDatabaseQueryLogsUseCase.kt | 4 +- .../usecase/GetDatabaseQueryLogsUseCase.kt | 4 +- .../ObserveDatabaseQueryLogsUseCase.kt | 4 +- .../mcp/database/DatabaseMcpToolProvider.kt | 3 +- .../database/DatabaseMcpToolProviderTest.kt | 16 +- docs2/database.md | 21 +- docs2/migration.md | 4 +- protocol/database.md | 8 +- 42 files changed, 2684 insertions(+), 114 deletions(-) create mode 100644 FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/90.json create mode 100644 FloconDesktop/data/remote/src/commonTest/kotlin/com/flocon/data/remote/database/datasource/QueryDatabaseRemoteDataSourceImplTest.kt diff --git a/FloconAndroid/database/core-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePlugin.kt b/FloconAndroid/database/core-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePlugin.kt index d2760dc6b..3189f2045 100644 --- a/FloconAndroid/database/core-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePlugin.kt +++ b/FloconAndroid/database/core-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePlugin.kt @@ -11,7 +11,7 @@ interface FloconDatabasePlugin : FloconPlugin { fun register(floconDatabaseModel: FloconDatabaseModel) - fun logQuery(dbName: String, sqlQuery: String, bindArgs: List) + fun logQuery(dbId: String, sqlQuery: String, bindArgs: List) } @@ -19,7 +19,7 @@ internal object FloconDatabasePluginNoOp : FloconDatabasePlugin { override val key: String = "database" override val providers: List = emptyList() override fun register(floconDatabaseModel: FloconDatabaseModel) = Unit - override fun logQuery(dbName: String, sqlQuery: String, bindArgs: List) = Unit + override fun logQuery(dbId: String, sqlQuery: String, bindArgs: List) = Unit override suspend fun onMessageReceived(method: String, body: String) = Unit override suspend fun onConnectedToServer() = Unit } diff --git a/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePlugin.kt b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePlugin.kt index 1877f38aa..2f8c9daf6 100644 --- a/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePlugin.kt +++ b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePlugin.kt @@ -10,9 +10,15 @@ interface FloconDatabasePlugin : FloconPlugin { val providers: List + /** + * @throws IllegalArgumentException if another database is already registered with the same [FloconDatabaseModel.id] + */ fun register(floconDatabaseModel: FloconDatabaseModel) - fun logQuery(dbName: String, sqlQuery: String, bindArgs: List) + /** + * @param dbId the id the database was registered with (see [register]), the queries are shown with this database in the desktop app + */ + fun logQuery(dbId: String, sqlQuery: String, bindArgs: List) } @@ -23,7 +29,7 @@ private class NoOpDatabasePlugin : FloconDatabasePlugin { override val key: String = "" override val providers: List = emptyList() override fun register(floconDatabaseModel: FloconDatabaseModel) = Unit - override fun logQuery(dbName: String, sqlQuery: String, bindArgs: List) = Unit + override fun logQuery(dbId: String, sqlQuery: String, bindArgs: List) = Unit override suspend fun onMessageReceived(method: String, body: String) = Unit override suspend fun onConnectedToServer() = Unit } \ No newline at end of file diff --git a/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePluginImpl.kt b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePluginImpl.kt index d8a0a6e5d..0d3ebbd27 100644 --- a/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePluginImpl.kt +++ b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePluginImpl.kt @@ -99,13 +99,17 @@ internal class FloconDatabasePluginImpl( override fun register(floconDatabaseModel: FloconDatabaseModel) { registeredDatabases.update { currentList -> + val existing = currentList.firstOrNull { it.id == floconDatabaseModel.id } + require(existing == null || existing == floconDatabaseModel) { + "A database is already registered with the id \"${floconDatabaseModel.id}\", the ids must be unique" + } val dbMap = currentList.associateBy { it.id }.toMutableMap() dbMap[floconDatabaseModel.id] = floconDatabaseModel dbMap.values.toList() } } - override fun logQuery(dbName: String, sqlQuery: String, bindArgs: List) { + override fun logQuery(dbId: String, sqlQuery: String, bindArgs: List) { scope.launch { try { sender.send( @@ -113,7 +117,7 @@ internal class FloconDatabasePluginImpl( method = Protocol.FromDevice.Database.Method.LogQuery, body = encoder.encode( DatabaseQueryLogModel( - dbName = dbName, + dbId = dbId, sqlQuery = sqlQuery, bindArgs = bindArgs.map { it.toString() }, timestamp = currentTimeMillis() diff --git a/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/model/fromdevice/sql/DatabaseQueryLogModel.kt b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/model/fromdevice/sql/DatabaseQueryLogModel.kt index 9621960e0..ed2e99edc 100644 --- a/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/model/fromdevice/sql/DatabaseQueryLogModel.kt +++ b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/model/fromdevice/sql/DatabaseQueryLogModel.kt @@ -4,7 +4,7 @@ import kotlinx.serialization.Serializable @Serializable internal data class DatabaseQueryLogModel( - val dbName: String, + val dbId: String, val sqlQuery: String, val bindArgs: List?, val timestamp: Long, diff --git a/FloconAndroid/database/core/src/jvmTest/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePluginTest.kt b/FloconAndroid/database/core/src/jvmTest/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePluginTest.kt index 40d48fdbf..1e7a13386 100644 --- a/FloconAndroid/database/core/src/jvmTest/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePluginTest.kt +++ b/FloconAndroid/database/core/src/jvmTest/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePluginTest.kt @@ -3,11 +3,13 @@ package io.github.openflocon.flocon.database.core import io.github.openflocon.flocon.Protocol import io.github.openflocon.flocon.core.FloconEncoder import io.github.openflocon.flocon.core.FloconMessageSender +import io.github.openflocon.flocon.database.core.model.FloconFileDatabaseModel import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlin.test.Test import kotlin.test.assertContains import kotlin.test.assertEquals +import kotlin.test.assertFailsWith class FloconDatabasePluginTest { @@ -29,15 +31,32 @@ class FloconDatabasePluginTest { @Test fun `logQuery sends the query`() { - plugin.logQuery(dbName = "dogs", sqlQuery = "SELECT * FROM dog WHERE id = ?", bindArgs = listOf(1)) + plugin.logQuery(dbId = "dogs", sqlQuery = "SELECT * FROM dog WHERE id = ?", bindArgs = listOf(1)) val (pluginName, method, body) = sender.messages.single() assertEquals(Protocol.FromDevice.Database.Plugin, pluginName) assertEquals(Protocol.FromDevice.Database.Method.LogQuery, method) - assertContains(body, "\"dbName\":\"dogs\"") + assertContains(body, "\"dbId\":\"dogs\"") assertContains(body, "\"bindArgs\":[\"1\"]") } + @Test + fun `register refuses a second database with the same id`() { + plugin.register(FloconFileDatabaseModel(id = "dogs", displayName = "dogs", absolutePath = "/a")) + + assertFailsWith { + plugin.register(FloconFileDatabaseModel(id = "dogs", displayName = "other", absolutePath = "/b")) + } + } + + @Test + fun `register accepts the same database twice`() { + val database = FloconFileDatabaseModel(id = "dogs", displayName = "dogs", absolutePath = "/a") + + plugin.register(database) + plugin.register(database) + } + @Test fun `key is the plugin name sent by the desktop`() { assertEquals(Protocol.ToDevice.Database.Plugin, plugin.key) diff --git a/FloconAndroid/database/room-no-op/src/androidMain/kotlin/io/github/openflocon/flocon/database/room/extensions/RoomBuilderExt.kt b/FloconAndroid/database/room-no-op/src/androidMain/kotlin/io/github/openflocon/flocon/database/room/extensions/RoomBuilderExt.kt index 44d557dea..b833337af 100644 --- a/FloconAndroid/database/room-no-op/src/androidMain/kotlin/io/github/openflocon/flocon/database/room/extensions/RoomBuilderExt.kt +++ b/FloconAndroid/database/room-no-op/src/androidMain/kotlin/io/github/openflocon/flocon/database/room/extensions/RoomBuilderExt.kt @@ -6,7 +6,7 @@ import java.util.concurrent.Executor import java.util.concurrent.Executors inline fun RoomDatabase.Builder.floconLogs( - name: String? = T::class.simpleName, + id: String, executor: Executor = Executors.newSingleThreadExecutor(), queryCallback: QueryCallback? = null ): RoomDatabase.Builder { diff --git a/FloconAndroid/database/room-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/database/room/FloconRoomDatabaseModel.kt b/FloconAndroid/database/room-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/database/room/FloconRoomDatabaseModel.kt index 1e9a17b00..bc5259f6c 100644 --- a/FloconAndroid/database/room-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/database/room/FloconRoomDatabaseModel.kt +++ b/FloconAndroid/database/room-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/database/room/FloconRoomDatabaseModel.kt @@ -3,6 +3,6 @@ package io.github.openflocon.flocon.database.room import androidx.room.RoomDatabase import io.github.openflocon.flocon.database.core.FloconDatabasePlugin -fun FloconDatabasePlugin.register(displayName: String, database: RoomDatabase) { +fun FloconDatabasePlugin.register(id: String, displayName: String, database: RoomDatabase) { // no op } diff --git a/FloconAndroid/database/room/src/androidMain/kotlin/io/github/openflocon/flocon/database/room/extensions/RoomBuilderExt.kt b/FloconAndroid/database/room/src/androidMain/kotlin/io/github/openflocon/flocon/database/room/extensions/RoomBuilderExt.kt index 0fdaacd78..73c1770f3 100644 --- a/FloconAndroid/database/room/src/androidMain/kotlin/io/github/openflocon/flocon/database/room/extensions/RoomBuilderExt.kt +++ b/FloconAndroid/database/room/src/androidMain/kotlin/io/github/openflocon/flocon/database/room/extensions/RoomBuilderExt.kt @@ -8,14 +8,14 @@ import java.util.concurrent.Executor import java.util.concurrent.Executors inline fun RoomDatabase.Builder.floconLogs( - name: String? = T::class.simpleName, + id: String, executor: Executor = Executors.newSingleThreadExecutor(), queryCallback: QueryCallback? = null ): RoomDatabase.Builder { return setQueryCallback( queryCallback = { sqlQuery, bindArgs -> Flocon.databasePlugin.logQuery( - dbName = name ?: "database", + dbId = id, sqlQuery = sqlQuery, bindArgs = bindArgs ) diff --git a/FloconAndroid/database/room/src/commonMain/kotlin/io/github/openflocon/flocon/database/room/FloconRoomDatabaseModel.kt b/FloconAndroid/database/room/src/commonMain/kotlin/io/github/openflocon/flocon/database/room/FloconRoomDatabaseModel.kt index 589be6717..4e55f2c2d 100644 --- a/FloconAndroid/database/room/src/commonMain/kotlin/io/github/openflocon/flocon/database/room/FloconRoomDatabaseModel.kt +++ b/FloconAndroid/database/room/src/commonMain/kotlin/io/github/openflocon/flocon/database/room/FloconRoomDatabaseModel.kt @@ -11,11 +11,13 @@ import io.github.openflocon.flocon.database.core.model.fromdevice.DatabaseExecut /** * Registers a Room database, to browse it from the desktop app + * + * @param id unique and stable, also the one to give to the query logs of this database */ -fun FloconDatabasePlugin.register(displayName: String, database: RoomDatabase) { +fun FloconDatabasePlugin.register(id: String, displayName: String, database: RoomDatabase) { register( FloconRoomDatabaseModel( - id = displayName, + id = id, displayName = displayName, database = database ) diff --git a/FloconAndroid/database/room3-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/database/room3/FloconRoom3DatabaseModel.kt b/FloconAndroid/database/room3-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/database/room3/FloconRoom3DatabaseModel.kt index 95e76b6de..9c1cae9a8 100644 --- a/FloconAndroid/database/room3-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/database/room3/FloconRoom3DatabaseModel.kt +++ b/FloconAndroid/database/room3-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/database/room3/FloconRoom3DatabaseModel.kt @@ -3,6 +3,6 @@ package io.github.openflocon.flocon.database.room3 import androidx.room3.RoomDatabase import io.github.openflocon.flocon.database.core.FloconDatabasePlugin -fun FloconDatabasePlugin.register(displayName: String, database: RoomDatabase) { +fun FloconDatabasePlugin.register(id: String, displayName: String, database: RoomDatabase) { // no op } diff --git a/FloconAndroid/database/room3/src/roomMain/kotlin/io/github/openflocon/flocon/database/room3/FloconRoom3DatabaseModel.kt b/FloconAndroid/database/room3/src/roomMain/kotlin/io/github/openflocon/flocon/database/room3/FloconRoom3DatabaseModel.kt index 6b203a47a..12afc6213 100644 --- a/FloconAndroid/database/room3/src/roomMain/kotlin/io/github/openflocon/flocon/database/room3/FloconRoom3DatabaseModel.kt +++ b/FloconAndroid/database/room3/src/roomMain/kotlin/io/github/openflocon/flocon/database/room3/FloconRoom3DatabaseModel.kt @@ -12,11 +12,13 @@ import io.github.openflocon.flocon.database.core.model.fromdevice.DatabaseExecut /** * Registers a Room database, to browse it from the desktop app + * + * @param id unique and stable, also the one to give to the query logs of this database */ -fun FloconDatabasePlugin.register(displayName: String, database: RoomDatabase) { +fun FloconDatabasePlugin.register(id: String, displayName: String, database: RoomDatabase) { register( FloconRoom3DatabaseModel( - id = displayName, + id = id, displayName = displayName, database = database ) diff --git a/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/Databases.kt b/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/Databases.kt index 868ac12e5..53e582ebb 100644 --- a/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/Databases.kt +++ b/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/Databases.kt @@ -23,6 +23,7 @@ object Databases { .build() dogDatabase = instance Flocon.databasePlugin.register( + id = "dogs", displayName = "dogs", database = instance ) @@ -41,6 +42,7 @@ object Databases { ).build() foodDatabase = instance Flocon.databasePlugin.register( + id = "food", displayName = "food", database = instance ) @@ -59,6 +61,7 @@ object Databases { ).build() inMemoryDogDatabase = instance Flocon.databasePlugin.register( + id = "inmemory_dogs", displayName = "inmemory_dogs", database = instance ) diff --git a/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/room2/CatDatabase.kt b/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/room2/CatDatabase.kt index 212b73826..9668b78a2 100644 --- a/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/room2/CatDatabase.kt +++ b/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/room2/CatDatabase.kt @@ -49,11 +49,11 @@ abstract class CatDatabase : RoomDatabase() { CatDatabase::class.java, "cats_database", ) - .floconLogs(name = "cats") + .floconLogs(id = "cats (Room 2)") .build() .also { instance = it - Flocon.databasePlugin.register(displayName = "cats (Room 2)", database = it) + Flocon.databasePlugin.register(id = "cats (Room 2)", displayName = "cats (Room 2)", database = it) } } } diff --git a/FloconAndroid/sample/desktop/src/jvmMain/kotlin/com/flocon/sample/desktop/Main.kt b/FloconAndroid/sample/desktop/src/jvmMain/kotlin/com/flocon/sample/desktop/Main.kt index 4e9f251ce..403ba02ce 100644 --- a/FloconAndroid/sample/desktop/src/jvmMain/kotlin/com/flocon/sample/desktop/Main.kt +++ b/FloconAndroid/sample/desktop/src/jvmMain/kotlin/com/flocon/sample/desktop/Main.kt @@ -90,8 +90,8 @@ fun main() { val dogDatabase = dogDatabase() val foodDatabase = foodDatabase() - Flocon.databasePlugin.register(displayName = "dogs", database = dogDatabase) - Flocon.databasePlugin.register(displayName = "food", database = foodDatabase) + Flocon.databasePlugin.register(id = "dogs", displayName = "dogs", database = dogDatabase) + Flocon.databasePlugin.register(id = "food", displayName = "food", database = foodDatabase) initializeDatabases(dogDatabase = dogDatabase, foodDatabase = foodDatabase) diff --git a/FloconAndroid/sample/ios/src/iosMain/kotlin/com/flocon/sample/ios/Databases.kt b/FloconAndroid/sample/ios/src/iosMain/kotlin/com/flocon/sample/ios/Databases.kt index 731aa5c97..a481bb455 100644 --- a/FloconAndroid/sample/ios/src/iosMain/kotlin/com/flocon/sample/ios/Databases.kt +++ b/FloconAndroid/sample/ios/src/iosMain/kotlin/com/flocon/sample/ios/Databases.kt @@ -23,7 +23,7 @@ object Databases { .setDriver(NativeSQLiteDriver()) .build() dogDatabase = instance - Flocon.databasePlugin.register(displayName = "Dog Database", database = instance) + Flocon.databasePlugin.register(id = "Dog Database", displayName = "Dog Database", database = instance) instance } } @@ -37,7 +37,7 @@ object Databases { .setDriver(NativeSQLiteDriver()) .build() foodDatabase = instance - Flocon.databasePlugin.register(displayName = "Food Database", database = instance) + Flocon.databasePlugin.register(id = "Food Database", displayName = "Food Database", database = instance) instance } } diff --git a/FloconAndroid/sample/wasm/src/wasmJsMain/kotlin/com/flocon/sample/wasm/Databases.kt b/FloconAndroid/sample/wasm/src/wasmJsMain/kotlin/com/flocon/sample/wasm/Databases.kt index 115158c14..aad062e56 100644 --- a/FloconAndroid/sample/wasm/src/wasmJsMain/kotlin/com/flocon/sample/wasm/Databases.kt +++ b/FloconAndroid/sample/wasm/src/wasmJsMain/kotlin/com/flocon/sample/wasm/Databases.kt @@ -24,13 +24,13 @@ object Databases { Room.databaseBuilder(name = "dogs_database.db") .setDriver(driver) .build() - .also { Flocon.databasePlugin.register(displayName = "dogs", database = it) } + .also { Flocon.databasePlugin.register(id = "dogs", displayName = "dogs", database = it) } } val foodDatabase: FoodDatabase by lazy { Room.databaseBuilder(name = "food_database.db") .setDriver(driver) .build() - .also { Flocon.databasePlugin.register(displayName = "food", database = it) } + .also { Flocon.databasePlugin.register(id = "food", displayName = "food", database = it) } } } diff --git a/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/90.json b/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/90.json new file mode 100644 index 000000000..d95387a45 --- /dev/null +++ b/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/90.json @@ -0,0 +1,2465 @@ +{ + "formatVersion": 1, + "database": { + "version": 90, + "identityHash": "6ab31e731dc7c18c42c4077afbc2587f", + "entities": [ + { + "tableName": "FloconNetworkCallEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`callId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `appInstance` INTEGER NOT NULL, `type` TEXT NOT NULL, `isReplayed` INTEGER NOT NULL, `request_url` TEXT NOT NULL, `request_method` TEXT NOT NULL, `request_startTime` INTEGER NOT NULL, `request_startTimeFormatted` TEXT NOT NULL, `request_byteSizeFormatted` TEXT NOT NULL, `request_requestHeaders` TEXT NOT NULL, `request_requestBody` TEXT, `request_requestByteSize` INTEGER NOT NULL, `request_isMocked` INTEGER NOT NULL, `request_domainFormatted` TEXT NOT NULL, `request_methodFormatted` TEXT NOT NULL, `request_queryFormatted` TEXT NOT NULL, `request_graphql_query` TEXT, `request_graphql_operationType` TEXT, `request_websocket_event` TEXT, `response_durationMs` REAL, `response_durationFormatted` TEXT, `response_responseContentType` TEXT, `response_responseBody` TEXT, `response_responseHeaders` TEXT, `response_responseByteSize` INTEGER, `response_responseByteSizeFormatted` TEXT, `response_responseError` TEXT, `response_isImage` INTEGER, `response_statusFormatted` TEXT, `response_graphql_isSuccess` INTEGER, `response_graphql_responseHttpCode` INTEGER, `response_http_responseHttpCode` INTEGER, `response_grpc_responseStatus` TEXT, PRIMARY KEY(`callId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "callId", + "columnName": "callId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "appInstance", + "columnName": "appInstance", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isReplayed", + "columnName": "isReplayed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.url", + "columnName": "request_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.method", + "columnName": "request_method", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.startTime", + "columnName": "request_startTime", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.startTimeFormatted", + "columnName": "request_startTimeFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.byteSizeFormatted", + "columnName": "request_byteSizeFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.requestHeaders", + "columnName": "request_requestHeaders", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.requestBody", + "columnName": "request_requestBody", + "affinity": "TEXT" + }, + { + "fieldPath": "request.requestByteSize", + "columnName": "request_requestByteSize", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.isMocked", + "columnName": "request_isMocked", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.domainFormatted", + "columnName": "request_domainFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.methodFormatted", + "columnName": "request_methodFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.queryFormatted", + "columnName": "request_queryFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.graphql.query", + "columnName": "request_graphql_query", + "affinity": "TEXT" + }, + { + "fieldPath": "request.graphql.operationType", + "columnName": "request_graphql_operationType", + "affinity": "TEXT" + }, + { + "fieldPath": "request.websocket.event", + "columnName": "request_websocket_event", + "affinity": "TEXT" + }, + { + "fieldPath": "response.durationMs", + "columnName": "response_durationMs", + "affinity": "REAL" + }, + { + "fieldPath": "response.durationFormatted", + "columnName": "response_durationFormatted", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseContentType", + "columnName": "response_responseContentType", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseBody", + "columnName": "response_responseBody", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseHeaders", + "columnName": "response_responseHeaders", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseByteSize", + "columnName": "response_responseByteSize", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.responseByteSizeFormatted", + "columnName": "response_responseByteSizeFormatted", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseError", + "columnName": "response_responseError", + "affinity": "TEXT" + }, + { + "fieldPath": "response.isImage", + "columnName": "response_isImage", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.statusFormatted", + "columnName": "response_statusFormatted", + "affinity": "TEXT" + }, + { + "fieldPath": "response.graphql.isSuccess", + "columnName": "response_graphql_isSuccess", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.graphql.responseHttpCode", + "columnName": "response_graphql_responseHttpCode", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.http.responseHttpCode", + "columnName": "response_http_responseHttpCode", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.grpc.responseStatus", + "columnName": "response_grpc_responseStatus", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "callId" + ] + }, + "indices": [ + { + "name": "index_FloconNetworkCallEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FloconNetworkCallEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "FileEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `name` TEXT NOT NULL, `isDirectory` INTEGER NOT NULL, `path` TEXT NOT NULL, `parentPath` TEXT NOT NULL, `size` INTEGER NOT NULL, `lastModifiedTimestamp` INTEGER NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isDirectory", + "columnName": "isDirectory", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentPath", + "columnName": "parentPath", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "size", + "columnName": "size", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastModifiedTimestamp", + "columnName": "lastModifiedTimestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_FileEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FileEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "FileOptionsEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `withFoldersSize` INTEGER NOT NULL, PRIMARY KEY(`deviceId`, `packageName`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "withFoldersSize", + "columnName": "withFoldersSize", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName" + ] + }, + "indices": [ + { + "name": "index_FileOptionsEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FileOptionsEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DashboardEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`dashboardId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, PRIMARY KEY(`dashboardId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "dashboardId", + "columnName": "dashboardId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "dashboardId" + ] + }, + "indices": [ + { + "name": "index_DashboardEntity_dashboardId", + "unique": false, + "columnNames": [ + "dashboardId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardEntity_dashboardId` ON `${TABLE_NAME}` (`dashboardId`)" + }, + { + "name": "index_DashboardEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DashboardContainerEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `dashboardId` TEXT NOT NULL, `containerOrder` INTEGER NOT NULL, `containerConfig` TEXT NOT NULL, `name` TEXT NOT NULL, FOREIGN KEY(`dashboardId`) REFERENCES `DashboardEntity`(`dashboardId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dashboardId", + "columnName": "dashboardId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "containerOrder", + "columnName": "containerOrder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "containerConfig", + "columnName": "containerConfig", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DashboardContainerEntity_dashboardId", + "unique": false, + "columnNames": [ + "dashboardId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardContainerEntity_dashboardId` ON `${TABLE_NAME}` (`dashboardId`)" + }, + { + "name": "index_DashboardContainerEntity_dashboardId_containerOrder", + "unique": true, + "columnNames": [ + "dashboardId", + "containerOrder" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DashboardContainerEntity_dashboardId_containerOrder` ON `${TABLE_NAME}` (`dashboardId`, `containerOrder`)" + } + ], + "foreignKeys": [ + { + "table": "DashboardEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "dashboardId" + ], + "referencedColumns": [ + "dashboardId" + ] + } + ] + }, + { + "tableName": "DashboardElementEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `containerId` INTEGER NOT NULL, `elementOrder` INTEGER NOT NULL, `elementAsJson` TEXT NOT NULL, FOREIGN KEY(`containerId`) REFERENCES `DashboardContainerEntity`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "containerId", + "columnName": "containerId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "elementOrder", + "columnName": "elementOrder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "elementAsJson", + "columnName": "elementAsJson", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DashboardElementEntity_containerId", + "unique": false, + "columnNames": [ + "containerId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardElementEntity_containerId` ON `${TABLE_NAME}` (`containerId`)" + }, + { + "name": "index_DashboardElementEntity_containerId_elementOrder", + "unique": true, + "columnNames": [ + "containerId", + "elementOrder" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DashboardElementEntity_containerId_elementOrder` ON `${TABLE_NAME}` (`containerId`, `elementOrder`)" + } + ], + "foreignKeys": [ + { + "table": "DashboardContainerEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "containerId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "TableEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `name` TEXT NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_TableEntity_deviceId_packageName_name", + "unique": true, + "columnNames": [ + "deviceId", + "packageName", + "name" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_TableEntity_deviceId_packageName_name` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `name`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "TableItemEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` TEXT NOT NULL, `tableId` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL, `columnsNames` TEXT NOT NULL, `values` TEXT NOT NULL, PRIMARY KEY(`itemId`), FOREIGN KEY(`tableId`) REFERENCES `TableEntity`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "itemId", + "columnName": "itemId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "tableId", + "columnName": "tableId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "columnsNames", + "columnName": "columnsNames", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "values", + "columnName": "values", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "itemId" + ] + }, + "indices": [ + { + "name": "index_TableItemEntity_tableId", + "unique": false, + "columnNames": [ + "tableId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_TableItemEntity_tableId` ON `${TABLE_NAME}` (`tableId`)" + } + ], + "foreignKeys": [ + { + "table": "TableEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "tableId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "DeviceImageEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `url` TEXT NOT NULL, `time` INTEGER NOT NULL, `headersJsonEncoded` TEXT NOT NULL, `byteSize` INTEGER, `contentType` TEXT, PRIMARY KEY(`deviceId`, `packageName`, `url`, `time`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "headersJsonEncoded", + "columnName": "headersJsonEncoded", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "byteSize", + "columnName": "byteSize", + "affinity": "INTEGER" + }, + { + "fieldPath": "contentType", + "columnName": "contentType", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName", + "url", + "time" + ] + }, + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "SuccessQueryEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `databaseId` TEXT NOT NULL, `queryString` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "databaseId", + "columnName": "databaseId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "queryString", + "columnName": "queryString", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_SuccessQueryEntity_deviceId_packageName_databaseId_queryString", + "unique": true, + "columnNames": [ + "deviceId", + "packageName", + "databaseId", + "queryString" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_SuccessQueryEntity_deviceId_packageName_databaseId_queryString` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `databaseId`, `queryString`)" + }, + { + "name": "index_SuccessQueryEntity_databaseId", + "unique": false, + "columnNames": [ + "databaseId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_SuccessQueryEntity_databaseId` ON `${TABLE_NAME}` (`databaseId`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "FavoriteQueryEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `databaseId` TEXT NOT NULL, `queryString` TEXT NOT NULL, `title` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "databaseId", + "columnName": "databaseId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "queryString", + "columnName": "queryString", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_FavoriteQueryEntity_databaseId", + "unique": false, + "columnNames": [ + "databaseId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FavoriteQueryEntity_databaseId` ON `${TABLE_NAME}` (`databaseId`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DeeplinkEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `link` TEXT NOT NULL, `label` TEXT, `description` TEXT, `parametersAsJson` TEXT NOT NULL, `isHistory` INTEGER NOT NULL, `executedAt` INTEGER NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "link", + "columnName": "link", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT" + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT" + }, + { + "fieldPath": "parametersAsJson", + "columnName": "parametersAsJson", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isHistory", + "columnName": "isHistory", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "executedAt", + "columnName": "executedAt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DeeplinkEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DeeplinkEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + }, + { + "name": "index_DeeplinkEntity_deviceId_link", + "unique": true, + "columnNames": [ + "deviceId", + "link" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DeeplinkEntity_deviceId_link` ON `${TABLE_NAME}` (`deviceId`, `link`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DeeplinkVariableEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `name` TEXT NOT NULL, `description` TEXT, `isHistory` INTEGER NOT NULL, `mode` TEXT NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT" + }, + { + "fieldPath": "isHistory", + "columnName": "isHistory", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "mode", + "columnName": "mode", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DeeplinkVariableEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DeeplinkVariableEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + }, + { + "name": "index_DeeplinkVariableEntity_deviceId_name", + "unique": true, + "columnNames": [ + "deviceId", + "name" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DeeplinkVariableEntity_deviceId_name` ON `${TABLE_NAME}` (`deviceId`, `name`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "AnalyticsItemEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` TEXT NOT NULL, `analyticsTableId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `appInstance` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL, `createdAtFormatted` TEXT NOT NULL, `eventName` TEXT NOT NULL, `propertiesColumnsNames` TEXT NOT NULL, `propertiesValues` TEXT NOT NULL, PRIMARY KEY(`itemId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "itemId", + "columnName": "itemId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "analyticsTableId", + "columnName": "analyticsTableId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "appInstance", + "columnName": "appInstance", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAtFormatted", + "columnName": "createdAtFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "eventName", + "columnName": "eventName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "propertiesColumnsNames", + "columnName": "propertiesColumnsNames", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "propertiesValues", + "columnName": "propertiesValues", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "itemId" + ] + }, + "indices": [ + { + "name": "index_AnalyticsItemEntity_deviceId_packageName_analyticsTableId", + "unique": false, + "columnNames": [ + "deviceId", + "packageName", + "analyticsTableId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_AnalyticsItemEntity_deviceId_packageName_analyticsTableId` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `analyticsTableId`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "FeatureFlagEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `provider` TEXT NOT NULL, `name` TEXT NOT NULL, `state` TEXT NOT NULL, `variant` TEXT NOT NULL, `payload` TEXT, `appInstance` INTEGER NOT NULL, PRIMARY KEY(`deviceId`, `packageName`, `provider`, `name`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "provider", + "columnName": "provider", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "state", + "columnName": "state", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "variant", + "columnName": "variant", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "payload", + "columnName": "payload", + "affinity": "TEXT" + }, + { + "fieldPath": "appInstance", + "columnName": "appInstance", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName", + "provider", + "name" + ] + }, + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "AccessibilityEventEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `id` TEXT NOT NULL, `appInstance` INTEGER NOT NULL, `type` TEXT NOT NULL, `text` TEXT, `contentDescription` TEXT, `className` TEXT, `screen` TEXT, `bounds` TEXT, `extras` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, PRIMARY KEY(`deviceId`, `packageName`, `id`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "appInstance", + "columnName": "appInstance", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "text", + "columnName": "text", + "affinity": "TEXT" + }, + { + "fieldPath": "contentDescription", + "columnName": "contentDescription", + "affinity": "TEXT" + }, + { + "fieldPath": "className", + "columnName": "className", + "affinity": "TEXT" + }, + { + "fieldPath": "screen", + "columnName": "screen", + "affinity": "TEXT" + }, + { + "fieldPath": "bounds", + "columnName": "bounds", + "affinity": "TEXT" + }, + { + "fieldPath": "extras", + "columnName": "extras", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName", + "id" + ] + }, + "indices": [ + { + "name": "index_AccessibilityEventEntity_deviceId_packageName_createdAt", + "unique": false, + "columnNames": [ + "deviceId", + "packageName", + "createdAt" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_AccessibilityEventEntity_deviceId_packageName_createdAt` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `createdAt`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "NetworkFilterEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `columnName` TEXT NOT NULL, `isEnabled` INTEGER NOT NULL, `itemsAsJson` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `columnName`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "columnName", + "columnName": "columnName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "itemsAsJson", + "columnName": "itemsAsJson", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "columnName" + ] + }, + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "NetworkSettingsEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `valueAsJson` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `packageName`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "valueAsJson", + "columnName": "valueAsJson", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName" + ] + }, + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "MockNetworkEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mockId` TEXT NOT NULL, `deviceId` TEXT, `packageName` TEXT, `isEnabled` INTEGER NOT NULL, `response` TEXT NOT NULL, `displayName` TEXT NOT NULL, `expectation_urlPattern` TEXT NOT NULL, `expectation_method` TEXT NOT NULL, PRIMARY KEY(`mockId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "mockId", + "columnName": "mockId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT" + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT" + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "response", + "columnName": "response", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "displayName", + "columnName": "displayName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expectation.urlPattern", + "columnName": "expectation_urlPattern", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expectation.method", + "columnName": "expectation_method", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "mockId" + ] + }, + "indices": [ + { + "name": "index_MockNetworkEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_MockNetworkEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DeviceWithSerialEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `serial` TEXT NOT NULL, PRIMARY KEY(`deviceId`), FOREIGN KEY(`deviceId`) REFERENCES `DeviceEntity`(`deviceId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "serial", + "columnName": "serial", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId" + ] + }, + "foreignKeys": [ + { + "table": "DeviceEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId" + ], + "referencedColumns": [ + "deviceId" + ] + } + ] + }, + { + "tableName": "BadQualityConfigEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `packageName` TEXT NOT NULL, `isEnabled` INTEGER NOT NULL, `errorProbability` REAL NOT NULL, `errors` TEXT NOT NULL, `triggerProbability` REAL NOT NULL, `minLatencyMs` INTEGER NOT NULL, `maxLatencyMs` INTEGER NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "errorProbability", + "columnName": "errorProbability", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "errors", + "columnName": "errors", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "latency.triggerProbability", + "columnName": "triggerProbability", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "latency.minLatencyMs", + "columnName": "minLatencyMs", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latency.maxLatencyMs", + "columnName": "maxLatencyMs", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_BadQualityConfigEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_BadQualityConfigEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DeviceEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `deviceName` TEXT NOT NULL, `platform` TEXT NOT NULL, `connectionType` TEXT NOT NULL, PRIMARY KEY(`deviceId`))", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceName", + "columnName": "deviceName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "platform", + "columnName": "platform", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "connectionType", + "columnName": "connectionType", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId" + ] + } + }, + { + "tableName": "DeviceAppEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `name` TEXT NOT NULL, `packageName` TEXT NOT NULL, `iconEncoded` TEXT, `lastAppInstance` INTEGER NOT NULL, `floconVersionOnDevice` TEXT NOT NULL, `installedPlugins` TEXT, PRIMARY KEY(`deviceId`, `packageName`), FOREIGN KEY(`deviceId`) REFERENCES `DeviceEntity`(`deviceId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "iconEncoded", + "columnName": "iconEncoded", + "affinity": "TEXT" + }, + { + "fieldPath": "lastAppInstance", + "columnName": "lastAppInstance", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "floconVersionOnDevice", + "columnName": "floconVersionOnDevice", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "installedPlugins", + "columnName": "installedPlugins", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName" + ] + }, + "foreignKeys": [ + { + "table": "DeviceEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId" + ], + "referencedColumns": [ + "deviceId" + ] + } + ] + }, + { + "tableName": "DatabaseTableEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `databaseId` TEXT NOT NULL, `tableName` TEXT NOT NULL, `columnsAsString` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `packageName`, `databaseId`, `tableName`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "databaseId", + "columnName": "databaseId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "tableName", + "columnName": "tableName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "columnsAsString", + "columnName": "columnsAsString", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName", + "databaseId", + "tableName" + ] + }, + "indices": [ + { + "name": "index_DatabaseTableEntity_databaseId", + "unique": false, + "columnNames": [ + "databaseId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DatabaseTableEntity_databaseId` ON `${TABLE_NAME}` (`databaseId`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "CrashReportEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`crashId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `exceptionType` TEXT NOT NULL, `exceptionMessage` TEXT NOT NULL, `stackTrace` TEXT NOT NULL, PRIMARY KEY(`crashId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "crashId", + "columnName": "crashId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exceptionType", + "columnName": "exceptionType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "exceptionMessage", + "columnName": "exceptionMessage", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "stackTrace", + "columnName": "stackTrace", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "crashId" + ] + }, + "indices": [ + { + "name": "index_CrashReportEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_CrashReportEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DatabaseQueryLogEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `dbId` TEXT NOT NULL, `sqlQuery` TEXT NOT NULL, `bindArgs` TEXT, `timestamp` INTEGER NOT NULL, `isTransaction` INTEGER NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `appInstance` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dbId", + "columnName": "dbId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sqlQuery", + "columnName": "sqlQuery", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "bindArgs", + "columnName": "bindArgs", + "affinity": "TEXT" + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isTransaction", + "columnName": "isTransaction", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "appInstance", + "columnName": "appInstance", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DatabaseQueryLogEntity_deviceId_packageName_dbId_timestamp", + "unique": false, + "columnNames": [ + "deviceId", + "packageName", + "dbId", + "timestamp" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DatabaseQueryLogEntity_deviceId_packageName_dbId_timestamp` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `dbId`, `timestamp`)" + } + ] + }, + { + "tableName": "AdbSavedCommandEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `name` TEXT NOT NULL, `command` TEXT NOT NULL, `description` TEXT, `createdAt` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "command", + "columnName": "command", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT" + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_AdbSavedCommandEntity_deviceId", + "unique": false, + "columnNames": [ + "deviceId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_AdbSavedCommandEntity_deviceId` ON `${TABLE_NAME}` (`deviceId`)" + } + ] + }, + { + "tableName": "AdbCommandHistoryEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `command` TEXT NOT NULL, `output` TEXT NOT NULL, `isSuccess` INTEGER NOT NULL, `executedAt` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "command", + "columnName": "command", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "output", + "columnName": "output", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isSuccess", + "columnName": "isSuccess", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "executedAt", + "columnName": "executedAt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_AdbCommandHistoryEntity_deviceId", + "unique": false, + "columnNames": [ + "deviceId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_AdbCommandHistoryEntity_deviceId` ON `${TABLE_NAME}` (`deviceId`)" + } + ] + }, + { + "tableName": "AdbFlowEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `name` TEXT NOT NULL, `description` TEXT, `createdAt` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT" + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_AdbFlowEntity_deviceId", + "unique": false, + "columnNames": [ + "deviceId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_AdbFlowEntity_deviceId` ON `${TABLE_NAME}` (`deviceId`)" + } + ] + }, + { + "tableName": "AdbFlowStepEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `flowId` INTEGER NOT NULL, `orderIndex` INTEGER NOT NULL, `command` TEXT NOT NULL, `delayAfterMs` INTEGER NOT NULL, `label` TEXT, FOREIGN KEY(`flowId`) REFERENCES `AdbFlowEntity`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "flowId", + "columnName": "flowId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "orderIndex", + "columnName": "orderIndex", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "command", + "columnName": "command", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "delayAfterMs", + "columnName": "delayAfterMs", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_AdbFlowStepEntity_flowId", + "unique": false, + "columnNames": [ + "flowId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_AdbFlowStepEntity_flowId` ON `${TABLE_NAME}` (`flowId`)" + } + ], + "foreignKeys": [ + { + "table": "AdbFlowEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "flowId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "WebViewEventEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`eventId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `webViewId` TEXT NOT NULL, `kind` TEXT NOT NULL, `url` TEXT, `label` TEXT NOT NULL, `message` TEXT, `sourceId` TEXT, `lineNumber` INTEGER, `statusCode` INTEGER, `result` TEXT, `timestamp` INTEGER NOT NULL, PRIMARY KEY(`eventId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "eventId", + "columnName": "eventId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "webViewId", + "columnName": "webViewId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "kind", + "columnName": "kind", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT" + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "message", + "columnName": "message", + "affinity": "TEXT" + }, + { + "fieldPath": "sourceId", + "columnName": "sourceId", + "affinity": "TEXT" + }, + { + "fieldPath": "lineNumber", + "columnName": "lineNumber", + "affinity": "INTEGER" + }, + { + "fieldPath": "statusCode", + "columnName": "statusCode", + "affinity": "INTEGER" + }, + { + "fieldPath": "result", + "columnName": "result", + "affinity": "TEXT" + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "eventId" + ] + }, + "indices": [ + { + "name": "index_WebViewEventEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_WebViewEventEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + } + ], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '6ab31e731dc7c18c42c4077afbc2587f')" + ] + } +} \ No newline at end of file diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/common/db/AppDatabase.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/common/db/AppDatabase.kt index 4f4dfe794..b38d8e1fa 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/common/db/AppDatabase.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/common/db/AppDatabase.kt @@ -65,7 +65,7 @@ import io.github.openflocon.flocondesktop.common.db.converters.MapStringsConvert import kotlinx.coroutines.Dispatchers @Database( - version = 89, + version = 90, entities = [ FloconNetworkCallEntity::class, FileEntity::class, diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DatabaseQueryLogPanelViewModel.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DatabaseQueryLogPanelViewModel.kt index f1dccf9d5..5c966fe39 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DatabaseQueryLogPanelViewModel.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DatabaseQueryLogPanelViewModel.kt @@ -29,7 +29,7 @@ class DatabaseQueryLogPanelViewModel( .filterNotNull() .map { DatabaseQueryLogPanelUiModel( - dbName = it.dbName, + dbName = it.dbId, sql = it.toFullSql(), rawSql = it.sqlQuery, arguments = it.bindArgs.orEmpty(), diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DatabaseQueryLogsViewModel.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DatabaseQueryLogsViewModel.kt index 6d8d6d2da..332e6f9ad 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DatabaseQueryLogsViewModel.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DatabaseQueryLogsViewModel.kt @@ -43,7 +43,7 @@ import java.util.Date import java.util.Locale class DatabaseQueryLogsViewModel( - private val dbName: String, + private val dbId: String, private val observeDatabaseQueryLogsUseCase: ObserveDatabaseQueryLogsUseCase, private val countDatabaseQueryLogsUseCase: CountDatabaseQueryLogsUseCase, private val getDatabaseQueryLogsUseCase: GetDatabaseQueryLogsUseCase, @@ -83,7 +83,7 @@ class DatabaseQueryLogsViewModel( ).flatMapLatest { (filter, page) -> val (showTransactions, filterChips, currentDeviceAndPackage) = filter observeDatabaseQueryLogsUseCase( - dbName = dbName, + dbId = dbId, showTransactions = showTransactions, filters = filterChips, limit = PAGE_SIZE, @@ -101,7 +101,7 @@ class DatabaseQueryLogsViewModel( val totalCount: Flow = filtersFlow.flatMapLatest { (showTransactions, filterChips, _) -> countDatabaseQueryLogsUseCase( - dbName = dbName, + dbId = dbId, showTransactions = showTransactions, filters = filterChips, ) @@ -211,7 +211,7 @@ class DatabaseQueryLogsViewModel( fun exportToCsv() { viewModelScope.launch { val logs = getDatabaseQueryLogsUseCase( - dbName = dbName, + dbId = dbId, showTransactions = _showTransactions.value, filters = _filterChips.value.map { it.toDomain() } ) @@ -229,7 +229,7 @@ class DatabaseQueryLogsViewModel( fun exportToMarkdown() { viewModelScope.launch { val logs = getDatabaseQueryLogsUseCase( - dbName = dbName, + dbId = dbId, showTransactions = _showTransactions.value, filters = _filterChips.value.map { it.toDomain() } ) diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DatabaseViewModel.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DatabaseViewModel.kt index af8e2e9cb..f86b8375a 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DatabaseViewModel.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DatabaseViewModel.kt @@ -167,7 +167,6 @@ class DatabaseViewModel( val generatedName = table.name createTab( databaseId = databaseId, - databaseName = "", // not used here tableName = null, generatedName = generatedName, favoriteId = null, @@ -178,7 +177,6 @@ class DatabaseViewModel( private suspend fun createTabForQueryLogs(databaseId: DeviceDataBaseId, dbName: String) { createTab( databaseId = databaseId, - databaseName = dbName, // only used here tableName = null, generatedName = "Logs ($dbName)", favoriteId = null, @@ -302,7 +300,6 @@ class DatabaseViewModel( generatedName: String, query: String?, isQueryLogs: Boolean = false, - databaseName: String? = null, // only used for isQueryLogs ) { val deviceIdAndPackageName = getCurrentDeviceIdAndPackageNameUseCase() ?: return @@ -319,7 +316,6 @@ class DatabaseViewModel( val addedTab = DatabaseTabState( databaseId = databaseId, - databaseName = databaseName ?: "", tableName = tableName, generatedName = generatedName, index = index, diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/model/DatabaseTabState.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/model/DatabaseTabState.kt index 984e47de7..14bcb3a15 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/model/DatabaseTabState.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/model/DatabaseTabState.kt @@ -5,7 +5,6 @@ import androidx.compose.runtime.Immutable @Immutable data class DatabaseTabState( val databaseId: String, - val databaseName: String, val tableName: String?, val generatedName: String, val index: Int, diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/DatabaseTab.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/DatabaseTab.kt index c43ed394a..2061bfed4 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/DatabaseTab.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/DatabaseTab.kt @@ -15,7 +15,7 @@ fun DatabaseTab( ) { if (tab.isQueryLogs) { DatabaseQueryLogs( - dbName = tab.databaseName, + dbId = tab.databaseId, modifier = modifier.fillMaxSize() ) } else { diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/logs/DatabaseQueryLogs.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/logs/DatabaseQueryLogs.kt index 13a04afb5..f051193bf 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/logs/DatabaseQueryLogs.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/logs/DatabaseQueryLogs.kt @@ -80,12 +80,12 @@ import org.koin.core.parameter.parametersOf @Composable fun DatabaseQueryLogs( - dbName: String, + dbId: String, modifier: Modifier = Modifier ) { val viewModel: DatabaseQueryLogsViewModel = koinViewModel( - key = dbName, - parameters = { parametersOf(dbName) } + key = dbId, + parameters = { parametersOf(dbId) } ) val logs: List by viewModel.logs.collectAsStateWithLifecycle() diff --git a/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/database/datasource/LocalDatabaseDataSource.kt b/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/database/datasource/LocalDatabaseDataSource.kt index 699babd18..02675d617 100644 --- a/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/database/datasource/LocalDatabaseDataSource.kt +++ b/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/database/datasource/LocalDatabaseDataSource.kt @@ -69,7 +69,7 @@ interface LocalDatabaseDataSource { fun observeQueryLogs( deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, - dbName: String, + dbId: String, showTransactions: Boolean, filters: List, limit: Int, @@ -78,21 +78,21 @@ interface LocalDatabaseDataSource { fun countQueryLogs( deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, - dbName: String, + dbId: String, showTransactions: Boolean, filters: List ): Flow suspend fun getQueryLogs( deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, - dbName: String, + dbId: String, showTransactions: Boolean, filters: List ): List suspend fun getLatestQueryLogs( deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, - dbName: String, + dbId: String, showTransactions: Boolean, filters: List, appInstance: Long?, diff --git a/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/database/repository/DatabaseRepositoryImpl.kt b/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/database/repository/DatabaseRepositoryImpl.kt index 286c768a3..9e2e52728 100644 --- a/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/database/repository/DatabaseRepositoryImpl.kt +++ b/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/database/repository/DatabaseRepositoryImpl.kt @@ -207,14 +207,14 @@ class DatabaseRepositoryImpl( } override fun observeQueryLogs( - dbName: String, + dbId: String, showTransactions: Boolean, filters: List, deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, limit: Int, offset: Int, ): Flow> = localDatabaseDataSource.observeQueryLogs( - dbName = dbName, + dbId = dbId, showTransactions = showTransactions, filters = filters, deviceIdAndPackageName = deviceIdAndPackageName, @@ -223,31 +223,31 @@ class DatabaseRepositoryImpl( ) override fun countQueryLogs( - dbName: String, + dbId: String, showTransactions: Boolean, filters: List, deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, ): Flow = localDatabaseDataSource.countQueryLogs( - dbName = dbName, + dbId = dbId, showTransactions = showTransactions, filters = filters, deviceIdAndPackageName = deviceIdAndPackageName, ) override suspend fun getQueryLogs( - dbName: String, + dbId: String, showTransactions: Boolean, filters: List, deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, ): List = localDatabaseDataSource.getQueryLogs( - dbName = dbName, + dbId = dbId, showTransactions = showTransactions, filters = filters, deviceIdAndPackageName = deviceIdAndPackageName, ) override suspend fun getLatestQueryLogs( - dbName: String, + dbId: String, showTransactions: Boolean, filters: List, deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, @@ -256,7 +256,7 @@ class DatabaseRepositoryImpl( ): List = withContext(dispatcherProvider.data) { localDatabaseDataSource.getLatestQueryLogs( deviceIdAndPackageName = deviceIdAndPackageName, - dbName = dbName, + dbId = dbId, showTransactions = showTransactions, filters = filters, appInstance = appInstance, diff --git a/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/database/datasource/LocalDatabaseDataSourceRoom.kt b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/database/datasource/LocalDatabaseDataSourceRoom.kt index 63c7d723c..c9e523149 100644 --- a/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/database/datasource/LocalDatabaseDataSourceRoom.kt +++ b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/database/datasource/LocalDatabaseDataSourceRoom.kt @@ -190,7 +190,7 @@ internal class LocalDatabaseDataSourceRoom( ) { databaseQueryLogDao.insert( DatabaseQueryLogEntity( - dbName = log.dbName, + dbId = log.dbId, sqlQuery = log.sqlQuery, bindArgs = log.bindArgs?.let { json.encodeToString(it) }, timestamp = log.timestamp, @@ -204,14 +204,14 @@ internal class LocalDatabaseDataSourceRoom( override fun observeQueryLogs( deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, - dbName: String, + dbId: String, showTransactions: Boolean, filters: List, limit: Int, offset: Int, ): Flow> { val query = buildQuery( - dbName = dbName, + dbId = dbId, showTransactions = showTransactions, filters = filters, deviceIdAndPackageName = deviceIdAndPackageName, @@ -222,7 +222,7 @@ internal class LocalDatabaseDataSourceRoom( return databaseQueryLogDao.observeLogs(query).map { list -> list.map { entity -> DatabaseQueryLogDomainModel( - dbName = entity.dbName, + dbId = entity.dbId, sqlQuery = entity.sqlQuery, bindArgs = entity.bindArgs?.let { json.decodeFromString(it) @@ -237,12 +237,12 @@ internal class LocalDatabaseDataSourceRoom( override fun countQueryLogs( deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, - dbName: String, + dbId: String, showTransactions: Boolean, filters: List ): Flow { val query = buildCountQuery( - dbName = dbName, + dbId = dbId, showTransactions = showTransactions, filters = filters, deviceIdAndPackageName = deviceIdAndPackageName @@ -252,12 +252,12 @@ internal class LocalDatabaseDataSourceRoom( override suspend fun getQueryLogs( deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, - dbName: String, + dbId: String, showTransactions: Boolean, filters: List ): List { val query = buildQuery( - dbName = dbName, + dbId = dbId, showTransactions = showTransactions, filters = filters, deviceIdAndPackageName = deviceIdAndPackageName, @@ -266,7 +266,7 @@ internal class LocalDatabaseDataSourceRoom( ) return databaseQueryLogDao.getLogs(query).map { entity -> DatabaseQueryLogDomainModel( - dbName = entity.dbName, + dbId = entity.dbId, sqlQuery = entity.sqlQuery, bindArgs = entity.bindArgs?.let { json.decodeFromString(it) @@ -280,14 +280,14 @@ internal class LocalDatabaseDataSourceRoom( override suspend fun getLatestQueryLogs( deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, - dbName: String, + dbId: String, showTransactions: Boolean, filters: List, appInstance: Long?, limit: Int, ): List { val (whereClause, queryParams) = buildWhereClauseAndParams( - dbName = dbName, + dbId = dbId, showTransactions = showTransactions, filters = filters, deviceIdAndPackageName = deviceIdAndPackageName, @@ -298,7 +298,7 @@ internal class LocalDatabaseDataSourceRoom( return databaseQueryLogDao.getLogs(query).map { entity -> DatabaseQueryLogDomainModel( - dbName = entity.dbName, + dbId = entity.dbId, sqlQuery = entity.sqlQuery, bindArgs = entity.bindArgs?.let { json.decodeFromString(it) @@ -313,7 +313,7 @@ internal class LocalDatabaseDataSourceRoom( override fun observeQueryLog(id: Int): Flow = databaseQueryLogDao.observeLog(id).map { entity -> entity?.let { DatabaseQueryLogDomainModel( - dbName = it.dbName, + dbId = it.dbId, sqlQuery = it.sqlQuery, bindArgs = it.bindArgs?.let { args -> json.decodeFromString(args) @@ -333,7 +333,7 @@ internal class LocalDatabaseDataSourceRoom( } private fun buildQuery( - dbName: String, + dbId: String, showTransactions: Boolean, filters: List, deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, @@ -341,7 +341,7 @@ internal class LocalDatabaseDataSourceRoom( offset: Int?, ): RoomRawQuery { val (whereClause, queryParams) = buildWhereClauseAndParams( - dbName = dbName, + dbId = dbId, showTransactions = showTransactions, filters = filters, deviceIdAndPackageName = deviceIdAndPackageName, @@ -360,13 +360,13 @@ internal class LocalDatabaseDataSourceRoom( } private fun buildCountQuery( - dbName: String, + dbId: String, showTransactions: Boolean, filters: List, deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, ): RoomRawQuery { val (whereClause, queryParams) = buildWhereClauseAndParams( - dbName = dbName, + dbId = dbId, showTransactions = showTransactions, filters = filters, deviceIdAndPackageName = deviceIdAndPackageName, @@ -377,17 +377,17 @@ internal class LocalDatabaseDataSourceRoom( } private fun buildWhereClauseAndParams( - dbName: String, + dbId: String, showTransactions: Boolean, filters: List, deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, appInstance: Long? = null, ): Pair> { val queryParams = ArrayList() - var whereClause = "WHERE deviceId = ? AND packageName = ? AND dbName = ?" + var whereClause = "WHERE deviceId = ? AND packageName = ? AND dbId = ?" queryParams.add(deviceIdAndPackageName.deviceId) queryParams.add(deviceIdAndPackageName.packageName) - queryParams.add(dbName) + queryParams.add(dbId) if (appInstance != null) { whereClause += " AND appInstance = ?" diff --git a/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/database/models/DatabaseQueryLogEntity.kt b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/database/models/DatabaseQueryLogEntity.kt index 1fbe2fd13..b37fc0bff 100644 --- a/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/database/models/DatabaseQueryLogEntity.kt +++ b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/database/models/DatabaseQueryLogEntity.kt @@ -7,12 +7,12 @@ import androidx.room.PrimaryKey @Entity( indices = [ // apps can log millions of queries: the logs are always read for one database of an app, ordered by time - Index(value = ["deviceId", "packageName", "dbName", "timestamp"]), + Index(value = ["deviceId", "packageName", "dbId", "timestamp"]), ], ) data class DatabaseQueryLogEntity( @PrimaryKey(autoGenerate = true) val id: Int = 0, - val dbName: String, + val dbId: String, val sqlQuery: String, val bindArgs: String?, val timestamp: Long, diff --git a/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/timeline/datasource/TimelineQueryBuilder.kt b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/timeline/datasource/TimelineQueryBuilder.kt index 1a2e32a4c..1e4008e34 100644 --- a/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/timeline/datasource/TimelineQueryBuilder.kt +++ b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/timeline/datasource/TimelineQueryBuilder.kt @@ -187,13 +187,13 @@ private fun buildUnion( add( branch( select = "'$KIND_DATABASE' AS kind, CAST(id AS TEXT) AS id, timestamp AS ts, " + - "sqlQuery AS title, dbName AS subtitle, appInstance AS appInstance, $NO_NETWORK_COLUMNS", + "sqlQuery AS title, dbId AS subtitle, appInstance AS appInstance, $NO_NETWORK_COLUMNS", from = "DatabaseQueryLogEntity", appInstanceColumn = "appInstance", - textColumns = listOf("sqlQuery", "dbName"), + textColumns = listOf("sqlQuery", "dbId"), timeColumn = "timestamp", facetExpressions = mapOf( - TimelineFacet.DatabaseName to "dbName", + TimelineFacet.DatabaseName to "dbId", TimelineFacet.DatabaseStatement to DATABASE_STATEMENT, ), orderLimit = "ORDER BY timestamp DESC LIMIT $MAX_DATABASE_LOGS", diff --git a/FloconDesktop/data/remote/src/commonMain/kotlin/com/flocon/data/remote/database/datasource/QueryDatabaseRemoteDataSourceImpl.kt b/FloconDesktop/data/remote/src/commonMain/kotlin/com/flocon/data/remote/database/datasource/QueryDatabaseRemoteDataSourceImpl.kt index 83aea1a62..cb7a757d6 100644 --- a/FloconDesktop/data/remote/src/commonMain/kotlin/com/flocon/data/remote/database/datasource/QueryDatabaseRemoteDataSourceImpl.kt +++ b/FloconDesktop/data/remote/src/commonMain/kotlin/com/flocon/data/remote/database/datasource/QueryDatabaseRemoteDataSourceImpl.kt @@ -99,7 +99,7 @@ class QueryDatabaseRemoteDataSourceImpl( upperQuery == "BEGIN IMMEDIATE TRANSACTION" DatabaseQueryLogDomainModel( - dbName = it.dbName, + dbId = it.dbId, sqlQuery = it.sqlQuery, bindArgs = it.bindArgs.orEmpty(), timestamp = it.timestamp, diff --git a/FloconDesktop/data/remote/src/commonMain/kotlin/com/flocon/data/remote/database/models/DatabaseQueryLogModel.kt b/FloconDesktop/data/remote/src/commonMain/kotlin/com/flocon/data/remote/database/models/DatabaseQueryLogModel.kt index bb9a9790f..0632668d5 100644 --- a/FloconDesktop/data/remote/src/commonMain/kotlin/com/flocon/data/remote/database/models/DatabaseQueryLogModel.kt +++ b/FloconDesktop/data/remote/src/commonMain/kotlin/com/flocon/data/remote/database/models/DatabaseQueryLogModel.kt @@ -4,7 +4,7 @@ import kotlinx.serialization.Serializable @Serializable data class DatabaseQueryLogModel( - val dbName: String, + val dbId: String, val sqlQuery: String, val bindArgs: List?, val timestamp: Long, diff --git a/FloconDesktop/data/remote/src/commonTest/kotlin/com/flocon/data/remote/database/datasource/QueryDatabaseRemoteDataSourceImplTest.kt b/FloconDesktop/data/remote/src/commonTest/kotlin/com/flocon/data/remote/database/datasource/QueryDatabaseRemoteDataSourceImplTest.kt new file mode 100644 index 000000000..60ea0bdcd --- /dev/null +++ b/FloconDesktop/data/remote/src/commonTest/kotlin/com/flocon/data/remote/database/datasource/QueryDatabaseRemoteDataSourceImplTest.kt @@ -0,0 +1,66 @@ +package com.flocon.data.remote.database.datasource + +import com.flocon.data.remote.models.FloconDeviceIdAndPackageNameDataModel +import com.flocon.data.remote.models.FloconIncomingMessageDataModel +import com.flocon.data.remote.models.FloconOutgoingMessageDataModel +import com.flocon.data.remote.server.Server +import io.github.openflocon.domain.messages.models.FloconIncomingMessageDomainModel +import io.github.openflocon.domain.messages.models.FloconReceivedFileDomainModel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.serialization.json.Json +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertTrue + +class QueryDatabaseRemoteDataSourceImplTest { + + private val server = object : Server { + override fun startWebsocket(port: Int) = Unit + override fun starHttp(port: Int) = Unit + override fun stop() = Unit + override val receivedMessages: Flow = emptyFlow() + override suspend fun sendMessageToClient( + deviceIdAndPackageName: FloconDeviceIdAndPackageNameDataModel, + message: FloconOutgoingMessageDataModel, + ) = Unit + override val activeDevices: Flow> = emptyFlow() + override val receivedFiles: Flow = emptyFlow() + } + + private val dataSource = QueryDatabaseRemoteDataSourceImpl(server, Json { ignoreUnknownKeys = true }) + + private fun message(body: String) = FloconIncomingMessageDomainModel( + deviceName = "Pixel 9", + deviceId = "pixel", + plugin = "database", + body = body, + method = "log_query", + appName = "Sample", + appPackageName = "io.flocon.sample", + appInstance = 7L, + platform = "android", + floconVersionOnDevice = "1.0.0", + ) + + @Test + fun `a log is linked to the database by its id`() { + val log = dataSource.getQueryLogs( + message("""{"dbId":"cats (Room 2)","sqlQuery":"SELECT 1","bindArgs":["1"],"timestamp":1000}"""), + ) + + assertNotNull(log) + assertEquals("cats (Room 2)", log.dbId) + assertEquals(7L, log.appInstance) + } + + @Test + fun `the transactions are still detected`() { + val log = dataSource.getQueryLogs( + message("""{"dbId":"cats","sqlQuery":"BEGIN TRANSACTION","bindArgs":null,"timestamp":1000}"""), + ) + + assertTrue(log!!.isTransaction) + } +} diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/models/DatabaseQueryLogDomainModel.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/models/DatabaseQueryLogDomainModel.kt index 190c544d1..a45ff25ad 100644 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/models/DatabaseQueryLogDomainModel.kt +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/models/DatabaseQueryLogDomainModel.kt @@ -3,7 +3,7 @@ package io.github.openflocon.domain.database.models import io.github.openflocon.domain.database.utils.injectSqlArgs data class DatabaseQueryLogDomainModel( - val dbName: String, + val dbId: String, val sqlQuery: String, val bindArgs: List?, val timestamp: Long, diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/repository/DatabaseRepository.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/repository/DatabaseRepository.kt index 619750e8e..f228f679d 100644 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/repository/DatabaseRepository.kt +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/repository/DatabaseRepository.kt @@ -79,7 +79,7 @@ interface DatabaseRepository { ): DatabaseFavoriteQueryDomainModel? fun observeQueryLogs( - dbName: String, + dbId: String, showTransactions: Boolean, filters: List, deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, @@ -88,14 +88,14 @@ interface DatabaseRepository { ): Flow> fun countQueryLogs( - dbName: String, + dbId: String, showTransactions: Boolean, filters: List, deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, ): Flow suspend fun getQueryLogs( - dbName: String, + dbId: String, showTransactions: Boolean, filters: List, deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, @@ -107,7 +107,7 @@ interface DatabaseRepository { * @param appInstance only the logs of this app session, all the sessions when null */ suspend fun getLatestQueryLogs( - dbName: String, + dbId: String, showTransactions: Boolean, filters: List, deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/usecase/CountDatabaseQueryLogsUseCase.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/usecase/CountDatabaseQueryLogsUseCase.kt index b839a8e1b..15ded122c 100644 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/usecase/CountDatabaseQueryLogsUseCase.kt +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/usecase/CountDatabaseQueryLogsUseCase.kt @@ -15,7 +15,7 @@ class CountDatabaseQueryLogsUseCase( private val observeCurrentDeviceIdAndPackageNameUseCase: ObserveCurrentDeviceIdAndPackageNameUseCase, ) { operator fun invoke( - dbName: String, + dbId: String, showTransactions: Boolean, filters: List ): Flow { @@ -24,7 +24,7 @@ class CountDatabaseQueryLogsUseCase( return@flatMapLatest flowOf(0) } else { databaseRepository.countQueryLogs( - dbName = dbName, + dbId = dbId, showTransactions = showTransactions, filters = filters, deviceIdAndPackageName = current, diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/usecase/GetDatabaseQueryLogsUseCase.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/usecase/GetDatabaseQueryLogsUseCase.kt index 6fc592cf3..10c9884b7 100644 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/usecase/GetDatabaseQueryLogsUseCase.kt +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/usecase/GetDatabaseQueryLogsUseCase.kt @@ -11,13 +11,13 @@ class GetDatabaseQueryLogsUseCase( private val observeCurrentDeviceIdAndPackageNameUseCase: ObserveCurrentDeviceIdAndPackageNameUseCase, ) { suspend operator fun invoke( - dbName: String, + dbId: String, showTransactions: Boolean, filters: List ): List { val current = observeCurrentDeviceIdAndPackageNameUseCase().first() ?: return emptyList() return databaseRepository.getQueryLogs( - dbName = dbName, + dbId = dbId, showTransactions = showTransactions, filters = filters, deviceIdAndPackageName = current, diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/usecase/ObserveDatabaseQueryLogsUseCase.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/usecase/ObserveDatabaseQueryLogsUseCase.kt index 76347ceb7..affaaa132 100644 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/usecase/ObserveDatabaseQueryLogsUseCase.kt +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/usecase/ObserveDatabaseQueryLogsUseCase.kt @@ -17,7 +17,7 @@ class ObserveDatabaseQueryLogsUseCase( private val observeCurrentDeviceIdAndPackageNameUseCase: ObserveCurrentDeviceIdAndPackageNameUseCase, ) { operator fun invoke( - dbName: String, + dbId: String, showTransactions: Boolean, filters: List, limit: Int, @@ -28,7 +28,7 @@ class ObserveDatabaseQueryLogsUseCase( return@flatMapLatest flowOf(emptyList()) } else { databaseRepository.observeQueryLogs( - dbName = dbName, + dbId = dbId, showTransactions = showTransactions, filters = filters, deviceIdAndPackageName = current, diff --git a/FloconDesktop/mcp/src/desktopMain/kotlin/io/github/openflocon/mcp/database/DatabaseMcpToolProvider.kt b/FloconDesktop/mcp/src/desktopMain/kotlin/io/github/openflocon/mcp/database/DatabaseMcpToolProvider.kt index 7ad5cf255..b7da0302f 100644 --- a/FloconDesktop/mcp/src/desktopMain/kotlin/io/github/openflocon/mcp/database/DatabaseMcpToolProvider.kt +++ b/FloconDesktop/mcp/src/desktopMain/kotlin/io/github/openflocon/mcp/database/DatabaseMcpToolProvider.kt @@ -124,7 +124,6 @@ internal class DatabaseMcpToolProvider( val includeOldSessions = input.includeOldSessions val limit = input.limit.coerceIn(1, MAX_LOGS_LIMIT) - // the logs are stored by database name val databaseName = databaseRepository.getDatabaseById(databaseId)?.name ?: databaseId val showTransactions = input.includeTransactions @@ -133,7 +132,7 @@ internal class DatabaseMcpToolProvider( // apps can log millions of queries: filtered and limited by the database, without counting them val logs = databaseRepository.getLatestQueryLogs( - dbName = databaseName, + dbId = databaseId, showTransactions = showTransactions, filters = filters, deviceIdAndPackageName = target, diff --git a/FloconDesktop/mcp/src/desktopTest/kotlin/io/github/openflocon/mcp/database/DatabaseMcpToolProviderTest.kt b/FloconDesktop/mcp/src/desktopTest/kotlin/io/github/openflocon/mcp/database/DatabaseMcpToolProviderTest.kt index 31b7b2528..75ef842d6 100644 --- a/FloconDesktop/mcp/src/desktopTest/kotlin/io/github/openflocon/mcp/database/DatabaseMcpToolProviderTest.kt +++ b/FloconDesktop/mcp/src/desktopTest/kotlin/io/github/openflocon/mcp/database/DatabaseMcpToolProviderTest.kt @@ -206,7 +206,7 @@ class DatabaseMcpToolProviderTest { appInstance: Long, isTransaction: Boolean = false, ) = DatabaseQueryLogDomainModel( - dbName = "app.db", + dbId = "/data/databases/app.db", sqlQuery = sql, bindArgs = args, timestamp = timestamp, @@ -248,29 +248,29 @@ private class FakeDatabaseRepository( } private fun filteredLogs( - dbName: String, + dbId: String, showTransactions: Boolean, filters: List, appInstance: Long?, ) = logs - .filter { it.dbName == dbName } + .filter { it.dbId == dbId } .filter { showTransactions || !it.isTransaction } .filter { appInstance == null || it.appInstance == appInstance } .filter { log -> filters.all { log.sqlQuery.contains(it.text) || log.bindArgs.orEmpty().any { arg -> arg.contains(it.text) } } } override suspend fun getLatestQueryLogs( - dbName: String, + dbId: String, showTransactions: Boolean, filters: List, deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, appInstance: Long?, limit: Int, - ): List = filteredLogs(dbName, showTransactions, filters, appInstance) + ): List = filteredLogs(dbId, showTransactions, filters, appInstance) .sortedByDescending { it.timestamp } .take(limit) override suspend fun getQueryLogs( - dbName: String, + dbId: String, showTransactions: Boolean, filters: List, deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, @@ -306,7 +306,7 @@ private class FakeDatabaseRepository( override suspend fun getFavorite(deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, databaseId: String, id: Long): DatabaseFavoriteQueryDomainModel? = error("not used") override fun observeQueryLogs( - dbName: String, + dbId: String, showTransactions: Boolean, filters: List, deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, @@ -315,7 +315,7 @@ private class FakeDatabaseRepository( ): Flow> = error("not used") override fun countQueryLogs( - dbName: String, + dbId: String, showTransactions: Boolean, filters: List, deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, diff --git a/docs2/database.md b/docs2/database.md index 456c497d3..cf85c69a4 100644 --- a/docs2/database.md +++ b/docs2/database.md @@ -74,25 +74,30 @@ To register a Room database (in-memory databases included) with a display name: val dogDatabase = Room.inMemoryDatabaseBuilder(context, DogDatabase::class.java).build() Flocon.databasePlugin.register( + id = "inmemory_dogs", displayName = "In-Memory Dogs", database = dogDatabase, ) ``` -`register(displayName, database)` comes from `io.github.openflocon.flocon.database.room` (Room 2) or `io.github.openflocon.flocon.database.room3` (Room 3). +The `id` is required, stable and **unique**: registering two different databases with the same id throws an +`IllegalArgumentException`. It is the id the desktop app uses for everything on this database (browsing, queries, +query logs), so the query logs of a database must be sent with the same id (see below). + +`register(id, displayName, database)` comes from `io.github.openflocon.flocon.database.room` (Room 2) or `io.github.openflocon.flocon.database.room3` (Room 3). ### Room 3 on the web (Wasm) Room 3 runs on Wasm with `WebWorkerSQLiteDriver` (`androidx.sqlite:sqlite-web`), which talks to a SQLite Web Worker. There is no file system, so the databases are not discovered: register each Room database -with `register(displayName, database)` (the plugin still has to be installed with `install(FloconDatabase) { room() }`): +with `register(id, displayName, database)` (the plugin still has to be installed with `install(FloconDatabase) { room() }`): ```kotlin val dogDatabase = Room.databaseBuilder(name = "dogs.db") .setDriver(WebWorkerSQLiteDriver(createWorker())) .build() -Flocon.databasePlugin.register(displayName = "dogs", database = dogDatabase) +Flocon.databasePlugin.register(id = "dogs", displayName = "dogs", database = dogDatabase) ``` See `FloconAndroid/sample/wasm` for a complete setup (worker, npm package and KSP for wasmJs). @@ -127,17 +132,21 @@ Flocon can display all SQL queries executed by your app in real-time — great f DogDatabase::class.java, "dogs_database" ) - .floconLogs() // io.github.openflocon.flocon.database.room.extensions.floconLogs + .floconLogs(id = "dogs") // io.github.openflocon.flocon.database.room.extensions.floconLogs .build() + .also { Flocon.databasePlugin.register(id = "dogs", displayName = "dogs", database = it) } ``` + The `id` given to `floconLogs` must be the one the database is registered with, otherwise the desktop app + cannot show the queries with the database. + Room 3 has no query callback: use the manual logging. === "Manual Query Logging" ```kotlin Flocon.databasePlugin.logQuery( - dbName = "my_custom_db", + dbId = "my_custom_db", // the id the database was registered with sqlQuery = "SELECT * FROM users WHERE id = ?", bindArgs = listOf(42) ) @@ -149,7 +158,7 @@ Flocon can display all SQL queries executed by your app in real-time — great f ```kotlin Room.databaseBuilder(context, DogDatabase::class.java, "dogs_database") .setQueryCallback({ sqlQuery, bindArgs -> - Flocon.databasePlugin.logQuery("dogs_database", sqlQuery, bindArgs) + Flocon.databasePlugin.logQuery("dogs", sqlQuery, bindArgs) myOwnLogger.log(sqlQuery) }, Executors.newSingleThreadExecutor()) .build() diff --git a/docs2/migration.md b/docs2/migration.md index 04df4d655..49e018526 100644 --- a/docs2/migration.md +++ b/docs2/migration.md @@ -96,8 +96,8 @@ Every plugin is now accessed with `Flocon.xxxPlugin`: | `floconTable("name").log("key" toParam "value")` | `Flocon.tablePlugin.table("name") { column("key", "value") }` | | `floconDashboard(id) { ... }` | `Flocon.dashboardPlugin.dashboard(id, scope) { ... }` | | `floconRegisterPreference(preference)` | `Flocon.preferencesPlugin.register(preference)` | -| `floconRegisterDatabase(...)` | `Flocon.databasePlugin.register("name", roomDatabase)` | -| `floconLogDatabaseQuery(...)` | `Flocon.databasePlugin.logQuery(...)` or `RoomDatabase.Builder.floconLogs()` | +| `floconRegisterDatabase(...)` | `Flocon.databasePlugin.register("id", "name", roomDatabase)` (the id is required and unique) | +| `floconLogDatabaseQuery(...)` | `Flocon.databasePlugin.logQuery(dbId, ...)` or `RoomDatabase.Builder.floconLogs(id)`, with the id the database is registered with | | `floconLogWebSocketEvent(event)` | `Flocon.networkPlugin.logWebSocket(event)` | The accessors are: `analyticsPlugin`, `crashReporterPlugin`, `dashboardPlugin`, `databasePlugin`, diff --git a/protocol/database.md b/protocol/database.md index d659bd40d..d1806fd58 100644 --- a/protocol/database.md +++ b/protocol/database.md @@ -18,8 +18,8 @@ Desktop → Mobile: empty body. Mobile → Desktop: list of available databases. | Key | Type | Description | |--------|----------|-----------------------------------| -| `id` | `String` | Database id (used in `query`) | -| `name` | `String` | Display name | +| `id` | `String` | Unique database id (used in `query` and `log_query`) | +| `name` | `String` | Display name, never used to identify a database | ```json [ @@ -110,14 +110,14 @@ Sent for each query executed by the app (when query logging is enabled). | Key | Type | Description | |-------------|-----------------|----------------------------------| -| `dbName` | `String` | Database name | +| `dbId` | `String` | Id of the database (the `id` sent in `GetDatabases`) | | `sqlQuery` | `String` | Executed SQL | | `bindArgs` | `List?` | Bound arguments (`toString()`) | | `timestamp` | `Long` | Execution timestamp (ms) | ```json { - "dbName": "app_database", + "dbId": "app_database", "sqlQuery": "SELECT * FROM user WHERE id = ?", "bindArgs": ["42"], "timestamp": 1729260000000 From 1c0448ecd2d87d3f71ca1c0ee28ed4197f51b79a Mon Sep 17 00:00:00 2001 From: Florent Champigny Date: Fri, 30 Oct 2026 23:14:20 +0100 Subject: [PATCH 2/6] fix: [DATABASE] Give the Room 2 sample database an id different from its name --- .../flocon/myapplication/multi/room2/CatDatabase.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/room2/CatDatabase.kt b/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/room2/CatDatabase.kt index 9668b78a2..085fea614 100644 --- a/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/room2/CatDatabase.kt +++ b/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/room2/CatDatabase.kt @@ -49,11 +49,11 @@ abstract class CatDatabase : RoomDatabase() { CatDatabase::class.java, "cats_database", ) - .floconLogs(id = "cats (Room 2)") + .floconLogs(id = "cats_room2") .build() .also { instance = it - Flocon.databasePlugin.register(id = "cats (Room 2)", displayName = "cats (Room 2)", database = it) + Flocon.databasePlugin.register(id = "cats_room2", displayName = "cats (Room 2)", database = it) } } } From 07e892962550b2b5668fe2c0c3db55ef073493f6 Mon Sep 17 00:00:00 2001 From: Florent Champigny Date: Fri, 30 Oct 2026 23:15:15 +0100 Subject: [PATCH 3/6] fix: [DATABASE] Give every sample database an id different from its name --- .../openflocon/flocon/myapplication/multi/Databases.kt | 6 +++--- .../src/jvmMain/kotlin/com/flocon/sample/desktop/Main.kt | 4 ++-- .../src/iosMain/kotlin/com/flocon/sample/ios/Databases.kt | 4 ++-- .../wasmJsMain/kotlin/com/flocon/sample/wasm/Databases.kt | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/Databases.kt b/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/Databases.kt index 53e582ebb..06b5572e8 100644 --- a/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/Databases.kt +++ b/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/Databases.kt @@ -23,7 +23,7 @@ object Databases { .build() dogDatabase = instance Flocon.databasePlugin.register( - id = "dogs", + id = "dogs_room3", displayName = "dogs", database = instance ) @@ -42,7 +42,7 @@ object Databases { ).build() foodDatabase = instance Flocon.databasePlugin.register( - id = "food", + id = "food_room3", displayName = "food", database = instance ) @@ -61,7 +61,7 @@ object Databases { ).build() inMemoryDogDatabase = instance Flocon.databasePlugin.register( - id = "inmemory_dogs", + id = "dogs_room3_memory", displayName = "inmemory_dogs", database = instance ) diff --git a/FloconAndroid/sample/desktop/src/jvmMain/kotlin/com/flocon/sample/desktop/Main.kt b/FloconAndroid/sample/desktop/src/jvmMain/kotlin/com/flocon/sample/desktop/Main.kt index 403ba02ce..64ac13145 100644 --- a/FloconAndroid/sample/desktop/src/jvmMain/kotlin/com/flocon/sample/desktop/Main.kt +++ b/FloconAndroid/sample/desktop/src/jvmMain/kotlin/com/flocon/sample/desktop/Main.kt @@ -90,8 +90,8 @@ fun main() { val dogDatabase = dogDatabase() val foodDatabase = foodDatabase() - Flocon.databasePlugin.register(id = "dogs", displayName = "dogs", database = dogDatabase) - Flocon.databasePlugin.register(id = "food", displayName = "food", database = foodDatabase) + Flocon.databasePlugin.register(id = "dogs_jvm", displayName = "dogs", database = dogDatabase) + Flocon.databasePlugin.register(id = "food_jvm", displayName = "food", database = foodDatabase) initializeDatabases(dogDatabase = dogDatabase, foodDatabase = foodDatabase) diff --git a/FloconAndroid/sample/ios/src/iosMain/kotlin/com/flocon/sample/ios/Databases.kt b/FloconAndroid/sample/ios/src/iosMain/kotlin/com/flocon/sample/ios/Databases.kt index a481bb455..0284a87ac 100644 --- a/FloconAndroid/sample/ios/src/iosMain/kotlin/com/flocon/sample/ios/Databases.kt +++ b/FloconAndroid/sample/ios/src/iosMain/kotlin/com/flocon/sample/ios/Databases.kt @@ -23,7 +23,7 @@ object Databases { .setDriver(NativeSQLiteDriver()) .build() dogDatabase = instance - Flocon.databasePlugin.register(id = "Dog Database", displayName = "Dog Database", database = instance) + Flocon.databasePlugin.register(id = "dogs_ios", displayName = "Dog Database", database = instance) instance } } @@ -37,7 +37,7 @@ object Databases { .setDriver(NativeSQLiteDriver()) .build() foodDatabase = instance - Flocon.databasePlugin.register(id = "Food Database", displayName = "Food Database", database = instance) + Flocon.databasePlugin.register(id = "food_ios", displayName = "Food Database", database = instance) instance } } diff --git a/FloconAndroid/sample/wasm/src/wasmJsMain/kotlin/com/flocon/sample/wasm/Databases.kt b/FloconAndroid/sample/wasm/src/wasmJsMain/kotlin/com/flocon/sample/wasm/Databases.kt index aad062e56..5675ddc15 100644 --- a/FloconAndroid/sample/wasm/src/wasmJsMain/kotlin/com/flocon/sample/wasm/Databases.kt +++ b/FloconAndroid/sample/wasm/src/wasmJsMain/kotlin/com/flocon/sample/wasm/Databases.kt @@ -24,13 +24,13 @@ object Databases { Room.databaseBuilder(name = "dogs_database.db") .setDriver(driver) .build() - .also { Flocon.databasePlugin.register(id = "dogs", displayName = "dogs", database = it) } + .also { Flocon.databasePlugin.register(id = "dogs_wasm", displayName = "dogs", database = it) } } val foodDatabase: FoodDatabase by lazy { Room.databaseBuilder(name = "food_database.db") .setDriver(driver) .build() - .also { Flocon.databasePlugin.register(id = "food", displayName = "food", database = it) } + .also { Flocon.databasePlugin.register(id = "food_wasm", displayName = "food", database = it) } } } From f30319b5319c21e7059fc5e50b4728d4ec820a64 Mon Sep 17 00:00:00 2001 From: Florent Champigny Date: Fri, 30 Oct 2026 23:26:50 +0100 Subject: [PATCH 4/6] fix: [DATABASE] Show the database name, not its id, and send the databases on register --- .../database/core/FloconDatabasePluginImpl.kt | 8 +- .../database/core/FloconDatabasePluginTest.kt | 10 + .../90.json | 12 +- .../91.json | 2471 +++++++++++++++++ .../flocondesktop/common/db/AppDatabase.kt | 2 +- .../DatabaseQueryLogPanelViewModel.kt | 2 +- .../repository/DatabaseRepositoryImpl.kt | 4 +- .../datasource/LocalDatabaseDataSourceRoom.kt | 5 + .../database/models/DatabaseQueryLogEntity.kt | 2 + .../datasource/TimelineQueryBuilder.kt | 6 +- .../QueryDatabaseRemoteDataSourceImpl.kt | 1 + .../models/DatabaseQueryLogDomainModel.kt | 2 + .../database/DatabaseMcpToolProviderTest.kt | 1 + 13 files changed, 2516 insertions(+), 10 deletions(-) create mode 100644 FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/91.json diff --git a/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePluginImpl.kt b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePluginImpl.kt index 0d3ebbd27..c07692ab2 100644 --- a/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePluginImpl.kt +++ b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePluginImpl.kt @@ -84,7 +84,11 @@ internal class FloconDatabasePluginImpl( dbMap.values.toList() } - val all = updatedDatabases.map { DeviceDataBaseDataModel(id = it.id, name = it.displayName) } + sendDatabases(updatedDatabases) + } + + private fun sendDatabases(databases: List) { + val all = databases.map { DeviceDataBaseDataModel(id = it.id, name = it.displayName) } try { sender.send( @@ -107,6 +111,8 @@ internal class FloconDatabasePluginImpl( dbMap[floconDatabaseModel.id] = floconDatabaseModel dbMap.values.toList() } + // the desktop shows the database, and the name of its logs, as soon as it is registered + sendDatabases(registeredDatabases.value) } override fun logQuery(dbId: String, sqlQuery: String, bindArgs: List) { diff --git a/FloconAndroid/database/core/src/jvmTest/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePluginTest.kt b/FloconAndroid/database/core/src/jvmTest/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePluginTest.kt index 1e7a13386..8fea1881d 100644 --- a/FloconAndroid/database/core/src/jvmTest/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePluginTest.kt +++ b/FloconAndroid/database/core/src/jvmTest/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePluginTest.kt @@ -49,6 +49,16 @@ class FloconDatabasePluginTest { } } + @Test + fun `register sends the databases to the desktop with their id and name`() { + plugin.register(FloconFileDatabaseModel(id = "dogs_room3", displayName = "dogs", absolutePath = "/a")) + + val (_, method, body) = sender.messages.single() + assertEquals(Protocol.FromDevice.Database.Method.GetDatabases, method) + assertContains(body, "\"id\":\"dogs_room3\"") + assertContains(body, "\"name\":\"dogs\"") + } + @Test fun `register accepts the same database twice`() { val database = FloconFileDatabaseModel(id = "dogs", displayName = "dogs", absolutePath = "/a") diff --git a/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/90.json b/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/90.json index d95387a45..4412508be 100644 --- a/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/90.json +++ b/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/90.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 90, - "identityHash": "6ab31e731dc7c18c42c4077afbc2587f", + "identityHash": "f51284ee7103296d25133cdd0e513368", "entities": [ { "tableName": "FloconNetworkCallEntity", @@ -2028,7 +2028,7 @@ }, { "tableName": "DatabaseQueryLogEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `dbId` TEXT NOT NULL, `sqlQuery` TEXT NOT NULL, `bindArgs` TEXT, `timestamp` INTEGER NOT NULL, `isTransaction` INTEGER NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `appInstance` INTEGER NOT NULL)", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `dbId` TEXT NOT NULL, `dbName` TEXT NOT NULL, `sqlQuery` TEXT NOT NULL, `bindArgs` TEXT, `timestamp` INTEGER NOT NULL, `isTransaction` INTEGER NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `appInstance` INTEGER NOT NULL)", "fields": [ { "fieldPath": "id", @@ -2042,6 +2042,12 @@ "affinity": "TEXT", "notNull": true }, + { + "fieldPath": "dbName", + "columnName": "dbName", + "affinity": "TEXT", + "notNull": true + }, { "fieldPath": "sqlQuery", "columnName": "sqlQuery", @@ -2459,7 +2465,7 @@ ], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '6ab31e731dc7c18c42c4077afbc2587f')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f51284ee7103296d25133cdd0e513368')" ] } } \ No newline at end of file diff --git a/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/91.json b/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/91.json new file mode 100644 index 000000000..9397160f4 --- /dev/null +++ b/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/91.json @@ -0,0 +1,2471 @@ +{ + "formatVersion": 1, + "database": { + "version": 91, + "identityHash": "f51284ee7103296d25133cdd0e513368", + "entities": [ + { + "tableName": "FloconNetworkCallEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`callId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `appInstance` INTEGER NOT NULL, `type` TEXT NOT NULL, `isReplayed` INTEGER NOT NULL, `request_url` TEXT NOT NULL, `request_method` TEXT NOT NULL, `request_startTime` INTEGER NOT NULL, `request_startTimeFormatted` TEXT NOT NULL, `request_byteSizeFormatted` TEXT NOT NULL, `request_requestHeaders` TEXT NOT NULL, `request_requestBody` TEXT, `request_requestByteSize` INTEGER NOT NULL, `request_isMocked` INTEGER NOT NULL, `request_domainFormatted` TEXT NOT NULL, `request_methodFormatted` TEXT NOT NULL, `request_queryFormatted` TEXT NOT NULL, `request_graphql_query` TEXT, `request_graphql_operationType` TEXT, `request_websocket_event` TEXT, `response_durationMs` REAL, `response_durationFormatted` TEXT, `response_responseContentType` TEXT, `response_responseBody` TEXT, `response_responseHeaders` TEXT, `response_responseByteSize` INTEGER, `response_responseByteSizeFormatted` TEXT, `response_responseError` TEXT, `response_isImage` INTEGER, `response_statusFormatted` TEXT, `response_graphql_isSuccess` INTEGER, `response_graphql_responseHttpCode` INTEGER, `response_http_responseHttpCode` INTEGER, `response_grpc_responseStatus` TEXT, PRIMARY KEY(`callId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "callId", + "columnName": "callId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "appInstance", + "columnName": "appInstance", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isReplayed", + "columnName": "isReplayed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.url", + "columnName": "request_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.method", + "columnName": "request_method", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.startTime", + "columnName": "request_startTime", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.startTimeFormatted", + "columnName": "request_startTimeFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.byteSizeFormatted", + "columnName": "request_byteSizeFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.requestHeaders", + "columnName": "request_requestHeaders", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.requestBody", + "columnName": "request_requestBody", + "affinity": "TEXT" + }, + { + "fieldPath": "request.requestByteSize", + "columnName": "request_requestByteSize", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.isMocked", + "columnName": "request_isMocked", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.domainFormatted", + "columnName": "request_domainFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.methodFormatted", + "columnName": "request_methodFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.queryFormatted", + "columnName": "request_queryFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.graphql.query", + "columnName": "request_graphql_query", + "affinity": "TEXT" + }, + { + "fieldPath": "request.graphql.operationType", + "columnName": "request_graphql_operationType", + "affinity": "TEXT" + }, + { + "fieldPath": "request.websocket.event", + "columnName": "request_websocket_event", + "affinity": "TEXT" + }, + { + "fieldPath": "response.durationMs", + "columnName": "response_durationMs", + "affinity": "REAL" + }, + { + "fieldPath": "response.durationFormatted", + "columnName": "response_durationFormatted", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseContentType", + "columnName": "response_responseContentType", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseBody", + "columnName": "response_responseBody", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseHeaders", + "columnName": "response_responseHeaders", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseByteSize", + "columnName": "response_responseByteSize", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.responseByteSizeFormatted", + "columnName": "response_responseByteSizeFormatted", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseError", + "columnName": "response_responseError", + "affinity": "TEXT" + }, + { + "fieldPath": "response.isImage", + "columnName": "response_isImage", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.statusFormatted", + "columnName": "response_statusFormatted", + "affinity": "TEXT" + }, + { + "fieldPath": "response.graphql.isSuccess", + "columnName": "response_graphql_isSuccess", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.graphql.responseHttpCode", + "columnName": "response_graphql_responseHttpCode", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.http.responseHttpCode", + "columnName": "response_http_responseHttpCode", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.grpc.responseStatus", + "columnName": "response_grpc_responseStatus", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "callId" + ] + }, + "indices": [ + { + "name": "index_FloconNetworkCallEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FloconNetworkCallEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "FileEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `name` TEXT NOT NULL, `isDirectory` INTEGER NOT NULL, `path` TEXT NOT NULL, `parentPath` TEXT NOT NULL, `size` INTEGER NOT NULL, `lastModifiedTimestamp` INTEGER NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isDirectory", + "columnName": "isDirectory", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentPath", + "columnName": "parentPath", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "size", + "columnName": "size", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastModifiedTimestamp", + "columnName": "lastModifiedTimestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_FileEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FileEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "FileOptionsEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `withFoldersSize` INTEGER NOT NULL, PRIMARY KEY(`deviceId`, `packageName`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "withFoldersSize", + "columnName": "withFoldersSize", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName" + ] + }, + "indices": [ + { + "name": "index_FileOptionsEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FileOptionsEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DashboardEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`dashboardId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, PRIMARY KEY(`dashboardId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "dashboardId", + "columnName": "dashboardId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "dashboardId" + ] + }, + "indices": [ + { + "name": "index_DashboardEntity_dashboardId", + "unique": false, + "columnNames": [ + "dashboardId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardEntity_dashboardId` ON `${TABLE_NAME}` (`dashboardId`)" + }, + { + "name": "index_DashboardEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DashboardContainerEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `dashboardId` TEXT NOT NULL, `containerOrder` INTEGER NOT NULL, `containerConfig` TEXT NOT NULL, `name` TEXT NOT NULL, FOREIGN KEY(`dashboardId`) REFERENCES `DashboardEntity`(`dashboardId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dashboardId", + "columnName": "dashboardId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "containerOrder", + "columnName": "containerOrder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "containerConfig", + "columnName": "containerConfig", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DashboardContainerEntity_dashboardId", + "unique": false, + "columnNames": [ + "dashboardId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardContainerEntity_dashboardId` ON `${TABLE_NAME}` (`dashboardId`)" + }, + { + "name": "index_DashboardContainerEntity_dashboardId_containerOrder", + "unique": true, + "columnNames": [ + "dashboardId", + "containerOrder" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DashboardContainerEntity_dashboardId_containerOrder` ON `${TABLE_NAME}` (`dashboardId`, `containerOrder`)" + } + ], + "foreignKeys": [ + { + "table": "DashboardEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "dashboardId" + ], + "referencedColumns": [ + "dashboardId" + ] + } + ] + }, + { + "tableName": "DashboardElementEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `containerId` INTEGER NOT NULL, `elementOrder` INTEGER NOT NULL, `elementAsJson` TEXT NOT NULL, FOREIGN KEY(`containerId`) REFERENCES `DashboardContainerEntity`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "containerId", + "columnName": "containerId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "elementOrder", + "columnName": "elementOrder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "elementAsJson", + "columnName": "elementAsJson", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DashboardElementEntity_containerId", + "unique": false, + "columnNames": [ + "containerId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardElementEntity_containerId` ON `${TABLE_NAME}` (`containerId`)" + }, + { + "name": "index_DashboardElementEntity_containerId_elementOrder", + "unique": true, + "columnNames": [ + "containerId", + "elementOrder" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DashboardElementEntity_containerId_elementOrder` ON `${TABLE_NAME}` (`containerId`, `elementOrder`)" + } + ], + "foreignKeys": [ + { + "table": "DashboardContainerEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "containerId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "TableEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `name` TEXT NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_TableEntity_deviceId_packageName_name", + "unique": true, + "columnNames": [ + "deviceId", + "packageName", + "name" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_TableEntity_deviceId_packageName_name` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `name`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "TableItemEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` TEXT NOT NULL, `tableId` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL, `columnsNames` TEXT NOT NULL, `values` TEXT NOT NULL, PRIMARY KEY(`itemId`), FOREIGN KEY(`tableId`) REFERENCES `TableEntity`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "itemId", + "columnName": "itemId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "tableId", + "columnName": "tableId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "columnsNames", + "columnName": "columnsNames", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "values", + "columnName": "values", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "itemId" + ] + }, + "indices": [ + { + "name": "index_TableItemEntity_tableId", + "unique": false, + "columnNames": [ + "tableId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_TableItemEntity_tableId` ON `${TABLE_NAME}` (`tableId`)" + } + ], + "foreignKeys": [ + { + "table": "TableEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "tableId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "DeviceImageEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `url` TEXT NOT NULL, `time` INTEGER NOT NULL, `headersJsonEncoded` TEXT NOT NULL, `byteSize` INTEGER, `contentType` TEXT, PRIMARY KEY(`deviceId`, `packageName`, `url`, `time`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "headersJsonEncoded", + "columnName": "headersJsonEncoded", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "byteSize", + "columnName": "byteSize", + "affinity": "INTEGER" + }, + { + "fieldPath": "contentType", + "columnName": "contentType", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName", + "url", + "time" + ] + }, + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "SuccessQueryEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `databaseId` TEXT NOT NULL, `queryString` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "databaseId", + "columnName": "databaseId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "queryString", + "columnName": "queryString", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_SuccessQueryEntity_deviceId_packageName_databaseId_queryString", + "unique": true, + "columnNames": [ + "deviceId", + "packageName", + "databaseId", + "queryString" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_SuccessQueryEntity_deviceId_packageName_databaseId_queryString` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `databaseId`, `queryString`)" + }, + { + "name": "index_SuccessQueryEntity_databaseId", + "unique": false, + "columnNames": [ + "databaseId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_SuccessQueryEntity_databaseId` ON `${TABLE_NAME}` (`databaseId`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "FavoriteQueryEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `databaseId` TEXT NOT NULL, `queryString` TEXT NOT NULL, `title` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "databaseId", + "columnName": "databaseId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "queryString", + "columnName": "queryString", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_FavoriteQueryEntity_databaseId", + "unique": false, + "columnNames": [ + "databaseId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FavoriteQueryEntity_databaseId` ON `${TABLE_NAME}` (`databaseId`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DeeplinkEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `link` TEXT NOT NULL, `label` TEXT, `description` TEXT, `parametersAsJson` TEXT NOT NULL, `isHistory` INTEGER NOT NULL, `executedAt` INTEGER NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "link", + "columnName": "link", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT" + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT" + }, + { + "fieldPath": "parametersAsJson", + "columnName": "parametersAsJson", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isHistory", + "columnName": "isHistory", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "executedAt", + "columnName": "executedAt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DeeplinkEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DeeplinkEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + }, + { + "name": "index_DeeplinkEntity_deviceId_link", + "unique": true, + "columnNames": [ + "deviceId", + "link" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DeeplinkEntity_deviceId_link` ON `${TABLE_NAME}` (`deviceId`, `link`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DeeplinkVariableEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `name` TEXT NOT NULL, `description` TEXT, `isHistory` INTEGER NOT NULL, `mode` TEXT NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT" + }, + { + "fieldPath": "isHistory", + "columnName": "isHistory", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "mode", + "columnName": "mode", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DeeplinkVariableEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DeeplinkVariableEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + }, + { + "name": "index_DeeplinkVariableEntity_deviceId_name", + "unique": true, + "columnNames": [ + "deviceId", + "name" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DeeplinkVariableEntity_deviceId_name` ON `${TABLE_NAME}` (`deviceId`, `name`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "AnalyticsItemEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` TEXT NOT NULL, `analyticsTableId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `appInstance` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL, `createdAtFormatted` TEXT NOT NULL, `eventName` TEXT NOT NULL, `propertiesColumnsNames` TEXT NOT NULL, `propertiesValues` TEXT NOT NULL, PRIMARY KEY(`itemId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "itemId", + "columnName": "itemId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "analyticsTableId", + "columnName": "analyticsTableId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "appInstance", + "columnName": "appInstance", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAtFormatted", + "columnName": "createdAtFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "eventName", + "columnName": "eventName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "propertiesColumnsNames", + "columnName": "propertiesColumnsNames", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "propertiesValues", + "columnName": "propertiesValues", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "itemId" + ] + }, + "indices": [ + { + "name": "index_AnalyticsItemEntity_deviceId_packageName_analyticsTableId", + "unique": false, + "columnNames": [ + "deviceId", + "packageName", + "analyticsTableId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_AnalyticsItemEntity_deviceId_packageName_analyticsTableId` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `analyticsTableId`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "FeatureFlagEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `provider` TEXT NOT NULL, `name` TEXT NOT NULL, `state` TEXT NOT NULL, `variant` TEXT NOT NULL, `payload` TEXT, `appInstance` INTEGER NOT NULL, PRIMARY KEY(`deviceId`, `packageName`, `provider`, `name`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "provider", + "columnName": "provider", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "state", + "columnName": "state", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "variant", + "columnName": "variant", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "payload", + "columnName": "payload", + "affinity": "TEXT" + }, + { + "fieldPath": "appInstance", + "columnName": "appInstance", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName", + "provider", + "name" + ] + }, + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "AccessibilityEventEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `id` TEXT NOT NULL, `appInstance` INTEGER NOT NULL, `type` TEXT NOT NULL, `text` TEXT, `contentDescription` TEXT, `className` TEXT, `screen` TEXT, `bounds` TEXT, `extras` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, PRIMARY KEY(`deviceId`, `packageName`, `id`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "appInstance", + "columnName": "appInstance", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "text", + "columnName": "text", + "affinity": "TEXT" + }, + { + "fieldPath": "contentDescription", + "columnName": "contentDescription", + "affinity": "TEXT" + }, + { + "fieldPath": "className", + "columnName": "className", + "affinity": "TEXT" + }, + { + "fieldPath": "screen", + "columnName": "screen", + "affinity": "TEXT" + }, + { + "fieldPath": "bounds", + "columnName": "bounds", + "affinity": "TEXT" + }, + { + "fieldPath": "extras", + "columnName": "extras", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName", + "id" + ] + }, + "indices": [ + { + "name": "index_AccessibilityEventEntity_deviceId_packageName_createdAt", + "unique": false, + "columnNames": [ + "deviceId", + "packageName", + "createdAt" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_AccessibilityEventEntity_deviceId_packageName_createdAt` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `createdAt`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "NetworkFilterEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `columnName` TEXT NOT NULL, `isEnabled` INTEGER NOT NULL, `itemsAsJson` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `columnName`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "columnName", + "columnName": "columnName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "itemsAsJson", + "columnName": "itemsAsJson", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "columnName" + ] + }, + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "NetworkSettingsEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `valueAsJson` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `packageName`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "valueAsJson", + "columnName": "valueAsJson", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName" + ] + }, + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "MockNetworkEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mockId` TEXT NOT NULL, `deviceId` TEXT, `packageName` TEXT, `isEnabled` INTEGER NOT NULL, `response` TEXT NOT NULL, `displayName` TEXT NOT NULL, `expectation_urlPattern` TEXT NOT NULL, `expectation_method` TEXT NOT NULL, PRIMARY KEY(`mockId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "mockId", + "columnName": "mockId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT" + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT" + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "response", + "columnName": "response", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "displayName", + "columnName": "displayName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expectation.urlPattern", + "columnName": "expectation_urlPattern", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expectation.method", + "columnName": "expectation_method", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "mockId" + ] + }, + "indices": [ + { + "name": "index_MockNetworkEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_MockNetworkEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DeviceWithSerialEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `serial` TEXT NOT NULL, PRIMARY KEY(`deviceId`), FOREIGN KEY(`deviceId`) REFERENCES `DeviceEntity`(`deviceId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "serial", + "columnName": "serial", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId" + ] + }, + "foreignKeys": [ + { + "table": "DeviceEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId" + ], + "referencedColumns": [ + "deviceId" + ] + } + ] + }, + { + "tableName": "BadQualityConfigEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `packageName` TEXT NOT NULL, `isEnabled` INTEGER NOT NULL, `errorProbability` REAL NOT NULL, `errors` TEXT NOT NULL, `triggerProbability` REAL NOT NULL, `minLatencyMs` INTEGER NOT NULL, `maxLatencyMs` INTEGER NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "errorProbability", + "columnName": "errorProbability", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "errors", + "columnName": "errors", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "latency.triggerProbability", + "columnName": "triggerProbability", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "latency.minLatencyMs", + "columnName": "minLatencyMs", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latency.maxLatencyMs", + "columnName": "maxLatencyMs", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_BadQualityConfigEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_BadQualityConfigEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DeviceEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `deviceName` TEXT NOT NULL, `platform` TEXT NOT NULL, `connectionType` TEXT NOT NULL, PRIMARY KEY(`deviceId`))", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceName", + "columnName": "deviceName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "platform", + "columnName": "platform", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "connectionType", + "columnName": "connectionType", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId" + ] + } + }, + { + "tableName": "DeviceAppEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `name` TEXT NOT NULL, `packageName` TEXT NOT NULL, `iconEncoded` TEXT, `lastAppInstance` INTEGER NOT NULL, `floconVersionOnDevice` TEXT NOT NULL, `installedPlugins` TEXT, PRIMARY KEY(`deviceId`, `packageName`), FOREIGN KEY(`deviceId`) REFERENCES `DeviceEntity`(`deviceId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "iconEncoded", + "columnName": "iconEncoded", + "affinity": "TEXT" + }, + { + "fieldPath": "lastAppInstance", + "columnName": "lastAppInstance", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "floconVersionOnDevice", + "columnName": "floconVersionOnDevice", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "installedPlugins", + "columnName": "installedPlugins", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName" + ] + }, + "foreignKeys": [ + { + "table": "DeviceEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId" + ], + "referencedColumns": [ + "deviceId" + ] + } + ] + }, + { + "tableName": "DatabaseTableEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `databaseId` TEXT NOT NULL, `tableName` TEXT NOT NULL, `columnsAsString` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `packageName`, `databaseId`, `tableName`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "databaseId", + "columnName": "databaseId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "tableName", + "columnName": "tableName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "columnsAsString", + "columnName": "columnsAsString", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName", + "databaseId", + "tableName" + ] + }, + "indices": [ + { + "name": "index_DatabaseTableEntity_databaseId", + "unique": false, + "columnNames": [ + "databaseId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DatabaseTableEntity_databaseId` ON `${TABLE_NAME}` (`databaseId`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "CrashReportEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`crashId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `exceptionType` TEXT NOT NULL, `exceptionMessage` TEXT NOT NULL, `stackTrace` TEXT NOT NULL, PRIMARY KEY(`crashId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "crashId", + "columnName": "crashId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exceptionType", + "columnName": "exceptionType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "exceptionMessage", + "columnName": "exceptionMessage", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "stackTrace", + "columnName": "stackTrace", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "crashId" + ] + }, + "indices": [ + { + "name": "index_CrashReportEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_CrashReportEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DatabaseQueryLogEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `dbId` TEXT NOT NULL, `dbName` TEXT NOT NULL, `sqlQuery` TEXT NOT NULL, `bindArgs` TEXT, `timestamp` INTEGER NOT NULL, `isTransaction` INTEGER NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `appInstance` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dbId", + "columnName": "dbId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "dbName", + "columnName": "dbName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sqlQuery", + "columnName": "sqlQuery", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "bindArgs", + "columnName": "bindArgs", + "affinity": "TEXT" + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isTransaction", + "columnName": "isTransaction", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "appInstance", + "columnName": "appInstance", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DatabaseQueryLogEntity_deviceId_packageName_dbId_timestamp", + "unique": false, + "columnNames": [ + "deviceId", + "packageName", + "dbId", + "timestamp" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DatabaseQueryLogEntity_deviceId_packageName_dbId_timestamp` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `dbId`, `timestamp`)" + } + ] + }, + { + "tableName": "AdbSavedCommandEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `name` TEXT NOT NULL, `command` TEXT NOT NULL, `description` TEXT, `createdAt` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "command", + "columnName": "command", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT" + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_AdbSavedCommandEntity_deviceId", + "unique": false, + "columnNames": [ + "deviceId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_AdbSavedCommandEntity_deviceId` ON `${TABLE_NAME}` (`deviceId`)" + } + ] + }, + { + "tableName": "AdbCommandHistoryEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `command` TEXT NOT NULL, `output` TEXT NOT NULL, `isSuccess` INTEGER NOT NULL, `executedAt` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "command", + "columnName": "command", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "output", + "columnName": "output", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isSuccess", + "columnName": "isSuccess", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "executedAt", + "columnName": "executedAt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_AdbCommandHistoryEntity_deviceId", + "unique": false, + "columnNames": [ + "deviceId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_AdbCommandHistoryEntity_deviceId` ON `${TABLE_NAME}` (`deviceId`)" + } + ] + }, + { + "tableName": "AdbFlowEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `name` TEXT NOT NULL, `description` TEXT, `createdAt` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT" + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_AdbFlowEntity_deviceId", + "unique": false, + "columnNames": [ + "deviceId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_AdbFlowEntity_deviceId` ON `${TABLE_NAME}` (`deviceId`)" + } + ] + }, + { + "tableName": "AdbFlowStepEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `flowId` INTEGER NOT NULL, `orderIndex` INTEGER NOT NULL, `command` TEXT NOT NULL, `delayAfterMs` INTEGER NOT NULL, `label` TEXT, FOREIGN KEY(`flowId`) REFERENCES `AdbFlowEntity`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "flowId", + "columnName": "flowId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "orderIndex", + "columnName": "orderIndex", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "command", + "columnName": "command", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "delayAfterMs", + "columnName": "delayAfterMs", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_AdbFlowStepEntity_flowId", + "unique": false, + "columnNames": [ + "flowId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_AdbFlowStepEntity_flowId` ON `${TABLE_NAME}` (`flowId`)" + } + ], + "foreignKeys": [ + { + "table": "AdbFlowEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "flowId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "WebViewEventEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`eventId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `webViewId` TEXT NOT NULL, `kind` TEXT NOT NULL, `url` TEXT, `label` TEXT NOT NULL, `message` TEXT, `sourceId` TEXT, `lineNumber` INTEGER, `statusCode` INTEGER, `result` TEXT, `timestamp` INTEGER NOT NULL, PRIMARY KEY(`eventId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "eventId", + "columnName": "eventId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "webViewId", + "columnName": "webViewId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "kind", + "columnName": "kind", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT" + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "message", + "columnName": "message", + "affinity": "TEXT" + }, + { + "fieldPath": "sourceId", + "columnName": "sourceId", + "affinity": "TEXT" + }, + { + "fieldPath": "lineNumber", + "columnName": "lineNumber", + "affinity": "INTEGER" + }, + { + "fieldPath": "statusCode", + "columnName": "statusCode", + "affinity": "INTEGER" + }, + { + "fieldPath": "result", + "columnName": "result", + "affinity": "TEXT" + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "eventId" + ] + }, + "indices": [ + { + "name": "index_WebViewEventEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_WebViewEventEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + } + ], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f51284ee7103296d25133cdd0e513368')" + ] + } +} \ No newline at end of file diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/common/db/AppDatabase.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/common/db/AppDatabase.kt index b38d8e1fa..14d828c92 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/common/db/AppDatabase.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/common/db/AppDatabase.kt @@ -65,7 +65,7 @@ import io.github.openflocon.flocondesktop.common.db.converters.MapStringsConvert import kotlinx.coroutines.Dispatchers @Database( - version = 90, + version = 91, entities = [ FloconNetworkCallEntity::class, FileEntity::class, diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DatabaseQueryLogPanelViewModel.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DatabaseQueryLogPanelViewModel.kt index 5c966fe39..f1dccf9d5 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DatabaseQueryLogPanelViewModel.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DatabaseQueryLogPanelViewModel.kt @@ -29,7 +29,7 @@ class DatabaseQueryLogPanelViewModel( .filterNotNull() .map { DatabaseQueryLogPanelUiModel( - dbName = it.dbId, + dbName = it.dbName, sql = it.toFullSql(), rawSql = it.sqlQuery, arguments = it.bindArgs.orEmpty(), diff --git a/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/database/repository/DatabaseRepositoryImpl.kt b/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/database/repository/DatabaseRepositoryImpl.kt index 9e2e52728..fbc90689f 100644 --- a/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/database/repository/DatabaseRepositoryImpl.kt +++ b/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/database/repository/DatabaseRepositoryImpl.kt @@ -99,8 +99,10 @@ class DatabaseRepositoryImpl( queryDatabaseDataSource.getQueryLogs( message, )?.let { + // the name is only displayed, it falls back to the id when the database is not listed (yet) + val dbName = deviceDatabasesDataSource.getDatabaseById(it.dbId)?.name ?: it.dbId localDatabaseDataSource.saveQueryLog( - it, + it.copy(dbName = dbName), deviceIdAndPackageName = deviceIdAndPackageName ) } diff --git a/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/database/datasource/LocalDatabaseDataSourceRoom.kt b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/database/datasource/LocalDatabaseDataSourceRoom.kt index c9e523149..6a4e45a90 100644 --- a/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/database/datasource/LocalDatabaseDataSourceRoom.kt +++ b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/database/datasource/LocalDatabaseDataSourceRoom.kt @@ -191,6 +191,7 @@ internal class LocalDatabaseDataSourceRoom( databaseQueryLogDao.insert( DatabaseQueryLogEntity( dbId = log.dbId, + dbName = log.dbName, sqlQuery = log.sqlQuery, bindArgs = log.bindArgs?.let { json.encodeToString(it) }, timestamp = log.timestamp, @@ -223,6 +224,7 @@ internal class LocalDatabaseDataSourceRoom( list.map { entity -> DatabaseQueryLogDomainModel( dbId = entity.dbId, + dbName = entity.dbName, sqlQuery = entity.sqlQuery, bindArgs = entity.bindArgs?.let { json.decodeFromString(it) @@ -267,6 +269,7 @@ internal class LocalDatabaseDataSourceRoom( return databaseQueryLogDao.getLogs(query).map { entity -> DatabaseQueryLogDomainModel( dbId = entity.dbId, + dbName = entity.dbName, sqlQuery = entity.sqlQuery, bindArgs = entity.bindArgs?.let { json.decodeFromString(it) @@ -299,6 +302,7 @@ internal class LocalDatabaseDataSourceRoom( return databaseQueryLogDao.getLogs(query).map { entity -> DatabaseQueryLogDomainModel( dbId = entity.dbId, + dbName = entity.dbName, sqlQuery = entity.sqlQuery, bindArgs = entity.bindArgs?.let { json.decodeFromString(it) @@ -314,6 +318,7 @@ internal class LocalDatabaseDataSourceRoom( entity?.let { DatabaseQueryLogDomainModel( dbId = it.dbId, + dbName = it.dbName, sqlQuery = it.sqlQuery, bindArgs = it.bindArgs?.let { args -> json.decodeFromString(args) diff --git a/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/database/models/DatabaseQueryLogEntity.kt b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/database/models/DatabaseQueryLogEntity.kt index b37fc0bff..b81a217f4 100644 --- a/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/database/models/DatabaseQueryLogEntity.kt +++ b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/database/models/DatabaseQueryLogEntity.kt @@ -13,6 +13,8 @@ import androidx.room.PrimaryKey data class DatabaseQueryLogEntity( @PrimaryKey(autoGenerate = true) val id: Int = 0, val dbId: String, + // display only, the logs are always read by dbId + val dbName: String, val sqlQuery: String, val bindArgs: String?, val timestamp: Long, diff --git a/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/timeline/datasource/TimelineQueryBuilder.kt b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/timeline/datasource/TimelineQueryBuilder.kt index 1e4008e34..1a2e32a4c 100644 --- a/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/timeline/datasource/TimelineQueryBuilder.kt +++ b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/timeline/datasource/TimelineQueryBuilder.kt @@ -187,13 +187,13 @@ private fun buildUnion( add( branch( select = "'$KIND_DATABASE' AS kind, CAST(id AS TEXT) AS id, timestamp AS ts, " + - "sqlQuery AS title, dbId AS subtitle, appInstance AS appInstance, $NO_NETWORK_COLUMNS", + "sqlQuery AS title, dbName AS subtitle, appInstance AS appInstance, $NO_NETWORK_COLUMNS", from = "DatabaseQueryLogEntity", appInstanceColumn = "appInstance", - textColumns = listOf("sqlQuery", "dbId"), + textColumns = listOf("sqlQuery", "dbName"), timeColumn = "timestamp", facetExpressions = mapOf( - TimelineFacet.DatabaseName to "dbId", + TimelineFacet.DatabaseName to "dbName", TimelineFacet.DatabaseStatement to DATABASE_STATEMENT, ), orderLimit = "ORDER BY timestamp DESC LIMIT $MAX_DATABASE_LOGS", diff --git a/FloconDesktop/data/remote/src/commonMain/kotlin/com/flocon/data/remote/database/datasource/QueryDatabaseRemoteDataSourceImpl.kt b/FloconDesktop/data/remote/src/commonMain/kotlin/com/flocon/data/remote/database/datasource/QueryDatabaseRemoteDataSourceImpl.kt index cb7a757d6..0ee7f4256 100644 --- a/FloconDesktop/data/remote/src/commonMain/kotlin/com/flocon/data/remote/database/datasource/QueryDatabaseRemoteDataSourceImpl.kt +++ b/FloconDesktop/data/remote/src/commonMain/kotlin/com/flocon/data/remote/database/datasource/QueryDatabaseRemoteDataSourceImpl.kt @@ -100,6 +100,7 @@ class QueryDatabaseRemoteDataSourceImpl( DatabaseQueryLogDomainModel( dbId = it.dbId, + dbName = it.dbId, // the repository replaces it with the name of the database sqlQuery = it.sqlQuery, bindArgs = it.bindArgs.orEmpty(), timestamp = it.timestamp, diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/models/DatabaseQueryLogDomainModel.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/models/DatabaseQueryLogDomainModel.kt index a45ff25ad..d8207d3f8 100644 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/models/DatabaseQueryLogDomainModel.kt +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/models/DatabaseQueryLogDomainModel.kt @@ -4,6 +4,8 @@ import io.github.openflocon.domain.database.utils.injectSqlArgs data class DatabaseQueryLogDomainModel( val dbId: String, + /** Display name of the database, only for the screens: the database is always identified by [dbId]. */ + val dbName: String, val sqlQuery: String, val bindArgs: List?, val timestamp: Long, diff --git a/FloconDesktop/mcp/src/desktopTest/kotlin/io/github/openflocon/mcp/database/DatabaseMcpToolProviderTest.kt b/FloconDesktop/mcp/src/desktopTest/kotlin/io/github/openflocon/mcp/database/DatabaseMcpToolProviderTest.kt index 75ef842d6..d2fb5b4b6 100644 --- a/FloconDesktop/mcp/src/desktopTest/kotlin/io/github/openflocon/mcp/database/DatabaseMcpToolProviderTest.kt +++ b/FloconDesktop/mcp/src/desktopTest/kotlin/io/github/openflocon/mcp/database/DatabaseMcpToolProviderTest.kt @@ -207,6 +207,7 @@ class DatabaseMcpToolProviderTest { isTransaction: Boolean = false, ) = DatabaseQueryLogDomainModel( dbId = "/data/databases/app.db", + dbName = "app.db", sqlQuery = sql, bindArgs = args, timestamp = timestamp, From 531f1cb5a909c73769bd60d19acfeace4ed5f9a0 Mon Sep 17 00:00:00 2001 From: Florent Champigny Date: Fri, 30 Oct 2026 23:27:00 +0100 Subject: [PATCH 5/6] chore: [DATABASE] Drop the unreleased room schema 90 --- .../90.json | 2471 ----------------- 1 file changed, 2471 deletions(-) delete mode 100644 FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/90.json diff --git a/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/90.json b/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/90.json deleted file mode 100644 index 4412508be..000000000 --- a/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/90.json +++ /dev/null @@ -1,2471 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 90, - "identityHash": "f51284ee7103296d25133cdd0e513368", - "entities": [ - { - "tableName": "FloconNetworkCallEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`callId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `appInstance` INTEGER NOT NULL, `type` TEXT NOT NULL, `isReplayed` INTEGER NOT NULL, `request_url` TEXT NOT NULL, `request_method` TEXT NOT NULL, `request_startTime` INTEGER NOT NULL, `request_startTimeFormatted` TEXT NOT NULL, `request_byteSizeFormatted` TEXT NOT NULL, `request_requestHeaders` TEXT NOT NULL, `request_requestBody` TEXT, `request_requestByteSize` INTEGER NOT NULL, `request_isMocked` INTEGER NOT NULL, `request_domainFormatted` TEXT NOT NULL, `request_methodFormatted` TEXT NOT NULL, `request_queryFormatted` TEXT NOT NULL, `request_graphql_query` TEXT, `request_graphql_operationType` TEXT, `request_websocket_event` TEXT, `response_durationMs` REAL, `response_durationFormatted` TEXT, `response_responseContentType` TEXT, `response_responseBody` TEXT, `response_responseHeaders` TEXT, `response_responseByteSize` INTEGER, `response_responseByteSizeFormatted` TEXT, `response_responseError` TEXT, `response_isImage` INTEGER, `response_statusFormatted` TEXT, `response_graphql_isSuccess` INTEGER, `response_graphql_responseHttpCode` INTEGER, `response_http_responseHttpCode` INTEGER, `response_grpc_responseStatus` TEXT, PRIMARY KEY(`callId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "callId", - "columnName": "callId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "deviceId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "packageName", - "columnName": "packageName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "appInstance", - "columnName": "appInstance", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isReplayed", - "columnName": "isReplayed", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "request.url", - "columnName": "request_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "request.method", - "columnName": "request_method", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "request.startTime", - "columnName": "request_startTime", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "request.startTimeFormatted", - "columnName": "request_startTimeFormatted", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "request.byteSizeFormatted", - "columnName": "request_byteSizeFormatted", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "request.requestHeaders", - "columnName": "request_requestHeaders", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "request.requestBody", - "columnName": "request_requestBody", - "affinity": "TEXT" - }, - { - "fieldPath": "request.requestByteSize", - "columnName": "request_requestByteSize", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "request.isMocked", - "columnName": "request_isMocked", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "request.domainFormatted", - "columnName": "request_domainFormatted", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "request.methodFormatted", - "columnName": "request_methodFormatted", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "request.queryFormatted", - "columnName": "request_queryFormatted", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "request.graphql.query", - "columnName": "request_graphql_query", - "affinity": "TEXT" - }, - { - "fieldPath": "request.graphql.operationType", - "columnName": "request_graphql_operationType", - "affinity": "TEXT" - }, - { - "fieldPath": "request.websocket.event", - "columnName": "request_websocket_event", - "affinity": "TEXT" - }, - { - "fieldPath": "response.durationMs", - "columnName": "response_durationMs", - "affinity": "REAL" - }, - { - "fieldPath": "response.durationFormatted", - "columnName": "response_durationFormatted", - "affinity": "TEXT" - }, - { - "fieldPath": "response.responseContentType", - "columnName": "response_responseContentType", - "affinity": "TEXT" - }, - { - "fieldPath": "response.responseBody", - "columnName": "response_responseBody", - "affinity": "TEXT" - }, - { - "fieldPath": "response.responseHeaders", - "columnName": "response_responseHeaders", - "affinity": "TEXT" - }, - { - "fieldPath": "response.responseByteSize", - "columnName": "response_responseByteSize", - "affinity": "INTEGER" - }, - { - "fieldPath": "response.responseByteSizeFormatted", - "columnName": "response_responseByteSizeFormatted", - "affinity": "TEXT" - }, - { - "fieldPath": "response.responseError", - "columnName": "response_responseError", - "affinity": "TEXT" - }, - { - "fieldPath": "response.isImage", - "columnName": "response_isImage", - "affinity": "INTEGER" - }, - { - "fieldPath": "response.statusFormatted", - "columnName": "response_statusFormatted", - "affinity": "TEXT" - }, - { - "fieldPath": "response.graphql.isSuccess", - "columnName": "response_graphql_isSuccess", - "affinity": "INTEGER" - }, - { - "fieldPath": "response.graphql.responseHttpCode", - "columnName": "response_graphql_responseHttpCode", - "affinity": "INTEGER" - }, - { - "fieldPath": "response.http.responseHttpCode", - "columnName": "response_http_responseHttpCode", - "affinity": "INTEGER" - }, - { - "fieldPath": "response.grpc.responseStatus", - "columnName": "response_grpc_responseStatus", - "affinity": "TEXT" - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "callId" - ] - }, - "indices": [ - { - "name": "index_FloconNetworkCallEntity_deviceId_packageName", - "unique": false, - "columnNames": [ - "deviceId", - "packageName" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_FloconNetworkCallEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" - } - ], - "foreignKeys": [ - { - "table": "DeviceAppEntity", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "deviceId", - "packageName" - ], - "referencedColumns": [ - "deviceId", - "packageName" - ] - } - ] - }, - { - "tableName": "FileEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `name` TEXT NOT NULL, `isDirectory` INTEGER NOT NULL, `path` TEXT NOT NULL, `parentPath` TEXT NOT NULL, `size` INTEGER NOT NULL, `lastModifiedTimestamp` INTEGER NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "deviceId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "packageName", - "columnName": "packageName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isDirectory", - "columnName": "isDirectory", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "path", - "columnName": "path", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "parentPath", - "columnName": "parentPath", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "size", - "columnName": "size", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lastModifiedTimestamp", - "columnName": "lastModifiedTimestamp", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [ - { - "name": "index_FileEntity_deviceId_packageName", - "unique": false, - "columnNames": [ - "deviceId", - "packageName" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_FileEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" - } - ], - "foreignKeys": [ - { - "table": "DeviceAppEntity", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "deviceId", - "packageName" - ], - "referencedColumns": [ - "deviceId", - "packageName" - ] - } - ] - }, - { - "tableName": "FileOptionsEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `withFoldersSize` INTEGER NOT NULL, PRIMARY KEY(`deviceId`, `packageName`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "deviceId", - "columnName": "deviceId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "packageName", - "columnName": "packageName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "withFoldersSize", - "columnName": "withFoldersSize", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "deviceId", - "packageName" - ] - }, - "indices": [ - { - "name": "index_FileOptionsEntity_deviceId_packageName", - "unique": false, - "columnNames": [ - "deviceId", - "packageName" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_FileOptionsEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" - } - ], - "foreignKeys": [ - { - "table": "DeviceAppEntity", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "deviceId", - "packageName" - ], - "referencedColumns": [ - "deviceId", - "packageName" - ] - } - ] - }, - { - "tableName": "DashboardEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`dashboardId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, PRIMARY KEY(`dashboardId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "dashboardId", - "columnName": "dashboardId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "deviceId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "packageName", - "columnName": "packageName", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "dashboardId" - ] - }, - "indices": [ - { - "name": "index_DashboardEntity_dashboardId", - "unique": false, - "columnNames": [ - "dashboardId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardEntity_dashboardId` ON `${TABLE_NAME}` (`dashboardId`)" - }, - { - "name": "index_DashboardEntity_deviceId_packageName", - "unique": false, - "columnNames": [ - "deviceId", - "packageName" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" - } - ], - "foreignKeys": [ - { - "table": "DeviceAppEntity", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "deviceId", - "packageName" - ], - "referencedColumns": [ - "deviceId", - "packageName" - ] - } - ] - }, - { - "tableName": "DashboardContainerEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `dashboardId` TEXT NOT NULL, `containerOrder` INTEGER NOT NULL, `containerConfig` TEXT NOT NULL, `name` TEXT NOT NULL, FOREIGN KEY(`dashboardId`) REFERENCES `DashboardEntity`(`dashboardId`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "dashboardId", - "columnName": "dashboardId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "containerOrder", - "columnName": "containerOrder", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "containerConfig", - "columnName": "containerConfig", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [ - { - "name": "index_DashboardContainerEntity_dashboardId", - "unique": false, - "columnNames": [ - "dashboardId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardContainerEntity_dashboardId` ON `${TABLE_NAME}` (`dashboardId`)" - }, - { - "name": "index_DashboardContainerEntity_dashboardId_containerOrder", - "unique": true, - "columnNames": [ - "dashboardId", - "containerOrder" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DashboardContainerEntity_dashboardId_containerOrder` ON `${TABLE_NAME}` (`dashboardId`, `containerOrder`)" - } - ], - "foreignKeys": [ - { - "table": "DashboardEntity", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "dashboardId" - ], - "referencedColumns": [ - "dashboardId" - ] - } - ] - }, - { - "tableName": "DashboardElementEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `containerId` INTEGER NOT NULL, `elementOrder` INTEGER NOT NULL, `elementAsJson` TEXT NOT NULL, FOREIGN KEY(`containerId`) REFERENCES `DashboardContainerEntity`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "containerId", - "columnName": "containerId", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "elementOrder", - "columnName": "elementOrder", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "elementAsJson", - "columnName": "elementAsJson", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [ - { - "name": "index_DashboardElementEntity_containerId", - "unique": false, - "columnNames": [ - "containerId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardElementEntity_containerId` ON `${TABLE_NAME}` (`containerId`)" - }, - { - "name": "index_DashboardElementEntity_containerId_elementOrder", - "unique": true, - "columnNames": [ - "containerId", - "elementOrder" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DashboardElementEntity_containerId_elementOrder` ON `${TABLE_NAME}` (`containerId`, `elementOrder`)" - } - ], - "foreignKeys": [ - { - "table": "DashboardContainerEntity", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "containerId" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "TableEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `name` TEXT NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "deviceId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "packageName", - "columnName": "packageName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [ - { - "name": "index_TableEntity_deviceId_packageName_name", - "unique": true, - "columnNames": [ - "deviceId", - "packageName", - "name" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_TableEntity_deviceId_packageName_name` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `name`)" - } - ], - "foreignKeys": [ - { - "table": "DeviceAppEntity", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "deviceId", - "packageName" - ], - "referencedColumns": [ - "deviceId", - "packageName" - ] - } - ] - }, - { - "tableName": "TableItemEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` TEXT NOT NULL, `tableId` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL, `columnsNames` TEXT NOT NULL, `values` TEXT NOT NULL, PRIMARY KEY(`itemId`), FOREIGN KEY(`tableId`) REFERENCES `TableEntity`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "itemId", - "columnName": "itemId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "tableId", - "columnName": "tableId", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "columnsNames", - "columnName": "columnsNames", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "values", - "columnName": "values", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "itemId" - ] - }, - "indices": [ - { - "name": "index_TableItemEntity_tableId", - "unique": false, - "columnNames": [ - "tableId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_TableItemEntity_tableId` ON `${TABLE_NAME}` (`tableId`)" - } - ], - "foreignKeys": [ - { - "table": "TableEntity", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "tableId" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "DeviceImageEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `url` TEXT NOT NULL, `time` INTEGER NOT NULL, `headersJsonEncoded` TEXT NOT NULL, `byteSize` INTEGER, `contentType` TEXT, PRIMARY KEY(`deviceId`, `packageName`, `url`, `time`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "deviceId", - "columnName": "deviceId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "packageName", - "columnName": "packageName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "url", - "columnName": "url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "time", - "columnName": "time", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "headersJsonEncoded", - "columnName": "headersJsonEncoded", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "byteSize", - "columnName": "byteSize", - "affinity": "INTEGER" - }, - { - "fieldPath": "contentType", - "columnName": "contentType", - "affinity": "TEXT" - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "deviceId", - "packageName", - "url", - "time" - ] - }, - "foreignKeys": [ - { - "table": "DeviceAppEntity", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "deviceId", - "packageName" - ], - "referencedColumns": [ - "deviceId", - "packageName" - ] - } - ] - }, - { - "tableName": "SuccessQueryEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `databaseId` TEXT NOT NULL, `queryString` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "deviceId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "packageName", - "columnName": "packageName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "databaseId", - "columnName": "databaseId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "queryString", - "columnName": "queryString", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "timestamp", - "columnName": "timestamp", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [ - { - "name": "index_SuccessQueryEntity_deviceId_packageName_databaseId_queryString", - "unique": true, - "columnNames": [ - "deviceId", - "packageName", - "databaseId", - "queryString" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_SuccessQueryEntity_deviceId_packageName_databaseId_queryString` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `databaseId`, `queryString`)" - }, - { - "name": "index_SuccessQueryEntity_databaseId", - "unique": false, - "columnNames": [ - "databaseId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_SuccessQueryEntity_databaseId` ON `${TABLE_NAME}` (`databaseId`)" - } - ], - "foreignKeys": [ - { - "table": "DeviceAppEntity", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "deviceId", - "packageName" - ], - "referencedColumns": [ - "deviceId", - "packageName" - ] - } - ] - }, - { - "tableName": "FavoriteQueryEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `databaseId` TEXT NOT NULL, `queryString` TEXT NOT NULL, `title` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "deviceId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "packageName", - "columnName": "packageName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "databaseId", - "columnName": "databaseId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "queryString", - "columnName": "queryString", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "timestamp", - "columnName": "timestamp", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [ - { - "name": "index_FavoriteQueryEntity_databaseId", - "unique": false, - "columnNames": [ - "databaseId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_FavoriteQueryEntity_databaseId` ON `${TABLE_NAME}` (`databaseId`)" - } - ], - "foreignKeys": [ - { - "table": "DeviceAppEntity", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "deviceId", - "packageName" - ], - "referencedColumns": [ - "deviceId", - "packageName" - ] - } - ] - }, - { - "tableName": "DeeplinkEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `link` TEXT NOT NULL, `label` TEXT, `description` TEXT, `parametersAsJson` TEXT NOT NULL, `isHistory` INTEGER NOT NULL, `executedAt` INTEGER NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "deviceId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "packageName", - "columnName": "packageName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "link", - "columnName": "link", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "label", - "columnName": "label", - "affinity": "TEXT" - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT" - }, - { - "fieldPath": "parametersAsJson", - "columnName": "parametersAsJson", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isHistory", - "columnName": "isHistory", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "executedAt", - "columnName": "executedAt", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [ - { - "name": "index_DeeplinkEntity_deviceId_packageName", - "unique": false, - "columnNames": [ - "deviceId", - "packageName" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_DeeplinkEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" - }, - { - "name": "index_DeeplinkEntity_deviceId_link", - "unique": true, - "columnNames": [ - "deviceId", - "link" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DeeplinkEntity_deviceId_link` ON `${TABLE_NAME}` (`deviceId`, `link`)" - } - ], - "foreignKeys": [ - { - "table": "DeviceAppEntity", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "deviceId", - "packageName" - ], - "referencedColumns": [ - "deviceId", - "packageName" - ] - } - ] - }, - { - "tableName": "DeeplinkVariableEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `name` TEXT NOT NULL, `description` TEXT, `isHistory` INTEGER NOT NULL, `mode` TEXT NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "deviceId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "packageName", - "columnName": "packageName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT" - }, - { - "fieldPath": "isHistory", - "columnName": "isHistory", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "mode", - "columnName": "mode", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [ - { - "name": "index_DeeplinkVariableEntity_deviceId_packageName", - "unique": false, - "columnNames": [ - "deviceId", - "packageName" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_DeeplinkVariableEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" - }, - { - "name": "index_DeeplinkVariableEntity_deviceId_name", - "unique": true, - "columnNames": [ - "deviceId", - "name" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DeeplinkVariableEntity_deviceId_name` ON `${TABLE_NAME}` (`deviceId`, `name`)" - } - ], - "foreignKeys": [ - { - "table": "DeviceAppEntity", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "deviceId", - "packageName" - ], - "referencedColumns": [ - "deviceId", - "packageName" - ] - } - ] - }, - { - "tableName": "AnalyticsItemEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` TEXT NOT NULL, `analyticsTableId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `appInstance` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL, `createdAtFormatted` TEXT NOT NULL, `eventName` TEXT NOT NULL, `propertiesColumnsNames` TEXT NOT NULL, `propertiesValues` TEXT NOT NULL, PRIMARY KEY(`itemId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "itemId", - "columnName": "itemId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "analyticsTableId", - "columnName": "analyticsTableId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "deviceId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "packageName", - "columnName": "packageName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "appInstance", - "columnName": "appInstance", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "createdAtFormatted", - "columnName": "createdAtFormatted", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "eventName", - "columnName": "eventName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "propertiesColumnsNames", - "columnName": "propertiesColumnsNames", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "propertiesValues", - "columnName": "propertiesValues", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "itemId" - ] - }, - "indices": [ - { - "name": "index_AnalyticsItemEntity_deviceId_packageName_analyticsTableId", - "unique": false, - "columnNames": [ - "deviceId", - "packageName", - "analyticsTableId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_AnalyticsItemEntity_deviceId_packageName_analyticsTableId` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `analyticsTableId`)" - } - ], - "foreignKeys": [ - { - "table": "DeviceAppEntity", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "deviceId", - "packageName" - ], - "referencedColumns": [ - "deviceId", - "packageName" - ] - } - ] - }, - { - "tableName": "FeatureFlagEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `provider` TEXT NOT NULL, `name` TEXT NOT NULL, `state` TEXT NOT NULL, `variant` TEXT NOT NULL, `payload` TEXT, `appInstance` INTEGER NOT NULL, PRIMARY KEY(`deviceId`, `packageName`, `provider`, `name`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "deviceId", - "columnName": "deviceId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "packageName", - "columnName": "packageName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "provider", - "columnName": "provider", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "state", - "columnName": "state", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "variant", - "columnName": "variant", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "payload", - "columnName": "payload", - "affinity": "TEXT" - }, - { - "fieldPath": "appInstance", - "columnName": "appInstance", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "deviceId", - "packageName", - "provider", - "name" - ] - }, - "foreignKeys": [ - { - "table": "DeviceAppEntity", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "deviceId", - "packageName" - ], - "referencedColumns": [ - "deviceId", - "packageName" - ] - } - ] - }, - { - "tableName": "AccessibilityEventEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `id` TEXT NOT NULL, `appInstance` INTEGER NOT NULL, `type` TEXT NOT NULL, `text` TEXT, `contentDescription` TEXT, `className` TEXT, `screen` TEXT, `bounds` TEXT, `extras` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, PRIMARY KEY(`deviceId`, `packageName`, `id`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "deviceId", - "columnName": "deviceId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "packageName", - "columnName": "packageName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "appInstance", - "columnName": "appInstance", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "text", - "columnName": "text", - "affinity": "TEXT" - }, - { - "fieldPath": "contentDescription", - "columnName": "contentDescription", - "affinity": "TEXT" - }, - { - "fieldPath": "className", - "columnName": "className", - "affinity": "TEXT" - }, - { - "fieldPath": "screen", - "columnName": "screen", - "affinity": "TEXT" - }, - { - "fieldPath": "bounds", - "columnName": "bounds", - "affinity": "TEXT" - }, - { - "fieldPath": "extras", - "columnName": "extras", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "deviceId", - "packageName", - "id" - ] - }, - "indices": [ - { - "name": "index_AccessibilityEventEntity_deviceId_packageName_createdAt", - "unique": false, - "columnNames": [ - "deviceId", - "packageName", - "createdAt" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_AccessibilityEventEntity_deviceId_packageName_createdAt` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `createdAt`)" - } - ], - "foreignKeys": [ - { - "table": "DeviceAppEntity", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "deviceId", - "packageName" - ], - "referencedColumns": [ - "deviceId", - "packageName" - ] - } - ] - }, - { - "tableName": "NetworkFilterEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `columnName` TEXT NOT NULL, `isEnabled` INTEGER NOT NULL, `itemsAsJson` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `columnName`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "deviceId", - "columnName": "deviceId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "packageName", - "columnName": "packageName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "columnName", - "columnName": "columnName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isEnabled", - "columnName": "isEnabled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "itemsAsJson", - "columnName": "itemsAsJson", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "deviceId", - "columnName" - ] - }, - "foreignKeys": [ - { - "table": "DeviceAppEntity", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "deviceId", - "packageName" - ], - "referencedColumns": [ - "deviceId", - "packageName" - ] - } - ] - }, - { - "tableName": "NetworkSettingsEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `valueAsJson` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `packageName`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "deviceId", - "columnName": "deviceId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "packageName", - "columnName": "packageName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "valueAsJson", - "columnName": "valueAsJson", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "deviceId", - "packageName" - ] - }, - "foreignKeys": [ - { - "table": "DeviceAppEntity", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "deviceId", - "packageName" - ], - "referencedColumns": [ - "deviceId", - "packageName" - ] - } - ] - }, - { - "tableName": "MockNetworkEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mockId` TEXT NOT NULL, `deviceId` TEXT, `packageName` TEXT, `isEnabled` INTEGER NOT NULL, `response` TEXT NOT NULL, `displayName` TEXT NOT NULL, `expectation_urlPattern` TEXT NOT NULL, `expectation_method` TEXT NOT NULL, PRIMARY KEY(`mockId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "mockId", - "columnName": "mockId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "deviceId", - "affinity": "TEXT" - }, - { - "fieldPath": "packageName", - "columnName": "packageName", - "affinity": "TEXT" - }, - { - "fieldPath": "isEnabled", - "columnName": "isEnabled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "response", - "columnName": "response", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "displayName", - "columnName": "displayName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "expectation.urlPattern", - "columnName": "expectation_urlPattern", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "expectation.method", - "columnName": "expectation_method", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "mockId" - ] - }, - "indices": [ - { - "name": "index_MockNetworkEntity_deviceId_packageName", - "unique": false, - "columnNames": [ - "deviceId", - "packageName" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_MockNetworkEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" - } - ], - "foreignKeys": [ - { - "table": "DeviceAppEntity", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "deviceId", - "packageName" - ], - "referencedColumns": [ - "deviceId", - "packageName" - ] - } - ] - }, - { - "tableName": "DeviceWithSerialEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `serial` TEXT NOT NULL, PRIMARY KEY(`deviceId`), FOREIGN KEY(`deviceId`) REFERENCES `DeviceEntity`(`deviceId`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "deviceId", - "columnName": "deviceId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "serial", - "columnName": "serial", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "deviceId" - ] - }, - "foreignKeys": [ - { - "table": "DeviceEntity", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "deviceId" - ], - "referencedColumns": [ - "deviceId" - ] - } - ] - }, - { - "tableName": "BadQualityConfigEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `packageName` TEXT NOT NULL, `isEnabled` INTEGER NOT NULL, `errorProbability` REAL NOT NULL, `errors` TEXT NOT NULL, `triggerProbability` REAL NOT NULL, `minLatencyMs` INTEGER NOT NULL, `maxLatencyMs` INTEGER NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "deviceId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "packageName", - "columnName": "packageName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isEnabled", - "columnName": "isEnabled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "errorProbability", - "columnName": "errorProbability", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "errors", - "columnName": "errors", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "latency.triggerProbability", - "columnName": "triggerProbability", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "latency.minLatencyMs", - "columnName": "minLatencyMs", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "latency.maxLatencyMs", - "columnName": "maxLatencyMs", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "id" - ] - }, - "indices": [ - { - "name": "index_BadQualityConfigEntity_deviceId_packageName", - "unique": false, - "columnNames": [ - "deviceId", - "packageName" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_BadQualityConfigEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" - } - ], - "foreignKeys": [ - { - "table": "DeviceAppEntity", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "deviceId", - "packageName" - ], - "referencedColumns": [ - "deviceId", - "packageName" - ] - } - ] - }, - { - "tableName": "DeviceEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `deviceName` TEXT NOT NULL, `platform` TEXT NOT NULL, `connectionType` TEXT NOT NULL, PRIMARY KEY(`deviceId`))", - "fields": [ - { - "fieldPath": "deviceId", - "columnName": "deviceId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "deviceName", - "columnName": "deviceName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "platform", - "columnName": "platform", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "connectionType", - "columnName": "connectionType", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "deviceId" - ] - } - }, - { - "tableName": "DeviceAppEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `name` TEXT NOT NULL, `packageName` TEXT NOT NULL, `iconEncoded` TEXT, `lastAppInstance` INTEGER NOT NULL, `floconVersionOnDevice` TEXT NOT NULL, `installedPlugins` TEXT, PRIMARY KEY(`deviceId`, `packageName`), FOREIGN KEY(`deviceId`) REFERENCES `DeviceEntity`(`deviceId`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "deviceId", - "columnName": "deviceId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "packageName", - "columnName": "packageName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "iconEncoded", - "columnName": "iconEncoded", - "affinity": "TEXT" - }, - { - "fieldPath": "lastAppInstance", - "columnName": "lastAppInstance", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "floconVersionOnDevice", - "columnName": "floconVersionOnDevice", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "installedPlugins", - "columnName": "installedPlugins", - "affinity": "TEXT" - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "deviceId", - "packageName" - ] - }, - "foreignKeys": [ - { - "table": "DeviceEntity", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "deviceId" - ], - "referencedColumns": [ - "deviceId" - ] - } - ] - }, - { - "tableName": "DatabaseTableEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `databaseId` TEXT NOT NULL, `tableName` TEXT NOT NULL, `columnsAsString` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `packageName`, `databaseId`, `tableName`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "deviceId", - "columnName": "deviceId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "packageName", - "columnName": "packageName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "databaseId", - "columnName": "databaseId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "tableName", - "columnName": "tableName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "columnsAsString", - "columnName": "columnsAsString", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "deviceId", - "packageName", - "databaseId", - "tableName" - ] - }, - "indices": [ - { - "name": "index_DatabaseTableEntity_databaseId", - "unique": false, - "columnNames": [ - "databaseId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_DatabaseTableEntity_databaseId` ON `${TABLE_NAME}` (`databaseId`)" - } - ], - "foreignKeys": [ - { - "table": "DeviceAppEntity", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "deviceId", - "packageName" - ], - "referencedColumns": [ - "deviceId", - "packageName" - ] - } - ] - }, - { - "tableName": "CrashReportEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`crashId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `exceptionType` TEXT NOT NULL, `exceptionMessage` TEXT NOT NULL, `stackTrace` TEXT NOT NULL, PRIMARY KEY(`crashId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "crashId", - "columnName": "crashId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "deviceId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "packageName", - "columnName": "packageName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "timestamp", - "columnName": "timestamp", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exceptionType", - "columnName": "exceptionType", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "exceptionMessage", - "columnName": "exceptionMessage", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "stackTrace", - "columnName": "stackTrace", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "crashId" - ] - }, - "indices": [ - { - "name": "index_CrashReportEntity_deviceId_packageName", - "unique": false, - "columnNames": [ - "deviceId", - "packageName" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_CrashReportEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" - } - ], - "foreignKeys": [ - { - "table": "DeviceAppEntity", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "deviceId", - "packageName" - ], - "referencedColumns": [ - "deviceId", - "packageName" - ] - } - ] - }, - { - "tableName": "DatabaseQueryLogEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `dbId` TEXT NOT NULL, `dbName` TEXT NOT NULL, `sqlQuery` TEXT NOT NULL, `bindArgs` TEXT, `timestamp` INTEGER NOT NULL, `isTransaction` INTEGER NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `appInstance` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "dbId", - "columnName": "dbId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "dbName", - "columnName": "dbName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "sqlQuery", - "columnName": "sqlQuery", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "bindArgs", - "columnName": "bindArgs", - "affinity": "TEXT" - }, - { - "fieldPath": "timestamp", - "columnName": "timestamp", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isTransaction", - "columnName": "isTransaction", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "deviceId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "packageName", - "columnName": "packageName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "appInstance", - "columnName": "appInstance", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [ - { - "name": "index_DatabaseQueryLogEntity_deviceId_packageName_dbId_timestamp", - "unique": false, - "columnNames": [ - "deviceId", - "packageName", - "dbId", - "timestamp" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_DatabaseQueryLogEntity_deviceId_packageName_dbId_timestamp` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `dbId`, `timestamp`)" - } - ] - }, - { - "tableName": "AdbSavedCommandEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `name` TEXT NOT NULL, `command` TEXT NOT NULL, `description` TEXT, `createdAt` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "deviceId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "command", - "columnName": "command", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT" - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [ - { - "name": "index_AdbSavedCommandEntity_deviceId", - "unique": false, - "columnNames": [ - "deviceId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_AdbSavedCommandEntity_deviceId` ON `${TABLE_NAME}` (`deviceId`)" - } - ] - }, - { - "tableName": "AdbCommandHistoryEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `command` TEXT NOT NULL, `output` TEXT NOT NULL, `isSuccess` INTEGER NOT NULL, `executedAt` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "deviceId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "command", - "columnName": "command", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "output", - "columnName": "output", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isSuccess", - "columnName": "isSuccess", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "executedAt", - "columnName": "executedAt", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [ - { - "name": "index_AdbCommandHistoryEntity_deviceId", - "unique": false, - "columnNames": [ - "deviceId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_AdbCommandHistoryEntity_deviceId` ON `${TABLE_NAME}` (`deviceId`)" - } - ] - }, - { - "tableName": "AdbFlowEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `name` TEXT NOT NULL, `description` TEXT, `createdAt` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "deviceId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT" - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [ - { - "name": "index_AdbFlowEntity_deviceId", - "unique": false, - "columnNames": [ - "deviceId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_AdbFlowEntity_deviceId` ON `${TABLE_NAME}` (`deviceId`)" - } - ] - }, - { - "tableName": "AdbFlowStepEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `flowId` INTEGER NOT NULL, `orderIndex` INTEGER NOT NULL, `command` TEXT NOT NULL, `delayAfterMs` INTEGER NOT NULL, `label` TEXT, FOREIGN KEY(`flowId`) REFERENCES `AdbFlowEntity`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "flowId", - "columnName": "flowId", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "orderIndex", - "columnName": "orderIndex", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "command", - "columnName": "command", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "delayAfterMs", - "columnName": "delayAfterMs", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "label", - "columnName": "label", - "affinity": "TEXT" - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [ - { - "name": "index_AdbFlowStepEntity_flowId", - "unique": false, - "columnNames": [ - "flowId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_AdbFlowStepEntity_flowId` ON `${TABLE_NAME}` (`flowId`)" - } - ], - "foreignKeys": [ - { - "table": "AdbFlowEntity", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "flowId" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "WebViewEventEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`eventId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `webViewId` TEXT NOT NULL, `kind` TEXT NOT NULL, `url` TEXT, `label` TEXT NOT NULL, `message` TEXT, `sourceId` TEXT, `lineNumber` INTEGER, `statusCode` INTEGER, `result` TEXT, `timestamp` INTEGER NOT NULL, PRIMARY KEY(`eventId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "eventId", - "columnName": "eventId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "deviceId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "packageName", - "columnName": "packageName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "webViewId", - "columnName": "webViewId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "kind", - "columnName": "kind", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "url", - "columnName": "url", - "affinity": "TEXT" - }, - { - "fieldPath": "label", - "columnName": "label", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "message", - "columnName": "message", - "affinity": "TEXT" - }, - { - "fieldPath": "sourceId", - "columnName": "sourceId", - "affinity": "TEXT" - }, - { - "fieldPath": "lineNumber", - "columnName": "lineNumber", - "affinity": "INTEGER" - }, - { - "fieldPath": "statusCode", - "columnName": "statusCode", - "affinity": "INTEGER" - }, - { - "fieldPath": "result", - "columnName": "result", - "affinity": "TEXT" - }, - { - "fieldPath": "timestamp", - "columnName": "timestamp", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "eventId" - ] - }, - "indices": [ - { - "name": "index_WebViewEventEntity_deviceId_packageName", - "unique": false, - "columnNames": [ - "deviceId", - "packageName" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_WebViewEventEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" - } - ], - "foreignKeys": [ - { - "table": "DeviceAppEntity", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "deviceId", - "packageName" - ], - "referencedColumns": [ - "deviceId", - "packageName" - ] - } - ] - } - ], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f51284ee7103296d25133cdd0e513368')" - ] - } -} \ No newline at end of file From 7bacf698218609c63b72cf8bf7b71be5a57a99d6 Mon Sep 17 00:00:00 2001 From: Florent CHAMPIGNY Date: Fri, 30 Oct 2026 23:40:57 +0100 Subject: [PATCH 6/6] feat: [DATABASE] Room 3 query logs through a logging SQLite driver (#743) Co-authored-by: Florent Champigny --- .../room3/FloconLoggingSQLiteDriver.kt | 5 + FloconAndroid/database/room3/build.gradle.kts | 4 + .../room3/FloconLoggingSQLiteDriverTest.kt | 95 +++++++++++++++++++ .../room3/FloconLoggingSQLiteDriver.nonWeb.kt | 85 +++++++++++++++++ .../flocon/database/room3/FloconLogging.kt | 20 ++++ .../flocon/database/room3/QueryLogger.kt | 14 +++ .../room3/FloconLoggingSQLiteDriver.wasmJs.kt | 85 +++++++++++++++++ .../flocon/myapplication/multi/Databases.kt | 11 ++- .../kotlin/com/flocon/sample/desktop/Main.kt | 5 +- .../kotlin/com/flocon/sample/ios/Databases.kt | 5 +- .../com/flocon/sample/wasm/Databases.kt | 5 +- docs2/database.md | 14 ++- 12 files changed, 339 insertions(+), 9 deletions(-) create mode 100644 FloconAndroid/database/room3-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/database/room3/FloconLoggingSQLiteDriver.kt create mode 100644 FloconAndroid/database/room3/src/jvmTest/kotlin/io/github/openflocon/flocon/database/room3/FloconLoggingSQLiteDriverTest.kt create mode 100644 FloconAndroid/database/room3/src/nonWebMain/kotlin/io/github/openflocon/flocon/database/room3/FloconLoggingSQLiteDriver.nonWeb.kt create mode 100644 FloconAndroid/database/room3/src/roomMain/kotlin/io/github/openflocon/flocon/database/room3/FloconLogging.kt create mode 100644 FloconAndroid/database/room3/src/roomMain/kotlin/io/github/openflocon/flocon/database/room3/QueryLogger.kt create mode 100644 FloconAndroid/database/room3/src/wasmJsMain/kotlin/io/github/openflocon/flocon/database/room3/FloconLoggingSQLiteDriver.wasmJs.kt diff --git a/FloconAndroid/database/room3-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/database/room3/FloconLoggingSQLiteDriver.kt b/FloconAndroid/database/room3-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/database/room3/FloconLoggingSQLiteDriver.kt new file mode 100644 index 000000000..1489b2512 --- /dev/null +++ b/FloconAndroid/database/room3-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/database/room3/FloconLoggingSQLiteDriver.kt @@ -0,0 +1,5 @@ +package io.github.openflocon.flocon.database.room3 + +import androidx.sqlite.SQLiteDriver + +fun SQLiteDriver.floconLogs(id: String): SQLiteDriver = this // no op diff --git a/FloconAndroid/database/room3/build.gradle.kts b/FloconAndroid/database/room3/build.gradle.kts index 47ea8cb61..768bb9b0c 100644 --- a/FloconAndroid/database/room3/build.gradle.kts +++ b/FloconAndroid/database/room3/build.gradle.kts @@ -13,6 +13,10 @@ kotlin { } sourceSets { + // the SQLite driver methods are not suspend on these targets, they are on the web + listOf("androidMain", "jvmMain", "iosMain").forEach { + named(it) { kotlin.srcDir("src/nonWebMain/kotlin") } + } commonMain { kotlin.srcDir("src/roomMain/kotlin") dependencies { diff --git a/FloconAndroid/database/room3/src/jvmTest/kotlin/io/github/openflocon/flocon/database/room3/FloconLoggingSQLiteDriverTest.kt b/FloconAndroid/database/room3/src/jvmTest/kotlin/io/github/openflocon/flocon/database/room3/FloconLoggingSQLiteDriverTest.kt new file mode 100644 index 000000000..23d10b0cd --- /dev/null +++ b/FloconAndroid/database/room3/src/jvmTest/kotlin/io/github/openflocon/flocon/database/room3/FloconLoggingSQLiteDriverTest.kt @@ -0,0 +1,95 @@ +package io.github.openflocon.flocon.database.room3 + +import androidx.sqlite.SQLiteConnection +import androidx.sqlite.SQLiteDriver +import androidx.sqlite.SQLiteStatement +import kotlin.test.Test +import kotlin.test.assertEquals + +class FloconLoggingSQLiteDriverTest { + + private data class Log(val dbId: String, val sql: String, val bindArgs: List) + + private val logs = mutableListOf() + private val statement = FakeStatement() + private val driver = FloconLoggingSQLiteDriver( + delegate = object : SQLiteDriver { + override fun open(fileName: String): SQLiteConnection = object : SQLiteConnection { + override fun prepare(sql: String): SQLiteStatement = statement + override fun close() = Unit + } + }, + dbId = "dogs", + logger = { dbId, sql, bindArgs -> logs += Log(dbId, sql, bindArgs) }, + ) + + private fun prepare(sql: String) = driver.open("dogs.db").prepare(sql) + + @Test + fun `the query is logged with its id and its arguments when it runs`() { + val prepared = prepare("INSERT INTO dog (name, age) VALUES (?, ?)") + prepared.bindText(1, "Rex") + prepared.bindLong(2, 4) + assertEquals(emptyList(), logs) + + prepared.step() + + assertEquals(listOf(Log("dogs", "INSERT INTO dog (name, age) VALUES (?, ?)", listOf("Rex", 4L))), logs) + } + + @Test + fun `the arguments are in the order of their index`() { + val prepared = prepare("SELECT * FROM dog WHERE age > ? AND name = ?") + prepared.bindText(2, "Rex") + prepared.bindNull(1) + + prepared.step() + + assertEquals(listOf(null, "Rex"), logs.single().bindArgs) + } + + @Test + fun `a query is logged once per run, not once per row`() { + val prepared = prepare("SELECT * FROM dog") + + prepared.step() + prepared.step() + prepared.step() + assertEquals(1, logs.size) + + prepared.reset() + prepared.step() + assertEquals(2, logs.size) + } + + @Test + fun `the statement still runs on the wrapped driver`() { + val prepared = prepare("SELECT 1") + prepared.bindLong(1, 1) + + prepared.step() + + assertEquals(listOf("bindLong 1", "step"), statement.calls) + } + + private class FakeStatement : SQLiteStatement { + val calls = mutableListOf() + override fun bindBlob(index: Int, value: ByteArray) { calls += "bindBlob $index" } + override fun bindDouble(index: Int, value: Double) { calls += "bindDouble $index" } + override fun bindLong(index: Int, value: Long) { calls += "bindLong $index" } + override fun bindText(index: Int, value: String) { calls += "bindText $index" } + override fun bindNull(index: Int) { calls += "bindNull $index" } + override fun getBlob(index: Int): ByteArray = ByteArray(0) + override fun getDouble(index: Int): Double = 0.0 + override fun getLong(index: Int): Long = 0 + override fun getText(index: Int): String = "" + override fun isNull(index: Int): Boolean = false + override fun getColumnCount(): Int = 0 + override fun getColumnName(index: Int): String = "" + override fun getColumnType(index: Int): Int = 0 + override fun step(): Boolean { calls += "step"; return false } + override fun reset() { calls += "reset" } + override fun clearBindings() { calls += "clearBindings" } + override fun close() = Unit + } +} diff --git a/FloconAndroid/database/room3/src/nonWebMain/kotlin/io/github/openflocon/flocon/database/room3/FloconLoggingSQLiteDriver.nonWeb.kt b/FloconAndroid/database/room3/src/nonWebMain/kotlin/io/github/openflocon/flocon/database/room3/FloconLoggingSQLiteDriver.nonWeb.kt new file mode 100644 index 000000000..7b1a8c31b --- /dev/null +++ b/FloconAndroid/database/room3/src/nonWebMain/kotlin/io/github/openflocon/flocon/database/room3/FloconLoggingSQLiteDriver.nonWeb.kt @@ -0,0 +1,85 @@ +package io.github.openflocon.flocon.database.room3 + +import androidx.sqlite.SQLiteConnection +import androidx.sqlite.SQLiteDriver +import androidx.sqlite.SQLiteStatement + +actual fun SQLiteDriver.floconLogs(id: String): SQLiteDriver = FloconLoggingSQLiteDriver(delegate = this, dbId = id, logger = DatabasePluginQueryLogger) + +internal class FloconLoggingSQLiteDriver( + private val delegate: SQLiteDriver, + private val dbId: String, + private val logger: QueryLogger, +) : SQLiteDriver { + + override val hasConnectionPool: Boolean + get() = delegate.hasConnectionPool + + override fun open(fileName: String): SQLiteConnection = FloconLoggingConnection(delegate.open(fileName), dbId, logger) +} + +private class FloconLoggingConnection( + private val delegate: SQLiteConnection, + private val dbId: String, + private val logger: QueryLogger, +) : SQLiteConnection by delegate { + + override fun prepare(sql: String): SQLiteStatement = FloconLoggingStatement(delegate.prepare(sql), dbId, sql, logger) +} + +/** + * Logs the query when it starts running: the first `step`, and the first one after each `reset`. + */ +private class FloconLoggingStatement( + private val delegate: SQLiteStatement, + private val dbId: String, + private val sql: String, + private val logger: QueryLogger, +) : SQLiteStatement by delegate { + + private val bindArgs = mutableMapOf() + private var logged = false + + override fun bindBlob(index: Int, value: ByteArray) { + bindArgs[index] = "[blob ${value.size} bytes]" + delegate.bindBlob(index, value) + } + + override fun bindDouble(index: Int, value: Double) { + bindArgs[index] = value + delegate.bindDouble(index, value) + } + + override fun bindLong(index: Int, value: Long) { + bindArgs[index] = value + delegate.bindLong(index, value) + } + + override fun bindText(index: Int, value: String) { + bindArgs[index] = value + delegate.bindText(index, value) + } + + override fun bindNull(index: Int) { + bindArgs[index] = null + delegate.bindNull(index) + } + + override fun step(): Boolean { + if (!logged) { + logged = true + logger.log(dbId = dbId, sql = sql, bindArgs = bindArgs.entries.sortedBy { it.key }.map { it.value }) + } + return delegate.step() + } + + override fun reset() { + logged = false + delegate.reset() + } + + override fun clearBindings() { + bindArgs.clear() + delegate.clearBindings() + } +} diff --git a/FloconAndroid/database/room3/src/roomMain/kotlin/io/github/openflocon/flocon/database/room3/FloconLogging.kt b/FloconAndroid/database/room3/src/roomMain/kotlin/io/github/openflocon/flocon/database/room3/FloconLogging.kt new file mode 100644 index 000000000..1a9bedccf --- /dev/null +++ b/FloconAndroid/database/room3/src/roomMain/kotlin/io/github/openflocon/flocon/database/room3/FloconLogging.kt @@ -0,0 +1,20 @@ +package io.github.openflocon.flocon.database.room3 + +import androidx.sqlite.SQLiteDriver + +/** + * Shows the queries run by a Room 3 database in the desktop app: Room 3 has no query callback, so the + * driver of the database is wrapped. + * + * ```kotlin + * Room.databaseBuilder(context, "dogs_database") + * .setDriver(BundledSQLiteDriver().floconLogs(id = "dogs")) + * .build() + * .also { Flocon.databasePlugin.register(id = "dogs", displayName = "Dogs", database = it) } + * ``` + * + * Meant for the drivers that run the queries themselves (bundled, framework, native), not for the web worker driver. + * + * @param id the id the database is registered with, see [register] + */ +expect fun SQLiteDriver.floconLogs(id: String): SQLiteDriver diff --git a/FloconAndroid/database/room3/src/roomMain/kotlin/io/github/openflocon/flocon/database/room3/QueryLogger.kt b/FloconAndroid/database/room3/src/roomMain/kotlin/io/github/openflocon/flocon/database/room3/QueryLogger.kt new file mode 100644 index 000000000..1da5ac7ac --- /dev/null +++ b/FloconAndroid/database/room3/src/roomMain/kotlin/io/github/openflocon/flocon/database/room3/QueryLogger.kt @@ -0,0 +1,14 @@ +package io.github.openflocon.flocon.database.room3 + +import io.github.openflocon.flocon.Flocon +import io.github.openflocon.flocon.database.core.databasePlugin + +internal fun interface QueryLogger { + fun log(dbId: String, sql: String, bindArgs: List) +} + +internal object DatabasePluginQueryLogger : QueryLogger { + override fun log(dbId: String, sql: String, bindArgs: List) { + Flocon.databasePlugin.logQuery(dbId = dbId, sqlQuery = sql, bindArgs = bindArgs) + } +} diff --git a/FloconAndroid/database/room3/src/wasmJsMain/kotlin/io/github/openflocon/flocon/database/room3/FloconLoggingSQLiteDriver.wasmJs.kt b/FloconAndroid/database/room3/src/wasmJsMain/kotlin/io/github/openflocon/flocon/database/room3/FloconLoggingSQLiteDriver.wasmJs.kt new file mode 100644 index 000000000..6a32b5804 --- /dev/null +++ b/FloconAndroid/database/room3/src/wasmJsMain/kotlin/io/github/openflocon/flocon/database/room3/FloconLoggingSQLiteDriver.wasmJs.kt @@ -0,0 +1,85 @@ +package io.github.openflocon.flocon.database.room3 + +import androidx.sqlite.SQLiteConnection +import androidx.sqlite.SQLiteDriver +import androidx.sqlite.SQLiteStatement + +actual fun SQLiteDriver.floconLogs(id: String): SQLiteDriver = FloconLoggingSQLiteDriver(delegate = this, dbId = id, logger = DatabasePluginQueryLogger) + +internal class FloconLoggingSQLiteDriver( + private val delegate: SQLiteDriver, + private val dbId: String, + private val logger: QueryLogger, +) : SQLiteDriver { + + override val hasConnectionPool: Boolean + get() = delegate.hasConnectionPool + + override suspend fun open(fileName: String): SQLiteConnection = FloconLoggingConnection(delegate.open(fileName), dbId, logger) +} + +private class FloconLoggingConnection( + private val delegate: SQLiteConnection, + private val dbId: String, + private val logger: QueryLogger, +) : SQLiteConnection by delegate { + + override suspend fun prepare(sql: String): SQLiteStatement = FloconLoggingStatement(delegate.prepare(sql), dbId, sql, logger) +} + +/** + * Logs the query when it starts running: the first `step`, and the first one after each `reset`. + */ +private class FloconLoggingStatement( + private val delegate: SQLiteStatement, + private val dbId: String, + private val sql: String, + private val logger: QueryLogger, +) : SQLiteStatement by delegate { + + private val bindArgs = mutableMapOf() + private var logged = false + + override fun bindBlob(index: Int, value: ByteArray) { + bindArgs[index] = "[blob ${value.size} bytes]" + delegate.bindBlob(index, value) + } + + override fun bindDouble(index: Int, value: Double) { + bindArgs[index] = value + delegate.bindDouble(index, value) + } + + override fun bindLong(index: Int, value: Long) { + bindArgs[index] = value + delegate.bindLong(index, value) + } + + override fun bindText(index: Int, value: String) { + bindArgs[index] = value + delegate.bindText(index, value) + } + + override fun bindNull(index: Int) { + bindArgs[index] = null + delegate.bindNull(index) + } + + override suspend fun step(): Boolean { + if (!logged) { + logged = true + logger.log(dbId = dbId, sql = sql, bindArgs = bindArgs.entries.sortedBy { it.key }.map { it.value }) + } + return delegate.step() + } + + override fun reset() { + logged = false + delegate.reset() + } + + override fun clearBindings() { + bindArgs.clear() + delegate.clearBindings() + } +} diff --git a/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/Databases.kt b/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/Databases.kt index 06b5572e8..5bed509d3 100644 --- a/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/Databases.kt +++ b/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/Databases.kt @@ -2,6 +2,8 @@ package io.github.openflocon.flocon.myapplication.multi import android.content.Context import androidx.room3.Room +import androidx.sqlite.driver.AndroidSQLiteDriver +import io.github.openflocon.flocon.database.room3.floconLogs import io.github.openflocon.flocon.Flocon import io.github.openflocon.flocon.database.core.databasePlugin import io.github.openflocon.flocon.database.room3.register @@ -19,6 +21,7 @@ object Databases { klass = DogDatabase::class.java, name = "dogs_database" ) + .setDriver(AndroidSQLiteDriver().floconLogs(id = "dogs_room3")) .fallbackToDestructiveMigration(dropAllTables = true) .build() dogDatabase = instance @@ -39,7 +42,9 @@ object Databases { context = context.applicationContext, klass = FoodDatabase::class.java, name = "food_database" - ).build() + ) + .setDriver(AndroidSQLiteDriver().floconLogs(id = "food_room3")) + .build() foodDatabase = instance Flocon.databasePlugin.register( id = "food_room3", @@ -58,7 +63,9 @@ object Databases { val instance = Room.inMemoryDatabaseBuilder( context.applicationContext, DogDatabase::class.java, - ).build() + ) + .setDriver(AndroidSQLiteDriver().floconLogs(id = "dogs_room3_memory")) + .build() inMemoryDogDatabase = instance Flocon.databasePlugin.register( id = "dogs_room3_memory", diff --git a/FloconAndroid/sample/desktop/src/jvmMain/kotlin/com/flocon/sample/desktop/Main.kt b/FloconAndroid/sample/desktop/src/jvmMain/kotlin/com/flocon/sample/desktop/Main.kt index 64ac13145..e36c7711d 100644 --- a/FloconAndroid/sample/desktop/src/jvmMain/kotlin/com/flocon/sample/desktop/Main.kt +++ b/FloconAndroid/sample/desktop/src/jvmMain/kotlin/com/flocon/sample/desktop/Main.kt @@ -4,6 +4,7 @@ import androidx.compose.ui.window.Window import androidx.compose.ui.window.application import androidx.room3.Room import androidx.sqlite.driver.bundled.BundledSQLiteDriver +import io.github.openflocon.flocon.database.room3.floconLogs import coil3.ImageLoader import coil3.PlatformContext import coil3.SingletonImageLoader @@ -108,7 +109,7 @@ private fun dogDatabase(): DogDatabase { val dbFile = File(System.getProperty("java.io.tmpdir"), "flocon_dogs_database.db") return Room.databaseBuilder(name = dbFile.absolutePath) .fallbackToDestructiveMigration(dropAllTables = true) - .setDriver(BundledSQLiteDriver()) + .setDriver(BundledSQLiteDriver().floconLogs(id = "dogs_jvm")) .setQueryCoroutineContext(Dispatchers.IO) .build() } @@ -117,7 +118,7 @@ private fun foodDatabase(): FoodDatabase { val dbFile = File(System.getProperty("java.io.tmpdir"), "flocon_food_database.db") return Room.databaseBuilder(name = dbFile.absolutePath) .fallbackToDestructiveMigration(dropAllTables = true) - .setDriver(BundledSQLiteDriver()) + .setDriver(BundledSQLiteDriver().floconLogs(id = "food_jvm")) .setQueryCoroutineContext(Dispatchers.IO) .build() } diff --git a/FloconAndroid/sample/ios/src/iosMain/kotlin/com/flocon/sample/ios/Databases.kt b/FloconAndroid/sample/ios/src/iosMain/kotlin/com/flocon/sample/ios/Databases.kt index 0284a87ac..2c5040525 100644 --- a/FloconAndroid/sample/ios/src/iosMain/kotlin/com/flocon/sample/ios/Databases.kt +++ b/FloconAndroid/sample/ios/src/iosMain/kotlin/com/flocon/sample/ios/Databases.kt @@ -2,6 +2,7 @@ package com.flocon.sample.ios import androidx.room3.Room import androidx.sqlite.driver.NativeSQLiteDriver +import io.github.openflocon.flocon.database.room3.floconLogs import io.github.openflocon.flocon.Flocon import io.github.openflocon.flocon.database.core.databasePlugin import io.github.openflocon.flocon.database.room3.register @@ -20,7 +21,7 @@ object Databases { return dogDatabase ?: run { val instance = Room.databaseBuilder(name = dbFile) .fallbackToDestructiveMigration(dropAllTables = true) - .setDriver(NativeSQLiteDriver()) + .setDriver(NativeSQLiteDriver().floconLogs(id = "dogs_ios")) .build() dogDatabase = instance Flocon.databasePlugin.register(id = "dogs_ios", displayName = "Dog Database", database = instance) @@ -34,7 +35,7 @@ object Databases { val dbFile = "${documentDirectory()}/food_database.db" return foodDatabase ?: run { val instance = Room.databaseBuilder(name = dbFile) - .setDriver(NativeSQLiteDriver()) + .setDriver(NativeSQLiteDriver().floconLogs(id = "food_ios")) .build() foodDatabase = instance Flocon.databasePlugin.register(id = "food_ios", displayName = "Food Database", database = instance) diff --git a/FloconAndroid/sample/wasm/src/wasmJsMain/kotlin/com/flocon/sample/wasm/Databases.kt b/FloconAndroid/sample/wasm/src/wasmJsMain/kotlin/com/flocon/sample/wasm/Databases.kt index 5675ddc15..11681727a 100644 --- a/FloconAndroid/sample/wasm/src/wasmJsMain/kotlin/com/flocon/sample/wasm/Databases.kt +++ b/FloconAndroid/sample/wasm/src/wasmJsMain/kotlin/com/flocon/sample/wasm/Databases.kt @@ -2,6 +2,7 @@ package com.flocon.sample.wasm import androidx.room3.Room import androidx.sqlite.driver.web.WebWorkerSQLiteDriver +import io.github.openflocon.flocon.database.room3.floconLogs import io.github.openflocon.flocon.Flocon import io.github.openflocon.flocon.database.core.databasePlugin import io.github.openflocon.flocon.database.room3.register @@ -22,14 +23,14 @@ object Databases { val dogDatabase: DogDatabase by lazy { Room.databaseBuilder(name = "dogs_database.db") - .setDriver(driver) + .setDriver(driver.floconLogs(id = "dogs_wasm")) .build() .also { Flocon.databasePlugin.register(id = "dogs_wasm", displayName = "dogs", database = it) } } val foodDatabase: FoodDatabase by lazy { Room.databaseBuilder(name = "food_database.db") - .setDriver(driver) + .setDriver(driver.floconLogs(id = "food_wasm")) .build() .also { Flocon.databasePlugin.register(id = "food_wasm", displayName = "food", database = it) } } diff --git a/docs2/database.md b/docs2/database.md index cf85c69a4..d9b9d1005 100644 --- a/docs2/database.md +++ b/docs2/database.md @@ -140,7 +140,19 @@ Flocon can display all SQL queries executed by your app in real-time — great f The `id` given to `floconLogs` must be the one the database is registered with, otherwise the desktop app cannot show the queries with the database. - Room 3 has no query callback: use the manual logging. +=== "Room 3" + + Room 3 has no query callback: wrap its SQLite driver instead. + + ```kotlin + Room.databaseBuilder(context, "dogs_database") + .setDriver(AndroidSQLiteDriver().floconLogs(id = "dogs")) // io.github.openflocon.flocon.database.room3.floconLogs + .build() + .also { Flocon.databasePlugin.register(id = "dogs", displayName = "dogs", database = it) } + ``` + + Wraps any driver (`BundledSQLiteDriver`, `NativeSQLiteDriver`, `WebWorkerSQLiteDriver`...), checked on Android with `AndroidSQLiteDriver`. Each statement + is logged when it runs, with its arguments. Same rule as Room 2: the `id` is the one the database is registered with. === "Manual Query Logging"