Produktiv- und Testtabellen per Prefix trennen
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require __DIR__ . '/dev-db.php';
|
||||
|
||||
if (PHP_SAPI !== 'cli') {
|
||||
http_response_code(404);
|
||||
exit;
|
||||
}
|
||||
|
||||
$options = getopt('', ['source-prefix:', 'target-prefix:', 'replace']);
|
||||
$sourcePrefix = app_validate_db_table_prefix((string)($options['source-prefix'] ?? ''));
|
||||
$targetOption = $options['target-prefix'] ?? null;
|
||||
if (!is_string($targetOption) || trim($targetOption) === '') {
|
||||
fwrite(STDERR, "Aufruf: php scripts/migrate-table-prefix.php --target-prefix=test_ --replace [--source-prefix=alt_]\n");
|
||||
exit(1);
|
||||
}
|
||||
$targetPrefix = app_validate_db_table_prefix($targetOption);
|
||||
|
||||
if ($sourcePrefix === $targetPrefix) {
|
||||
fwrite(STDERR, "Quell- und Ziel-Prefix müssen verschieden sein.\n");
|
||||
exit(1);
|
||||
}
|
||||
if (!array_key_exists('replace', $options)) {
|
||||
fwrite(STDERR, "Abbruch: --replace ist erforderlich, weil vorhandene Ziel-Testdaten ersetzt werden.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/** @return string */
|
||||
$quoteIdentifier = static function (string $name): string {
|
||||
return '`' . str_replace('`', '``', $name) . '`';
|
||||
};
|
||||
|
||||
$rawPdo = dev_pdo_for_prefix('');
|
||||
$targetPdo = dev_pdo_for_prefix($targetPrefix);
|
||||
|
||||
echo "Zielschema {$targetPrefix} wird auf den aktuellen Migrationsstand gebracht.\n";
|
||||
$applied = dev_apply_migrations($targetPdo);
|
||||
echo count($applied) . " neue Migration(en) für {$targetPrefix} angewendet.\n";
|
||||
|
||||
$tableExists = static function (PDO $pdo, string $table): bool {
|
||||
$stmt = $pdo->prepare(
|
||||
'SELECT 1 FROM INFORMATION_SCHEMA.TABLES
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?'
|
||||
);
|
||||
$stmt->execute([$table]);
|
||||
|
||||
return $stmt->fetchColumn() !== false;
|
||||
};
|
||||
|
||||
/** @return list<string> */
|
||||
$tableColumns = static function (PDO $pdo, string $table): array {
|
||||
$stmt = $pdo->prepare(
|
||||
'SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?
|
||||
ORDER BY ORDINAL_POSITION'
|
||||
);
|
||||
$stmt->execute([$table]);
|
||||
|
||||
return array_map('strval', $stmt->fetchAll(PDO::FETCH_COLUMN));
|
||||
};
|
||||
|
||||
$targetsToClear = [];
|
||||
$copies = [];
|
||||
$sourceTableCount = 0;
|
||||
|
||||
foreach (app_database_table_names() as $baseName) {
|
||||
if ($baseName === 'schema_migrations') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$sourceTable = app_db_table_name($baseName, $sourcePrefix);
|
||||
$targetTable = app_db_table_name($baseName, $targetPrefix);
|
||||
$sourceExists = $tableExists($rawPdo, $sourceTable);
|
||||
$targetExists = $tableExists($rawPdo, $targetTable);
|
||||
|
||||
if ($sourceExists) {
|
||||
$sourceTableCount++;
|
||||
}
|
||||
if ($sourceExists && !$targetExists) {
|
||||
throw new RuntimeException("Zieltabelle {$targetTable} fehlt nach den Migrationen.");
|
||||
}
|
||||
if (!$targetExists) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$targetsToClear[] = $targetTable;
|
||||
if (!$sourceExists) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$sourceColumns = $tableColumns($rawPdo, $sourceTable);
|
||||
$targetColumns = $tableColumns($rawPdo, $targetTable);
|
||||
if ($sourceColumns !== $targetColumns) {
|
||||
throw new RuntimeException("Spalten von {$sourceTable} und {$targetTable} stimmen nicht überein.");
|
||||
}
|
||||
$copies[] = [$sourceTable, $targetTable, $sourceColumns];
|
||||
}
|
||||
|
||||
if ($sourceTableCount === 0) {
|
||||
throw new RuntimeException('Im Quellschema wurden keine Anwendungstabellen gefunden.');
|
||||
}
|
||||
|
||||
$copiedRows = 0;
|
||||
$rawPdo->exec('SET FOREIGN_KEY_CHECKS = 0');
|
||||
try {
|
||||
$rawPdo->beginTransaction();
|
||||
|
||||
foreach ($targetsToClear as $targetTable) {
|
||||
$rawPdo->exec('DELETE FROM ' . $quoteIdentifier($targetTable));
|
||||
}
|
||||
|
||||
foreach ($copies as [$sourceTable, $targetTable, $columns]) {
|
||||
$quotedColumns = implode(', ', array_map($quoteIdentifier, $columns));
|
||||
$sourceRows = (int)$rawPdo
|
||||
->query('SELECT COUNT(*) FROM ' . $quoteIdentifier($sourceTable))
|
||||
->fetchColumn();
|
||||
|
||||
if ($sourceRows > 0) {
|
||||
$rawPdo->exec(
|
||||
'INSERT INTO ' . $quoteIdentifier($targetTable)
|
||||
. ' (' . $quotedColumns . ') SELECT ' . $quotedColumns
|
||||
. ' FROM ' . $quoteIdentifier($sourceTable)
|
||||
);
|
||||
}
|
||||
|
||||
$targetRows = (int)$rawPdo
|
||||
->query('SELECT COUNT(*) FROM ' . $quoteIdentifier($targetTable))
|
||||
->fetchColumn();
|
||||
if ($sourceRows !== $targetRows) {
|
||||
throw new RuntimeException(
|
||||
"Zeilenzahl stimmt für {$sourceTable} -> {$targetTable} nicht: {$sourceRows} != {$targetRows}."
|
||||
);
|
||||
}
|
||||
$copiedRows += $targetRows;
|
||||
echo "{$sourceTable} -> {$targetTable}: {$targetRows} Zeile(n)\n";
|
||||
}
|
||||
|
||||
$rawPdo->commit();
|
||||
} catch (Throwable $e) {
|
||||
if ($rawPdo->inTransaction()) {
|
||||
$rawPdo->rollBack();
|
||||
}
|
||||
throw $e;
|
||||
} finally {
|
||||
$rawPdo->exec('SET FOREIGN_KEY_CHECKS = 1');
|
||||
}
|
||||
|
||||
echo "Datenübernahme abgeschlossen: {$copiedRows} Zeile(n). Das Quellschema blieb unverändert.\n";
|
||||
Reference in New Issue
Block a user