Update website
This commit is contained in:
parent
0a686aeb9a
commit
c4ffa0f6ee
4360 changed files with 1727 additions and 718385 deletions
|
@ -1,456 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* PhpMyAdmin\Server\Status\Data class
|
||||
* Used by server_status_*.php pages
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Server\Status;
|
||||
|
||||
use PhpMyAdmin\ReplicationInfo;
|
||||
use PhpMyAdmin\Url;
|
||||
|
||||
use function __;
|
||||
use function basename;
|
||||
use function mb_strtolower;
|
||||
use function str_contains;
|
||||
|
||||
/**
|
||||
* This class provides data about the server status
|
||||
*
|
||||
* All properties of the class are read-only
|
||||
*
|
||||
* TODO: Use lazy initialisation for some of the properties
|
||||
* since not all of the server_status_*.php pages need
|
||||
* all the data that this class provides.
|
||||
*/
|
||||
class Data
|
||||
{
|
||||
/** @var array */
|
||||
public $status;
|
||||
|
||||
/** @var array */
|
||||
public $sections;
|
||||
|
||||
/** @var array */
|
||||
public $variables;
|
||||
|
||||
/** @var array */
|
||||
public $usedQueries;
|
||||
|
||||
/** @var array */
|
||||
public $allocationMap;
|
||||
|
||||
/** @var array */
|
||||
public $links;
|
||||
|
||||
/** @var bool */
|
||||
public $dbIsLocal;
|
||||
|
||||
/** @var mixed */
|
||||
public $section;
|
||||
|
||||
/** @var array */
|
||||
public $sectionUsed;
|
||||
|
||||
/** @var string */
|
||||
public $selfUrl;
|
||||
|
||||
/** @var bool */
|
||||
public $dataLoaded;
|
||||
|
||||
/** @var ReplicationInfo */
|
||||
private $replicationInfo;
|
||||
|
||||
public function getReplicationInfo(): ReplicationInfo
|
||||
{
|
||||
return $this->replicationInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* An empty setter makes the above properties read-only
|
||||
*
|
||||
* @param string $a key
|
||||
* @param mixed $b value
|
||||
*/
|
||||
public function __set($a, $b): void
|
||||
{
|
||||
// Discard everything
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the allocations for constructor
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getAllocations()
|
||||
{
|
||||
return [
|
||||
// variable name => section
|
||||
// variable names match when they begin with the given string
|
||||
|
||||
'Com_' => 'com',
|
||||
'Innodb_' => 'innodb',
|
||||
'Ndb_' => 'ndb',
|
||||
'Handler_' => 'handler',
|
||||
'Qcache_' => 'qcache',
|
||||
'Threads_' => 'threads',
|
||||
'Slow_launch_threads' => 'threads',
|
||||
|
||||
'Binlog_cache_' => 'binlog_cache',
|
||||
'Created_tmp_' => 'created_tmp',
|
||||
'Key_' => 'key',
|
||||
|
||||
'Delayed_' => 'delayed',
|
||||
'Not_flushed_delayed_rows' => 'delayed',
|
||||
|
||||
'Flush_commands' => 'query',
|
||||
'Last_query_cost' => 'query',
|
||||
'Slow_queries' => 'query',
|
||||
'Queries' => 'query',
|
||||
'Prepared_stmt_count' => 'query',
|
||||
|
||||
'Select_' => 'select',
|
||||
'Sort_' => 'sort',
|
||||
|
||||
'Open_tables' => 'table',
|
||||
'Opened_tables' => 'table',
|
||||
'Open_table_definitions' => 'table',
|
||||
'Opened_table_definitions' => 'table',
|
||||
'Table_locks_' => 'table',
|
||||
|
||||
'Rpl_status' => 'repl',
|
||||
'Slave_' => 'repl',
|
||||
|
||||
'Tc_' => 'tc',
|
||||
|
||||
'Ssl_' => 'ssl',
|
||||
|
||||
'Open_files' => 'files',
|
||||
'Open_streams' => 'files',
|
||||
'Opened_files' => 'files',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the sections for constructor
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getSections()
|
||||
{
|
||||
return [
|
||||
// section => section name (description)
|
||||
'com' => 'Com',
|
||||
'query' => __('SQL query'),
|
||||
'innodb' => 'InnoDB',
|
||||
'ndb' => 'NDB',
|
||||
'handler' => __('Handler'),
|
||||
'qcache' => __('Query cache'),
|
||||
'threads' => __('Threads'),
|
||||
'binlog_cache' => __('Binary log'),
|
||||
'created_tmp' => __('Temporary data'),
|
||||
'delayed' => __('Delayed inserts'),
|
||||
'key' => __('Key cache'),
|
||||
'select' => __('Joins'),
|
||||
'repl' => __('Replication'),
|
||||
'sort' => __('Sorting'),
|
||||
'table' => __('Tables'),
|
||||
'tc' => __('Transaction coordinator'),
|
||||
'files' => __('Files'),
|
||||
'ssl' => 'SSL',
|
||||
'other' => __('Other'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the links for constructor
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getLinks()
|
||||
{
|
||||
$primaryInfo = $this->replicationInfo->getPrimaryInfo();
|
||||
$replicaInfo = $this->replicationInfo->getReplicaInfo();
|
||||
|
||||
$links = [];
|
||||
// variable or section name => (name => url)
|
||||
|
||||
$links['table'][__('Flush (close) all tables')] = [
|
||||
'url' => $this->selfUrl,
|
||||
'params' => Url::getCommon(['flush' => 'TABLES'], ''),
|
||||
];
|
||||
$links['table'][__('Show open tables')] = [
|
||||
'url' => Url::getFromRoute('/sql'),
|
||||
'params' => Url::getCommon([
|
||||
'sql_query' => 'SHOW OPEN TABLES',
|
||||
'goto' => $this->selfUrl,
|
||||
], ''),
|
||||
];
|
||||
|
||||
if ($primaryInfo['status']) {
|
||||
$links['repl'][__('Show replica hosts')] = [
|
||||
'url' => Url::getFromRoute('/sql'),
|
||||
'params' => Url::getCommon([
|
||||
'sql_query' => 'SHOW SLAVE HOSTS',
|
||||
'goto' => $this->selfUrl,
|
||||
], ''),
|
||||
];
|
||||
$links['repl'][__('Show primary status')] = [
|
||||
'url' => '#replication_primary',
|
||||
'params' => '',
|
||||
];
|
||||
}
|
||||
|
||||
if ($replicaInfo['status']) {
|
||||
$links['repl'][__('Show replica status')] = [
|
||||
'url' => '#replication_replica',
|
||||
'params' => '',
|
||||
];
|
||||
}
|
||||
|
||||
$links['repl']['doc'] = 'replication';
|
||||
|
||||
$links['qcache'][__('Flush query cache')] = [
|
||||
'url' => $this->selfUrl,
|
||||
'params' => Url::getCommon(['flush' => 'QUERY CACHE'], ''),
|
||||
];
|
||||
$links['qcache']['doc'] = 'query_cache';
|
||||
|
||||
$links['threads']['doc'] = 'mysql_threads';
|
||||
|
||||
$links['key']['doc'] = 'myisam_key_cache';
|
||||
|
||||
$links['binlog_cache']['doc'] = 'binary_log';
|
||||
|
||||
$links['Slow_queries']['doc'] = 'slow_query_log';
|
||||
|
||||
$links['innodb'][__('Variables')] = [
|
||||
'url' => Url::getFromRoute('/server/engines/InnoDB'),
|
||||
'params' => '',
|
||||
];
|
||||
$links['innodb'][__('InnoDB Status')] = [
|
||||
'url' => Url::getFromRoute('/server/engines/InnoDB/Status'),
|
||||
'params' => '',
|
||||
];
|
||||
$links['innodb']['doc'] = 'innodb';
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate some values
|
||||
*
|
||||
* @param array $server_status contains results of SHOW GLOBAL STATUS
|
||||
* @param array $server_variables contains results of SHOW GLOBAL VARIABLES
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function calculateValues(array $server_status, array $server_variables)
|
||||
{
|
||||
// Key_buffer_fraction
|
||||
if (
|
||||
isset($server_status['Key_blocks_unused'], $server_variables['key_cache_block_size'])
|
||||
&& isset($server_variables['key_buffer_size'])
|
||||
&& $server_variables['key_buffer_size'] != 0
|
||||
) {
|
||||
$server_status['Key_buffer_fraction_%'] = 100
|
||||
- $server_status['Key_blocks_unused']
|
||||
* $server_variables['key_cache_block_size']
|
||||
/ $server_variables['key_buffer_size']
|
||||
* 100;
|
||||
} elseif (
|
||||
isset($server_status['Key_blocks_used'], $server_variables['key_buffer_size'])
|
||||
&& $server_variables['key_buffer_size'] != 0
|
||||
) {
|
||||
$server_status['Key_buffer_fraction_%'] = $server_status['Key_blocks_used']
|
||||
* 1024
|
||||
/ $server_variables['key_buffer_size'];
|
||||
}
|
||||
|
||||
// Ratio for key read/write
|
||||
if (
|
||||
isset($server_status['Key_writes'], $server_status['Key_write_requests'])
|
||||
&& $server_status['Key_write_requests'] > 0
|
||||
) {
|
||||
$key_writes = $server_status['Key_writes'];
|
||||
$key_write_requests = $server_status['Key_write_requests'];
|
||||
$server_status['Key_write_ratio_%'] = 100 * $key_writes / $key_write_requests;
|
||||
}
|
||||
|
||||
if (
|
||||
isset($server_status['Key_reads'], $server_status['Key_read_requests'])
|
||||
&& $server_status['Key_read_requests'] > 0
|
||||
) {
|
||||
$key_reads = $server_status['Key_reads'];
|
||||
$key_read_requests = $server_status['Key_read_requests'];
|
||||
$server_status['Key_read_ratio_%'] = 100 * $key_reads / $key_read_requests;
|
||||
}
|
||||
|
||||
// Threads_cache_hitrate
|
||||
if (
|
||||
isset($server_status['Threads_created'], $server_status['Connections'])
|
||||
&& $server_status['Connections'] > 0
|
||||
) {
|
||||
$server_status['Threads_cache_hitrate_%'] = 100 - $server_status['Threads_created']
|
||||
/ $server_status['Connections'] * 100;
|
||||
}
|
||||
|
||||
return $server_status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort variables into arrays
|
||||
*
|
||||
* @param array $server_status contains results of SHOW GLOBAL STATUS
|
||||
* @param array $allocations allocations for sections
|
||||
* @param array $allocationMap map variables to their section
|
||||
* @param array $sectionUsed is a section used?
|
||||
* @param array $used_queries used queries
|
||||
*
|
||||
* @return array ($allocationMap, $sectionUsed, $used_queries)
|
||||
*/
|
||||
private function sortVariables(
|
||||
array $server_status,
|
||||
array $allocations,
|
||||
array $allocationMap,
|
||||
array $sectionUsed,
|
||||
array $used_queries
|
||||
) {
|
||||
foreach ($server_status as $name => $value) {
|
||||
$section_found = false;
|
||||
foreach ($allocations as $filter => $section) {
|
||||
if (! str_contains($name, $filter)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$allocationMap[$name] = $section;
|
||||
$sectionUsed[$section] = true;
|
||||
$section_found = true;
|
||||
if ($section === 'com' && $value > 0) {
|
||||
$used_queries[$name] = $value;
|
||||
}
|
||||
|
||||
break; // Only exits inner loop
|
||||
}
|
||||
|
||||
if ($section_found) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$allocationMap[$name] = 'other';
|
||||
$sectionUsed['other'] = true;
|
||||
}
|
||||
|
||||
return [
|
||||
$allocationMap,
|
||||
$sectionUsed,
|
||||
$used_queries,
|
||||
];
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
global $dbi;
|
||||
|
||||
$this->replicationInfo = new ReplicationInfo($dbi);
|
||||
$this->replicationInfo->load($_POST['primary_connection'] ?? null);
|
||||
|
||||
$this->selfUrl = basename($GLOBALS['PMA_PHP_SELF']);
|
||||
|
||||
// get status from server
|
||||
$server_status_result = $dbi->tryQuery('SHOW GLOBAL STATUS');
|
||||
if ($server_status_result === false) {
|
||||
$server_status = [];
|
||||
$this->dataLoaded = false;
|
||||
} else {
|
||||
$this->dataLoaded = true;
|
||||
$server_status = $server_status_result->fetchAllKeyPair();
|
||||
unset($server_status_result);
|
||||
}
|
||||
|
||||
// for some calculations we require also some server settings
|
||||
$server_variables = $dbi->fetchResult('SHOW GLOBAL VARIABLES', 0, 1);
|
||||
|
||||
// cleanup of some deprecated values
|
||||
$server_status = self::cleanDeprecated($server_status);
|
||||
|
||||
// calculate some values
|
||||
$server_status = $this->calculateValues($server_status, $server_variables);
|
||||
|
||||
// split variables in sections
|
||||
$allocations = $this->getAllocations();
|
||||
|
||||
$sections = $this->getSections();
|
||||
|
||||
// define some needful links/commands
|
||||
$links = $this->getLinks();
|
||||
|
||||
// Variable to contain all com_ variables (query statistics)
|
||||
$used_queries = [];
|
||||
|
||||
// Variable to map variable names to their respective section name
|
||||
// (used for js category filtering)
|
||||
$allocationMap = [];
|
||||
|
||||
// Variable to mark used sections
|
||||
$sectionUsed = [];
|
||||
|
||||
// sort vars into arrays
|
||||
[
|
||||
$allocationMap,
|
||||
$sectionUsed,
|
||||
$used_queries,
|
||||
] = $this->sortVariables($server_status, $allocations, $allocationMap, $sectionUsed, $used_queries);
|
||||
|
||||
// admin commands are not queries (e.g. they include COM_PING,
|
||||
// which is excluded from $server_status['Questions'])
|
||||
unset($used_queries['Com_admin_commands']);
|
||||
|
||||
// Set all class properties
|
||||
$this->dbIsLocal = false;
|
||||
// can be null if $cfg['ServerDefault'] = 0;
|
||||
$serverHostToLower = mb_strtolower((string) $GLOBALS['cfg']['Server']['host']);
|
||||
if (
|
||||
$serverHostToLower === 'localhost'
|
||||
|| $GLOBALS['cfg']['Server']['host'] === '127.0.0.1'
|
||||
|| $GLOBALS['cfg']['Server']['host'] === '::1'
|
||||
) {
|
||||
$this->dbIsLocal = true;
|
||||
}
|
||||
|
||||
$this->status = $server_status;
|
||||
$this->sections = $sections;
|
||||
$this->variables = $server_variables;
|
||||
$this->usedQueries = $used_queries;
|
||||
$this->allocationMap = $allocationMap;
|
||||
$this->links = $links;
|
||||
$this->sectionUsed = $sectionUsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* cleanup of some deprecated values
|
||||
*
|
||||
* @param array $server_status status array to process
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function cleanDeprecated(array $server_status)
|
||||
{
|
||||
$deprecated = [
|
||||
'Com_prepare_sql' => 'Com_stmt_prepare',
|
||||
'Com_execute_sql' => 'Com_stmt_execute',
|
||||
'Com_dealloc_sql' => 'Com_stmt_close',
|
||||
];
|
||||
foreach ($deprecated as $old => $new) {
|
||||
if (! isset($server_status[$old], $server_status[$new])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
unset($server_status[$old]);
|
||||
}
|
||||
|
||||
return $server_status;
|
||||
}
|
||||
}
|
|
@ -1,555 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* functions for displaying server status sub item: monitor
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Server\Status;
|
||||
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Profiling;
|
||||
use PhpMyAdmin\Server\SysInfo\SysInfo;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function array_sum;
|
||||
use function count;
|
||||
use function implode;
|
||||
use function is_numeric;
|
||||
use function json_decode;
|
||||
use function mb_strlen;
|
||||
use function mb_strpos;
|
||||
use function mb_strtolower;
|
||||
use function mb_substr;
|
||||
use function microtime;
|
||||
use function preg_match;
|
||||
use function preg_replace;
|
||||
use function strlen;
|
||||
|
||||
/**
|
||||
* functions for displaying server status sub item: monitor
|
||||
*/
|
||||
class Monitor
|
||||
{
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
/**
|
||||
* @param DatabaseInterface $dbi DatabaseInterface instance
|
||||
*/
|
||||
public function __construct($dbi)
|
||||
{
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns JSON for real-time charting data
|
||||
*
|
||||
* @param string $requiredData Required data
|
||||
*
|
||||
* @return array JSON
|
||||
*/
|
||||
public function getJsonForChartingData(string $requiredData): array
|
||||
{
|
||||
$ret = json_decode($requiredData, true);
|
||||
$statusVars = [];
|
||||
$serverVars = [];
|
||||
$sysinfo = $cpuload = $memory = 0;
|
||||
|
||||
/* Accumulate all required variables and data */
|
||||
[$serverVars, $statusVars, $ret] = $this->getJsonForChartingDataGet(
|
||||
$ret,
|
||||
$serverVars,
|
||||
$statusVars,
|
||||
$sysinfo,
|
||||
$cpuload,
|
||||
$memory
|
||||
);
|
||||
|
||||
// Retrieve all required status variables
|
||||
$statusVarValues = [];
|
||||
if (count($statusVars)) {
|
||||
$statusVarValues = $this->dbi->fetchResult(
|
||||
"SHOW GLOBAL STATUS WHERE Variable_name='"
|
||||
. implode("' OR Variable_name='", $statusVars) . "'",
|
||||
0,
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
// Retrieve all required server variables
|
||||
$serverVarValues = [];
|
||||
if (count($serverVars)) {
|
||||
$serverVarValues = $this->dbi->fetchResult(
|
||||
"SHOW GLOBAL VARIABLES WHERE Variable_name='"
|
||||
. implode("' OR Variable_name='", $serverVars) . "'",
|
||||
0,
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
// ...and now assign them
|
||||
$ret = $this->getJsonForChartingDataSet($ret, $statusVarValues, $serverVarValues);
|
||||
|
||||
$ret['x'] = (int) (microtime(true) * 1000);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign the variables for real-time charting data
|
||||
*
|
||||
* @param array $ret Real-time charting data
|
||||
* @param array $statusVarValues Status variable values
|
||||
* @param array $serverVarValues Server variable values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getJsonForChartingDataSet(
|
||||
array $ret,
|
||||
array $statusVarValues,
|
||||
array $serverVarValues
|
||||
): array {
|
||||
foreach ($ret as $chart_id => $chartNodes) {
|
||||
foreach ($chartNodes as $node_id => $nodeDataPoints) {
|
||||
foreach ($nodeDataPoints as $point_id => $dataPoint) {
|
||||
switch ($dataPoint['type']) {
|
||||
case 'statusvar':
|
||||
$ret[$chart_id][$node_id][$point_id]['value'] = $statusVarValues[$dataPoint['name']];
|
||||
break;
|
||||
case 'servervar':
|
||||
$ret[$chart_id][$node_id][$point_id]['value'] = $serverVarValues[$dataPoint['name']];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get called to get JSON for charting data
|
||||
*
|
||||
* @param array $ret Real-time charting data
|
||||
* @param array $serverVars Server variable values
|
||||
* @param array $statusVars Status variable values
|
||||
* @param mixed $sysinfo System info
|
||||
* @param mixed $cpuload CPU load
|
||||
* @param mixed $memory Memory
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getJsonForChartingDataGet(
|
||||
array $ret,
|
||||
array $serverVars,
|
||||
array $statusVars,
|
||||
$sysinfo,
|
||||
$cpuload,
|
||||
$memory
|
||||
) {
|
||||
// For each chart
|
||||
foreach ($ret as $chartId => $chartNodes) {
|
||||
// For each data series
|
||||
foreach ($chartNodes as $nodeId => $nodeDataPoints) {
|
||||
// For each data point in the series (usually just 1)
|
||||
foreach ($nodeDataPoints as $pointId => $dataPoint) {
|
||||
[$serverVars, $statusVars, $ret[$chartId][$nodeId][$pointId]] = $this->getJsonForChartingDataSwitch(
|
||||
$dataPoint['type'],
|
||||
$dataPoint['name'],
|
||||
$serverVars,
|
||||
$statusVars,
|
||||
$ret[$chartId][$nodeId][$pointId],
|
||||
$sysinfo,
|
||||
$cpuload,
|
||||
$memory
|
||||
);
|
||||
} /* foreach */
|
||||
} /* foreach */
|
||||
}
|
||||
|
||||
return [
|
||||
$serverVars,
|
||||
$statusVars,
|
||||
$ret,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch called to get JSON for charting data
|
||||
*
|
||||
* @param string $type Type
|
||||
* @param string $pName Name
|
||||
* @param array $serverVars Server variable values
|
||||
* @param array $statusVars Status variable values
|
||||
* @param array $ret Real-time charting data
|
||||
* @param mixed $sysinfo System info
|
||||
* @param mixed $cpuload CPU load
|
||||
* @param mixed $memory Memory
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getJsonForChartingDataSwitch(
|
||||
$type,
|
||||
$pName,
|
||||
array $serverVars,
|
||||
array $statusVars,
|
||||
array $ret,
|
||||
$sysinfo,
|
||||
$cpuload,
|
||||
$memory
|
||||
) {
|
||||
/**
|
||||
* We only collect the status and server variables here to read them all in one query,
|
||||
* and only afterwards assign them. Also do some allow list filtering on the names
|
||||
*/
|
||||
switch ($type) {
|
||||
case 'servervar':
|
||||
if (! preg_match('/[^a-zA-Z_]+/', $pName)) {
|
||||
$serverVars[] = $pName;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'statusvar':
|
||||
if (! preg_match('/[^a-zA-Z_]+/', $pName)) {
|
||||
$statusVars[] = $pName;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'proc':
|
||||
$result = $this->dbi->query('SHOW PROCESSLIST');
|
||||
$ret['value'] = $result->numRows();
|
||||
break;
|
||||
|
||||
case 'cpu':
|
||||
if (! $sysinfo) {
|
||||
$sysinfo = SysInfo::get();
|
||||
}
|
||||
|
||||
if (! $cpuload) {
|
||||
$cpuload = $sysinfo->loadavg();
|
||||
}
|
||||
|
||||
if (SysInfo::getOs() === 'Linux') {
|
||||
$ret['idle'] = $cpuload['idle'];
|
||||
$ret['busy'] = $cpuload['busy'];
|
||||
} else {
|
||||
$ret['value'] = $cpuload['loadavg'];
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'memory':
|
||||
if (! $sysinfo) {
|
||||
$sysinfo = SysInfo::get();
|
||||
}
|
||||
|
||||
if (! $memory) {
|
||||
$memory = $sysinfo->memory();
|
||||
}
|
||||
|
||||
$ret['value'] = $memory[$pName] ?? 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return [
|
||||
$serverVars,
|
||||
$statusVars,
|
||||
$ret,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns JSON for log data with type: slow
|
||||
*
|
||||
* @param int $start Unix Time: Start time for query
|
||||
* @param int $end Unix Time: End time for query
|
||||
*/
|
||||
public function getJsonForLogDataTypeSlow(int $start, int $end): ?array
|
||||
{
|
||||
$query = 'SELECT start_time, user_host, ';
|
||||
$query .= 'Sec_to_Time(Sum(Time_to_Sec(query_time))) as query_time, ';
|
||||
$query .= 'Sec_to_Time(Sum(Time_to_Sec(lock_time))) as lock_time, ';
|
||||
$query .= 'SUM(rows_sent) AS rows_sent, ';
|
||||
$query .= 'SUM(rows_examined) AS rows_examined, db, sql_text, ';
|
||||
$query .= 'COUNT(sql_text) AS \'#\' ';
|
||||
$query .= 'FROM `mysql`.`slow_log` ';
|
||||
$query .= 'WHERE start_time > FROM_UNIXTIME(' . $start . ') ';
|
||||
// See: mode = ONLY_FULL_GROUP_BY
|
||||
$query .= 'AND start_time < FROM_UNIXTIME(' . $end . ') GROUP BY start_time, user_host, db, sql_text';
|
||||
|
||||
$result = $this->dbi->tryQuery($query);
|
||||
if ($result === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$return = [
|
||||
'rows' => [],
|
||||
'sum' => [],
|
||||
];
|
||||
|
||||
while ($row = $result->fetchAssoc()) {
|
||||
$type = mb_strtolower(
|
||||
mb_substr(
|
||||
$row['sql_text'],
|
||||
0,
|
||||
(int) mb_strpos($row['sql_text'], ' ')
|
||||
)
|
||||
);
|
||||
|
||||
switch ($type) {
|
||||
case 'insert':
|
||||
case 'update':
|
||||
//Cut off big inserts and updates, but append byte count instead
|
||||
if (mb_strlen($row['sql_text']) > 220) {
|
||||
$implodeSqlText = implode(
|
||||
' ',
|
||||
(array) Util::formatByteDown(
|
||||
mb_strlen($row['sql_text']),
|
||||
2,
|
||||
2
|
||||
)
|
||||
);
|
||||
$row['sql_text'] = mb_substr($row['sql_text'], 0, 200)
|
||||
. '... [' . $implodeSqlText . ']';
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (! isset($return['sum'][$type])) {
|
||||
$return['sum'][$type] = 0;
|
||||
}
|
||||
|
||||
$return['sum'][$type] += $row['#'];
|
||||
$return['rows'][] = $row;
|
||||
}
|
||||
|
||||
$return['sum']['TOTAL'] = array_sum($return['sum']);
|
||||
$return['numRows'] = count($return['rows']);
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns JSON for log data with type: general
|
||||
*
|
||||
* @param int $start Unix Time: Start time for query
|
||||
* @param int $end Unix Time: End time for query
|
||||
* @param bool $isTypesLimited Whether to limit types or not
|
||||
* @param bool $removeVariables Whether to remove variables or not
|
||||
*/
|
||||
public function getJsonForLogDataTypeGeneral(
|
||||
int $start,
|
||||
int $end,
|
||||
bool $isTypesLimited,
|
||||
bool $removeVariables
|
||||
): ?array {
|
||||
$limitTypes = '';
|
||||
if ($isTypesLimited) {
|
||||
$limitTypes = 'AND argument REGEXP \'^(INSERT|SELECT|UPDATE|DELETE)\' ';
|
||||
}
|
||||
|
||||
$query = 'SELECT TIME(event_time) as event_time, user_host, thread_id, ';
|
||||
$query .= 'server_id, argument, count(argument) as \'#\' ';
|
||||
$query .= 'FROM `mysql`.`general_log` ';
|
||||
$query .= 'WHERE command_type=\'Query\' ';
|
||||
$query .= 'AND event_time > FROM_UNIXTIME(' . $start . ') ';
|
||||
$query .= 'AND event_time < FROM_UNIXTIME(' . $end . ') ';
|
||||
// See: mode = ONLY_FULL_GROUP_BY
|
||||
$query .= $limitTypes . 'GROUP by event_time, user_host, thread_id, server_id, argument'; // HAVING count > 1';
|
||||
|
||||
$result = $this->dbi->tryQuery($query);
|
||||
if ($result === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$return = [
|
||||
'rows' => [],
|
||||
'sum' => [],
|
||||
];
|
||||
$insertTables = [];
|
||||
$insertTablesFirst = -1;
|
||||
$i = 0;
|
||||
|
||||
while ($row = $result->fetchAssoc()) {
|
||||
preg_match('/^(\w+)\s/', $row['argument'], $match);
|
||||
$type = mb_strtolower($match[1]);
|
||||
|
||||
if (! isset($return['sum'][$type])) {
|
||||
$return['sum'][$type] = 0;
|
||||
}
|
||||
|
||||
$return['sum'][$type] += $row['#'];
|
||||
|
||||
switch ($type) {
|
||||
/** @noinspection PhpMissingBreakStatementInspection */
|
||||
case 'insert':
|
||||
// Group inserts if selected
|
||||
if (
|
||||
$removeVariables && preg_match(
|
||||
'/^INSERT INTO (`|\'|"|)([^\s\\1]+)\\1/i',
|
||||
$row['argument'],
|
||||
$matches
|
||||
)
|
||||
) {
|
||||
if (! isset($insertTables[$matches[2]])) {
|
||||
$insertTables[$matches[2]] = 0;
|
||||
}
|
||||
|
||||
$insertTables[$matches[2]]++;
|
||||
if ($insertTables[$matches[2]] > 1) {
|
||||
$return['rows'][$insertTablesFirst]['#'] = $insertTables[$matches[2]];
|
||||
|
||||
// Add a ... to the end of this query to indicate that
|
||||
// there's been other queries
|
||||
$temp = $return['rows'][$insertTablesFirst]['argument'];
|
||||
$return['rows'][$insertTablesFirst]['argument'] .= $this->getSuspensionPoints(
|
||||
$temp[strlen($temp) - 1]
|
||||
);
|
||||
|
||||
// Group this value, thus do not add to the result list
|
||||
continue 2;
|
||||
}
|
||||
|
||||
$insertTablesFirst = $i;
|
||||
$insertTables[$matches[2]] += $row['#'] - 1;
|
||||
}
|
||||
|
||||
// No break here
|
||||
|
||||
case 'update':
|
||||
// Cut off big inserts and updates,
|
||||
// but append byte count therefor
|
||||
if (mb_strlen($row['argument']) > 220) {
|
||||
$row['argument'] = mb_substr($row['argument'], 0, 200)
|
||||
. '... ['
|
||||
. implode(
|
||||
' ',
|
||||
(array) Util::formatByteDown(
|
||||
mb_strlen($row['argument']),
|
||||
2,
|
||||
2
|
||||
)
|
||||
)
|
||||
. ']';
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
$return['rows'][] = $row;
|
||||
$i++;
|
||||
}
|
||||
|
||||
$return['sum']['TOTAL'] = array_sum($return['sum']);
|
||||
$return['numRows'] = count($return['rows']);
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return suspension points if needed
|
||||
*
|
||||
* @param string $lastChar Last char
|
||||
*
|
||||
* @return string Return suspension points if needed
|
||||
*/
|
||||
private function getSuspensionPoints(string $lastChar): string
|
||||
{
|
||||
if ($lastChar !== '.') {
|
||||
return '<br>...';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns JSON for logging vars
|
||||
*
|
||||
* @param string|null $name Variable name
|
||||
* @param string|null $value Variable value
|
||||
*
|
||||
* @return array JSON
|
||||
*/
|
||||
public function getJsonForLoggingVars(?string $name, ?string $value): array
|
||||
{
|
||||
if (isset($name, $value)) {
|
||||
$escapedValue = $this->dbi->escapeString($value);
|
||||
if (! is_numeric($escapedValue)) {
|
||||
$escapedValue = "'" . $escapedValue . "'";
|
||||
}
|
||||
|
||||
if (! preg_match('/[^a-zA-Z0-9_]+/', $name)) {
|
||||
$this->dbi->query('SET GLOBAL ' . $name . ' = ' . $escapedValue);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->dbi->fetchResult(
|
||||
'SHOW GLOBAL VARIABLES WHERE Variable_name IN'
|
||||
. ' ("general_log","slow_query_log","long_query_time","log_output")',
|
||||
0,
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns JSON for query_analyzer
|
||||
*
|
||||
* @param string $database Database name
|
||||
* @param string $query SQL query
|
||||
*
|
||||
* @return array JSON
|
||||
*/
|
||||
public function getJsonForQueryAnalyzer(
|
||||
string $database,
|
||||
string $query
|
||||
): array {
|
||||
global $cached_affected_rows;
|
||||
|
||||
$return = [];
|
||||
|
||||
if (strlen($database) > 0) {
|
||||
$this->dbi->selectDb($database);
|
||||
}
|
||||
|
||||
$profiling = Profiling::isSupported($this->dbi);
|
||||
|
||||
if ($profiling) {
|
||||
$this->dbi->query('SET PROFILING=1;');
|
||||
}
|
||||
|
||||
// Do not cache query
|
||||
$sqlQuery = preg_replace('/^(\s*SELECT)/i', '\\1 SQL_NO_CACHE', $query);
|
||||
|
||||
$this->dbi->tryQuery($sqlQuery);
|
||||
$return['affectedRows'] = $cached_affected_rows;
|
||||
|
||||
$result = $this->dbi->tryQuery('EXPLAIN ' . $sqlQuery);
|
||||
if ($result !== false) {
|
||||
$return['explain'] = $result->fetchAllAssoc();
|
||||
}
|
||||
|
||||
// In case an error happened
|
||||
$return['error'] = $this->dbi->getError();
|
||||
|
||||
if ($profiling) {
|
||||
$return['profiling'] = [];
|
||||
$result = $this->dbi->tryQuery(
|
||||
'SELECT seq,state,duration FROM INFORMATION_SCHEMA.PROFILING WHERE QUERY_ID=1 ORDER BY seq'
|
||||
);
|
||||
if ($result !== false) {
|
||||
$return['profiling'] = $result->fetchAllAssoc();
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
|
@ -1,191 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Server\Status;
|
||||
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function __;
|
||||
use function array_keys;
|
||||
use function count;
|
||||
use function mb_strtolower;
|
||||
use function strlen;
|
||||
use function ucfirst;
|
||||
|
||||
final class Processes
|
||||
{
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(DatabaseInterface $dbi)
|
||||
{
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $params Request parameters
|
||||
*
|
||||
* @return array<string, array|string|bool>
|
||||
*/
|
||||
public function getList(array $params): array
|
||||
{
|
||||
$urlParams = [];
|
||||
|
||||
$showFullSql = ! empty($params['full']);
|
||||
if ($showFullSql) {
|
||||
$urlParams['full'] = '';
|
||||
} else {
|
||||
$urlParams['full'] = 1;
|
||||
}
|
||||
|
||||
$sqlQuery = $showFullSql
|
||||
? 'SHOW FULL PROCESSLIST'
|
||||
: 'SHOW PROCESSLIST';
|
||||
if (
|
||||
(! empty($params['order_by_field'])
|
||||
&& ! empty($params['sort_order']))
|
||||
|| ! empty($params['showExecuting'])
|
||||
) {
|
||||
$urlParams['order_by_field'] = $params['order_by_field'];
|
||||
$urlParams['sort_order'] = $params['sort_order'];
|
||||
$urlParams['showExecuting'] = $params['showExecuting'];
|
||||
$sqlQuery = 'SELECT * FROM `INFORMATION_SCHEMA`.`PROCESSLIST` ';
|
||||
}
|
||||
|
||||
if (! empty($params['showExecuting'])) {
|
||||
$sqlQuery .= ' WHERE state != "" ';
|
||||
}
|
||||
|
||||
if (! empty($params['order_by_field']) && ! empty($params['sort_order'])) {
|
||||
$sqlQuery .= ' ORDER BY '
|
||||
. Util::backquote($params['order_by_field'])
|
||||
. ' ' . $params['sort_order'];
|
||||
}
|
||||
|
||||
$result = $this->dbi->query($sqlQuery);
|
||||
$rows = [];
|
||||
while ($process = $result->fetchAssoc()) {
|
||||
// Array keys need to modify due to the way it has used
|
||||
// to display column values
|
||||
foreach (array_keys($process) as $key) {
|
||||
$newKey = ucfirst(mb_strtolower($key));
|
||||
if ($newKey === $key) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$process[$newKey] = $process[$key];
|
||||
unset($process[$key]);
|
||||
}
|
||||
|
||||
$rows[] = [
|
||||
'id' => $process['Id'],
|
||||
'user' => $process['User'],
|
||||
'host' => $process['Host'],
|
||||
'db' => ! isset($process['Db']) || strlen($process['Db']) === 0 ? '' : $process['Db'],
|
||||
'command' => $process['Command'],
|
||||
'time' => $process['Time'],
|
||||
'state' => ! empty($process['State']) ? $process['State'] : '---',
|
||||
'progress' => ! empty($process['Progress']) ? $process['Progress'] : '---',
|
||||
'info' => ! empty($process['Info']) ? Generator::formatSql($process['Info'], ! $showFullSql) : '---',
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'columns' => $this->getSortableColumnsForProcessList($showFullSql, $params),
|
||||
'rows' => $rows,
|
||||
'refresh_params' => $urlParams,
|
||||
'is_mariadb' => $this->dbi->isMariaDB(),
|
||||
];
|
||||
}
|
||||
|
||||
private function getSortableColumnsForProcessList(bool $showFullSql, array $params): array
|
||||
{
|
||||
// This array contains display name and real column name of each
|
||||
// sortable column in the table
|
||||
$sortableColumns = [
|
||||
[
|
||||
'column_name' => __('ID'),
|
||||
'order_by_field' => 'Id',
|
||||
],
|
||||
[
|
||||
'column_name' => __('User'),
|
||||
'order_by_field' => 'User',
|
||||
],
|
||||
[
|
||||
'column_name' => __('Host'),
|
||||
'order_by_field' => 'Host',
|
||||
],
|
||||
[
|
||||
'column_name' => __('Database'),
|
||||
'order_by_field' => 'Db',
|
||||
],
|
||||
[
|
||||
'column_name' => __('Command'),
|
||||
'order_by_field' => 'Command',
|
||||
],
|
||||
[
|
||||
'column_name' => __('Time'),
|
||||
'order_by_field' => 'Time',
|
||||
],
|
||||
[
|
||||
'column_name' => __('Status'),
|
||||
'order_by_field' => 'State',
|
||||
],
|
||||
];
|
||||
|
||||
if ($this->dbi->isMariaDB()) {
|
||||
$sortableColumns[] = [
|
||||
'column_name' => __('Progress'),
|
||||
'order_by_field' => 'Progress',
|
||||
];
|
||||
}
|
||||
|
||||
$sortableColumns[] = [
|
||||
'column_name' => __('SQL query'),
|
||||
'order_by_field' => 'Info',
|
||||
];
|
||||
|
||||
$sortableColCount = count($sortableColumns);
|
||||
|
||||
$columns = [];
|
||||
foreach ($sortableColumns as $columnKey => $column) {
|
||||
$is_sorted = ! empty($params['order_by_field'])
|
||||
&& ! empty($params['sort_order'])
|
||||
&& ($params['order_by_field'] == $column['order_by_field']);
|
||||
|
||||
$column['sort_order'] = 'ASC';
|
||||
if ($is_sorted && $params['sort_order'] === 'ASC') {
|
||||
$column['sort_order'] = 'DESC';
|
||||
}
|
||||
|
||||
if (isset($params['showExecuting'])) {
|
||||
$column['showExecuting'] = 'on';
|
||||
}
|
||||
|
||||
$columns[$columnKey] = [
|
||||
'name' => $column['column_name'],
|
||||
'params' => $column,
|
||||
'is_sorted' => $is_sorted,
|
||||
'sort_order' => $column['sort_order'],
|
||||
'has_full_query' => false,
|
||||
'is_full' => false,
|
||||
];
|
||||
|
||||
if (0 !== --$sortableColCount) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$columns[$columnKey]['has_full_query'] = true;
|
||||
if (! $showFullSql) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$columns[$columnKey]['is_full'] = true;
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue