Я не могу скопировать ForeignKeys и индексы
Для Foreign Keys генерация их может быть основана на выводе из PRAGMA foreign_keys_list(the_table)
, который производит вывод, такой как: -
Для указывает , вы можете основать генерациюих на выходе из PRAGMA index_list(the_table)
, чтобы получить имена индексов и проверить ограничение UNIQUE.
В качестве имени индекса вы можете использовать PRAGMA index_info(the_index_name)
Например, будет работать следующая адаптация вашего кода (КОММЕНТАРИИ относительно ограничений): -
private static void deleteColumns(SupportSQLiteDatabase database, String tableName, List<String> columnsToRemove){
List<String> columnNames = new ArrayList<>();
List<String> columnNamesWithType = new ArrayList<>();
List<String> primaryKeys = new ArrayList<>();
String query = "pragma table_info(" + tableName + ");";
Cursor cursor = database.query(query);
while (cursor.moveToNext()){
String columnName = cursor.getString(cursor.getColumnIndex("name"));
if (columnsToRemove.contains(columnName)){
continue;
}
String columnType = cursor.getString(cursor.getColumnIndex("type"));
boolean isNotNull = cursor.getInt(cursor.getColumnIndex("notnull")) == 1;
boolean isPk = cursor.getInt(cursor.getColumnIndex("pk")) == 1;
columnNames.add(columnName);
String tmp = "`" + columnName + "` " + columnType + " ";
if (isNotNull){
tmp += " NOT NULL ";
}
int defaultValueType = cursor.getType(cursor.getColumnIndex("dflt_value"));
if (defaultValueType == Cursor.FIELD_TYPE_STRING){
tmp += " DEFAULT " + "\"" + cursor.getString(cursor.getColumnIndex("dflt_value")) + "\" ";
}else if(defaultValueType == Cursor.FIELD_TYPE_INTEGER){
tmp += " DEFAULT " + cursor.getInt(cursor.getColumnIndex("dflt_value")) + " ";
}else if (defaultValueType == Cursor.FIELD_TYPE_FLOAT){
tmp += " DEFAULT " + cursor.getFloat(cursor.getColumnIndex("dflt_value")) + " ";
}
columnNamesWithType.add(tmp);
if (isPk){
primaryKeys.add("`" + columnName + "`");
}
}
//<<<<<<<<<< GET THE FOREIGN KEYS SQL FOR THE TABLE >>>>>>>>>>
//!!!!!WARNING!!!!! DOES NOT ENCLOSE COLUMN NAMES AS THE COULD BE CSV LIST of columns
// (should split and enclose them)
//!!!!!WARNING!!!!! As information gathered is from the ORIGINAL table there may be issues
// columns are skipped
cursor = database.query("PRAGMA foreign_key_list(" + tableName + ");");
StringBuilder foreignKeySQL = new StringBuilder();
while (cursor.moveToNext()) {
if (isColumnToBeRemoved(cursor.getString(cursor.getColumnIndex("from")),columnsToRemove)) {
continue;
}
foreignKeySQL.append(",FOREIGN KEY (")
.append(cursor.getString(cursor.getColumnIndex("from")))
.append(") REFERENCES `")
.append(cursor.getString(cursor.getColumnIndex("table")))
.append("`(")
.append(cursor.getString(cursor.getColumnIndex("to")))
.append(") ")
.append(" ON DELETE ")
.append(cursor.getString(cursor.getColumnIndex("on_delete")))
.append(" ON UPDATE ").append(cursor.getString(cursor.getColumnIndex("on_update")))
;
}
//<<<<<<<<<< GET THE INDICES CREATE SQL as an ArrayList<string> >>>>>>>>>>
//!!!!!WARNING As information gathered is from the ORIGINAL table there may be issues
ArrayList<String> indicesSQL = new ArrayList<>();
cursor = database.query("PRAGMA index_list(" + tableName + ")");
//Cursor cursor2;
while (cursor.moveToNext()) {
boolean includesRemovedColumn = false;
String unique = "";
if (cursor.getInt(cursor.getColumnIndex("unique")) > 0) {
unique = " UNIQUE ";
}
StringBuilder currentIndex = new StringBuilder().append("CREATE " + unique + " INDEX IF NOT EXISTS `")
.append(cursor.getString(cursor.getColumnIndex("name")))
.append("` ON `").append(tableName).append("`(");
Cursor cursor2 = database.query("PRAGMA index_info(" + cursor.getString(cursor.getColumnIndex("name")) + ")");
boolean afterFirst = false;
while (cursor2.moveToNext()) {
if (isColumnToBeRemoved(cursor2.getString(cursor2.getColumnIndex("name")),columnsToRemove)) {
includesRemovedColumn = true;
}
if (afterFirst) {
currentIndex.append(",");
}
afterFirst = true;
currentIndex.append("`")
.append(cursor2.getString(cursor2.getColumnIndex("name"))).append("`");
}
cursor2.close();
currentIndex.append(");");
if (!includesRemovedColumn) {
indicesSQL.add(currentIndex.toString());
}
}
cursor.close();
String columnNamesSeparated = TextUtils.join(", ", columnNames);
if (primaryKeys.size() > 0){
columnNamesWithType.add("PRIMARY KEY("+ TextUtils.join(", ", primaryKeys) +")");
}
String columnNamesWithTypeSeparated = TextUtils.join(", ", columnNamesWithType);
//<<<<<<<<<< FOR LOGGING/ CHECKING >>>>>>>>>
String createTableSQL = "CREATE TABLE " + tableName + " (" + columnNamesWithTypeSeparated + foreignKeySQL + ")";
String insertSQL = "INSERT INTO " + tableName + " (" + columnNamesSeparated + ") SELECT "
+ columnNamesSeparated + " FROM " + tableName + "_old;";
String alterTableSQL = "ALTER TABLE " + tableName + " RENAME TO " + tableName + "_old;";
String dropTableSQL = "DROP TABLE " + tableName + "_old;";
Log.d("ALTERSQL",alterTableSQL);
Log.d("CREATESQL",createTableSQL);
Log.d("INSERTSQL",insertSQL);
Log.d("DROPSQL",dropTableSQL);
for (String ixsql: indicesSQL) {
Log.d("CREATEINDEXSQL",ixsql);
}
database.beginTransaction();
try {
database.execSQL("ALTER TABLE " + tableName + " RENAME TO " + tableName + "_old;");
//database.execSQL("CREATE TABLE " + tableName + " (" + columnNamesWithTypeSeparated + ") + FOREIGN KEY +(trackartist) REFERENCES artist(artistid) ;" );
database.execSQL("CREATE TABLE " + tableName + " (" + columnNamesWithTypeSeparated + foreignKeySQL + ")" );
database.execSQL("INSERT INTO " + tableName + " (" + columnNamesSeparated + ") SELECT "
+ columnNamesSeparated + " FROM " + tableName + "_old;");
database.execSQL("DROP TABLE " + tableName + "_old;");
for (String ixsql: indicesSQL) {
database.execSQL(ixsql);
}
database.setTransactionSuccessful();
}finally {
database.endTransaction();
}
}
//<<<<<<<<<< FOR CHECKING IF COLUMN EXISTS >>>>>>>>>>
private static boolean isColumnToBeRemoved(String column, List<String> columnsToRemove) {
for (String s: columnsToRemove) {
if (s.toLowerCase().equals(column.toLowerCase())) return true;
}
return false;
}
Альтернативный подход, который может бытьпроще, чем пытаться удовлетворить все перестановки, было бы
- a) изменить сущности и затем
- b) извлечь SQL из сгенерированного кода из _impl @ Database. Например, вышеупомянутое было успешно проверено на простом удалении из 1 столбца, с двумя FK и индексом, покрывающим оба отображенных столбца, при этом можно было использовать следующие 10 *
: -
public void createAllTables(SupportSQLiteDatabase _db) {
_db.execSQL("CREATE TABLE IF NOT EXISTS `device_item` (`id` INTEGER, `initial` TEXT, `added1` INTEGER NOT NULL DEFAULT 0, `added2` TEXT DEFAULT '', PRIMARY KEY(`id`))");
_db.execSQL("CREATE TABLE IF NOT EXISTS `table1` (`id` INTEGER, `name` TEXT, `mapToTable2` INTEGER NOT NULL, `mapToTable3` INTEGER NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`mapToTable2`) REFERENCES `table2`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`mapToTable3`) REFERENCES `table3`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )");
_db.execSQL("CREATE INDEX IF NOT EXISTS `index_table1_mapToTable2_mapToTable3` ON `table1` (`mapToTable2`, `mapToTable3`)");
_db.execSQL("CREATE TABLE IF NOT EXISTS `table2` (`id` INTEGER, `nameOfT2` TEXT, `anotherNameOfT2` TEXT, PRIMARY KEY(`id`))");
_db.execSQL("CREATE TABLE IF NOT EXISTS `table3` (`id` INTEGER, `nameOfT3` TEXT, `anotherNameOfT3` TEXT, PRIMARY KEY(`id`))");
_db.execSQL("CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)");
_db.execSQL("INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '2177a2b52b96e912c0af8db6e8cad3d2')");
}
- как написан SQL для вас. это просто вопрос создания ALTER, INSERT AND DROP SQL.
Вышеприведенное сравнение сравнивается с Logged SQL при выполнении вышеуказанного: -
2019-10-12 15:01:34.242 D/ALTERSQL: ALTER TABLE table1 RENAME TO table1_old;
2019-10-12 15:01:34.242 D/CREATESQL: CREATE TABLE table1 (`id` INTEGER , `name` TEXT , `mapToTable2` INTEGER NOT NULL , `mapToTable3` INTEGER NOT NULL , PRIMARY KEY(`id`),FOREIGN KEY (mapToTable3) REFERENCES `table3`(id) ON DELETE NO ACTION ON UPDATE NO ACTION,FOREIGN KEY (mapToTable2) REFERENCES `table2`(id) ON DELETE NO ACTION ON UPDATE NO ACTION)
2019-10-12 15:01:34.242 D/INSERTSQL: INSERT INTO table1 (id, name, mapToTable2, mapToTable3) SELECT id, name, mapToTable2, mapToTable3 FROM table1_old;
2019-10-12 15:01:34.242 D/DROPSQL: DROP TABLE table1_old;
2019-10-12 15:01:34.242 D/CREATEINDEXSQL: CREATE INDEX IF NOT EXISTS `index_table1_mapToTable2_mapToTable3` ON `table1`(`mapToTable2`,`mapToTable3`);
Disclaimers
Вышеуказанное не подходит для всех ситуаций, оно предназначено исключительно как демонстрация основных приемов. Код содержит некоторые комментарии относительно ограничений. Однако существуют и другие соображения, например, частичные индексы и предложение WHERE, FTS и столбец MATCH для FK.