package com.raizlabs.android.dbflow.sql.migration;
import android.database.Cursor;
import android.support.annotation.CallSuper;
import android.support.annotation.NonNull;
import com.raizlabs.android.dbflow.config.FlowManager;
import com.raizlabs.android.dbflow.sql.QueryBuilder;
import com.raizlabs.android.dbflow.sql.SQLiteType;
import com.raizlabs.android.dbflow.sql.language.SQLite;
import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper;
import java.util.ArrayList;
import java.util.List;
/**
* Description: Provides a very nice way to alter a single table quickly and easily.
*/
public class AlterTableMigration<TModel> extends BaseMigration {
/**
* The table to ALTER
*/
private final Class<TModel> table;
/**
* The query we use
*/
private QueryBuilder query;
/**
* The query to rename the table with
*/
private QueryBuilder renameQuery;
/**
* The columns to ALTER within a table
*/
private List<QueryBuilder> columnDefinitions;
private List<String> columnNames;
/**
* The old name of the table before renaming it
*/
private String oldTableName;
public AlterTableMigration(Class<TModel> table) {
this.table = table;
}
@Override
public final void migrate(DatabaseWrapper database) {
// "ALTER TABLE "
String sql = getAlterTableQueryBuilder().getQuery();
String tableName = FlowManager.getTableName(table);
// "{oldName} RENAME TO {newName}"
// Since the structure has been updated already, the manager knows only the new name.
if (renameQuery != null) {
String renameQuery = new QueryBuilder(sql).appendQuotedIfNeeded(oldTableName)
.append(this.renameQuery.getQuery())
.append(tableName)
.toString();
database.execSQL(renameQuery);
}
// We have column definitions to add here
// ADD COLUMN columnName {type}
if (columnDefinitions != null) {
Cursor cursorToCheckColumnFor = SQLite.select().from(table).limit(0).query(database);
if (cursorToCheckColumnFor != null) {
try {
sql = new QueryBuilder(sql).append(tableName).toString();
for (int i = 0; i < columnDefinitions.size(); i++) {
QueryBuilder columnDefinition = columnDefinitions.get(i);
String columnName = QueryBuilder.stripQuotes(columnNames.get(i));
if (cursorToCheckColumnFor.getColumnIndex(columnName) == -1) {
database.execSQL(sql + " ADD COLUMN " + columnDefinition.getQuery());
}
}
} finally {
cursorToCheckColumnFor.close();
}
}
}
}
@CallSuper
@Override
public void onPostMigrate() {
// cleanup and make fields eligible for garbage collection
query = null;
renameQuery = null;
columnDefinitions = null;
columnNames = null;
}
/**
* Call this to rename a table to a new name, such as changing either the {@link com.raizlabs.android.dbflow.structure.Model} class name
* or by changing the name through a {@link com.raizlabs.android.dbflow.annotation.Table}
*
* @param oldName The new name to call the table.
* @return This instance
*/
public AlterTableMigration<TModel> renameFrom(@NonNull String oldName) {
oldTableName = oldName;
renameQuery = new QueryBuilder().append(" RENAME").appendSpaceSeparated("TO");
return this;
}
/**
* Add a column to the DB. This does not necessarily need to be reflected in the {@link TModel},
* but it is recommended.
*
* @param sqLiteType The type of column represented in the DB.
* @param columnName The name of the column to add. Use the "_Table" class for the specified table.
* @return This instance
*/
public AlterTableMigration<TModel> addColumn(@NonNull SQLiteType sqLiteType, @NonNull String columnName) {
if (columnDefinitions == null) {
columnDefinitions = new ArrayList<>();
columnNames = new ArrayList<>();
}
QueryBuilder queryBuilder = new QueryBuilder()
.append(QueryBuilder.quoteIfNeeded(columnName)).appendSpace().appendSQLiteType(sqLiteType);
columnDefinitions.add(queryBuilder);
columnNames.add(columnName);
return this;
}
/**
* Add a column to the DB. This does not necessarily need to be reflected in the {@link TModel},
* but it is recommended.
*
* @param sqLiteType The type of column that pertains to an {@link SQLiteType}
* @param columnName The name of the column to add. Use the "$Table" class for the specified table.
* @param referenceClause The clause of the references that this foreign key points to.
* @return This instance
*/
public AlterTableMigration<TModel> addForeignKeyColumn(SQLiteType sqLiteType, String columnName, String referenceClause) {
if (columnDefinitions == null) {
columnDefinitions = new ArrayList<>();
columnNames = new ArrayList<>();
}
QueryBuilder queryBuilder = new QueryBuilder()
.append(QueryBuilder.quoteIfNeeded(columnName)).appendSpace().appendSQLiteType(sqLiteType)
.appendSpace().append("REFERENCES ").append(referenceClause);
columnDefinitions.add(queryBuilder);
columnNames.add(columnName);
return this;
}
/**
* @return The query that renames the table.
*/
public String getRenameQuery() {
QueryBuilder queryBuilder = new QueryBuilder(getAlterTableQueryBuilder().getQuery()).appendQuotedIfNeeded(oldTableName)
.append(renameQuery).append(FlowManager.getTableName(table));
return queryBuilder.getQuery();
}
/**
* @return A List of column definitions that add op to a table in the DB.
*/
public List<String> getColumnDefinitions() {
String sql = new QueryBuilder(getAlterTableQueryBuilder()).append(FlowManager.getTableName(table)).toString();
List<String> columnDefinitions = new ArrayList<>();
if (this.columnDefinitions != null) {
for (QueryBuilder columnDefinition : this.columnDefinitions) {
QueryBuilder queryBuilder = new QueryBuilder(sql).appendSpaceSeparated("ADD COLUMN").append(
columnDefinition.getQuery());
columnDefinitions.add(queryBuilder.getQuery());
}
}
return columnDefinitions;
}
public QueryBuilder getAlterTableQueryBuilder() {
if (query == null) {
query = new QueryBuilder().append("ALTER").appendSpaceSeparated("TABLE");
}
return query;
}
}