add incremental migration runner to deploy recipe — execute whole SQL files (not semicolon-split), catch 'no such column' for idempotent re-runs, merge into migrate.sh

This commit is contained in:
Pontoporeia
2026-05-11 15:54:09 +02:00
parent c1960d224b
commit cb6394e119
4 changed files with 84 additions and 39 deletions

View File

@@ -64,38 +64,26 @@ foreach ($files as $name => $file) {
echo "Applying: $name\n";
$sql = file_get_contents($file);
// Split into individual statements to handle partial failures gracefully
// (e.g. ALTER TABLE may fail with "duplicate column" but DROP VIEW must still run)
$statements = array_filter(
array_map('trim', explode(';', $sql)),
fn($s) => $s !== ''
);
$errors = [];
foreach ($statements as $stmt) {
try {
$pdo->exec($stmt . ';');
} catch (PDOException $e) {
$msg = $e->getMessage();
if (stripos($msg, 'duplicate column name') !== false
|| stripos($msg, 'no such column') !== false) {
echo " Skipping (already applied): " . substr($stmt, 0, 60) . "...\n";
continue;
}
$errors[] = $msg;
try {
$pdo->exec($sql);
} catch (PDOException $e) {
$msg = $e->getMessage();
// Ignore idempotent errors (column/trigger/index already exists or already removed)
if (stripos($msg, 'duplicate column name') !== false
|| stripos($msg, 'already exists') !== false
|| stripos($msg, 'no such column') !== false) {
echo " Skipping (already applied)\n";
} else {
echo " FAILED: $msg\n";
throw $e;
}
}
if (empty($errors)) {
$pdo->prepare("INSERT OR REPLACE INTO _migrations (name) VALUES (?)")->execute([$name]);
if (str_starts_with($file, $pendingDir)) {
rename($file, $appliedDir . '/' . $name);
}
$count++;
} else {
echo " FAILED: " . implode(' | ', $errors) . "\n";
throw new PDOException(implode(' | ', $errors));
$pdo->prepare("INSERT OR REPLACE INTO _migrations (name) VALUES (?)")->execute([$name]);
if (str_starts_with($file, $pendingDir)) {
rename($file, $appliedDir . '/' . $name);
}
$count++;
}
echo "$count migration(s) applied.\n";