140 lines
3.8 KiB
PHP
140 lines
3.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
function parseDatabaseUrl(string $url): array
|
|
{
|
|
$parts = parse_url($url);
|
|
if ($parts === false) {
|
|
throw new RuntimeException('Invalid database URL.');
|
|
}
|
|
|
|
$host = $parts['host'] ?? 'localhost';
|
|
$port = isset($parts['port']) ? (int) $parts['port'] : 5432;
|
|
$user = $parts['user'] ?? '';
|
|
$pass = $parts['pass'] ?? '';
|
|
$db = ltrim($parts['path'] ?? '', '/');
|
|
|
|
return [
|
|
'dsn' => sprintf('pgsql:host=%s;port=%d;dbname=%s', $host, $port, $db),
|
|
'user' => $user,
|
|
'pass' => $pass,
|
|
];
|
|
}
|
|
|
|
function connect(string $url): PDO
|
|
{
|
|
$config = parseDatabaseUrl($url);
|
|
$pdo = new PDO($config['dsn'], $config['user'], $config['pass'], [
|
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
|
]);
|
|
|
|
return $pdo;
|
|
}
|
|
|
|
$sourceUrl = getenv('SOURCE_DATABASE_URL') ?: '';
|
|
$targetUrl = getenv('TARGET_DATABASE_URL') ?: '';
|
|
|
|
if ($sourceUrl === '' || $targetUrl === '') {
|
|
fwrite(STDERR, "Usage: SOURCE_DATABASE_URL=... TARGET_DATABASE_URL=... php scripts/validate-migration.php\n");
|
|
exit(1);
|
|
}
|
|
|
|
$tables = [
|
|
'sites',
|
|
'type_machines',
|
|
'machines',
|
|
'model_types',
|
|
'composants',
|
|
'pieces',
|
|
'products',
|
|
'constructeurs',
|
|
'documents',
|
|
'custom_fields',
|
|
'custom_field_values',
|
|
'machine_component_links',
|
|
'machine_piece_links',
|
|
'machine_product_links',
|
|
'type_machine_component_requirements',
|
|
'type_machine_piece_requirements',
|
|
'type_machine_product_requirements',
|
|
'_machineconstructeurs',
|
|
'_composantconstructeurs',
|
|
'_piececonstructeurs',
|
|
'_productconstructeurs',
|
|
'profiles',
|
|
];
|
|
|
|
$skipTables = array_filter(array_map('trim', explode(',', getenv('SKIP_TABLES') ?: 'profiles')));
|
|
|
|
$sourceTableMap = [
|
|
'model_types' => ['ModelType', 'model_types'],
|
|
'_machineconstructeurs' => ['_MachineConstructeurs', '_machineconstructeurs'],
|
|
'_composantconstructeurs' => ['_ComposantConstructeurs', '_composantconstructeurs'],
|
|
'_piececonstructeurs' => ['_PieceConstructeurs', '_piececonstructeurs'],
|
|
'_productconstructeurs' => ['_ProductConstructeurs', '_productconstructeurs'],
|
|
];
|
|
|
|
function resolveTable(PDO $db, array $candidates): ?string
|
|
{
|
|
foreach ($candidates as $candidate) {
|
|
$exists = (bool) $db
|
|
->query(sprintf("SELECT to_regclass('public.%s')", $candidate))
|
|
->fetchColumn();
|
|
if ($exists) {
|
|
return $candidate;
|
|
}
|
|
|
|
$quoted = (bool) $db
|
|
->query(sprintf("SELECT to_regclass('public.\"%s\"')", $candidate))
|
|
->fetchColumn();
|
|
if ($quoted) {
|
|
return sprintf('"%s"', $candidate);
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
$source = connect($sourceUrl);
|
|
$target = connect($targetUrl);
|
|
|
|
$hasDifferences = false;
|
|
|
|
foreach ($tables as $table) {
|
|
if (in_array($table, $skipTables, true)) {
|
|
continue;
|
|
}
|
|
$sourceCandidates = $sourceTableMap[$table] ?? [$table];
|
|
$sourceTable = resolveTable($source, $sourceCandidates);
|
|
$sourceExists = $sourceTable !== null;
|
|
$targetExists = (bool) $target
|
|
->query(sprintf("SELECT to_regclass('public.%s')", $table))
|
|
->fetchColumn();
|
|
|
|
if (!$sourceExists || !$targetExists) {
|
|
$hasDifferences = true;
|
|
printf(
|
|
"%s: source=%s target=%s\n",
|
|
$table,
|
|
$sourceExists ? 'exists' : 'missing',
|
|
$targetExists ? 'exists' : 'missing'
|
|
);
|
|
continue;
|
|
}
|
|
|
|
$sourceCount = (int) $source->query(sprintf('SELECT COUNT(*) FROM public.%s', $sourceTable))->fetchColumn();
|
|
$targetCount = (int) $target->query(sprintf('SELECT COUNT(*) FROM public.%s', $table))->fetchColumn();
|
|
|
|
if ($sourceCount !== $targetCount) {
|
|
$hasDifferences = true;
|
|
printf("%s: source=%d target=%d\n", $table, $sourceCount, $targetCount);
|
|
}
|
|
}
|
|
|
|
if ($hasDifferences) {
|
|
exit(2);
|
|
}
|
|
|
|
echo "Counts match for all tables.\n";
|