SQLiteOpenHelperWriter.kt revision fa3905934508aa143d899cb9b62b3b074748c9e9
1d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar/* 2d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar * Copyright (C) 2016 The Android Open Source Project 3d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar * 4d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar * Licensed under the Apache License, Version 2.0 (the "License"); 5d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar * you may not use this file except in compliance with the License. 6d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar * You may obtain a copy of the License at 7d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar * 8d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar * http://www.apache.org/licenses/LICENSE-2.0 9d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar * 10d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar * Unless required by applicable law or agreed to in writing, software 11d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar * distributed under the License is distributed on an "AS IS" BASIS, 12d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar * See the License for the specific language governing permissions and 14d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar * limitations under the License. 15d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar */ 16d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar 17d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyarpackage com.android.support.room.writer 18d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar 19c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyarimport android.support.annotation.VisibleForTesting 20fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyarimport com.android.support.room.ext.AndroidTypeNames 21d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyarimport com.android.support.room.ext.L 22d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyarimport com.android.support.room.ext.N 23fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyarimport com.android.support.room.ext.RoomTypeNames 24c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyarimport com.android.support.room.ext.S 25d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyarimport com.android.support.room.ext.SupportDbTypeNames 26d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyarimport com.android.support.room.ext.T 27fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyarimport com.android.support.room.ext.typeName 28d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyarimport com.android.support.room.solver.CodeGenScope 29d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyarimport com.android.support.room.vo.Database 30c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyarimport com.android.support.room.vo.Entity 31d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyarimport com.squareup.javapoet.MethodSpec 32d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyarimport com.squareup.javapoet.ParameterSpec 33d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyarimport com.squareup.javapoet.TypeName 34d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyarimport com.squareup.javapoet.TypeSpec 35d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyarimport javax.lang.model.element.Modifier.PUBLIC 36d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar 37d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar/** 38d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar * Create an open helper using SupportSQLiteOpenHelperFactory 39d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar */ 40d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyarclass SQLiteOpenHelperWriter(val database : Database) { 41fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar companion object { 42fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar // must match the runtime property Room#MASTER_TABLE_NAME 43fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar val MASTER_TABLE_NAME = "room_master_table" 44fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar val MASTER_TABLE_ID_COLUMN = "id" 45fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar val MASTER_TABLE_IDENTITY_HASH_COLUMN = "identity_hash" 46fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar val MASTER_TABLE_ID = 42 47fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar } 48d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar fun write(outVar : String, configuration : ParameterSpec, scope: CodeGenScope) { 49d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar scope.builder().apply { 50d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar val sqliteConfigVar = scope.getTmpVar("_sqliteConfig") 51d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar val callbackVar = scope.getTmpVar("_openCallback") 52d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addStatement("final $T $L = $L", 53d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar SupportDbTypeNames.SQLITE_OPEN_HELPER_CALLBACK, 54d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar callbackVar, createOpenCallback()) 55d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar // build configuration 56d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addStatement( 57d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar """ 58d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar final $T $L = $T.builder($N.context) 59d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar .name($N.name) 60d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar .version($N.version) 61d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar .callback($L) 62d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar .build() 63d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar """.trimIndent(), 64d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar SupportDbTypeNames.SQLITE_OPEN_HELPER_CONFIG, sqliteConfigVar, 65d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar SupportDbTypeNames.SQLITE_OPEN_HELPER_CONFIG, 66d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar configuration, configuration, configuration, callbackVar) 67d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addStatement("final $T $N = $N.sqliteOpenHelperFactory.create($L)", 68d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar SupportDbTypeNames.SQLITE_OPEN_HELPER, outVar, 69d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar configuration, sqliteConfigVar) 70d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar } 71d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar } 72d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar 73d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar private fun createOpenCallback() : TypeSpec { 74d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar return TypeSpec.anonymousClassBuilder("").apply { 75d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar superclass(SupportDbTypeNames.SQLITE_OPEN_HELPER_CALLBACK) 76d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addMethod(createOnCreate()) 77d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addMethod(createOnUpgrade()) 78d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addMethod(createOnDowngrade()) 7934e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar addMethod(createOnOpen()) 8034e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar }.build() 8134e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar } 8234e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar 8334e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar private fun createOnOpen(): MethodSpec { 8434e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar return MethodSpec.methodBuilder("onOpen").apply { 8534e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar addModifiers(PUBLIC) 8634e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar addParameter(SupportDbTypeNames.DB, "_db") 87fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar addStatement("String identityHash = \"\"") 88fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar addStatement("$T cursor = _db.rawQuery($S, $T.EMPTY_STRING_ARRAY)", 89fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar AndroidTypeNames.CURSOR, readIdentityHashQuery(), 90fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar RoomTypeNames.STRING_UTIL) 91fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar beginControlFlow("try").apply { 92fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar beginControlFlow("if (cursor.moveToFirst())").apply { 93fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar addStatement("identityHash = cursor.getString(0)") 94fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar } 95fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar endControlFlow() 96fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar } 97fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar nextControlFlow("finally").apply { 98fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar addStatement("cursor.close()") 99fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar } 100fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar endControlFlow() 101fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar beginControlFlow("if(!$S.equals(identityHash))", database.identityHash).apply { 102fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar addStatement("throw new $T($S)", IllegalStateException::class.typeName(), 103fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar "Room cannot verify the data integrity. Looks like you've changed schema" + 104fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar " but forgot to update the version number. You can simply fix" + 105fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar " this by increasing the version number.") 106fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar } 107fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar endControlFlow() 108fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar addStatement("mDatabase = _db") 10934e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar addStatement("internalInitInvalidationTracker(_db)") 110d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar }.build() 111d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar } 112d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar 113dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar private fun MethodSpec.Builder.writeCreateStatements() { 114fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar addStatement("_db.execSQL($S)", createMasterTableQuery()) 115fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar addStatement("_db.execSQL($S)", setIdentityHashQuery()) 116dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar // this is already called in transaction so no need for a transaction 117dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar database.entities.forEach { 118dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar addStatement("_db.execSQL($S)", createQuery(it)) 119dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 120dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar database.entities.forEach { 121dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar it.createIndexQueries.forEach { 122dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar addStatement("_db.execSQL($S)", it) 123dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 124dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 125dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 126dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar 127fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar private fun setIdentityHashQuery(): String { 128fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar return "INSERT OR REPLACE INTO $MASTER_TABLE_NAME VALUES($MASTER_TABLE_ID," + 129fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar "\"${database.identityHash}\")" 130fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar } 131fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar 132fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar private fun readIdentityHashQuery() : String { 133fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar return "SELECT $MASTER_TABLE_IDENTITY_HASH_COLUMN FROM $MASTER_TABLE_NAME WHERE" + 134fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar " $MASTER_TABLE_ID_COLUMN = $MASTER_TABLE_ID LIMIT 1" 135fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar } 136fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar 137fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar private fun createMasterTableQuery() : String { 138fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar return "CREATE TABLE IF NOT EXISTS `$MASTER_TABLE_NAME`(" + 139fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar "$MASTER_TABLE_ID_COLUMN INTEGER PRIMARY KEY," + 140fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar "$MASTER_TABLE_IDENTITY_HASH_COLUMN TEXT)" 141fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar } 142fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar 143fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar private fun dropMasterTableQuery() : String { 144fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar return "DROP TABLE IF EXISTS `$MASTER_TABLE_NAME`" 145fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar } 146fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar 147d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar private fun createOnCreate() : MethodSpec { 148d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar return MethodSpec.methodBuilder("onCreate").apply { 149d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addModifiers(PUBLIC) 150d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addParameter(SupportDbTypeNames.DB, "_db") 151dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar writeCreateStatements() 152d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar }.build() 153d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar } 154d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar 155d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar private fun createOnUpgrade() : MethodSpec { 156d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar return MethodSpec.methodBuilder("onUpgrade").apply { 157d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addModifiers(PUBLIC) 158d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addParameter(SupportDbTypeNames.DB, "_db") 159d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addParameter(TypeName.INT, "_oldVersion") 160d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addParameter(TypeName.INT, "_newVersion") 161c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar database.entities.forEach { 162c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar addStatement("_db.execSQL($S)", createDropTableQuery(it)) 163c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar } 164dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar writeCreateStatements() 165d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar }.build() 166d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar } 167d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar 168d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar private fun createOnDowngrade() : MethodSpec { 169d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar return MethodSpec.methodBuilder("onDowngrade").apply { 170d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addModifiers(PUBLIC) 171d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addParameter(SupportDbTypeNames.DB, "_db") 172d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addParameter(TypeName.INT, "_oldVersion") 173d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addParameter(TypeName.INT, "_newVersion") 174c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar // TODO better handle this 175c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar addStatement("onUpgrade(_db, _oldVersion, _newVersion)") 176d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar }.build() 177d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar } 178c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar 17988865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar 180c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar 181c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar @VisibleForTesting 182c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar fun createQuery(entity : Entity) : String { 18388865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar return entity.createTableQuery 184c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar } 185c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar 186c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar @VisibleForTesting 187c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar fun createDropTableQuery(entity: Entity) : String { 188c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar return "DROP TABLE IF EXISTS `${entity.tableName}`" 189c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar } 190d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar} 191