param( [switch]$DryRun, [Alias('Host')][string]$TargetHost = '127.0.0.1', [int]$Port = 3306, [string]$InputPath, [string]$MysqlExe = 'C:\Program Files\MySQL\MySQL Server 8.4\bin\mysql.exe', [string]$User = 'root' ) $ErrorActionPreference = 'Stop' $Repo = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path $Workspace = (Resolve-Path (Join-Path $Repo '..')).Path $Acceptance = Join-Path $Workspace '.codex-tmp\p23-04-acceptance' $ExpectedSha256 = '3E0A02C2E3B971AB7D8AB9C9D8AEE1D933F950DBCE82BD9B6AD431A4DD179F8A' $ExpectedLines = 14713 if ($TargetHost -notin @('127.0.0.1', 'localhost')) { [Console]::Error.WriteLine('Refusing non-local database; only 127.0.0.1 or localhost is allowed.') exit 2 } if ([string]::IsNullOrWhiteSpace($InputPath)) { $InputPath = (Get-ChildItem (Join-Path $Repo 'deploy\sql') -Filter '*MySQL8.sql' -File | Where-Object { $_.Length -gt 100000 } | Select-Object -First 1).FullName } if (-not (Test-Path -LiteralPath $InputPath)) { [Console]::Error.WriteLine("InputPath does not exist: $InputPath") exit 2 } $actual = ((Get-FileHash -Algorithm SHA256 -LiteralPath $InputPath).Hash).Trim().ToUpperInvariant() $lines = [System.IO.File]::ReadAllLines($InputPath).Length if ($actual -ne $ExpectedSha256 -or $lines -ne $ExpectedLines) { [Console]::Error.WriteLine("Unified SQL snapshot mismatch: $actual/$lines") exit 2 } $sqlText = [System.IO.File]::ReadAllText($InputPath) $commentGateTokens = @( 'CREATE PROCEDURE queue_column_comment', 'CREATE PROCEDURE apply_one_table_comments', 'CALL apply_queued_comments();', '字段注释批量升级改变了结构签名', '存在缺失中文注释的业务表', '存在缺失中文注释的业务字段' ) foreach ($token in $commentGateTokens) { if (-not $sqlText.Contains($token)) { [Console]::Error.WriteLine("Unified SQL comment acceptance marker missing: $token") exit 2 } } if ([regex]::Matches($sqlText, "SELECT 'PASS' AS migration_status").Count -ne 1) { [Console]::Error.WriteLine('Unified SQL must contain exactly one final PASS statement') exit 2 } $sequence = @( 'empty: base.sql', 'replay: base.sql + input.sql + input.sql', 'smart-old: fixture-smart-old.sql', 'inbound-old: fixture-inbound-old.sql', 'waitsplit-iam: fixture-test-shape.sql', 'overseas-old: fixture-overseas-old.sql', 'overseas-missing-objects: fixture-overseas-missing-objects.sql', 'interrupted-recovery: fixture-interrupted.sql', 'final-structure: verify-final.sql') Write-Output "SQL SHA-256: $actual / lines: $lines" Write-Output 'Comment gate: queued validation, per-table ALTER, signature verification, nine-schema coverage, unique PASS' Write-Output "Target: $TargetHost`:$Port" Write-Output 'Fixture order:' $sequence | ForEach-Object { Write-Output " $_" } if ($DryRun) { Write-Output 'DRY-RUN PASS (mysql not started; no database writes)'; exit 0 } $acceptanceInput = Join-Path $Acceptance 'input.sql' Copy-Item -LiteralPath $InputPath -Destination $acceptanceInput -Force $stagedHash = ((Get-FileHash -Algorithm SHA256 -LiteralPath $acceptanceInput).Hash).Trim().ToUpperInvariant() if ($stagedHash -ne $actual) { [Console]::Error.WriteLine("Failed to stage the validated unified SQL snapshot: $stagedHash") exit 2 } $run = 0 function Invoke-Stage([string]$Name, [string]$File) { $script = Join-Path $Acceptance $File $log = Join-Path $Acceptance ("acceptance-run-{0:D2}-{1}.log" -f $script:run, $Name) $script:run++ if (-not (Test-Path -LiteralPath $script)) { [Console]::Error.WriteLine("Script does not exist: $script"); return 2 } & $MysqlExe '--protocol=TCP' "--host=$TargetHost" "--port=$Port" "--user=$User" '--database=mysql' '--batch' ("--execute=source " + $script.Replace('\','/')) *> $log return $LASTEXITCODE } $calls = @( @('empty','base.sql'), @('replay-reset','base.sql'), @('replay-1','input.sql'), @('replay-2','input.sql'), @('smart-old','fixture-smart-old.sql'), @('inbound-old','fixture-inbound-old.sql'), @('waitsplit-iam','fixture-test-shape.sql'), @('overseas-old','fixture-overseas-old.sql'), @('overseas-missing','fixture-overseas-missing-objects.sql'), @('interrupted','fixture-interrupted.sql'), @('final-verify','verify-final.sql')) foreach ($call in $calls) { $code = Invoke-Stage $call[0] $call[1]; if ($code -ne 0) { exit $code } } Write-Output 'FULL LOCAL ACCEPTANCE PASS' exit 0