From 59b93e9f782e60fa42cc8764ae0eeb841116b8ba Mon Sep 17 00:00:00 2001 From: Prodiglagla <348945921@qq.com> Date: Sun, 14 Jun 2026 12:46:49 +0800 Subject: [PATCH] add prod data migration schema preflight --- scripts/migrate-compatible-data.ps1 | 51 +++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/scripts/migrate-compatible-data.ps1 b/scripts/migrate-compatible-data.ps1 index a567c4a..c63f2c2 100644 --- a/scripts/migrate-compatible-data.ps1 +++ b/scripts/migrate-compatible-data.ps1 @@ -26,6 +26,23 @@ function Invoke-Postgres($Namespace, $Sql) { kubectl -n $Namespace exec statefulset/postgres -- sh -lc "PGPASSWORD=\`$POSTGRES_PASSWORD psql -U postgres -d $Database -Atc '$escapedSql'" } +function Get-TableColumns($Namespace, $TableName) { + $sql = "select column_name || '|' || udt_name || '|' || is_nullable || '|' || coalesce(column_default, '') from information_schema.columns where table_schema='public' and table_name='$TableName' order by ordinal_position" + $lines = @(Invoke-Postgres $Namespace $sql) + $columns = @() + foreach ($line in $lines) { + if (-not $line) { continue } + $parts = $line -split '\|', 4 + $columns += [pscustomobject]@{ + Name = $parts[0] + UdtName = $parts[1] + Nullable = $parts[2] + Default = if ($parts.Count -ge 4) { $parts[3] } else { '' } + } + } + return $columns +} + Write-Host "Compatible business tables planned for migration: $($tables.Count)" Write-Host "Excluded tables: databasechangelog, databasechangeloglock, V3 runtime audit/outbox/maintenance derived tables." @@ -36,6 +53,40 @@ $missingNew = $tables | Where-Object { $_ -notin $newTables } if ($missingOld) { throw "Old namespace is missing expected tables: $($missingOld -join ', ')" } if ($missingNew) { throw "New namespace is missing expected tables. Run ircs-prod core migrator first: $($missingNew -join ', ')" } +$schemaIssues = @() +foreach ($table in $tables) { + $oldColumns = @(Get-TableColumns $OldNamespace $table) + $newColumns = @(Get-TableColumns $NewNamespace $table) + $newByName = @{} + foreach ($column in $newColumns) { + $newByName[$column.Name] = $column + } + $oldByName = @{} + foreach ($column in $oldColumns) { + $oldByName[$column.Name] = $column + if (-not $newByName.ContainsKey($column.Name)) { + $schemaIssues += "${table}: old column '$($column.Name)' is missing in target" + continue + } + $target = $newByName[$column.Name] + if ($column.UdtName -ne $target.UdtName) { + $schemaIssues += "$table.$($column.Name): type mismatch old=$($column.UdtName) new=$($target.UdtName)" + } + } + foreach ($column in $newColumns) { + if (-not $oldByName.ContainsKey($column.Name) -and $column.Nullable -eq 'NO' -and -not $column.Default) { + $schemaIssues += "${table}: target required column '$($column.Name)' has no old value and no default" + } + } +} + +if ($schemaIssues.Count -gt 0) { + $schemaIssues | ForEach-Object { Write-Host "SCHEMA ISSUE: $_" } + throw "Schema preflight failed with $($schemaIssues.Count) issue(s)." +} + +Write-Host "Schema preflight passed for $($tables.Count) compatible business tables." + if (-not $Execute) { Write-Host "Dry run only. Add -Execute to stream data from $OldNamespace to $NewNamespace." exit 0