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";