mŐjfmyǚ;Y6떝vZFX/WҶ;b8B&LuC8`";|k)Ij^fR nHY62JBV92C(zXe/3U2wzpL+F 큣PCH% DM"͚DG=`fEBZ>FB?b*vGZ!qLs*M,˽E,ePC ۱?Hs(ZDHZ)|thnǙ?c!.wpfŋ8M6e8gE5fSVփ03fSVփ03fSVփ03ڝ.bHNfDHmP"6_GdV*jşGv3[,f m 3ԭL~QJ.}gk׺AB*)ƻ}`v-k #Y_cirw}/i@d7+Ώ9bd_vyu=}aܪx3y `F!;̟k^oD d~pN=Rڽ:WʴL׶:BSh9ts~+[7z̾F@@Zr,dO*hY"zi/([8a#ZFa~gJN1mP6N$Y 3ԯɼ Ll3pC͑A-ΫKT՛#h@_uugI<6Ԡq=iGu$`+HMRBh)Tw1+2âp@iY1:tR#Ղ,_QGH IW9_{W`ZULCfz+8KP7?` K#Z7baqäLo oz ''~(Ͷn h'ؽ2?8٭ %7ukQXDw׸T1+H-G7ei?'/K~>en`U^g#M@7IF'pL4~od6c=E\Η_1Rε杛xS{%6(T2Z8N&O˜6'֕\ZJ ۱y}9{D듏61K%zͦcz;[,HHHsrrd/`}Zd12|T>ZWRM{e'#}|ކx߹GiFS3Nᾣ1#ɎzRL=,@rIqfx>ؙPς4{ѕ< mI܄2&}ǯoNfCQ>eU2,,o'mwuzNG̋Xmy6`e?%OLcl@:!id`םa$\fJQӛsvVkX/Ͱ fԄdBp<䣣M,  r>~Nn6Yucy|oI,_] EOX9i$IT6 P u3QdONsJNt4~!16cA&}!^xy@F[x:47(ezf]py'odU$[ |y%ltYS9agtmy`_TT9”ת1FXU&daepk? I ~SXmH 0!nlA@ՓNU}*Tlb[B&v#/+tCOߕAe,y _ ]@j4?9tOk7Yv@aXxCM X#дVJl{T-A^x-O^j殷_㟏\{i\ )JB*kwT ~g[  (3U/vݷĞ1SA[ti>:z>޾< m|8WL@KtCnk/dPiY$!,!@< 'zu"lwO=^ cl<̗L֥P&Z]Җ3YW(WK97Bx&}@X5gx4 ۃEi°ioĐ .ALwZ&R/,_dɽTf2UPT'W5 4Py{}\Pģ&05m\eY:5WT?kvҐG\0uU`, XXoVYb>>Z8N)Sy^ʨpzCG3/zdRUCR3ʸcGoP2ޛ;K΃pyE$ yfK"B&v#/+' DEFAULT NULL'; } if (!empty($col['EXTRA'])) { $def .= ' ' . strtoupper($col['EXTRA']); } return $def; } function cols_are_equal($a, $b) { return strtolower($a['COLUMN_TYPE']) === strtolower($b['COLUMN_TYPE']) && $a['IS_NULLABLE'] === $b['IS_NULLABLE'] && $a['COLUMN_DEFAULT'] === $b['COLUMN_DEFAULT'] && strtolower($a['EXTRA']) === strtolower($b['EXTRA']); } // ============================================================ // COMPARACIÓN COMPLETA // ============================================================ function do_compare($conn, $db_modelo, $db_antigua) { $tables_modelo = db_get_tables($conn, $db_modelo); $tables_antigua = db_get_tables($conn, $db_antigua); $result = array( 'db_modelo' => $db_modelo, 'db_antigua' => $db_antigua, 'new_tables' => array(), // en modelo, no en antigua 'diff_tables' => array(), // en ambas, con diferencias 'extra_tables' => array(), // en antigua, no en modelo ); $antigua_map = array_flip($tables_antigua); $modelo_map = array_flip($tables_modelo); // Tablas nuevas (CREATE) foreach ($tables_modelo as $t) { if (!isset($antigua_map[$t])) { $create_sql = db_get_create_table($conn, $db_modelo, $t); $result['new_tables'][] = array( 'table' => $t, 'sql' => $create_sql . ';', ); } } // Tablas extra en antigua (sólo info) foreach ($tables_antigua as $t) { if (!isset($modelo_map[$t])) { $result['extra_tables'][] = $t; } } // Tablas en ambas: comparar columnas foreach ($tables_modelo as $t) { if (!isset($antigua_map[$t])) continue; $cols_modelo = db_get_columns($conn, $db_modelo, $t); $cols_antigua = db_get_columns($conn, $db_antigua, $t); $changes = array(); $prev_col = null; foreach ($cols_modelo as $col_name => $col_def) { if (!isset($cols_antigua[$col_name])) { // Columna faltante → ADD $alter = "ALTER TABLE `$t` ADD COLUMN " . col_definition_sql($col_def); if ($prev_col) { $alter .= " AFTER `$prev_col`"; } else { $alter .= " FIRST"; } $alter .= ';'; $changes[] = array( 'type' => 'add', 'col' => $col_name, 'sql' => $alter, 'modelo' => $col_def['COLUMN_TYPE'], 'actual' => '—', ); } else { // Columna existe: comparar tipo/nullable/default/extra if (!cols_are_equal($col_def, $cols_antigua[$col_name])) { $alter = "ALTER TABLE `$t` MODIFY COLUMN " . col_definition_sql($col_def) . ';'; $changes[] = array( 'type' => 'modify', 'col' => $col_name, 'sql' => $alter, 'modelo' => $col_def['COLUMN_TYPE'] . ($col_def['IS_NULLABLE']==='NO' ? ' NOT NULL' : '') . ($col_def['COLUMN_DEFAULT']!==null ? " DEFAULT '{$col_def['COLUMN_DEFAULT']}'" : '') . (!empty($col_def['EXTRA']) ? ' '.$col_def['EXTRA'] : ''), 'actual' => $cols_antigua[$col_name]['COLUMN_TYPE'] . ($cols_antigua[$col_name]['IS_NULLABLE']==='NO' ? ' NOT NULL' : '') . ($cols_antigua[$col_name]['COLUMN_DEFAULT']!==null ? " DEFAULT '{$cols_antigua[$col_name]['COLUMN_DEFAULT']}'" : '') . (!empty($cols_antigua[$col_name]['EXTRA']) ? ' '.$cols_antigua[$col_name]['EXTRA'] : ''), ); } } $prev_col = $col_name; } if (!empty($changes)) { $result['diff_tables'][] = array( 'table' => $t, 'changes' => $changes, ); } } return $result; } // ============================================================ // APLICAR SQL // ============================================================ function apply_sql($conn, $db, $sql_statements) { $conn->select_db($db); // Desactivar modo estricto para permitir fechas cero y defaults vacíos $conn->query("SET SESSION sql_mode = 'NO_ENGINE_SUBSTITUTION'"); // Desactivar FK checks para evitar errores por dependencias entre tablas $conn->query("SET FOREIGN_KEY_CHECKS = 0"); $results = array(); foreach ($sql_statements as $sql) { $sql = trim($sql); if (empty($sql)) continue; $ok = $conn->query($sql); $results[] = array( 'sql' => $sql, 'ok' => $ok !== false, 'error' => $ok === false ? $conn->error : '', ); } // Restaurar FK checks $conn->query("SET FOREIGN_KEY_CHECKS = 1"); return $results; } // ============================================================ // PROCESS REQUEST // ============================================================ $action = isset($_POST['action']) ? $_POST['action'] : ''; $host = isset($_POST['host']) ? trim($_POST['host']) : $default_host; $user = isset($_POST['user']) ? trim($_POST['user']) : $default_user; $pass = isset($_POST['pass']) ? $_POST['pass'] : $default_pass; $port = isset($_POST['port']) ? (int)$_POST['port'] : $default_port; $db_modelo = isset($_POST['db_modelo']) ? $_POST['db_modelo'] : ''; $db_antigua = isset($_POST['db_antigua']) ? $_POST['db_antigua'] : ''; $conn = null; $databases = array(); $errors = array(); $compare = null; $apply_log = array(); $connected = false; if (in_array($action, array('load_dbs', 'compare', 'apply_selected', 'apply_all', 'clone_table'))) { $conn = db_connect($host, $user, $pass, $port); if (!$conn) { $errors[] = 'No se pudo conectar a MySQL: ' . mysqli_connect_error(); } else { $connected = true; $databases = db_get_databases($conn); } } if ($action === 'compare' && $connected && $db_modelo && $db_antigua) { if ($db_modelo === $db_antigua) { $errors[] = 'Las dos bases de datos no pueden ser la misma.'; } else { $compare = do_compare($conn, $db_modelo, $db_antigua); } } if (($action === 'apply_all' || $action === 'apply_selected') && $connected && $db_modelo && $db_antigua) { $sqls_to_run = array(); if ($action === 'apply_all') { // Rehacer comparación y recopilar todos los SQL $compare = do_compare($conn, $db_modelo, $db_antigua); foreach ($compare['new_tables'] as $nt) { $sqls_to_run[] = $nt['sql']; } foreach ($compare['diff_tables'] as $dt) { foreach ($dt['changes'] as $ch) { $sqls_to_run[] = $ch['sql']; } } } else { // Solo los SQL seleccionados por checkbox $sqls_to_run = isset($_POST['selected_sqls']) ? (array)$_POST['selected_sqls'] : array(); // Reconstruir compare para mostrar panel $compare = do_compare($conn, $db_modelo, $db_antigua); } if (!empty($sqls_to_run)) { $apply_log = apply_sql($conn, $db_antigua, $sqls_to_run); // Refrescar compare después de aplicar $compare = do_compare($conn, $db_modelo, $db_antigua); } } if ($action === 'clone_table' && $connected && $db_modelo && $db_antigua) { $clone_table = isset($_POST['clone_table']) ? $_POST['clone_table'] : ''; if ($clone_table) { $create_sql = db_get_create_table($conn, $db_modelo, $clone_table); if ($create_sql) { $conn->select_db($db_antigua); // Verificar si ya existe y dropear $conn->query("DROP TABLE IF EXISTS `$clone_table`"); $ok = $conn->query($create_sql); $apply_log = array(array( 'sql' => $create_sql, 'ok' => $ok !== false, 'error' => $ok === false ? $conn->error : '', )); $compare = do_compare($conn, $db_modelo, $db_antigua); } } } $total_changes = $compare ? (count($compare['new_tables']) + count($compare['diff_tables'])) : 0; ?> Migración de Base de Datos

🗄️ Migración / Comparación de Bases de Datos

Compara estructuras, detecta diferencias y aplica cambios de forma segura
⚠️ ' . htmlspecialchars($e) . '
'; ?>
🔌 Conexión MySQL
✅ Conectado — bases de datos
🌿 Seleccionar Bases de Datos
➡️
⌨️ Resultado de la Aplicación

exitosos   0): ?> con error

➕ Tablas nuevas
✏️ Tablas con diferencias
📦 Tablas extra en antigua
✅ Total cambios
¡Las bases de datos tienen la misma estructura! No hay cambios necesarios.
0): ?>
📊 Tablas Nuevas Existen en pero NO en
$nt): ?>
📊
⚠️ Tablas con Diferencias en Columnas Requieren ALTER TABLE
🟰 cambio(s)
Columna Tipo en Modelo Tipo en Antigua Operación SQL
ADD MODIFY
0): ?>
Solo se aplicarán los ítems con ✓ marcado
📦 Tablas solo en "" No existen en el modelo — solo informativo, no se eliminan
📊

🛡️ Estas tablas NO se eliminarán automáticamente para proteger tus datos. Si deseas eliminarlas, hazlo manualmente desde phpMyAdmin o MySQL Workbench.