aboutsummaryrefslogtreecommitdiff
path: root/srcs/phpmyadmin/libraries/classes/Server/Status
diff options
context:
space:
mode:
authorCharles <sircharlesaze@gmail.com>2020-01-09 10:55:03 +0100
committerCharles <sircharlesaze@gmail.com>2020-01-09 13:09:38 +0100
commit04d6d5ca99ebfd1cebb8ce06618fb3811fc1a8aa (patch)
tree5c691241355c943a3c68ddb06b8cf8c60aa11319 /srcs/phpmyadmin/libraries/classes/Server/Status
parent7e0d85db834d6351ed85d01e5126ac31dc510b86 (diff)
downloadft_server-04d6d5ca99ebfd1cebb8ce06618fb3811fc1a8aa.tar.gz
ft_server-04d6d5ca99ebfd1cebb8ce06618fb3811fc1a8aa.tar.bz2
ft_server-04d6d5ca99ebfd1cebb8ce06618fb3811fc1a8aa.zip
phpmyadmin working
Diffstat (limited to 'srcs/phpmyadmin/libraries/classes/Server/Status')
-rw-r--r--srcs/phpmyadmin/libraries/classes/Server/Status/Data.php430
-rw-r--r--srcs/phpmyadmin/libraries/classes/Server/Status/Monitor.php546
2 files changed, 976 insertions, 0 deletions
diff --git a/srcs/phpmyadmin/libraries/classes/Server/Status/Data.php b/srcs/phpmyadmin/libraries/classes/Server/Status/Data.php
new file mode 100644
index 0000000..7d352f7
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Server/Status/Data.php
@@ -0,0 +1,430 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * PhpMyAdmin\Server\Status\Data class
+ * Used by server_status_*.php pages
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Server\Status;
+
+use PhpMyAdmin\Url;
+
+/**
+ * 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.
+ *
+ * @package PhpMyAdmin
+ */
+class Data
+{
+ public $status;
+ public $sections;
+ public $variables;
+ public $used_queries;
+ public $allocationMap;
+ public $links;
+ public $db_isLocal;
+ public $section;
+ public $sectionUsed;
+ public $selfUrl;
+ public $dataLoaded;
+
+ /**
+ * An empty setter makes the above properties read-only
+ *
+ * @param string $a key
+ * @param mixed $b value
+ *
+ * @return void
+ */
+ public function __set($a, $b)
+ {
+ // 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()
+ {
+ $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' => 'sql.php',
+ 'params' => Url::getCommon([
+ 'sql_query' => 'SHOW OPEN TABLES',
+ 'goto' => $this->selfUrl,
+ ], ''),
+ ];
+
+ if ($GLOBALS['replication_info']['master']['status']) {
+ $links['repl'][__('Show slave hosts')] = [
+ 'url' => 'sql.php',
+ 'params' => Url::getCommon([
+ 'sql_query' => 'SHOW SLAVE HOSTS',
+ 'goto' => $this->selfUrl,
+ ], ''),
+ ];
+ $links['repl'][__('Show master status')] = [
+ 'url' => '#replication_master',
+ 'params' => '',
+ ];
+ }
+ if ($GLOBALS['replication_info']['slave']['status']) {
+ $links['repl'][__('Show slave status')] = [
+ 'url' => '#replication_slave',
+ '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' => 'server_engines.php',
+ 'params' => Url::getCommon(['engine' => 'InnoDB'], ''),
+ ];
+ $links['innodb'][__('InnoDB Status')] = [
+ 'url' => 'server_engines.php',
+ 'params' => Url::getCommon([
+ 'engine' => 'InnoDB',
+ 'page' => 'Status',
+ ], ''),
+ ];
+ $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'])
+ && isset($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'])
+ && isset($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'])
+ && isset($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'])
+ && isset($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'])
+ && isset($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 (mb_strpos($name, $filter) !== false) {
+ $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) {
+ $allocationMap[$name] = 'other';
+ $sectionUsed['other'] = true;
+ }
+ }
+ return [
+ $allocationMap,
+ $sectionUsed,
+ $used_queries,
+ ];
+ }
+
+ /**
+ * Constructor
+ */
+ public function __construct()
+ {
+ $this->selfUrl = basename($GLOBALS['PMA_PHP_SELF']);
+
+ // get status from server
+ $server_status_result = $GLOBALS['dbi']->tryQuery('SHOW GLOBAL STATUS');
+ $server_status = [];
+ if ($server_status_result === false) {
+ $this->dataLoaded = false;
+ } else {
+ $this->dataLoaded = true;
+ while ($arr = $GLOBALS['dbi']->fetchRow($server_status_result)) {
+ $server_status[$arr[0]] = $arr[1];
+ }
+ $GLOBALS['dbi']->freeResult($server_status_result);
+ }
+
+ // for some calculations we require also some server settings
+ $server_variables = $GLOBALS['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
+ list(
+ $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->db_isLocal = false;
+ $serverHostToLower = mb_strtolower(
+ $GLOBALS['cfg']['Server']['host']
+ );
+ if ($serverHostToLower === 'localhost'
+ || $GLOBALS['cfg']['Server']['host'] === '127.0.0.1'
+ || $GLOBALS['cfg']['Server']['host'] === '::1'
+ ) {
+ $this->db_isLocal = true;
+ }
+ $this->status = $server_status;
+ $this->sections = $sections;
+ $this->variables = $server_variables;
+ $this->used_queries = $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]) && isset($server_status[$new])) {
+ unset($server_status[$old]);
+ }
+ }
+ return $server_status;
+ }
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Server/Status/Monitor.php b/srcs/phpmyadmin/libraries/classes/Server/Status/Monitor.php
new file mode 100644
index 0000000..efa9e40
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Server/Status/Monitor.php
@@ -0,0 +1,546 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * functions for displaying server status sub item: monitor
+ *
+ * @usedby server_status_monitor.php
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Server\Status;
+
+use PhpMyAdmin\DatabaseInterface;
+use PhpMyAdmin\SysInfo;
+use PhpMyAdmin\Util;
+
+/**
+ * functions for displaying server status sub item: monitor
+ *
+ * @package PhpMyAdmin
+ */
+class Monitor
+{
+ /**
+ * @var DatabaseInterface
+ */
+ private $dbi;
+
+ /**
+ * Monitor constructor.
+ * @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 */
+ list($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'] = 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) {
+ list($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
+ ) {
+ switch ($type) {
+ /* We only collect the status and server variables here to
+ * read them all in one query,
+ * and only afterwards assign them.
+ * Also do some white list filtering on the names
+ */
+ 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'] = $this->dbi->numRows($result);
+ 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'] = isset($memory[$pName]) ? $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
+ *
+ * @return array
+ */
+ 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 . ') ';
+ $query .= 'AND start_time < FROM_UNIXTIME(' . $end . ') GROUP BY sql_text';
+
+ $result = $this->dbi->tryQuery($query);
+
+ $return = [
+ 'rows' => [],
+ 'sum' => [],
+ ];
+
+ while ($row = $this->dbi->fetchAssoc($result)) {
+ $type = mb_strtolower(
+ mb_substr(
+ $row['sql_text'],
+ 0,
+ 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(
+ ' ',
+ 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']);
+
+ $this->dbi->freeResult($result);
+ 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
+ *
+ * @return array
+ */
+ 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 . ') ';
+ $query .= $limitTypes . 'GROUP by argument'; // HAVING count > 1';
+
+ $result = $this->dbi->tryQuery($query);
+
+ $return = [
+ 'rows' => [],
+ 'sum' => [],
+ ];
+ $insertTables = [];
+ $insertTablesFirst = -1;
+ $i = 0;
+
+ while ($row = $this->dbi->fetchAssoc($result)) {
+ 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
+ )
+ ) {
+ $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;
+ } else {
+ $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(
+ ' ',
+ 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']);
+
+ $this->dbi->freeResult($result);
+
+ 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) && isset($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
+ );
+ }
+ }
+
+ $loggingVars = $this->dbi->fetchResult(
+ 'SHOW GLOBAL VARIABLES WHERE Variable_name IN'
+ . ' ("general_log","slow_query_log","long_query_time","log_output")',
+ 0,
+ 1
+ );
+ return $loggingVars;
+ }
+
+ /**
+ * 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);
+ }
+
+ if ($profiling = Util::profilingSupported()) {
+ $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);
+ while ($row = $this->dbi->fetchAssoc($result)) {
+ $return['explain'][] = $row;
+ }
+
+ // In case an error happened
+ $return['error'] = $this->dbi->getError();
+
+ $this->dbi->freeResult($result);
+
+ if ($profiling) {
+ $return['profiling'] = [];
+ $result = $this->dbi->tryQuery(
+ 'SELECT seq,state,duration FROM INFORMATION_SCHEMA.PROFILING'
+ . ' WHERE QUERY_ID=1 ORDER BY seq'
+ );
+ while ($row = $this->dbi->fetchAssoc($result)) {
+ $return['profiling'][] = $row;
+ }
+ $this->dbi->freeResult($result);
+ }
+ return $return;
+ }
+}