SQLiteOpenHelperWriter.kt revision 34e5031083f735db3a395b0f6aa430880b072d71
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())
7134e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar            addMethod(createOnOpen())
7234e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        }.build()
7334e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    }
7434e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
7534e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    private fun createOnOpen(): MethodSpec {
7634e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        return MethodSpec.methodBuilder("onOpen").apply {
7734e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar            addModifiers(PUBLIC)
7834e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar            addParameter(SupportDbTypeNames.DB, "_db")
7934e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar            addStatement("internalInitInvalidationTracker(_db)")
80d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar        }.build()
81d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar    }
82d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar
83d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar    private fun createOnCreate() : MethodSpec {
84d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar        return MethodSpec.methodBuilder("onCreate").apply {
85d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar            addModifiers(PUBLIC)
86d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar            addParameter(SupportDbTypeNames.DB, "_db")
87c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar            // this is already called in transaction so no need for a transaction
88c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar            database.entities.forEach {
89c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar                addStatement("_db.execSQL($S)", createQuery(it))
90c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar            }
91d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar        }.build()
92d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar    }
93d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar
94d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar    private fun createOnUpgrade() : MethodSpec {
95d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar        return MethodSpec.methodBuilder("onUpgrade").apply {
96d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar            addModifiers(PUBLIC)
97d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar            addParameter(SupportDbTypeNames.DB, "_db")
98d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar            addParameter(TypeName.INT, "_oldVersion")
99d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar            addParameter(TypeName.INT, "_newVersion")
100c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar            database.entities.forEach {
101c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar                addStatement("_db.execSQL($S)", createDropTableQuery(it))
102c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar                addStatement("_db.execSQL($S)", createQuery(it))
103c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar            }
104d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar        }.build()
105d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar    }
106d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar
107d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar    private fun createOnDowngrade() : MethodSpec {
108d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar        return MethodSpec.methodBuilder("onDowngrade").apply {
109d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar            addModifiers(PUBLIC)
110d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar            addParameter(SupportDbTypeNames.DB, "_db")
111d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar            addParameter(TypeName.INT, "_oldVersion")
112d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar            addParameter(TypeName.INT, "_newVersion")
113c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar            // TODO better handle this
114c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar            addStatement("onUpgrade(_db, _oldVersion, _newVersion)")
115d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar        }.build()
116d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar    }
117c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar
118c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar    private fun createDatabaseDefinition(field : Field) : String {
119c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar        val affinity = field.let {
120c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar            val adapter = it.getter.columnAdapter ?: it.setter.columnAdapter
121c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar            adapter?.typeAffinity ?: SQLTypeAffinity.TEXT
122c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar        }
123c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar        return "`${field.columnName}` ${affinity.name}"
124c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar    }
125c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar
126c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar    @VisibleForTesting
127c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar    fun createQuery(entity : Entity) : String {
128c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar        val definitions = entity.fields.map {
129c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar            field -> createDatabaseDefinition(field)
130c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar        } + createPrimaryKeyDefinition(entity)
131c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar        return "CREATE TABLE IF NOT EXISTS `${entity.tableName}` " +
132c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar                "(${definitions.joinToString(", ")})"
133c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar    }
134c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar
135c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar    private fun createPrimaryKeyDefinition(entity: Entity): String {
136c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar        val keys = entity.fields
137c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar                .filter { it.primaryKey }
138c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar                .map { "`${it.columnName}`" }
139c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar                .joinToString(", ")
140c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar        return "PRIMARY KEY($keys)"
141c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar    }
142c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar
143c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar    @VisibleForTesting
144c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar    fun createDropTableQuery(entity: Entity) : String {
145c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar        return "DROP TABLE IF EXISTS `${entity.tableName}`"
146c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar    }
147d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar}
148