From 90692a84c675ca00244d28860453a5f601ff0662 Mon Sep 17 00:00:00 2001 From: DevSoftTech Server Date: Wed, 4 Mar 2026 03:49:46 +0530 Subject: [PATCH] Skip FK-protected modifies and unsafe auto-increment/PK sync changes --- app/Controllers/Setup.php | 110 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/app/Controllers/Setup.php b/app/Controllers/Setup.php index 3d98357..880bef3 100644 --- a/app/Controllers/Setup.php +++ b/app/Controllers/Setup.php @@ -835,12 +835,28 @@ class Setup extends BaseController $clientCols = $this->readColumns($client, $table); $clientIdx = $this->readIndexes($client, $table); $tableCollation = $this->readTableCollation($client, $table); + $fkProtectedCols = $this->readForeignKeyProtectedColumns($client, $table); + $hasAutoIncrement = $this->tableHasAutoIncrementColumn($clientCols); $prevCol = ''; foreach ($masterCols as $colName => $masterCol) { if (!isset($clientCols[$colName])) { + if ($this->isAutoIncrementColumn((array) $masterCol) && $hasAutoIncrement) { + $prevCol = $colName; + continue; + } + $position = $prevCol === '' ? ' FIRST' : (' AFTER `' . $prevCol . '`'); $columnAlterClauses[] = 'ADD COLUMN ' . $this->columnDefinition($masterCol) . $position; + $clientCols[$colName] = (array) $masterCol; + if ($this->isAutoIncrementColumn((array) $masterCol)) { + $hasAutoIncrement = true; + } + $prevCol = $colName; + continue; + } + + if (isset($fkProtectedCols[strtolower((string) $colName)])) { $prevCol = $colName; continue; } @@ -858,6 +874,10 @@ class Setup extends BaseController $masterIdx = is_array($masterTableDef['indexes'] ?? null) ? $masterTableDef['indexes'] : []; foreach ($masterIdx as $keyName => $masterDef) { + if (!$this->areIndexColumnsAvailable((array) $masterDef, $clientCols)) { + continue; + } + if (!isset($clientIdx[$keyName])) { if ($this->hasEquivalentIndexDefinition($clientIdx, (array) $masterDef)) { continue; @@ -900,6 +920,96 @@ class Setup extends BaseController return $sql; } + /** + * @param array $idxDef + * @param array> $clientCols + */ + private function areIndexColumnsAvailable(array $idxDef, array $clientCols): bool + { + $columns = array_values(array_filter(array_map(static fn ($col) => (string) $col, (array) ($idxDef['columns'] ?? [])))); + if (empty($columns)) { + return false; + } + + foreach ($columns as $column) { + if (!isset($clientCols[$column])) { + return false; + } + } + + return true; + } + + /** + * @param array> $clientCols + */ + private function tableHasAutoIncrementColumn(array $clientCols): bool + { + foreach ($clientCols as $colMeta) { + if ($this->isAutoIncrementColumn((array) $colMeta)) { + return true; + } + } + + return false; + } + + /** + * @param array $colMeta + */ + private function isAutoIncrementColumn(array $colMeta): bool + { + $extra = strtolower(trim((string) ($colMeta['Extra'] ?? ''))); + return str_contains($extra, 'auto_increment'); + } + + /** + * @return array + */ + private function readForeignKeyProtectedColumns($db, string $table): array + { + $safeTable = preg_replace('/[^a-zA-Z0-9_]/', '', $table) ?? ''; + if ($safeTable === '') { + return []; + } + + try { + $dbNameRow = $db->query('SELECT DATABASE() AS db_name')->getRowArray(); + $dbName = (string) ($dbNameRow['db_name'] ?? ''); + if ($dbName === '') { + return []; + } + + $safeDb = str_replace("'", "''", $dbName); + + $out = []; + + $outbound = $db->query( + "SELECT COLUMN_NAME FROM information_schema.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA='{$safeDb}' AND TABLE_NAME='{$safeTable}' AND REFERENCED_TABLE_NAME IS NOT NULL" + )->getResultArray(); + foreach ($outbound as $row) { + $name = strtolower((string) ($row['COLUMN_NAME'] ?? '')); + if ($name !== '') { + $out[$name] = true; + } + } + + $inbound = $db->query( + "SELECT REFERENCED_COLUMN_NAME AS COLUMN_NAME FROM information_schema.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA='{$safeDb}' AND REFERENCED_TABLE_NAME='{$safeTable}' AND REFERENCED_COLUMN_NAME IS NOT NULL" + )->getResultArray(); + foreach ($inbound as $row) { + $name = strtolower((string) ($row['COLUMN_NAME'] ?? '')); + if ($name !== '') { + $out[$name] = true; + } + } + + return $out; + } catch (\Throwable $e) { + return []; + } + } + /** * @param array> $clientIdx * @param array $masterDef