fix : connect to target database for table count and largest table queries
Some checks failed
Auto Tag Develop / tag (push) Has been cancelled
Some checks failed
Auto Tag Develop / tag (push) Has been cancelled
information_schema and pg_class only show tables from the current database. Open a temporary connection to the target database for these queries. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||||||
namespace App\Service;
|
namespace App\Service;
|
||||||
|
|
||||||
use Doctrine\DBAL\Connection;
|
use Doctrine\DBAL\Connection;
|
||||||
|
use Doctrine\DBAL\DriverManager;
|
||||||
|
|
||||||
final class DatabaseService
|
final class DatabaseService
|
||||||
{
|
{
|
||||||
@@ -28,7 +29,7 @@ final class DatabaseService
|
|||||||
];
|
];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Check database exists
|
// Check database exists (cross-database query via system catalog)
|
||||||
$exists = $this->connection->fetchOne(
|
$exists = $this->connection->fetchOne(
|
||||||
'SELECT 1 FROM pg_database WHERE datname = :dbname',
|
'SELECT 1 FROM pg_database WHERE datname = :dbname',
|
||||||
['dbname' => $databaseName]
|
['dbname' => $databaseName]
|
||||||
@@ -38,33 +39,40 @@ final class DatabaseService
|
|||||||
return $fallback;
|
return $fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Database size
|
// Database size (cross-database, works from any connection)
|
||||||
$sizeBytes = (int) $this->connection->fetchOne(
|
$sizeBytes = (int) $this->connection->fetchOne(
|
||||||
'SELECT pg_database_size(:dbname)',
|
'SELECT pg_database_size(:dbname)',
|
||||||
['dbname' => $databaseName]
|
['dbname' => $databaseName]
|
||||||
);
|
);
|
||||||
$size = $this->formatBytes($sizeBytes);
|
$size = $this->formatBytes($sizeBytes);
|
||||||
|
|
||||||
// Table count
|
// Active connections (cross-database system view)
|
||||||
$tableCount = (int) $this->connection->fetchOne(
|
|
||||||
"SELECT count(*) FROM information_schema.tables WHERE table_schema = 'public' AND table_catalog = :dbname",
|
|
||||||
['dbname' => $databaseName]
|
|
||||||
);
|
|
||||||
|
|
||||||
// Active connections
|
|
||||||
$activeConnections = (int) $this->connection->fetchOne(
|
$activeConnections = (int) $this->connection->fetchOne(
|
||||||
'SELECT count(*) FROM pg_stat_activity WHERE datname = :dbname',
|
'SELECT count(*) FROM pg_stat_activity WHERE datname = :dbname',
|
||||||
['dbname' => $databaseName]
|
['dbname' => $databaseName]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Cache hit ratio
|
// Cache hit ratio (cross-database system view)
|
||||||
$cacheHitRatio = (float) ($this->connection->fetchOne(
|
$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',
|
'SELECT round(100.0 * sum(blks_hit) / nullif(sum(blks_hit + blks_read), 0), 2) FROM pg_stat_database WHERE datname = :dbname',
|
||||||
['dbname' => $databaseName]
|
['dbname' => $databaseName]
|
||||||
) ?? 0);
|
) ?? 0);
|
||||||
|
|
||||||
// Largest table — requires querying the target database catalog
|
// Connect to the target database for table-specific queries
|
||||||
$largestTable = $this->fetchLargestTable($databaseName);
|
$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 [
|
return [
|
||||||
'connected' => true,
|
'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 {
|
try {
|
||||||
$row = $this->connection->fetchAssociative(
|
$row = $conn->fetchAssociative(
|
||||||
"SELECT relname, pg_total_relation_size(c.oid) as total_size
|
"SELECT relname, pg_total_relation_size(c.oid) as total_size
|
||||||
FROM pg_catalog.pg_class c
|
FROM pg_catalog.pg_class c
|
||||||
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
||||||
WHERE n.nspname = 'public' AND c.relkind = 'r'
|
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
|
ORDER BY pg_total_relation_size(c.oid) DESC
|
||||||
LIMIT 1"
|
LIMIT 1"
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user