|
|
|
|
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
|
|
|
|
namespace App\Service;
|
|
|
|
|
|
|
|
|
|
use Doctrine\DBAL\Connection;
|
|
|
|
|
use Doctrine\DBAL\DriverManager;
|
|
|
|
|
|
|
|
|
|
final class DatabaseService
|
|
|
|
|
{
|
|
|
|
|
@@ -28,7 +29,7 @@ final class DatabaseService
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// Check database exists
|
|
|
|
|
// Check database exists (cross-database query via system catalog)
|
|
|
|
|
$exists = $this->connection->fetchOne(
|
|
|
|
|
'SELECT 1 FROM pg_database WHERE datname = :dbname',
|
|
|
|
|
['dbname' => $databaseName]
|
|
|
|
|
@@ -38,33 +39,40 @@ final class DatabaseService
|
|
|
|
|
return $fallback;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Database size
|
|
|
|
|
// Database size (cross-database, works from any connection)
|
|
|
|
|
$sizeBytes = (int) $this->connection->fetchOne(
|
|
|
|
|
'SELECT pg_database_size(:dbname)',
|
|
|
|
|
['dbname' => $databaseName]
|
|
|
|
|
);
|
|
|
|
|
$size = $this->formatBytes($sizeBytes);
|
|
|
|
|
|
|
|
|
|
// Table count
|
|
|
|
|
$tableCount = (int) $this->connection->fetchOne(
|
|
|
|
|
"SELECT count(*) FROM information_schema.tables WHERE table_schema = 'public' AND table_catalog = :dbname",
|
|
|
|
|
['dbname' => $databaseName]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Active connections
|
|
|
|
|
// Active connections (cross-database system view)
|
|
|
|
|
$activeConnections = (int) $this->connection->fetchOne(
|
|
|
|
|
'SELECT count(*) FROM pg_stat_activity WHERE datname = :dbname',
|
|
|
|
|
['dbname' => $databaseName]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Cache hit ratio
|
|
|
|
|
// Cache hit ratio (cross-database system view)
|
|
|
|
|
$cacheHitRatio = (float) ($this->connection->fetchOne(
|
|
|
|
|
'SELECT round(100.0 * sum(blks_hit) / nullif(sum(blks_hit + blks_read), 0), 2) FROM pg_stat_database WHERE datname = :dbname',
|
|
|
|
|
['dbname' => $databaseName]
|
|
|
|
|
) ?? 0);
|
|
|
|
|
|
|
|
|
|
// Largest table — requires querying the target database catalog
|
|
|
|
|
$largestTable = $this->fetchLargestTable($databaseName);
|
|
|
|
|
// Connect to the target database for table-specific queries
|
|
|
|
|
$targetConn = $this->connectToDatabase($databaseName);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// Table count (must query target database's information_schema)
|
|
|
|
|
$tableCount = (int) $targetConn->fetchOne(
|
|
|
|
|
"SELECT count(*) FROM information_schema.tables WHERE table_schema = 'public' AND table_catalog = :dbname",
|
|
|
|
|
['dbname' => $databaseName]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Largest table (must query target database's pg_class)
|
|
|
|
|
$largestTable = $this->fetchLargestTable($targetConn);
|
|
|
|
|
} finally {
|
|
|
|
|
$targetConn->close();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
'connected' => true,
|
|
|
|
|
@@ -80,15 +88,22 @@ final class DatabaseService
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function fetchLargestTable(string $databaseName): string
|
|
|
|
|
private function connectToDatabase(string $databaseName): Connection
|
|
|
|
|
{
|
|
|
|
|
$params = $this->connection->getParams();
|
|
|
|
|
$params['dbname'] = $databaseName;
|
|
|
|
|
|
|
|
|
|
return DriverManager::getConnection($params);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function fetchLargestTable(Connection $conn): string
|
|
|
|
|
{
|
|
|
|
|
try {
|
|
|
|
|
$row = $this->connection->fetchAssociative(
|
|
|
|
|
$row = $conn->fetchAssociative(
|
|
|
|
|
"SELECT relname, pg_total_relation_size(c.oid) as total_size
|
|
|
|
|
FROM pg_catalog.pg_class c
|
|
|
|
|
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
|
|
|
WHERE n.nspname = 'public' AND c.relkind = 'r'
|
|
|
|
|
AND c.relowner = (SELECT oid FROM pg_roles WHERE rolname = current_user)
|
|
|
|
|
ORDER BY pg_total_relation_size(c.oid) DESC
|
|
|
|
|
LIMIT 1"
|
|
|
|
|
);
|
|
|
|
|
|