make(Kernel::class)->bootstrap(); portalQaAssertTarget($target); $pdo = DB::connection()->getPdo(); $directory = storage_path('app/private/investmoz-v2/portal-qa-preservation/'.gmdate('Ymd_His')); try { $pdo->exec('BEGIN ISOLATION LEVEL REPEATABLE READ READ ONLY'); $identity = $pdo->query("SELECT current_database() AS database, inet_server_port() AS port, current_setting('data_directory') AS data_directory")->fetch(PDO::FETCH_ASSOC); if ((int) $identity['port'] !== 55441 || $identity['database'] !== $target['database']) { throw new RuntimeException('QA identity changed.'); } mkdir($directory, 0700, true); $tables = []; $quote = fn (string $name): string => '"'.str_replace('"', '""', $name).'"'; foreach ($pdo->query("SELECT schemaname,tablename FROM pg_tables WHERE schemaname NOT IN ('pg_catalog','information_schema') ORDER BY schemaname,tablename")->fetchAll(PDO::FETCH_ASSOC) as $table) { $name = $quote($table['schemaname']).'.'.$quote($table['tablename']); $file = sprintf('%04d.ndjson', count($tables) + 1); $stream = fopen($directory.'/'.$file, 'xb'); $statement = $pdo->query('SELECT row_to_json(t)::text FROM '.$name.' t ORDER BY row_to_json(t)::text'); $rows = 0; while (($row = $statement->fetchColumn()) !== false) { if (fwrite($stream, $row."\n") !== strlen($row) + 1) { throw new RuntimeException('Private preservation write incomplete.'); } $rows++; } fclose($stream); $tables[$name] = ['file' => $file, 'rows' => $rows, 'sha256' => hash_file('sha256', $directory.'/'.$file)]; } $sequences = []; foreach ($pdo->query("SELECT schemaname,sequencename FROM pg_sequences WHERE schemaname NOT IN ('pg_catalog','information_schema') ORDER BY schemaname,sequencename")->fetchAll(PDO::FETCH_ASSOC) as $sequence) { $name = $quote($sequence['schemaname']).'.'.$quote($sequence['sequencename']); $sequences[$name] = $pdo->query('SELECT last_value,is_called FROM '.$name)->fetch(PDO::FETCH_ASSOC); } $missing = []; foreach ($pdo->query("SELECT oid,relname,pg_relation_filepath(oid) AS path FROM pg_class WHERE relisshared AND relkind IN ('r','i','t') ORDER BY oid")->fetchAll(PDO::FETCH_ASSOC) as $relation) { if (! is_file($identity['data_directory'].'/'.$relation['path'])) { $missing[] = $relation; } } $pdo->exec('ROLLBACK'); $proof = ['created_at' => gmdate('c'), 'target' => $identity, 'transaction_read_only' => true, 'tables' => $tables, 'sequences' => $sequences, 'missing_shared_catalog_files' => $missing, 'active_access' => false, 'database_writes' => 0, 'restore_verified' => false, 'scope' => 'Readable table bytes and sequence values preserved privately; schema/roles/catalog integrity and restoration NOT certified. Sequence values are not MVCC snapshots.']; file_put_contents($directory.'/manifest.json', json_encode($proof, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR)); $receipt = ['created_at' => $proof['created_at'], 'target' => ['database' => $identity['database'], 'port' => $identity['port']], 'directory' => $directory, 'tables' => count($tables), 'rows' => array_sum(array_column($tables, 'rows')), 'sequences' => count($sequences), 'missing_shared_files' => count($missing), 'transaction_read_only' => true, 'database_writes' => 0, 'active_access' => false, 'restore_verified' => false]; file_put_contents(storage_path('app/private/investmoz-v2/portal-audit/qa-readable-preservation.json'), json_encode($receipt, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR)); echo json_encode($receipt, JSON_THROW_ON_ERROR).PHP_EOL; } catch (Throwable $error) { if ($pdo->inTransaction()) { $pdo->rollBack(); } fwrite(STDERR, 'QA read-only preservation failed; no verified backup receipt produced.'.PHP_EOL); exit(1); }