Skip to content

Commit

Permalink
add incremental support
Browse files Browse the repository at this point in the history
  • Loading branch information
RBusarow committed Jun 2, 2020
1 parent 836062b commit ea8cd44
Show file tree
Hide file tree
Showing 21 changed files with 673 additions and 88 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package dev.matrix.roomigrant.compiler

import com.squareup.kotlinpoet.*
import dev.matrix.roomigrant.compiler.data.Field
import dev.matrix.roomigrant.compiler.data.Index
import dev.matrix.roomigrant.compiler.data.Scheme
import dev.matrix.roomigrant.compiler.data.Table
import dev.matrix.roomigrant.compiler.data.*
import dev.matrix.roomigrant.compiler.diff.SchemeDiff
import dev.matrix.roomigrant.compiler.rules.FieldRule
import java.util.*
Expand Down Expand Up @@ -65,14 +62,14 @@ class Migration(
var variableIndex = 0
val diff = SchemeDiff(scheme1, scheme2)

for (tableDiff in diff.added) {
createTable(tableDiff.table2)
createTableIndices(tableDiff.table2)
for (tableDiff in diff.addedTables) {
createTable(tableDiff.new)
createTableIndices(tableDiff.new)
}

for (tableDiff in diff.changed) {
val table1 = tableDiff.table1
val table2 = tableDiff.table2
for (tableDiff in diff.changedTables) {
val table1 = tableDiff.old
val table2 = tableDiff.new

if (!tableDiff.primaryKeyChanged && tableDiff.fieldsDiff.onlyAdded) {
val sb = StringBuilder()
Expand Down Expand Up @@ -103,16 +100,16 @@ class Migration(

val fields = LinkedHashMap<String, String>()
for (it in tableDiff.fieldsDiff.same) {
fields[it.field2.name] = toSql(getFieldRule(table2, it.field2), it.copySql)
fields[it.newField.name] = toSql(getFieldRule(table2, it.newField), it.copySql)
}
for (it in tableDiff.fieldsDiff.added) {
fields[it.name] = toSql(getFieldRule(table2, it), it.defaultSqlValue)
}
for (it in tableDiff.fieldsDiff.affinityChanged) {
fields[it.field2.name] = toSql(getFieldRule(table2, it.field2), it.castSql)
fields[it.newField.name] = toSql(getFieldRule(table2, it.newField), it.castSql)
}
for (it in tableDiff.fieldsDiff.nullabilityChanged) {
fields[it.field2.name] = toSql(getFieldRule(table2, it.field2), it.toNotNullableSql)
fields[it.newField.name] = toSql(getFieldRule(table2, it.newField), it.toNotNullableSql)
}

val sb = StringBuilder()
Expand All @@ -139,9 +136,25 @@ class Migration(
}
}

for (table in diff.removed) {
for (table in diff.removedTables) {
dropTable(table.name)
}

for (viewDiff in diff.addedViews) {
createView(viewDiff.new)
}

for (viewDiff in diff.changedViews) {
val old = viewDiff.old
val new = viewDiff.new

old?.name?.let { dropView(it)}
createView(new)
}

for (view in diff.removedViews) {
dropView(view.name)
}
}

private fun getFieldRule(table: Table, field: Field): FieldRule? {
Expand All @@ -164,6 +177,18 @@ class Migration(
execSql(table.createSql(table.name))
}

private fun dropView(viewName: String) {
execSql("DROP VIEW IF EXISTS `$viewName`")
}

private fun renameView(viewName1: String, viewName2: String) {
execSql("ALTER VIEW `$viewName1` RENAME TO `$viewName2`")
}

private fun createView(view: View) {
execSql(view.createSql(view.name))
}

private fun dropTableIndex(index: Index) {
execSql("DROP INDEX IF EXISTS ${index.name}")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import dev.matrix.roomigrant.GenerateRoomMigrations
import dev.matrix.roomigrant.compiler.data.Root
import net.ltgt.gradle.incap.IncrementalAnnotationProcessor
import net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.ISOLATING
import dev.matrix.roomigrant.compiler.data.Scheme
import java.io.File
import java.io.InputStreamReader
import javax.annotation.processing.AbstractProcessor
Expand All @@ -25,38 +26,38 @@ import androidx.room.Database as DatabaseAnnotation
@SupportedSourceVersion(SourceVersion.RELEASE_7)
class Processor : AbstractProcessor() {

override fun getSupportedSourceVersion() = SourceVersion.latestSupported()!!
override fun getSupportedAnnotationTypes() = mutableSetOf(GenerateRoomMigrations::class.java.name)
override fun getSupportedSourceVersion() = SourceVersion.latestSupported()!!
override fun getSupportedAnnotationTypes() = mutableSetOf(GenerateRoomMigrations::class.java.name)

private val moshi = Moshi.Builder().build()
private val moshi = Moshi.Builder().build()

override fun process(annotations: MutableSet<out TypeElement>, roundEnvironment: RoundEnvironment): Boolean {
val schemaLocation = processingEnv.options["room.schemaLocation"] ?: return true
val elements = roundEnvironment.getElementsAnnotatedWith(GenerateRoomMigrations::class.java)
.filterIsInstance<TypeElement>()
override fun process(annotations: MutableSet<out TypeElement>, roundEnvironment: RoundEnvironment): Boolean {
val schemaLocation = processingEnv.options["room.schemaLocation"] ?: return true
val elements = roundEnvironment.getElementsAnnotatedWith(GenerateRoomMigrations::class.java)
.filterIsInstance<TypeElement>()

for (element in elements) {
if (element.getAnnotation(DatabaseAnnotation::class.java) == null) {
throw Exception("$element is not annotated with ${DatabaseAnnotation::class.simpleName}")
}
processDatabase(schemaLocation, element)
}
return true
}
for (element in elements) {
if (element.getAnnotation(DatabaseAnnotation::class.java) == null) {
throw Exception("$element is not annotated with ${DatabaseAnnotation::class.simpleName}")
}
processDatabase(schemaLocation, element)
}
return true
}

private fun processDatabase(schemaLocation: String, element: TypeElement) {
val folder = File(schemaLocation, element.asClassName().toString())
val schemes = folder.listFiles().mapNotNull { readScheme(it) }.sortedBy { it.version }
private fun processDatabase(schemaLocation: String, element: TypeElement) {
val folder = File(schemaLocation, element.asClassName().toString())
val schemes = folder.listFiles().mapNotNull { readScheme(it) }.sortedBy { it.version }

val database = Database(processingEnv, element)
for (scheme in schemes) {
database.addScheme(scheme)
}
for (index in 1 until schemes.size) {
database.addMigration(schemes[index - 1], schemes[index]).generate()
}
database.generate()
}
val database = Database(processingEnv, element)
for (scheme in schemes) {
database.addScheme(scheme)
}
for (index in 1 until schemes.size) {
database.addMigration(schemes[index - 1], schemes[index]).generate()
}
database.generate()
}

private fun readScheme(file: File) = try {
InputStreamReader(file.inputStream()).use {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,7 @@ data class Scheme(
val version: Int,

@Json(name = "entities")
val tables: List<Table>)
val tables: List<Table>,

@Json(name ="views")
val views: List<View> = emptyList())
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package dev.matrix.roomigrant.compiler.data

import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass

@JsonClass(generateAdapter = true)
data class View(
@Json(name ="viewName")
val name: String,

@Json(name ="createSql")
val createSqlTemplate: String) {

fun createSql(viewName: String = name) = createSqlTemplate.replace("\${VIEW_NAME}", viewName)

constructor(view: View, name: String) : this(
name = name,
createSqlTemplate = view.createSqlTemplate)

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ import dev.matrix.roomigrant.compiler.data.Table
* @author matrixdev
*/
@Suppress("CanBeParameter", "MemberVisibilityCanBePrivate", "unused")
class FieldDiff(val table1: Table, val table2: Table, val field1: Field, val field2: Field) {
val affinityChanged = field1.affinity != field2.affinity
val nullabilityChanged = !field1.notNull && field2.notNull
class FieldDiff(val oldTable: Table, val newTable: Table, val oldField: Field, val newField: Field) {
val affinityChanged = oldField.affinity != newField.affinity
val nullabilityChanged = !oldField.notNull && newField.notNull

val copySql: String
get() = "`${table1.name}`.`${field1.name}`"
get() = "`${oldTable.name}`.`${oldField.name}`"

val castSql: String
get() = "CAST(`${table1.name}`.`${field1.name}` AS ${field2.affinity})"
get() = "CAST(`${oldTable.name}`.`${oldField.name}` AS ${newField.affinity})"

val toNotNullableSql: String
get() = "IFNULL(`${table1.name}`.`${field2.name}`, ${field2.defaultSqlValue})"
}
get() = "IFNULL(`${oldTable.name}`.`${newField.name}`, ${newField.defaultSqlValue})"
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import dev.matrix.roomigrant.compiler.data.Table
* @author matrixdev
*/
@Suppress("CanBeParameter", "MemberVisibilityCanBePrivate")
class FieldsDiff(val table1: Table?, val table2: Table) {
class FieldsDiff(val old: Table?, val new: Table) {

val same = ArrayList<FieldDiff>()
val added = ArrayList<Field>()
Expand All @@ -26,20 +26,20 @@ class FieldsDiff(val table1: Table?, val table2: Table) {
}

fun init() {
if (table1 == null) {
added.addAll(table2.fields)
if (old == null) {
added.addAll(new.fields)
return
}

val fields1Map = table1.fields.associateByTo(HashMap()) { it.name }
for (field2 in table2.fields) {
val fields1Map = old.fields.associateByTo(HashMap()) { it.name }
for (field2 in new.fields) {
val field1 = fields1Map.remove(field2.name)
if (field1 == null) {
added.add(field2)
continue
}

val diff = FieldDiff(table1, table2, field1, field2)
val diff = FieldDiff(old, new, field1, field2)
when {
diff.affinityChanged -> affinityChanged.add(diff)
diff.nullabilityChanged -> nullabilityChanged.add(diff)
Expand All @@ -49,4 +49,4 @@ class FieldsDiff(val table1: Table?, val table2: Table) {
removed.addAll(fields1Map.values)
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import dev.matrix.roomigrant.compiler.data.Table
* @author matrixdev
*/
@Suppress("CanBeParameter", "MemberVisibilityCanBePrivate")
class IndicesDiff(val table1: Table?, val table2: Table) {
class IndicesDiff(val old: Table?, val new: Table) {

val same = ArrayList<Index>()
val added = ArrayList<Index>()
Expand All @@ -22,13 +22,13 @@ class IndicesDiff(val table1: Table?, val table2: Table) {
}

fun init() {
if (table1 == null) {
added.addAll(table2.indices)
if (old == null) {
added.addAll(new.indices)
return
}

val indexes1Map = table1.indices.associateByTo(HashMap()) { it.name }
for (index2 in table2.indices) {
val indexes1Map = old.indices.associateByTo(HashMap()) { it.name }
for (index2 in new.indices) {
val index1 = indexes1Map.remove(index2.name)
when (index1) {
null -> added.add(index2)
Expand All @@ -39,4 +39,4 @@ class IndicesDiff(val table1: Table?, val table2: Table) {
removed.addAll(indexes1Map.values)
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package dev.matrix.roomigrant.compiler.diff

import dev.matrix.roomigrant.compiler.data.Scheme
import dev.matrix.roomigrant.compiler.data.Table
import dev.matrix.roomigrant.compiler.data.View
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashMap
Expand All @@ -10,31 +11,57 @@ import kotlin.collections.HashMap
* @author matrixdev
*/
@Suppress("CanBeParameter", "MemberVisibilityCanBePrivate", "unused")
class SchemeDiff(val scheme1: Scheme, val scheme2: Scheme) {
class SchemeDiff(val old: Scheme, val new: Scheme) {

val same = ArrayList<TableDiff>()
val added = ArrayList<TableDiff>()
val removed = ArrayList<Table>()
val changed = ArrayList<TableDiff>()
val sameTables = ArrayList<TableDiff>()
val addedTables = ArrayList<TableDiff>()
val removedTables = ArrayList<Table>()
val changedTables = ArrayList<TableDiff>()

val sameViews = ArrayList<ViewDiff>()
val addedViews = ArrayList<ViewDiff>()
val removedViews = ArrayList<View>()
val changedViews = ArrayList<ViewDiff>()

val wasChanged: Boolean
get() = added.isEmpty() && removed.isEmpty() && changed.isEmpty()
get() = addedTables.isEmpty()
&& removedTables.isEmpty()
&& changedTables.isEmpty()
&& addedViews.isEmpty()
&& removedViews.isEmpty()
&& changedViews.isEmpty()

init {
val tables1Map = scheme1.tables.associateByTo(HashMap()) { it.name.toLowerCase(Locale.getDefault()) }
for (table2 in scheme2.tables) {
val table1 = tables1Map.remove(table2.name.toLowerCase(Locale.getDefault()))
if (table1 == null) {
added.add(TableDiff(null, table2))
val oldTableMap = old.tables.associateByTo(HashMap()) { it.name.toLowerCase(Locale.getDefault()) }
for (newTable in new.tables) {
val oldTable = oldTableMap.remove(newTable.name.toLowerCase(Locale.getDefault()))
if (oldTable == null) {
addedTables.add(TableDiff(null, newTable))
continue
}
val diff = TableDiff(oldTable, newTable)
if (diff.wasChanged) {
changedTables.add(diff)
} else {
sameTables.add(TableDiff(null, newTable))
}
}
removedTables.addAll(oldTableMap.values)

val newViewMap = old.views.associateByTo(HashMap()) { it.name.toLowerCase(Locale.getDefault()) }
for (newView in new.views) {
val oldView = newViewMap.remove(newView.name.toLowerCase(Locale.getDefault()))
if (oldView == null) {
addedViews.add(ViewDiff(null, newView))
continue
}
val diff = TableDiff(table1, table2)
val diff = ViewDiff(oldView, newView)
if (diff.wasChanged) {
changed.add(diff)
changedViews.add(diff)
} else {
same.add(TableDiff(null, table2))
sameViews.add(ViewDiff(null, newView))
}
}
removed.addAll(tables1Map.values)
removedViews.addAll(newViewMap.values)
}
}
Loading

0 comments on commit ea8cd44

Please sign in to comment.