public static void MigrateTables(this MySqlConnection conn, IMigrationElement[] processTable, ILog log) { var tableFields = new Dictionary <string, IColumnInfo>(); PrimaryKeyInfo primaryKey = null; var tableKeys = new Dictionary <string, NamedKeyInfo>(); SqlTable table = null; ChangeEngine selectedEngine = null; uint processingTableRevision = 0; uint currentAtRevision = 0; bool insideTransaction = false; if (processTable.Length == 0) { throw new MySQLMigrationException("Invalid MySQL migration"); } if (null == processTable[0] as SqlTable) { throw new MySQLMigrationException("First entry must be table name"); } foreach (IMigrationElement migration in processTable) { Type migrationType = migration.GetType(); if (typeof(SqlTable) == migrationType) { if (insideTransaction) { ExecuteStatement(conn, string.Format("ALTER TABLE {0} COMMENT='{1}';", table.Name, processingTableRevision), log); ExecuteStatement(conn, "COMMIT", log); insideTransaction = false; } if (null != table && 0 != processingTableRevision) { if (currentAtRevision == 0) { conn.CreateTable( table, selectedEngine, primaryKey, tableFields, tableKeys, processingTableRevision, log); } tableKeys.Clear(); tableFields.Clear(); primaryKey = null; } table = (SqlTable)migration; selectedEngine = null; currentAtRevision = conn.GetTableRevision(table.Name); processingTableRevision = 1; } else if (typeof(TableRevision) == migrationType) { if (insideTransaction) { ExecuteStatement(conn, string.Format("ALTER TABLE {0} COMMENT='{1}';", table.Name, processingTableRevision), log); ExecuteStatement(conn, "COMMIT", log); insideTransaction = false; if (currentAtRevision != 0) { currentAtRevision = processingTableRevision; } } var rev = (TableRevision)migration; if (rev.Revision != processingTableRevision + 1) { throw new MySQLMigrationException(string.Format("Invalid TableRevision entry. Expected {0}. Got {1}", processingTableRevision + 1, rev.Revision)); } processingTableRevision = rev.Revision; if (processingTableRevision - 1 == currentAtRevision && 0 != currentAtRevision) { ExecuteStatement(conn, "BEGIN", log); insideTransaction = true; log.InfoFormat("Migration table '{0}' to revision {1}", table.Name, processingTableRevision); } } else if (processingTableRevision == 0 || table == null) { if (table != null) { throw new MySQLMigrationException("Unexpected processing element for " + table.Name); } else { throw new MySQLMigrationException("Unexpected processing element"); } } else { Type[] interfaces = migration.GetType().GetInterfaces(); if (interfaces.Contains(typeof(IAddColumn))) { var columnInfo = (IAddColumn)migration; if (tableFields.ContainsKey(columnInfo.Name)) { throw new ArgumentException("Column " + columnInfo.Name + " was added twice."); } tableFields.Add(columnInfo.Name, columnInfo); if (insideTransaction) { ExecuteStatement(conn, columnInfo.Sql(table.Name), log); } } else if (interfaces.Contains(typeof(IChangeColumn))) { var columnInfo = (IChangeColumn)migration; IColumnInfo oldColumn; if (columnInfo.OldName?.Length != 0) { if (!tableFields.TryGetValue(columnInfo.OldName, out oldColumn)) { throw new ArgumentException("Change column for " + columnInfo.Name + " has no preceeding AddColumn for " + columnInfo.OldName); } } else if (!tableFields.TryGetValue(columnInfo.Name, out oldColumn)) { throw new ArgumentException("Change column for " + columnInfo.Name + " has no preceeding AddColumn"); } if (insideTransaction) { ExecuteStatement(conn, columnInfo.Sql(table.Name, oldColumn.FieldType), log); } if (columnInfo.OldName?.Length != 0) { tableFields.Remove(columnInfo.OldName); if (primaryKey != null) { string[] fields = primaryKey.FieldNames; int n = fields.Length; for (int i = 0; i < n; ++i) { if (fields[i] == columnInfo.OldName) { fields[i] = columnInfo.Name; } } } foreach (NamedKeyInfo keyinfo in tableKeys.Values) { string[] fields = keyinfo.FieldNames; int n = fields.Length; for (int i = 0; i < n; ++i) { if (fields[i] == columnInfo.OldName) { fields[i] = columnInfo.Name; } } } } tableFields[columnInfo.Name] = columnInfo; } else if (migrationType == typeof(DropColumn)) { var columnInfo = (DropColumn)migration; if (insideTransaction) { ExecuteStatement(conn, columnInfo.Sql(table.Name, tableFields[columnInfo.Name].FieldType), log); } tableFields.Remove(columnInfo.Name); } else if (migrationType == typeof(ChangeEngine)) { var engineInfo = (ChangeEngine)migration; if (insideTransaction) { ExecuteStatement(conn, engineInfo.Sql(table.Name), log); } selectedEngine = engineInfo; } else if (migrationType == typeof(PrimaryKeyInfo)) { if (null != primaryKey && insideTransaction) { ExecuteStatement(conn, "ALTER TABLE `" + MySqlHelper.EscapeString(table.Name) + "` DROP PRIMARY KEY;", log); } primaryKey = (PrimaryKeyInfo)migration; if (insideTransaction) { ExecuteStatement(conn, primaryKey.Sql(table.Name), log); } } else if (migrationType == typeof(DropPrimaryKeyinfo)) { if (null != primaryKey && insideTransaction) { ExecuteStatement(conn, ((DropPrimaryKeyinfo)migration).Sql(table.Name), log); } primaryKey = null; } else if (migrationType == typeof(NamedKeyInfo)) { var namedKey = (NamedKeyInfo)migration; tableKeys.Add(namedKey.Name, namedKey); if (insideTransaction) { ExecuteStatement(conn, namedKey.Sql(table.Name), log); } } else if (migrationType == typeof(DropNamedKeyInfo)) { var namedKey = (DropNamedKeyInfo)migration; tableKeys.Remove(namedKey.Name); if (insideTransaction) { ExecuteStatement(conn, namedKey.Sql(table.Name), log); } } else { throw new MySQLMigrationException("Invalid type " + migrationType.FullName + " in migration list"); } } } if (insideTransaction) { ExecuteStatement(conn, string.Format("ALTER TABLE {0} COMMENT='{1}';", table.Name, processingTableRevision), log); ExecuteStatement(conn, "COMMIT", log); if (currentAtRevision != 0) { currentAtRevision = processingTableRevision; } } if (null != table && 0 != processingTableRevision && currentAtRevision == 0) { conn.CreateTable( table, selectedEngine, primaryKey, tableFields, tableKeys, processingTableRevision, log); } }