SQLiteOpenHelperWriter.kt revision c678b3a4eebc102a1a3b5923c5e07478c0eecae3
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 20d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyarimport com.android.support.room.ext.L 21d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyarimport com.android.support.room.ext.N 22c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyarimport com.android.support.room.ext.S 23d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyarimport com.android.support.room.ext.SupportDbTypeNames 24d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyarimport com.android.support.room.ext.T 25c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyarimport com.android.support.room.parser.SQLTypeAffinity 26d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyarimport com.android.support.room.solver.CodeGenScope 27d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyarimport com.android.support.room.vo.Database 28c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyarimport com.android.support.room.vo.Entity 29c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyarimport com.android.support.room.vo.Field 30d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyarimport com.squareup.javapoet.MethodSpec 31d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyarimport com.squareup.javapoet.ParameterSpec 32d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyarimport com.squareup.javapoet.TypeName 33d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyarimport com.squareup.javapoet.TypeSpec 34d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyarimport javax.lang.model.element.Modifier.PUBLIC 35d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar 36d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar/** 37d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar * Create an open helper using SupportSQLiteOpenHelperFactory 38d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar */ 39d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyarclass SQLiteOpenHelperWriter(val database : Database) { 40d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar fun write(outVar : String, configuration : ParameterSpec, scope: CodeGenScope) { 41d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar scope.builder().apply { 42d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar val sqliteConfigVar = scope.getTmpVar("_sqliteConfig") 43d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar val callbackVar = scope.getTmpVar("_openCallback") 44d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addStatement("final $T $L = $L", 45d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar SupportDbTypeNames.SQLITE_OPEN_HELPER_CALLBACK, 46d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar callbackVar, createOpenCallback()) 47d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar // build configuration 48d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addStatement( 49d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar """ 50d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar final $T $L = $T.builder($N.context) 51d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar .name($N.name) 52d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar .version($N.version) 53d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar .callback($L) 54d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar .build() 55d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar """.trimIndent(), 56d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar SupportDbTypeNames.SQLITE_OPEN_HELPER_CONFIG, sqliteConfigVar, 57d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar SupportDbTypeNames.SQLITE_OPEN_HELPER_CONFIG, 58d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar configuration, configuration, configuration, callbackVar) 59d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addStatement("final $T $N = $N.sqliteOpenHelperFactory.create($L)", 60d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar SupportDbTypeNames.SQLITE_OPEN_HELPER, outVar, 61d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar configuration, sqliteConfigVar) 62d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar } 63d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar } 64d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar 65d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar private fun createOpenCallback() : TypeSpec { 66d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar return TypeSpec.anonymousClassBuilder("").apply { 67d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar superclass(SupportDbTypeNames.SQLITE_OPEN_HELPER_CALLBACK) 68d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addMethod(createOnCreate()) 69d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addMethod(createOnUpgrade()) 70d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addMethod(createOnDowngrade()) 71d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar }.build() 72d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar } 73d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar 74d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar private fun createOnCreate() : MethodSpec { 75d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar return MethodSpec.methodBuilder("onCreate").apply { 76d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addModifiers(PUBLIC) 77d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addParameter(SupportDbTypeNames.DB, "_db") 78c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar // this is already called in transaction so no need for a transaction 79c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar database.entities.forEach { 80c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar addStatement("_db.execSQL($S)", createQuery(it)) 81c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar } 82d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar }.build() 83d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar } 84d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar 85d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar private fun createOnUpgrade() : MethodSpec { 86d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar return MethodSpec.methodBuilder("onUpgrade").apply { 87d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addModifiers(PUBLIC) 88d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addParameter(SupportDbTypeNames.DB, "_db") 89d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addParameter(TypeName.INT, "_oldVersion") 90d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addParameter(TypeName.INT, "_newVersion") 91c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar database.entities.forEach { 92c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar addStatement("_db.execSQL($S)", createDropTableQuery(it)) 93c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar addStatement("_db.execSQL($S)", createQuery(it)) 94c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar } 95d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar }.build() 96d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar } 97d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar 98d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar private fun createOnDowngrade() : MethodSpec { 99d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar return MethodSpec.methodBuilder("onDowngrade").apply { 100d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addModifiers(PUBLIC) 101d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addParameter(SupportDbTypeNames.DB, "_db") 102d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addParameter(TypeName.INT, "_oldVersion") 103d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addParameter(TypeName.INT, "_newVersion") 104c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar // TODO better handle this 105c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar addStatement("onUpgrade(_db, _oldVersion, _newVersion)") 106d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar }.build() 107d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar } 108c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar 109c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar private fun createDatabaseDefinition(field : Field) : String { 110c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar val affinity = field.let { 111c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar val adapter = it.getter.columnAdapter ?: it.setter.columnAdapter 112c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar adapter?.typeAffinity ?: SQLTypeAffinity.TEXT 113c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar } 114c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar return "`${field.columnName}` ${affinity.name}" 115c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar } 116c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar 117c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar @VisibleForTesting 118c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar fun createQuery(entity : Entity) : String { 119c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar val definitions = entity.fields.map { 120c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar field -> createDatabaseDefinition(field) 121c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar } + createPrimaryKeyDefinition(entity) 122c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar return "CREATE TABLE IF NOT EXISTS `${entity.tableName}` " + 123c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar "(${definitions.joinToString(", ")})" 124c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar } 125c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar 126c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar private fun createPrimaryKeyDefinition(entity: Entity): String { 127c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar val keys = entity.fields 128c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar .filter { it.primaryKey } 129c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar .map { "`${it.columnName}`" } 130c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar .joinToString(", ") 131c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar return "PRIMARY KEY($keys)" 132c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar } 133c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar 134c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar @VisibleForTesting 135c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar fun createDropTableQuery(entity: Entity) : String { 136c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar return "DROP TABLE IF EXISTS `${entity.tableName}`" 137c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar } 138d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar} 139