DatabaseInspectionCommand.php
TLDR
The DatabaseInspectionCommand
class is an abstract class that provides common functionality for inspecting database information. It contains methods for registering type mappings, getting the platform name, getting the size of a table, getting the number of open connections, getting the connection configuration details, ensuring dependencies exist, and installing dependencies.
Methods
registerTypeMappings
This method registers custom Doctrine type mappings for inspection commands. It takes an instance of Doctrine\DBAL\Platforms\AbstractPlatform
as a parameter.
getPlatformName
This method returns a human-readable platform name for the given platform. It takes an instance of Doctrine\DBAL\Platforms\AbstractPlatform
and a string representing the database name as parameters.
getTableSize
This method returns the size of a table in bytes. It takes an instance of Illuminate\Database\ConnectionInterface
and a string representing the table name as parameters.
getMySQLTableSize
This method returns the size of a MySQL table in bytes. It takes an instance of Illuminate\Database\ConnectionInterface
and a string representing the table name as parameters.
getPostgresTableSize
This method returns the size of a Postgres table in bytes. It takes an instance of Illuminate\Database\ConnectionInterface
and a string representing the table name as parameters.
getSqliteTableSize
This method returns the size of a SQLite table in bytes. It takes an instance of Illuminate\Database\ConnectionInterface
and a string representing the table name as parameters.
getConnectionCount
This method returns the number of open connections for a database. It takes an instance of Illuminate\Database\ConnectionInterface
as a parameter.
getConfigFromDatabase
This method returns the connection configuration details for the given connection. It takes a string representing the database name as a parameter.
ensureDependenciesExist
This method ensures that the dependencies for the database commands are available. It returns a boolean value indicating whether the dependencies exist.
installDependencies
This method installs the command's dependencies. It throws a Symfony\Component\Process\Exception\ProcessSignaledException
if the installation process is terminated by a signal.
<?php
namespace Illuminate\Database\Console;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Illuminate\Console\Command;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Database\MySqlConnection;
use Illuminate\Database\PostgresConnection;
use Illuminate\Database\QueryException;
use Illuminate\Database\SQLiteConnection;
use Illuminate\Database\SqlServerConnection;
use Illuminate\Support\Arr;
use Illuminate\Support\Composer;
use Symfony\Component\Process\Exception\ProcessSignaledException;
use Symfony\Component\Process\Exception\RuntimeException;
use Symfony\Component\Process\Process;
use function Laravel\Prompts\confirm;
abstract class DatabaseInspectionCommand extends Command
{
/**
* A map of database column types.
*
* @var array
*/
protected $typeMappings = [
'bit' => 'string',
'citext' => 'string',
'enum' => 'string',
'geometry' => 'string',
'geomcollection' => 'string',
'linestring' => 'string',
'ltree' => 'string',
'multilinestring' => 'string',
'multipoint' => 'string',
'multipolygon' => 'string',
'point' => 'string',
'polygon' => 'string',
'sysname' => 'string',
];
/**
* The Composer instance.
*
* @var \Illuminate\Support\Composer
*/
protected $composer;
/**
* Create a new command instance.
*
* @param \Illuminate\Support\Composer|null $composer
* @return void
*/
public function __construct(Composer $composer = null)
{
parent::__construct();
$this->composer = $composer ?? $this->laravel->make(Composer::class);
}
/**
* Register the custom Doctrine type mappings for inspection commands.
*
* @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
* @return void
*/
protected function registerTypeMappings(AbstractPlatform $platform)
{
foreach ($this->typeMappings as $type => $value) {
$platform->registerDoctrineTypeMapping($type, $value);
}
}
/**
* Get a human-readable platform name for the given platform.
*
* @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
* @param string $database
* @return string
*/
protected function getPlatformName(AbstractPlatform $platform, $database)
{
return match (class_basename($platform)) {
'MySQLPlatform' => 'MySQL <= 5',
'MySQL57Platform' => 'MySQL 5.7',
'MySQL80Platform' => 'MySQL 8',
'PostgreSQL100Platform', 'PostgreSQLPlatform' => 'Postgres',
'SqlitePlatform' => 'SQLite',
'SQLServerPlatform' => 'SQL Server',
'SQLServer2012Platform' => 'SQL Server 2012',
default => $database,
};
}
/**
* Get the size of a table in bytes.
*
* @param \Illuminate\Database\ConnectionInterface $connection
* @param string $table
* @return int|null
*/
protected function getTableSize(ConnectionInterface $connection, string $table)
{
return match (true) {
$connection instanceof MySqlConnection => $this->getMySQLTableSize($connection, $table),
$connection instanceof PostgresConnection => $this->getPostgresTableSize($connection, $table),
$connection instanceof SQLiteConnection => $this->getSqliteTableSize($connection, $table),
default => null,
};
}
/**
* Get the size of a MySQL table in bytes.
*
* @param \Illuminate\Database\ConnectionInterface $connection
* @param string $table
* @return mixed
*/
protected function getMySQLTableSize(ConnectionInterface $connection, string $table)
{
$result = $connection->selectOne('SELECT (data_length + index_length) AS size FROM information_schema.TABLES WHERE table_schema = ? AND table_name = ?', [
$connection->getDatabaseName(),
$table,
]);
return Arr::wrap((array) $result)['size'];
}
/**
* Get the size of a Postgres table in bytes.
*
* @param \Illuminate\Database\ConnectionInterface $connection
* @param string $table
* @return mixed
*/
protected function getPostgresTableSize(ConnectionInterface $connection, string $table)
{
$result = $connection->selectOne('SELECT pg_total_relation_size(?) AS size;', [
$table,
]);
return Arr::wrap((array) $result)['size'];
}
/**
* Get the size of a SQLite table in bytes.
*
* @param \Illuminate\Database\ConnectionInterface $connection
* @param string $table
* @return mixed
*/
protected function getSqliteTableSize(ConnectionInterface $connection, string $table)
{
try {
$result = $connection->selectOne('SELECT SUM(pgsize) AS size FROM dbstat WHERE name=?', [
$table,
]);
return Arr::wrap((array) $result)['size'];
} catch (QueryException) {
return null;
}
}
/**
* Get the number of open connections for a database.
*
* @param \Illuminate\Database\ConnectionInterface $connection
* @return int|null
*/
protected function getConnectionCount(ConnectionInterface $connection)
{
$result = match (true) {
$connection instanceof MySqlConnection => $connection->selectOne('show status where variable_name = "threads_connected"'),
$connection instanceof PostgresConnection => $connection->selectOne('select count(*) AS "Value" from pg_stat_activity'),
$connection instanceof SqlServerConnection => $connection->selectOne('SELECT COUNT(*) Value FROM sys.dm_exec_sessions WHERE status = ?', ['running']),
default => null,
};
if (! $result) {
return null;
}
return Arr::wrap((array) $result)['Value'];
}
/**
* Get the connection configuration details for the given connection.
*
* @param string $database
* @return array
*/
protected function getConfigFromDatabase($database)
{
$database ??= config('database.default');
return Arr::except(config('database.connections.'.$database), ['password']);
}
/**
* Ensure the dependencies for the database commands are available.
*
* @return bool
*/
protected function ensureDependenciesExist()
{
return tap(interface_exists('Doctrine\DBAL\Driver'), function ($dependenciesExist) {
if (! $dependenciesExist && confirm('Inspecting database information requires the Doctrine DBAL (doctrine/dbal) package. Would you like to install it?', default: false)) {
$this->installDependencies();
}
});
}
/**
* Install the command's dependencies.
*
* @return void
*
* @throws \Symfony\Component\Process\Exception\ProcessSignaledException
*/
protected function installDependencies()
{
$command = collect($this->composer->findComposer())
->push('require doctrine/dbal:^3.5.1')
->implode(' ');
$process = Process::fromShellCommandline($command, null, null, null, null);
if ('\\' !== DIRECTORY_SEPARATOR && file_exists('/dev/tty') && is_readable('/dev/tty')) {
try {
$process->setTty(true);
} catch (RuntimeException $e) {
$this->components->warn($e->getMessage());
}
}
try {
$process->run(fn ($type, $line) => $this->output->write($line));
} catch (ProcessSignaledException $e) {
if (extension_loaded('pcntl') && $e->getSignal() !== SIGINT) {
throw $e;
}
}
}
}