2 Commits

Author SHA1 Message Date
gitea-actions
2145d2cf9b chore: bump version to v0.1.29
All checks were successful
Auto Tag Develop / tag (push) Successful in 5s
Build & Push Docker Image / build (push) Successful in 19s
2026-04-08 14:21:24 +00:00
Matthieu
ce744b3aba fix : connect to target database for table count and largest table queries
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>
2026-04-08 16:21:15 +02:00
2 changed files with 31 additions and 16 deletions

View File

@@ -1,2 +1,2 @@
parameters: parameters:
app.version: '0.1.28' app.version: '0.1.29'

View File

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