Skip to content

Commit

Permalink
New TableMigration operations
Browse files Browse the repository at this point in the history
 - renameFrom
 - addColumnAndSet, addColumnAndSetRaw
 - dropColumns(String, String*)
 - renameColumnFrom
 - renameIndexFrom
  • Loading branch information
nafg committed Feb 26, 2019
1 parent afe2e71 commit 1be3416
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 62 deletions.
25 changes: 7 additions & 18 deletions src/main/scala/slick/migration/api/AddColumnWithInitialValue.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,20 @@ package slick.migration.api

import slick.jdbc.{JdbcProfile, JdbcType}
import slick.lifted.{Rep, TableQuery}
import slick.migration.api.AstHelpers.TableInfo


object AddColumnWithInitialValue extends AstHelpers {
object AddColumnWithInitialValue {
@deprecated("Use TableMigration#addColumnAndSetRaw instead", "0.6.0")
def raw[T <: JdbcProfile#Table[_]](tableQuery: TableQuery[T])
(col: T => Rep[_])
(valueSql: String)
(implicit dialect: Dialect[_]) = {
val table = tableQuery.baseTableRow

val tableInfo = TableInfo(table.schemaName, table.tableName)

val columnInfo = colInfo(table)(col)

SqlMigration(dialect.addColumn(tableInfo, columnInfo.copy(notNull = false))) &
SqlMigration(
s"UPDATE ${dialect.quoteTableName(tableInfo)} SET ${dialect.quoteIdentifier(columnInfo.name)} = $valueSql;"
) &
SqlMigration(dialect.alterColumnNullability(tableInfo, columnInfo))
}
(implicit dialect: Dialect[_]): Migration =
TableMigration(tableQuery).addColumnAndSetRaw(col, valueSql)

@deprecated("Use TableMigration#addColumnAndSet instead", "0.6.0")
def apply[T <: JdbcProfile#Table[_], A](tableQuery: TableQuery[T])
(col: T => Rep[A])
(value: A)
(implicit dialect: Dialect[_],
jdbcType: JdbcType[A]) =
raw[T](tableQuery)(col)(jdbcType.valueToSQLLiteral(value))(dialect)
(implicit dialect: Dialect[_], jdbcType: JdbcType[A]) =
TableMigration(tableQuery).addColumnAndSet(col, value)
}
68 changes: 42 additions & 26 deletions src/main/scala/slick/migration/api/Dialect.scala
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,20 @@ class Dialect[-P <: JdbcProfile] extends AstHelpers {
s"""alter table ${quoteTableName(table)}
| add column ${columnSql(column, newTable = false)}""".stripMargin

def addColumnWithInitialValue(table: TableInfo, column: ColumnInfo, rawSqlExpr: String) =
List(addColumn(table, column.copy(default = Some(rawSqlExpr)))) ++
(if (column.default.contains(rawSqlExpr)) Nil else List(alterColumnDefault(table, column)))

def dropColumn(table: TableInfo, column: String) =
s"""alter table ${quoteTableName(table)}
| drop column ${quoteIdentifier(column)}""".stripMargin

def renameColumn(table: TableInfo, from: ColumnInfo, to: String) =
def renameColumn(table: TableInfo, from: String, to: String) =
s"""alter table ${quoteTableName(table)}
| alter column ${quoteIdentifier(from.name)}
| rename to ${quoteIdentifier(to)}""".stripMargin
| alter column ${quoteIdentifier(from)}
| rename to ${quoteIdentifier(to)}""".stripMargin

def renameColumn(table: TableInfo, from: ColumnInfo, to: String): String = renameColumn(table, from.name, to)

def alterColumnType(table: TableInfo, column: ColumnInfo): List[String] = List(
s"""alter table ${quoteTableName(table)}
Expand All @@ -135,23 +141,26 @@ class Dialect[-P <: JdbcProfile] extends AstHelpers {

def migrateTable(table: TableInfo, actions: List[TableMigration.Action]): List[String] = {
def loop(actions: List[TableMigration.Action]): List[String] = actions match {
case Nil => Nil
case CreateTable :: rest =>
case Nil => Nil
case CreateTable :: rest =>
val (cols, other) = partition(rest) { case a: AddColumn => a }
createTable(table, cols.map(_.info)) :: loop(other)
case AlterColumnType(info) :: rest => alterColumnType(table, info) ::: loop(rest)
case RenameIndexTo(info, to) :: rest => renameIndex(info, to) ::: loop(rest)
case DropTable :: rest => dropTable(table) :: loop(rest)
case RenameTableTo(to) :: rest => renameTable(table, to) :: loop(rest)
case AddColumn(info) :: rest => addColumn(table, info) :: loop(rest)
case DropColumn(info) :: rest => dropColumn(table, info.name) :: loop(rest)
case RenameColumnTo(originalInfo, to) :: rest => renameColumn(table, originalInfo, to) :: loop(rest)
case AlterColumnDefault(info) :: rest => alterColumnDefault(table, info) :: loop(rest)
case AlterColumnNullable(info) :: rest => alterColumnNullability(table, info) :: loop(rest)
case DropPrimaryKey(info) :: rest => dropPrimaryKey(table, info.name) :: loop(rest)
case AddPrimaryKey(info) :: rest => createPrimaryKey(table, info.name, info.columns) :: loop(rest)
case DropForeignKey(fk) :: rest => dropForeignKey(table, fk.name) :: loop(rest)
case AddForeignKey(fk) :: rest =>
case AlterColumnType(info) :: rest => alterColumnType(table, info) ::: loop(rest)
case DropTable :: rest => dropTable(table) :: loop(rest)
case RenameTableTo(to) :: rest => renameTable(table, to) :: loop(rest)
case RenameTableFrom(from) :: rest => renameTable(table.copy(tableName = from), table.tableName) :: loop(rest)
case AddColumn(info) :: rest => addColumn(table, info) :: loop(rest)
case AddColumnAndSetInitialValue(info, expr) :: rest => addColumnWithInitialValue(table, info, expr) ::: loop(rest)
case DropColumn(info) :: rest => dropColumn(table, info.name) :: loop(rest)
case DropColumnOfName(name) :: rest => dropColumn(table, name) :: loop(rest)
case RenameColumnTo(originalInfo, to) :: rest => renameColumn(table, originalInfo.name, to) :: loop(rest)
case RenameColumnFrom(currentInfo, from) :: rest => renameColumn(table, from, currentInfo.name) :: loop(rest)
case AlterColumnDefault(info) :: rest => alterColumnDefault(table, info) :: loop(rest)
case AlterColumnNullable(info) :: rest => alterColumnNullability(table, info) :: loop(rest)
case DropPrimaryKey(info) :: rest => dropPrimaryKey(table, info.name) :: loop(rest)
case AddPrimaryKey(info) :: rest => createPrimaryKey(table, info.name, info.columns) :: loop(rest)
case DropForeignKey(fk) :: rest => dropForeignKey(table, fk.name) :: loop(rest)
case AddForeignKey(fk) :: rest =>
val sql =
createForeignKey(
sourceTable = table,
Expand All @@ -163,8 +172,9 @@ class Dialect[-P <: JdbcProfile] extends AstHelpers {
onDelete = fk.onDelete
)
sql :: loop(rest)
case DropIndex(info) :: rest => dropIndex(info) :: loop(rest)
case CreateIndex(info) :: rest => createIndex(info) :: loop(rest)
case DropIndex(info) :: rest => dropIndex(info) :: loop(rest)
case CreateIndex(info) :: rest => createIndex(info) :: loop(rest)
case RenameIndexTo(info, to) :: rest => renameIndex(info, to) ::: loop(rest)
}

loop(actions.reverse.sortBy(_.sort))
Expand All @@ -182,12 +192,12 @@ class DerbyDialect extends Dialect[DerbyProfile] {
addColumn(table, tmpColumn),
s"update ${quoteTableName(table)} set ${quoteIdentifier(tmpColumnName)} = ${quoteIdentifier(column.name)}",
dropColumn(table, column.name),
renameColumn(table, tmpColumn, column.name)
renameColumn(table, tmpColumn.name, column.name)
)
}

override def renameColumn(table: TableInfo, from: ColumnInfo, to: String) =
s"rename column ${quoteTableName(table)}.${quoteIdentifier(from.name)} to ${quoteIdentifier(to)}"
override def renameColumn(table: TableInfo, from: String, to: String) =
s"rename column ${quoteTableName(table)}.${quoteIdentifier(from)} to ${quoteIdentifier(to)}"

override def alterColumnNullability(table: TableInfo, column: ColumnInfo) =
s"""alter table ${quoteTableName(table)}
Expand Down Expand Up @@ -239,6 +249,12 @@ class MySQLDialect extends Dialect[MySQLProfile] with SimulatedRenameIndex[MySQL
override def dropIndex(index: IndexInfo) =
s"drop index ${quoteIdentifier(index.name)} on ${quoteTableName(tableInfo(index.table))}"

/**
* Requires MySQL 8.0
*/
override def renameColumn(table: TableInfo, from: String, to: String) =
s"ALTER TABLE ${quoteTableName(table)} RENAME COLUMN ${quoteIdentifier(from)} TO ${quoteIdentifier(to)}"

override def renameColumn(table: TableInfo, from: ColumnInfo, to: String) = {
val newCol = from.copy(name = to)
s"""alter table ${quoteTableName(table)}
Expand Down Expand Up @@ -273,10 +289,10 @@ class PostgresDialect extends Dialect[PostgresProfile] {
case (true, _) => throw new RuntimeException("Unsupported autoincrement type")
}
override def autoInc(ci: ColumnInfo) = ""
override def renameColumn(table: TableInfo, from: ColumnInfo, to: String) =
override def renameColumn(table: TableInfo, from: String, to: String) =
s"""alter table ${quoteTableName(table)}
| rename column ${quoteIdentifier(from.name)}
| to ${quoteIdentifier(to)}""".stripMargin
| rename column ${quoteIdentifier(from)}
| to ${quoteIdentifier(to)}""".stripMargin
}

object GenericDialect {
Expand Down
71 changes: 53 additions & 18 deletions src/main/scala/slick/migration/api/TableMigration.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package slick.migration.api

import slick.jdbc.JdbcProfile
import slick.jdbc.{JdbcProfile, JdbcType}
import slick.lifted._
import slick.migration.api.AstHelpers.{ColumnInfo, IndexInfo, PrimaryKeyInfo, TableInfo}

Expand All @@ -13,9 +13,13 @@ object TableMigration {
case object DropTable extends Action(0)
case object CreateTable extends Reversible(1)
case class RenameTableTo(to: String) extends Reversible(2)
case class RenameTableFrom(from: String) extends Reversible(2)
case class AddColumn(info: ColumnInfo) extends Reversible(3)
case class AddColumnAndSetInitialValue(info: ColumnInfo, rawSqlExpr: String) extends Reversible(3)
case class DropColumn(info: ColumnInfo) extends Reversible(4)
case class DropColumnOfName(name: String) extends Action(4)
case class RenameColumnTo(originalInfo: ColumnInfo, to: String) extends Reversible(5)
case class RenameColumnFrom(currentInfo: ColumnInfo, from: String) extends Reversible(5)
case class AlterColumnType(info: ColumnInfo) extends Action(6)
case class AlterColumnDefault(info: ColumnInfo) extends Action(6)
case class AlterColumnNullable(info: ColumnInfo) extends Action(6)
Expand All @@ -26,6 +30,7 @@ object TableMigration {
case class AddForeignKey(fk: ForeignKey) extends Reversible(8)
case class CreateIndex(info: IndexInfo) extends Reversible(8)
case class RenameIndexTo(originalInfo: IndexInfo, to: String) extends Reversible(9)
case class RenameIndexFrom(currentInfo: IndexInfo, from: String) extends Reversible(9)
}

sealed trait WithActions[A <: Action] {
Expand Down Expand Up @@ -62,23 +67,22 @@ object TableMigration {
val tm0 = self.modActions(_ => List.empty[Action])
self.actions.foldLeft(tm0) { (tm, action) =>
action match {
case Action.CreateTable => tm.modActions(Action.DropTable :: _)
case Action.RenameTableTo(to) =>
tm
.modActions(Action.RenameTableTo(self.tableInfo.tableName) :: _)
.copy(tableInfo = self.tableInfo.copy(tableName = to))(self.table)
case Action.AddColumn(info) => tm.modActions(Action.DropColumn(info) :: _)
case Action.DropColumn(info) => tm.modActions(Action.AddColumn(info) :: _)
case Action.RenameColumnTo(originalInfo, to) =>
tm.modActions(Action.RenameColumnTo(originalInfo.copy(name = to), originalInfo.name) :: _)
case Action.DropPrimaryKey(info) => tm.modActions(Action.AddPrimaryKey(info) :: _)
case Action.DropForeignKey(fk) => tm.modActions(Action.AddForeignKey(fk) :: _)
case Action.DropIndex(info) => tm.modActions(Action.CreateIndex(info) :: _)
case Action.AddPrimaryKey(info) => tm.modActions(Action.DropPrimaryKey(info) :: _)
case Action.AddForeignKey(fk) => tm.modActions(Action.DropForeignKey(fk) :: _)
case Action.CreateIndex(info) => tm.modActions(Action.DropIndex(info) :: _)
case Action.RenameIndexTo(originalInfo, to) =>
tm.modActions(Action.RenameIndexTo(originalInfo.copy(name = to), originalInfo.name) :: _)
case Action.CreateTable => tm.modActions(Action.DropTable :: _)
case Action.RenameTableTo(to) => tm.modActions(Action.RenameTableFrom(to) :: _)
case Action.RenameTableFrom(from) => tm.modActions(Action.RenameTableTo(from) :: _)
case Action.AddColumn(info) => tm.modActions(Action.DropColumn(info) :: _)
case Action.AddColumnAndSetInitialValue(info, _) => tm.modActions(Action.DropColumn(info) :: _)
case Action.DropColumn(info) => tm.modActions(Action.AddColumn(info) :: _)
case Action.RenameColumnTo(originalInfo, to) => tm.modActions(Action.RenameColumnFrom(originalInfo, to) :: _)
case Action.RenameColumnFrom(currentInfo, from) => tm.modActions(Action.RenameColumnTo(currentInfo, from) :: _)
case Action.DropPrimaryKey(info) => tm.modActions(Action.AddPrimaryKey(info) :: _)
case Action.DropForeignKey(fk) => tm.modActions(Action.AddForeignKey(fk) :: _)
case Action.DropIndex(info) => tm.modActions(Action.CreateIndex(info) :: _)
case Action.AddPrimaryKey(info) => tm.modActions(Action.DropPrimaryKey(info) :: _)
case Action.AddForeignKey(fk) => tm.modActions(Action.DropForeignKey(fk) :: _)
case Action.CreateIndex(info) => tm.modActions(Action.DropIndex(info) :: _)
case Action.RenameIndexTo(originalInfo, to) => tm.modActions(Action.RenameIndexFrom(originalInfo, to) :: _)
case Action.RenameIndexFrom(currentInfo, from) => tm.modActions(Action.RenameIndexTo(currentInfo, from) :: _)

}
}
Expand Down Expand Up @@ -135,6 +139,9 @@ case class TableMigration[T <: JdbcProfile#Table[_], A <: Action](tableInfo: Tab
def rename(to: String) =
modActions(withActions(Action.RenameTableTo(to)))

def renameFrom(from: String) =
modActions(withActions(Action.RenameTableFrom(from)))

/**
* Add columns to the table.
* (If the table is being created, these may be incorporated into the `CREATE TABLE` statement.)
Expand All @@ -146,6 +153,25 @@ case class TableMigration[T <: JdbcProfile#Table[_], A <: Action](tableInfo: Tab
def addColumns(cols: (T => Rep[_])*) =
modActions(withActions(cols.map(columnInfo andThen Action.AddColumn)))

/**
* @note `rawSqlExpr` is used as raw SQL, with the security implications thereof
*/
def addColumnAndSetRaw(col: T => Rep[_], rawSqlExpr: String) =
modActions(withActions(Action.AddColumnAndSetInitialValue(columnInfo(col), rawSqlExpr)))

/**
* Adds a column and populates it without setting a column default for the future.
*
* If the column is NOT NULL, it is first created nullable
* @param col
* @param value
* @param jdbcType
* @tparam C
* @return
*/
def addColumnAndSet[C](col: T => Rep[C], value: C)(implicit jdbcType: JdbcType[C]) =
addColumnAndSetRaw(col, jdbcType.valueToSQLLiteral(value))

/**
* Drop columns.
*
Expand All @@ -156,6 +182,9 @@ case class TableMigration[T <: JdbcProfile#Table[_], A <: Action](tableInfo: Tab
def dropColumns(cols: (T => Rep[_])*) =
modActions(withActions(cols.map(columnInfo andThen Action.DropColumn)))

def dropColumns(name: String, names: String*) =
modActions(withActions((name +: names).map(Action.DropColumnOfName)))

/**
* Rename a column.
*
Expand All @@ -166,6 +195,9 @@ case class TableMigration[T <: JdbcProfile#Table[_], A <: Action](tableInfo: Tab
def renameColumn(col: T => Rep[_], to: String) =
modActions(withActions(Action.RenameColumnTo(columnInfo(col), to)))

def renameColumnFrom(from: String, col: T => Rep[_]) =
modActions(withActions(Action.RenameColumnFrom(columnInfo(col), from)))

/**
* Changes the data type of columns based on the column definitions in `cols`
*
Expand Down Expand Up @@ -270,4 +302,7 @@ case class TableMigration[T <: JdbcProfile#Table[_], A <: Action](tableInfo: Tab
*/
def renameIndex(index: T => Index, to: String) =
modActions(withActions(Action.RenameIndexTo(indexInfo(index(table)), to)))

def renameIndexFrom(from: String, index: T => Index) =
modActions(withActions(Action.RenameIndexFrom(indexInfo(index(table)), from)))
}

0 comments on commit 1be3416

Please sign in to comment.