diff --git a/modules/mining-checker/src/Infrastructure/SchemaManager.php b/modules/mining-checker/src/Infrastructure/SchemaManager.php index 2bd049d..bca4937 100644 --- a/modules/mining-checker/src/Infrastructure/SchemaManager.php +++ b/modules/mining-checker/src/Infrastructure/SchemaManager.php @@ -429,19 +429,18 @@ final class SchemaManager $this->pdo->beginTransaction(); } - $executed = 0; + $normalizedStatements = []; foreach ($statements as $statement) { $trimmed = trim($statement); if ($trimmed === '') { continue; } - $normalizedStatement = $this->normalizeImportStatement($trimmed); - $currentStatement = $normalizedStatement; - $this->pdo->exec($normalizedStatement); - $executed++; + $normalizedStatements[] = $this->normalizeImportStatement($trimmed); } + $executed = $this->executeImportPass($normalizedStatements, $currentStatement); + if ($useTransaction && $this->pdo->inTransaction()) { $this->pdo->commit(); } @@ -464,6 +463,65 @@ final class SchemaManager } } + /** + * @param list $statements + */ + private function executeImportPass(array $statements, ?string &$currentStatement): int + { + $pendingStatements = $statements; + $executed = 0; + $maxPasses = max(1, count($pendingStatements)); + + for ($pass = 0; $pass < $maxPasses && $pendingStatements !== []; $pass++) { + $deferredStatements = []; + $progressMade = false; + + foreach ($pendingStatements as $statement) { + $currentStatement = $statement; + + try { + $this->pdo->exec($statement); + $executed++; + $progressMade = true; + } catch (\Throwable $exception) { + if ($this->shouldRetryDeferredImportStatement($exception, $statement)) { + $deferredStatements[] = $statement; + continue; + } + + throw $exception; + } + } + + if ($deferredStatements === []) { + return $executed; + } + + if (!$progressMade) { + $currentStatement = $deferredStatements[0]; + $this->pdo->exec($deferredStatements[0]); + } + + $pendingStatements = $deferredStatements; + } + + return $executed; + } + + private function shouldRetryDeferredImportStatement(\Throwable $exception, string $statement): bool + { + if ($this->driver !== 'pgsql') { + return false; + } + + if (!preg_match('/^(INSERT|COPY)\\b/i', ltrim($statement))) { + return false; + } + + $errorCode = (string) $exception->getCode(); + return $errorCode === '23503'; + } + private function normalizeImportStatement(string $statement): string { if ($this->driver !== 'pgsql') {