[go: nahoru, domu]

blob: d453f00725d78c547901d4d27e01c920b4d53667 [file] [log] [blame]
Elif Bilgin63ffe602021-02-19 10:46:51 -08001/*
2 * Copyright 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package androidx.room.writer
18
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -070019import androidx.room.compiler.codegen.CodeLanguage
20import androidx.room.compiler.codegen.VisibilityModifier
21import androidx.room.compiler.codegen.XCodeBlock
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -070022import androidx.room.compiler.codegen.XFunSpec
23import androidx.room.compiler.codegen.XFunSpec.Builder.Companion.addStatement
24import androidx.room.compiler.codegen.XTypeSpec
Daniel Santiago Rivera885c9eb2022-09-29 21:49:35 -040025import androidx.room.compiler.codegen.XTypeSpec.Builder.Companion.addOriginatingElement
Daniel Santiago Rivera8b793f82022-09-28 13:23:51 -040026import androidx.room.compiler.codegen.XTypeSpec.Builder.Companion.addProperty
Daniel Santiago Rivera051c4a5e2021-09-02 10:21:25 -070027import androidx.room.compiler.processing.XTypeElement
Elif Bilgin63ffe602021-02-19 10:46:51 -080028import androidx.room.ext.RoomTypeNames
Elif Bilgin63ffe602021-02-19 10:46:51 -080029import androidx.room.ext.SupportDbTypeNames
Elif Bilgin26e3f9d2021-03-29 05:57:30 -070030import androidx.room.migration.bundle.EntityBundle
Elif Bilgin7a55b712021-04-06 09:24:33 -070031import androidx.room.migration.bundle.FtsEntityBundle
Elif Bilgin51f4b352021-04-20 16:02:55 -070032import androidx.room.vo.AutoMigration
Elif Bilgin63ffe602021-02-19 10:46:51 -080033
34/**
35 * Writes the implementation of migrations that were annotated with @AutoMigration.
36 */
37class AutoMigrationWriter(
Daniel Santiago Rivera051c4a5e2021-09-02 10:21:25 -070038 private val dbElement: XTypeElement,
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -070039 val autoMigration: AutoMigration,
Daniel Santiago Riverafe98c702022-09-22 22:58:50 -040040 codeLanguage: CodeLanguage
41) : TypeWriter(codeLanguage) {
Elif Bilgin51f4b352021-04-20 16:02:55 -070042 private val addedColumns = autoMigration.schemaDiff.addedColumns
43 private val addedTables = autoMigration.schemaDiff.addedTables
44 private val renamedTables = autoMigration.schemaDiff.renamedTables
45 private val complexChangedTables = autoMigration.schemaDiff.complexChangedTables
46 private val deletedTables = autoMigration.schemaDiff.deletedTables
Elif Bilgin63ffe602021-02-19 10:46:51 -080047
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -070048 override fun createTypeSpecBuilder(): XTypeSpec.Builder {
49 val builder = XTypeSpec.classBuilder(
50 codeLanguage,
Daniel Santiago Rivera587c02192022-09-17 12:55:48 -070051 autoMigration.getImplTypeName(dbElement.asClassName())
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -070052 )
Elif Bilgin63ffe602021-02-19 10:46:51 -080053 builder.apply {
54 addOriginatingElement(dbElement)
Daniel Santiago Rivera7e7bb0b2022-11-07 10:54:49 -050055 superclass(RoomTypeNames.MIGRATION)
Daniel Santiago Rivera63239ab2022-12-21 16:36:29 -050056 // Class is package-protected in Java (no visibility modifier) and internal in Kotlin
57 if (language == CodeLanguage.KOTLIN) {
58 setVisibility(VisibilityModifier.INTERNAL)
59 }
Elif Bilgin51f4b352021-04-20 16:02:55 -070060 if (autoMigration.specClassName != null) {
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -070061 builder.addProperty(
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -070062 name = "callback",
Daniel Santiago Rivera7e7bb0b2022-11-07 10:54:49 -050063 typeName = RoomTypeNames.AUTO_MIGRATION_SPEC,
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -070064 visibility = VisibilityModifier.PRIVATE,
65 initExpr = if (!autoMigration.isSpecProvided) {
Daniel Santiago Rivera9b412152022-09-23 22:26:42 -040066 XCodeBlock.ofNewInstance(
67 codeLanguage,
Daniel Santiago Rivera7e7bb0b2022-11-07 10:54:49 -050068 autoMigration.specClassName
Daniel Santiago Rivera9b412152022-09-23 22:26:42 -040069 )
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -070070 } else {
71 null
Elif Bilgine6f17b42021-04-20 15:32:25 -070072 }
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -070073 )
Elif Bilginc6db95e2021-04-07 11:57:50 -070074 }
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -070075 addFunction(createConstructor())
76 addFunction(createMigrateMethod())
Elif Bilgin63ffe602021-02-19 10:46:51 -080077 }
78 return builder
79 }
80
Elif Bilginfba75de2021-03-09 04:44:10 -080081 /**
82 * Builds the constructor of the generated AutoMigration.
83 *
84 * @return The constructor of the generated AutoMigration
85 */
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -070086 private fun createConstructor(): XFunSpec {
87 return XFunSpec.constructorBuilder(codeLanguage, VisibilityModifier.PUBLIC).apply {
88 callSuperConstructor(
89 XCodeBlock.of(codeLanguage, "%L", autoMigration.from),
90 XCodeBlock.of(codeLanguage, "%L", autoMigration.to),
Elif Bilgin51f4b352021-04-20 16:02:55 -070091 )
92 if (autoMigration.isSpecProvided) {
Elif Bilgine6f17b42021-04-20 15:32:25 -070093 addParameter(
Daniel Santiago Rivera7e7bb0b2022-11-07 10:54:49 -050094 typeName = RoomTypeNames.AUTO_MIGRATION_SPEC,
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -070095 name = "callback",
Elif Bilgine6f17b42021-04-20 15:32:25 -070096 )
97 addStatement("this.callback = callback")
98 }
Elif Bilginfba75de2021-03-09 04:44:10 -080099 }.build()
100 }
101
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -0700102 private fun createMigrateMethod(): XFunSpec {
103 val migrateFunctionBuilder: XFunSpec.Builder = XFunSpec.builder(
104 language = codeLanguage,
105 name = "migrate",
106 visibility = VisibilityModifier.PUBLIC,
Daniel Santiago Rivera76bf2562022-10-16 12:31:34 -0400107 isOverride = true,
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -0700108 ).apply {
Daniel Santiago Rivera7e7bb0b2022-11-07 10:54:49 -0500109 addParameter(
110 typeName = SupportDbTypeNames.DB,
111 name = "database",
112 )
113 addMigrationStatements(this)
114 if (autoMigration.specClassName != null) {
115 addStatement("callback.onPostMigrate(database)")
Elif Bilgin63ffe602021-02-19 10:46:51 -0800116 }
Daniel Santiago Rivera7e7bb0b2022-11-07 10:54:49 -0500117 }
Elif Bilgin63ffe602021-02-19 10:46:51 -0800118 return migrateFunctionBuilder.build()
119 }
120
121 /**
122 * Takes the changes provided in the {@link AutoMigrationResult} which are differences detected
123 * between the two versions of the same database, and converts them to the appropriate
124 * sequence of SQL statements that migrate the database from one version to the other.
125 *
Elif Bilginfba75de2021-03-09 04:44:10 -0800126 * @param migrateBuilder Builder for the migrate() function to be generated
Elif Bilgin63ffe602021-02-19 10:46:51 -0800127 */
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -0700128 private fun addMigrationStatements(migrateBuilder: XFunSpec.Builder) {
Elif Bilgin51f4b352021-04-20 16:02:55 -0700129 addDropViewStatements(migrateBuilder)
Elif Bilginfba75de2021-03-09 04:44:10 -0800130 addSimpleChangeStatements(migrateBuilder)
Elif Bilgin7a55b712021-04-06 09:24:33 -0700131 addComplexChangeStatements(migrateBuilder)
Elif Bilgin51f4b352021-04-20 16:02:55 -0700132 addRecreateViewStatements(migrateBuilder)
133 }
134
135 /**
136 * Adds SQL statements to drop all views of the database in the 'from' version.
137 *
138 * @param migrateBuilder Builder for the migrate() function to be generated
139 */
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -0700140 private fun addDropViewStatements(migrateBuilder: XFunSpec.Builder) {
Elif Bilgin51f4b352021-04-20 16:02:55 -0700141 autoMigration.schemaDiff.fromViews.forEach { view ->
142 addDatabaseExecuteSqlStatement(migrateBuilder, "DROP VIEW ${view.viewName}")
143 }
144 }
145
146 /**
147 * Adds SQL statements to create all views of the database in the 'to' version.
148 *
149 * @param migrateBuilder Builder for the migrate() function to be generated
150 */
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -0700151 private fun addRecreateViewStatements(migrateBuilder: XFunSpec.Builder) {
Elif Bilgin51f4b352021-04-20 16:02:55 -0700152 autoMigration.schemaDiff.toViews.forEach { view ->
153 addDatabaseExecuteSqlStatement(migrateBuilder, view.createView())
154 }
Elif Bilginfba75de2021-03-09 04:44:10 -0800155 }
156
157 /**
158 * Adds SQL statements performing schema altering commands that are not directly supported by
159 * SQLite (e.g. foreign key changes). These changes are referred to as "complex" changes.
160 *
161 * @param migrateBuilder Builder for the migrate() function to be generated
162 */
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -0700163 private fun addComplexChangeStatements(migrateBuilder: XFunSpec.Builder) {
Elif Bilgin7a55b712021-04-06 09:24:33 -0700164 // Create a collection that is sorted such that FTS bundles are handled after the normal
165 // tables have been processed
166 complexChangedTables.values.sortedBy {
167 it.newVersionEntityBundle is FtsEntityBundle
168 }.forEach {
Elif Bilgin26e3f9d2021-03-29 05:57:30 -0700169 (
170 _,
171 tableNameWithNewPrefix,
172 oldEntityBundle,
173 newEntityBundle,
174 renamedColumnsMap
175 ) ->
176
Elif Bilgin7a55b712021-04-06 09:24:33 -0700177 if (oldEntityBundle is FtsEntityBundle &&
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -0700178 oldEntityBundle.ftsOptions.contentTable.isNotBlank()
Elif Bilgin7a55b712021-04-06 09:24:33 -0700179 ) {
180 addStatementsToMigrateFtsTable(
181 migrateBuilder,
182 oldEntityBundle,
183 newEntityBundle,
184 renamedColumnsMap
185 )
186 } else {
187 addStatementsToCreateNewTable(newEntityBundle, migrateBuilder)
188 addStatementsToContentTransfer(
189 oldEntityBundle.tableName,
190 tableNameWithNewPrefix,
191 oldEntityBundle,
192 newEntityBundle,
193 renamedColumnsMap,
194 migrateBuilder
195 )
196 addStatementsToDropTableAndRenameTempTable(
197 oldEntityBundle.tableName,
198 newEntityBundle.tableName,
199 tableNameWithNewPrefix,
200 migrateBuilder
201 )
202 addStatementsToRecreateIndexes(newEntityBundle, migrateBuilder)
203 if (newEntityBundle.foreignKeys.isNotEmpty()) {
204 addStatementsToCheckForeignKeyConstraint(
205 newEntityBundle.tableName,
206 migrateBuilder
207 )
208 }
Elif Bilgin26e3f9d2021-03-29 05:57:30 -0700209 }
Elif Bilgin63ffe602021-02-19 10:46:51 -0800210 }
211 }
212
Elif Bilgin7a55b712021-04-06 09:24:33 -0700213 private fun addStatementsToMigrateFtsTable(
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -0700214 migrateBuilder: XFunSpec.Builder,
Elif Bilgin7a55b712021-04-06 09:24:33 -0700215 oldTable: EntityBundle,
216 newTable: EntityBundle,
217 renamedColumnsMap: MutableMap<String, String>
218 ) {
219 addDatabaseExecuteSqlStatement(migrateBuilder, "DROP TABLE `${oldTable.tableName}`")
220 addDatabaseExecuteSqlStatement(migrateBuilder, newTable.createTable())
221
222 // Transfer contents of the FTS table, using the content table if available.
223 val newColumnSequence = oldTable.fieldsByColumnName.keys.filter {
224 oldTable.fieldsByColumnName.keys.contains(it) ||
225 renamedColumnsMap.containsKey(it)
226 }.toMutableList()
227 val oldColumnSequence = mutableListOf<String>()
228 newColumnSequence.forEach { column ->
229 oldColumnSequence.add(renamedColumnsMap[column] ?: column)
230 }
231 if (oldTable is FtsEntityBundle) {
232 oldColumnSequence.add("rowid")
233 newColumnSequence.add("docid")
234 }
235 val contentTable = (newTable as FtsEntityBundle).ftsOptions.contentTable
236 val selectFromTable = if (contentTable.isEmpty()) {
237 oldTable.tableName
238 } else {
239 contentTable
240 }
241 addDatabaseExecuteSqlStatement(
242 migrateBuilder,
243 buildString {
244 append(
Elif Bilgin10279982021-10-26 16:59:42 -0700245 "INSERT INTO `${newTable.tableName}` " +
246 "(${newColumnSequence.joinToString(",") { "`$it`" }})" +
247 " SELECT ${oldColumnSequence.joinToString(",") { "`$it`" }} " +
248 "FROM `$selectFromTable`",
Elif Bilgin7a55b712021-04-06 09:24:33 -0700249 )
250 }
251 )
252 }
253
Elif Bilgin63ffe602021-02-19 10:46:51 -0800254 /**
Elif Bilginfba75de2021-03-09 04:44:10 -0800255 * Adds SQL statements performing schema altering commands directly supported by SQLite
256 * (adding tables/columns, renaming tables/columns, dropping tables/columns). These changes
257 * are referred to as "simple" changes.
Elif Bilgin63ffe602021-02-19 10:46:51 -0800258 *
Elif Bilginfba75de2021-03-09 04:44:10 -0800259 * @param migrateBuilder Builder for the migrate() function to be generated
Elif Bilgin63ffe602021-02-19 10:46:51 -0800260 */
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -0700261 private fun addSimpleChangeStatements(migrateBuilder: XFunSpec.Builder) {
Elif Bilgin26e3f9d2021-03-29 05:57:30 -0700262 addDeleteTableStatements(migrateBuilder)
263 addRenameTableStatements(migrateBuilder)
264 addNewColumnStatements(migrateBuilder)
265 addNewTableStatements(migrateBuilder)
Elif Bilginfba75de2021-03-09 04:44:10 -0800266 }
267
268 /**
269 * Adds the SQL statements for creating a new table in the desired revised format of table.
270 *
Elif Bilgin26e3f9d2021-03-29 05:57:30 -0700271 * @param newTable Schema of the new table to be created
Elif Bilginfba75de2021-03-09 04:44:10 -0800272 * @param migrateBuilder Builder for the migrate() function to be generated
273 */
274 private fun addStatementsToCreateNewTable(
Elif Bilgin26e3f9d2021-03-29 05:57:30 -0700275 newTable: EntityBundle,
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -0700276 migrateBuilder: XFunSpec.Builder
Elif Bilginfba75de2021-03-09 04:44:10 -0800277 ) {
278 addDatabaseExecuteSqlStatement(
279 migrateBuilder,
Elif Bilgin26e3f9d2021-03-29 05:57:30 -0700280 newTable.createNewTable()
Elif Bilginfba75de2021-03-09 04:44:10 -0800281 )
282 }
283
284 /**
285 * Adds the SQL statements for transferring the contents of the old table to the new version.
286 *
Elif Bilgin26e3f9d2021-03-29 05:57:30 -0700287 * @param oldTableName Name of the table in the old version of the database
288 * @param tableNameWithNewPrefix Name of the table with the '_new_' prefix added
289 * @param oldEntityBundle Entity bundle of the table in the old version of the database
290 * @param newEntityBundle Entity bundle of the table in the new version of the database
291 * @param renamedColumnsMap Map of the renamed columns of the table (new name -> old name)
Elif Bilginfba75de2021-03-09 04:44:10 -0800292 * @param migrateBuilder Builder for the migrate() function to be generated
293 */
294 private fun addStatementsToContentTransfer(
Elif Bilgin26e3f9d2021-03-29 05:57:30 -0700295 oldTableName: String,
296 tableNameWithNewPrefix: String,
297 oldEntityBundle: EntityBundle,
298 newEntityBundle: EntityBundle,
299 renamedColumnsMap: MutableMap<String, String>,
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -0700300 migrateBuilder: XFunSpec.Builder
Elif Bilginfba75de2021-03-09 04:44:10 -0800301 ) {
Elif Bilgin26e3f9d2021-03-29 05:57:30 -0700302 val newColumnSequence = newEntityBundle.fieldsByColumnName.keys.filter {
303 oldEntityBundle.fieldsByColumnName.keys.contains(it) ||
304 renamedColumnsMap.containsKey(it)
Elif Bilgin7a55b712021-04-06 09:24:33 -0700305 }.toMutableList()
Elif Bilgin26e3f9d2021-03-29 05:57:30 -0700306 val oldColumnSequence = mutableListOf<String>()
307 newColumnSequence.forEach { column ->
308 oldColumnSequence.add(renamedColumnsMap[column] ?: column)
309 }
Elif Bilginfba75de2021-03-09 04:44:10 -0800310
311 addDatabaseExecuteSqlStatement(
312 migrateBuilder,
313 buildString {
Elif Bilgin63ffe602021-02-19 10:46:51 -0800314 append(
Elif Bilgin7a55b712021-04-06 09:24:33 -0700315 "INSERT INTO `$tableNameWithNewPrefix` " +
Elif Bilgin10279982021-10-26 16:59:42 -0700316 "(${newColumnSequence.joinToString(",") { "`$it`" }})" +
317 " SELECT ${oldColumnSequence.joinToString(",") { "`$it`" }} FROM " +
Elif Bilgin7a55b712021-04-06 09:24:33 -0700318 "`$oldTableName`",
Elif Bilgin63ffe602021-02-19 10:46:51 -0800319 )
Elif Bilgin63ffe602021-02-19 10:46:51 -0800320 }
Elif Bilginfba75de2021-03-09 04:44:10 -0800321 )
Elif Bilgin63ffe602021-02-19 10:46:51 -0800322 }
323
324 /**
Elif Bilginfba75de2021-03-09 04:44:10 -0800325 * Adds the SQL statements for dropping the table at the old version and renaming the
326 * temporary table to the name of the original table.
Elif Bilginf2f87f52021-02-24 09:12:24 -0800327 *
Elif Bilgin26e3f9d2021-03-29 05:57:30 -0700328 * @param oldTableName Name of the table in the old version of the database
329 * @param newTableName Name of the table in the new version of the database
330 * @param tableNameWithNewPrefix Name of the table with the '_new_' prefix added
Elif Bilginfba75de2021-03-09 04:44:10 -0800331 * @param migrateBuilder Builder for the migrate() function to be generated
Elif Bilginf2f87f52021-02-24 09:12:24 -0800332 */
Elif Bilginfba75de2021-03-09 04:44:10 -0800333 private fun addStatementsToDropTableAndRenameTempTable(
Elif Bilgin26e3f9d2021-03-29 05:57:30 -0700334 oldTableName: String,
335 newTableName: String,
336 tableNameWithNewPrefix: String,
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -0700337 migrateBuilder: XFunSpec.Builder
Elif Bilginfba75de2021-03-09 04:44:10 -0800338 ) {
339 addDatabaseExecuteSqlStatement(
340 migrateBuilder,
Elif Bilgin26e3f9d2021-03-29 05:57:30 -0700341 "DROP TABLE `$oldTableName`"
Elif Bilginfba75de2021-03-09 04:44:10 -0800342 )
343 addDatabaseExecuteSqlStatement(
344 migrateBuilder,
Elif Bilgin26e3f9d2021-03-29 05:57:30 -0700345 "ALTER TABLE `$tableNameWithNewPrefix` RENAME TO `$newTableName`"
Elif Bilginfba75de2021-03-09 04:44:10 -0800346 )
347 }
348
349 /**
350 * Adds the SQL statements for recreating indexes.
351 *
Elif Bilgin26e3f9d2021-03-29 05:57:30 -0700352 * @param table The table the indexes of which will be recreated
Elif Bilginfba75de2021-03-09 04:44:10 -0800353 * @param migrateBuilder Builder for the migrate() function to be generated
354 */
355 private fun addStatementsToRecreateIndexes(
Elif Bilgin26e3f9d2021-03-29 05:57:30 -0700356 table: EntityBundle,
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -0700357 migrateBuilder: XFunSpec.Builder
Elif Bilginfba75de2021-03-09 04:44:10 -0800358 ) {
Elif Bilgin26e3f9d2021-03-29 05:57:30 -0700359 table.indices.forEach { index ->
Elif Bilginfba75de2021-03-09 04:44:10 -0800360 addDatabaseExecuteSqlStatement(
361 migrateBuilder,
362 index.getCreateSql(table.tableName)
Elif Bilginf2f87f52021-02-24 09:12:24 -0800363 )
364 }
365 }
366
367 /**
Elif Bilginfba75de2021-03-09 04:44:10 -0800368 * Adds the SQL statement for checking the foreign key constraints.
Elif Bilgin63ffe602021-02-19 10:46:51 -0800369 *
Elif Bilgin26e3f9d2021-03-29 05:57:30 -0700370 * @param tableName Name of the table
Elif Bilginfba75de2021-03-09 04:44:10 -0800371 * @param migrateBuilder Builder for the migrate() function to be generated
Elif Bilgin63ffe602021-02-19 10:46:51 -0800372 */
Elif Bilginfba75de2021-03-09 04:44:10 -0800373 private fun addStatementsToCheckForeignKeyConstraint(
Elif Bilgin26e3f9d2021-03-29 05:57:30 -0700374 tableName: String,
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -0700375 migrateBuilder: XFunSpec.Builder
Elif Bilginfba75de2021-03-09 04:44:10 -0800376 ) {
Elif Bilgin7917c782021-06-07 13:58:31 -0700377 migrateBuilder.addStatement(
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -0700378 "%T.foreignKeyCheck(database, %S)",
Elif Bilgin7917c782021-06-07 13:58:31 -0700379 RoomTypeNames.DB_UTIL,
380 tableName
Elif Bilginfba75de2021-03-09 04:44:10 -0800381 )
382 }
383
384 /**
Elif Bilgin26e3f9d2021-03-29 05:57:30 -0700385 * Adds the SQL statements for removing a table.
386 *
387 * @param migrateBuilder Builder for the migrate() function to be generated
388 */
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -0700389 private fun addDeleteTableStatements(migrateBuilder: XFunSpec.Builder) {
Elif Bilgin26e3f9d2021-03-29 05:57:30 -0700390 deletedTables.forEach { tableName ->
391 val deleteTableSql = buildString {
392 append(
393 "DROP TABLE `$tableName`"
394 )
395 }
396 addDatabaseExecuteSqlStatement(
397 migrateBuilder,
398 deleteTableSql
399 )
400 }
401 }
402
403 /**
404 * Adds the SQL statements for renaming a table.
405 *
406 * @param migrateBuilder Builder for the migrate() function to be generated
407 */
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -0700408 private fun addRenameTableStatements(migrateBuilder: XFunSpec.Builder) {
Elif Bilgin26e3f9d2021-03-29 05:57:30 -0700409 renamedTables.forEach { (oldName, newName) ->
410 val renameTableSql = buildString {
411 append(
412 "ALTER TABLE `$oldName` RENAME TO `$newName`"
413 )
414 }
415 addDatabaseExecuteSqlStatement(
416 migrateBuilder,
417 renameTableSql
418 )
419 }
420 }
421
422 /**
Elif Bilginfba75de2021-03-09 04:44:10 -0800423 * Adds the SQL statements for adding new columns to a table.
424 *
425 * @param migrateBuilder Builder for the migrate() function to be generated
426 */
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -0700427 private fun addNewColumnStatements(migrateBuilder: XFunSpec.Builder) {
Elif Bilginfba75de2021-03-09 04:44:10 -0800428 addedColumns.forEach {
429 val addNewColumnSql = buildString {
430 append(
Daniel Santiago Riverabfa17ff2021-09-23 12:10:59 -0700431 "ALTER TABLE `${it.tableName}` ADD COLUMN `${it.fieldBundle.columnName}` " +
Elif Bilgin87c1e432021-07-19 10:05:33 -0700432 "${it.fieldBundle.affinity}"
Elif Bilginfba75de2021-03-09 04:44:10 -0800433 )
Daniel Santiago Riverabfa17ff2021-09-23 12:10:59 -0700434 if (it.fieldBundle.isNonNull) {
Elif Bilgin87c1e432021-07-19 10:05:33 -0700435 append(" NOT NULL")
436 }
437 if (it.fieldBundle.defaultValue?.isNotEmpty() == true) {
438 append(" DEFAULT ${it.fieldBundle.defaultValue}")
Elif Bilginfba75de2021-03-09 04:44:10 -0800439 } else {
Elif Bilgin87c1e432021-07-19 10:05:33 -0700440 check(
441 !it.fieldBundle.isNonNull
442 ) { "A Non-Null field should always have a default value." }
443 append(" DEFAULT NULL")
Elif Bilginfba75de2021-03-09 04:44:10 -0800444 }
445 }
446 addDatabaseExecuteSqlStatement(
447 migrateBuilder,
448 addNewColumnSql
449 )
450 }
451 }
452
453 /**
454 * Adds the SQL statements for adding new tables to a database.
455 *
456 * @param migrateBuilder Builder for the migrate() function to be generated
457 */
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -0700458 private fun addNewTableStatements(migrateBuilder: XFunSpec.Builder) {
Elif Bilginfba75de2021-03-09 04:44:10 -0800459 addedTables.forEach { addedTable ->
460 addDatabaseExecuteSqlStatement(
461 migrateBuilder,
462 addedTable.entityBundle.createTable()
463 )
Elif Bilginbc4d0b02021-06-06 12:02:05 -0700464 addStatementsToRecreateIndexes(addedTable.entityBundle, migrateBuilder)
Elif Bilginfba75de2021-03-09 04:44:10 -0800465 }
466 }
467
468 /**
469 * Adds the given SQL statements into the generated migrate() function to be executed by the
470 * database.
471 *
472 * @param migrateBuilder Builder for the migrate() function to be generated
Elif Bilgin26e3f9d2021-03-29 05:57:30 -0700473 * @param sql The SQL statement to be executed by the database
Elif Bilginfba75de2021-03-09 04:44:10 -0800474 */
475 private fun addDatabaseExecuteSqlStatement(
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -0700476 migrateBuilder: XFunSpec.Builder,
Elif Bilginfba75de2021-03-09 04:44:10 -0800477 sql: String
478 ) {
479 migrateBuilder.addStatement(
Daniel Santiago Riverab604ea32022-08-10 10:06:19 -0700480 "database.execSQL(%S)",
Elif Bilgin26e3f9d2021-03-29 05:57:30 -0700481 sql
Elif Bilginfba75de2021-03-09 04:44:10 -0800482 )
Elif Bilgin63ffe602021-02-19 10:46:51 -0800483 }
484}