1. Check existence of mb_string, mysql and xml extensions before installation.
[openemr.git] / phpmyadmin / libraries / DatabaseInterface.class.php
blob6678fcc2a32f24a1feb6b7d8efb048d868c6cc5a
1 <?php
2 /* vim: set expandtab sw=4 ts=4 sts=4: */
3 /**
4 * Main interface for database interactions
6 * @package PhpMyAdmin-DBI
7 */
8 if (! defined('PHPMYADMIN')) {
9 exit;
12 require_once './libraries/logging.lib.php';
13 require_once './libraries/Index.class.php';
14 require_once './libraries/SystemDatabase.class.php';
15 require_once './libraries/util.lib.php';
17 use PMA\Util;
19 /**
20 * Main interface for database interactions
22 * @package PhpMyAdmin-DBI
24 class PMA_DatabaseInterface
26 /**
27 * Force STORE_RESULT method, ignored by classic MySQL.
29 const QUERY_STORE = 1;
30 /**
31 * Do not read whole query.
33 const QUERY_UNBUFFERED = 2;
34 /**
35 * Get session variable.
37 const GETVAR_SESSION = 1;
38 /**
39 * Get global variable.
41 const GETVAR_GLOBAL = 2;
43 /**
44 * @var PMA_DBI_Extension
46 private $_extension;
48 /**
49 * @var array Table data cache
51 private $_table_cache;
53 /**
54 * Constructor
56 * @param PMA_DBI_Extension $ext Object to be used for database queries
58 public function __construct($ext)
60 $this->_extension = $ext;
61 $this->_table_cache = array();
64 /**
65 * Checks whether database extension is loaded
67 * @param string $extension mysql extension to check
69 * @return bool
71 public static function checkDbExtension($extension = 'mysql')
73 if ($extension == 'drizzle' && function_exists('drizzle_create')) {
74 return true;
75 } else if (function_exists($extension . '_connect')) {
76 return true;
78 return false;
81 /**
82 * runs a query
84 * @param string $query SQL query to execute
85 * @param mixed $link optional database link to use
86 * @param int $options optional query options
87 * @param bool $cache_affected_rows whether to cache affected rows
89 * @return mixed
91 public function query($query, $link = null, $options = 0,
92 $cache_affected_rows = true
93 ) {
94 $res = $this->tryQuery($query, $link, $options, $cache_affected_rows)
95 or PMA_Util::mysqlDie($this->getError($link), $query);
96 return $res;
99 /**
100 * Get a cached value from table cache.
102 * @param array $contentPath Array of the name of the target value
103 * @param mixed $default Return value on cache miss
105 * @return mixed cached value or default
107 public function getCachedTableContent($contentPath, $default = null)
109 return Util\get($this->_table_cache, $contentPath, $default);
113 * Set an item in table cache using dot notation.
115 * @param array $contentPath Array with the target path
116 * @param mixed $value Target value
118 * @return void
120 public function cacheTableContent($contentPath, $value)
122 $loc = &$this->_table_cache;
124 if (!isset($contentPath)) {
125 $loc = $value;
126 return;
129 while (count($contentPath) > 1) {
130 $key = array_shift($contentPath);
132 // If the key doesn't exist at this depth, we will just create an empty array
133 // to hold the next value, allowing us to create the arrays to hold final
134 // values at the correct depth. Then we'll keep digging into the array.
135 if (!isset($loc[$key]) || !is_array($loc[$key])) {
136 $loc[$key] = array();
138 $loc = &$loc[$key];
141 $loc[array_shift($contentPath)] = $value;
145 * Clear the table cache.
147 * @return void
149 public function clearTableCache()
151 $this->_table_cache = array();
155 * Caches table data so PMA_Table does not require to issue
156 * SHOW TABLE STATUS again
158 * @param array $tables information for tables of some databases
159 * @param string $table table name
161 * @return void
163 private function _cacheTableData($tables, $table)
165 // Note: I don't see why we would need array_merge_recursive() here,
166 // as it creates double entries for the same table (for example a double
167 // entry for Comment when changing the storage engine in Operations)
168 // Note 2: Instead of array_merge(), simply use the + operator because
169 // array_merge() renumbers numeric keys starting with 0, therefore
170 // we would lose a db name that consists only of numbers
172 foreach ($tables as $one_database => $its_tables) {
173 if (isset($this->_table_cache[$one_database])) {
174 // the + operator does not do the intended effect
175 // when the cache for one table already exists
176 if ($table
177 && isset($this->_table_cache[$one_database][$table])
179 unset($this->_table_cache[$one_database][$table]);
181 $this->_table_cache[$one_database]
182 = $this->_table_cache[$one_database] + $tables[$one_database];
183 } else {
184 $this->_table_cache[$one_database] = $tables[$one_database];
190 * Stores query data into session data for debugging purposes
192 * @param string $query Query text
193 * @param object $link database link
194 * @param object|boolean $result Query result
195 * @param integer $time Time to execute query
197 * @return void
199 private function _dbgQuery($query, $link, $result, $time)
201 $dbgInfo = array();
202 $error_message = $this->getError($link);
203 if ($result == false && is_string($error_message)) {
204 $dbgInfo['error']
205 = '<span style="color:red">'
206 . htmlspecialchars($error_message) . '</span>';
208 $dbgInfo['query'] = htmlspecialchars($query);
209 $dbgInfo['time'] = $time;
210 // Get and slightly format backtrace
211 $dbgInfo['trace'] = debug_backtrace();
212 foreach ($dbgInfo['trace'] as $key => $step) {
213 if (isset($step['file'])) {
214 $dbgInfo['trace'][$key]['file'] = PMA_Error::relPath($step['file']);
217 $dbgInfo['hash'] = md5($query);
219 $_SESSION['debug']['queries'][] = $dbgInfo;
223 * runs a query and returns the result
225 * @param string $query query to run
226 * @param object $link mysql link resource
227 * @param integer $options query options
228 * @param bool $cache_affected_rows whether to cache affected row
230 * @return mixed
232 public function tryQuery($query, $link = null, $options = 0,
233 $cache_affected_rows = true
235 $link = $this->getLink($link);
236 if ($link === false) {
237 return false;
240 if ($GLOBALS['cfg']['DBG']['sql']) {
241 $time = microtime(true);
244 $result = $this->_extension->realQuery($query, $link, $options);
246 if ($cache_affected_rows) {
247 $GLOBALS['cached_affected_rows'] = $this->affectedRows($link, false);
250 if ($GLOBALS['cfg']['DBG']['sql']) {
251 $time = microtime(true) - $time;
252 $this->_dbgQuery($query, $link, $result, $time);
255 if ((!empty($result)) && (PMA_Tracker::isActive())) {
256 PMA_Tracker::handleQuery($query);
259 return $result;
263 * Run multi query statement and return results
265 * @param string $multi_query multi query statement to execute
266 * @param mysqli $link mysqli object
268 * @return mysqli_result collection | boolean(false)
270 public function tryMultiQuery($multi_query = '', $link = null)
272 $link = $this->getLink($link);
273 if ($link === false) {
274 return false;
277 return $this->_extension->realMultiQuery($link, $multi_query);
281 * returns array with table names for given db
283 * @param string $database name of database
284 * @param mixed $link mysql link resource|object
286 * @return array tables names
288 public function getTables($database, $link = null)
290 return $this->fetchResult(
291 'SHOW TABLES FROM ' . PMA_Util::backquote($database) . ';',
292 null,
294 $link,
295 self::QUERY_STORE
300 * returns a segment of the SQL WHERE clause regarding table name and type
302 * @param string $table table
303 * @param boolean $tbl_is_group $table is a table group
304 * @param string $table_type whether table or view
306 * @return string a segment of the WHERE clause
308 private function _getTableCondition($table, $tbl_is_group, $table_type)
310 // get table information from information_schema
311 if ($table) {
312 if (true === $tbl_is_group) {
313 $sql_where_table = 'AND t.`TABLE_NAME` LIKE \''
314 . PMA_Util::escapeMysqlWildcards(
315 PMA_Util::sqlAddSlashes($table)
317 . '%\'';
318 } else {
319 $sql_where_table = 'AND t.`TABLE_NAME` '
320 . PMA_Util::getCollateForIS() . ' = \''
321 . PMA_Util::sqlAddSlashes($table) . '\'';
323 } else {
324 $sql_where_table = '';
327 if ($table_type) {
328 if ($table_type == 'view') {
329 if (PMA_DRIZZLE) {
330 $sql_where_table .= " AND t.`TABLE_TYPE` != 'BASE'";
331 } else {
332 $sql_where_table .= " AND t.`TABLE_TYPE` != 'BASE TABLE'";
334 } else if ($table_type == 'table') {
335 if (PMA_DRIZZLE) {
336 $sql_where_table .= " AND t.`TABLE_TYPE` = 'BASE'";
337 } else {
338 $sql_where_table .= " AND t.`TABLE_TYPE` = 'BASE TABLE'";
342 return $sql_where_table;
346 * returns the beginning of the SQL statement to fetch the list of tables
348 * @param string[] $this_databases databases to list
349 * @param string $sql_where_table additional condition
351 * @return string the SQL statement
353 private function _getSqlForTablesFull($this_databases, $sql_where_table)
355 if (PMA_DRIZZLE) {
356 $stats_join = $this->_getDrizzeStatsJoin();
358 // data_dictionary.table_cache may not contain any data
359 // for some tables, it's just a table cache
360 // auto_increment == 0 is cast to NULL because currently
361 // (2011.03.13 GA)
362 // Drizzle doesn't provide correct value
363 $sql = "
364 SELECT t.*,
365 t.TABLE_SCHEMA AS `Db`,
366 t.TABLE_NAME AS `Name`,
367 t.TABLE_TYPE AS `TABLE_TYPE`,
368 t.ENGINE AS `Engine`,
369 t.ENGINE AS `Type`,
370 t.TABLE_VERSION AS `Version`,-- VERSION
371 t.ROW_FORMAT AS `Row_format`,
372 coalesce(tc.ROWS, stat.NUM_ROWS)
373 AS `Rows`,-- TABLE_ROWS,
374 coalesce(tc.ROWS, stat.NUM_ROWS)
375 AS `TABLE_ROWS`,
376 tc.AVG_ROW_LENGTH AS `Avg_row_length`, -- AVG_ROW_LENGTH
377 tc.TABLE_SIZE AS `Data_length`, -- DATA_LENGTH
378 NULL AS `Max_data_length`, -- MAX_DATA_LENGTH
379 NULL AS `Index_length`, -- INDEX_LENGTH
380 NULL AS `Data_free`, -- DATA_FREE
381 nullif(t.AUTO_INCREMENT, 0)
382 AS `Auto_increment`,
383 t.TABLE_CREATION_TIME AS `Create_time`, -- CREATE_TIME
384 t.TABLE_UPDATE_TIME AS `Update_time`, -- UPDATE_TIME
385 NULL AS `Check_time`, -- CHECK_TIME
386 t.TABLE_COLLATION AS `Collation`,
387 NULL AS `Checksum`, -- CHECKSUM
388 NULL AS `Create_options`, -- CREATE_OPTIONS
389 t.TABLE_COMMENT AS `Comment`
390 FROM data_dictionary.TABLES t
391 LEFT JOIN data_dictionary.TABLE_CACHE tc
392 ON tc.TABLE_SCHEMA = t.TABLE_SCHEMA AND tc.TABLE_NAME
393 = t.TABLE_NAME
394 $stats_join
395 WHERE t.TABLE_SCHEMA IN ('" . implode("', '", $this_databases) . "')
396 " . $sql_where_table;
397 } else {
398 $sql = '
399 SELECT *,
400 `TABLE_SCHEMA` AS `Db`,
401 `TABLE_NAME` AS `Name`,
402 `TABLE_TYPE` AS `TABLE_TYPE`,
403 `ENGINE` AS `Engine`,
404 `ENGINE` AS `Type`,
405 `VERSION` AS `Version`,
406 `ROW_FORMAT` AS `Row_format`,
407 `TABLE_ROWS` AS `Rows`,
408 `AVG_ROW_LENGTH` AS `Avg_row_length`,
409 `DATA_LENGTH` AS `Data_length`,
410 `MAX_DATA_LENGTH` AS `Max_data_length`,
411 `INDEX_LENGTH` AS `Index_length`,
412 `DATA_FREE` AS `Data_free`,
413 `AUTO_INCREMENT` AS `Auto_increment`,
414 `CREATE_TIME` AS `Create_time`,
415 `UPDATE_TIME` AS `Update_time`,
416 `CHECK_TIME` AS `Check_time`,
417 `TABLE_COLLATION` AS `Collation`,
418 `CHECKSUM` AS `Checksum`,
419 `CREATE_OPTIONS` AS `Create_options`,
420 `TABLE_COMMENT` AS `Comment`
421 FROM `information_schema`.`TABLES` t
422 WHERE `TABLE_SCHEMA` ' . PMA_Util::getCollateForIS() . '
423 IN (\'' . implode("', '", $this_databases) . '\')
424 ' . $sql_where_table;
426 return $sql;
430 * returns array of all tables in given db or dbs
431 * this function expects unquoted names:
432 * RIGHT: my_database
433 * WRONG: `my_database`
434 * WRONG: my\_database
435 * if $tbl_is_group is true, $table is used as filter for table names
437 * <code>
438 * $GLOBALS['dbi']->getTablesFull('my_database');
439 * $GLOBALS['dbi']->getTablesFull('my_database', 'my_table'));
440 * $GLOBALS['dbi']->getTablesFull('my_database', 'my_tables_', true));
441 * </code>
443 * @param string $database database
444 * @param string $table table name
445 * @param boolean $tbl_is_group $table is a table group
446 * @param mixed $link mysql link
447 * @param integer $limit_offset zero-based offset for the count
448 * @param boolean|integer $limit_count number of tables to return
449 * @param string $sort_by table attribute to sort by
450 * @param string $sort_order direction to sort (ASC or DESC)
451 * @param string $table_type whether table or view
453 * @todo move into PMA_Table
455 * @return array list of tables in given db(s)
457 public function getTablesFull($database, $table = '',
458 $tbl_is_group = false, $link = null, $limit_offset = 0,
459 $limit_count = false, $sort_by = 'Name', $sort_order = 'ASC',
460 $table_type = null
462 if (true === $limit_count) {
463 $limit_count = $GLOBALS['cfg']['MaxTableList'];
465 // prepare and check parameters
466 if (! is_array($database)) {
467 $databases = array($database);
468 } else {
469 $databases = $database;
472 $tables = array();
474 if (! isset($GLOBALS['cfg']['Server']['DisableIS']) || !$GLOBALS['cfg']['Server']['DisableIS']) {
475 $sql_where_table = $this->_getTableCondition(
476 $table, $tbl_is_group, $table_type
479 // for PMA bc:
480 // `SCHEMA_FIELD_NAME` AS `SHOW_TABLE_STATUS_FIELD_NAME`
482 // on non-Windows servers,
483 // added BINARY in the WHERE clause to force a case sensitive
484 // comparison (if we are looking for the db Aa we don't want
485 // to find the db aa)
486 $this_databases = array_map('PMA_Util::sqlAddSlashes', $databases);
488 $sql = $this->_getSqlForTablesFull($this_databases, $sql_where_table);
490 // Sort the tables
491 $sql .= " ORDER BY $sort_by $sort_order";
493 if ($limit_count) {
494 $sql .= ' LIMIT ' . $limit_count . ' OFFSET ' . $limit_offset;
497 $tables = $this->fetchResult(
498 $sql, array('TABLE_SCHEMA', 'TABLE_NAME'), null, $link
501 if (PMA_DRIZZLE) {
502 // correct I_S and D_D names returned by D_D.TABLES -
503 // Drizzle generally uses lower case for them,
504 // but TABLES returns uppercase
505 foreach ((array)$database as $db) {
506 $db_upper = /*overload*/mb_strtoupper($db);
507 if (!isset($tables[$db]) && isset($tables[$db_upper])) {
508 $tables[$db] = $tables[$db_upper];
509 unset($tables[$db_upper]);
514 if ($sort_by == 'Name' && $GLOBALS['cfg']['NaturalOrder']) {
515 // here, the array's first key is by schema name
516 foreach ($tables as $one_database_name => $one_database_tables) {
517 uksort($one_database_tables, 'strnatcasecmp');
519 if ($sort_order == 'DESC') {
520 $one_database_tables = array_reverse($one_database_tables);
522 $tables[$one_database_name] = $one_database_tables;
524 } else if ($sort_by == 'Data_length') { // Size = Data_length + Index_length
525 foreach ($tables as $one_database_name => $one_database_tables) {
526 uasort(
527 $one_database_tables,
528 function ($a, $b) {
529 $aLength = $a['Data_length'] + $a['Index_length'];
530 $bLength = $b['Data_length'] + $b['Index_length'];
531 return ($aLength == $bLength)
533 : ($aLength < $bLength) ? -1 : 1;
537 if ($sort_order == 'DESC') {
538 $one_database_tables = array_reverse($one_database_tables);
540 $tables[$one_database_name] = $one_database_tables;
543 } // end (get information from table schema)
545 // If permissions are wrong on even one database directory,
546 // information_schema does not return any table info for any database
547 // this is why we fall back to SHOW TABLE STATUS even for MySQL >= 50002
548 if (empty($tables) && !PMA_DRIZZLE) {
549 foreach ($databases as $each_database) {
550 if ($table || (true === $tbl_is_group) || ! empty($table_type)) {
551 $sql = 'SHOW TABLE STATUS FROM '
552 . PMA_Util::backquote($each_database)
553 . ' WHERE';
554 $needAnd = false;
555 if ($table || (true === $tbl_is_group)) {
556 $sql .= " `Name` LIKE '"
557 . PMA_Util::escapeMysqlWildcards(
558 PMA_Util::sqlAddSlashes($table, true)
560 . "%'";
561 $needAnd = true;
563 if (! empty($table_type)) {
564 if ($needAnd) {
565 $sql .= " AND";
567 if ($table_type == 'view') {
568 $sql .= " `Comment` = 'VIEW'";
569 } else if ($table_type == 'table') {
570 $sql .= " `Comment` != 'VIEW'";
573 } else {
574 $sql = 'SHOW TABLE STATUS FROM '
575 . PMA_Util::backquote($each_database);
578 $useStatusCache = false;
580 if (extension_loaded('apc')
581 && isset($GLOBALS['cfg']['Server']['StatusCacheDatabases'])
582 && ! empty($GLOBALS['cfg']['Server']['StatusCacheLifetime'])
584 $statusCacheDatabases
585 = (array) $GLOBALS['cfg']['Server']['StatusCacheDatabases'];
586 if (in_array($each_database, $statusCacheDatabases)) {
587 $useStatusCache = true;
591 $each_tables = null;
593 if ($useStatusCache) {
594 $cacheKey = 'phpMyAdmin_tableStatus_'
595 . sha1($GLOBALS['cfg']['Server']['host'] . '_' . $sql);
597 $each_tables = apc_fetch($cacheKey);
600 if (! $each_tables) {
601 $each_tables = $this->fetchResult($sql, 'Name', null, $link);
604 if ($useStatusCache) {
605 apc_store(
606 $cacheKey, $each_tables,
607 $GLOBALS['cfg']['Server']['StatusCacheLifetime']
611 // Sort naturally if the config allows it and we're sorting
612 // the Name column.
613 if ($sort_by == 'Name' && $GLOBALS['cfg']['NaturalOrder']) {
614 uksort($each_tables, 'strnatcasecmp');
616 if ($sort_order == 'DESC') {
617 $each_tables = array_reverse($each_tables);
619 } else {
620 // Prepare to sort by creating array of the selected sort
621 // value to pass to array_multisort
623 // Size = Data_length + Index_length
624 if ($sort_by == 'Data_length') {
625 foreach ($each_tables as $table_name => $table_data) {
626 ${$sort_by}[$table_name] = strtolower(
627 $table_data['Data_length'] + $table_data['Index_length']
630 } else {
631 foreach ($each_tables as $table_name => $table_data) {
632 ${$sort_by}[$table_name]
633 = strtolower($table_data[$sort_by]);
637 if (! empty($$sort_by)) {
638 if ($sort_order == 'DESC') {
639 array_multisort($$sort_by, SORT_DESC, $each_tables);
640 } else {
641 array_multisort($$sort_by, SORT_ASC, $each_tables);
645 // cleanup the temporary sort array
646 unset($$sort_by);
649 if ($limit_count) {
650 $each_tables = array_slice(
651 $each_tables, $limit_offset, $limit_count
655 foreach ($each_tables as $table_name => $each_table) {
656 if (! isset($each_tables[$table_name]['Type'])
657 && isset($each_tables[$table_name]['Engine'])
659 // pma BC, same parts of PMA still uses 'Type'
660 $each_tables[$table_name]['Type']
661 =& $each_tables[$table_name]['Engine'];
662 } elseif (! isset($each_tables[$table_name]['Engine'])
663 && isset($each_tables[$table_name]['Type'])
665 // old MySQL reports Type, newer MySQL reports Engine
666 $each_tables[$table_name]['Engine']
667 =& $each_tables[$table_name]['Type'];
670 // MySQL forward compatibility
671 // so pma could use this array as if every server
672 // is of version >5.0
673 // todo : remove and check usage in the rest of the code,
674 // MySQL 5.0 is required by current PMA version
675 $each_tables[$table_name]['TABLE_SCHEMA']
676 = $each_database;
677 $each_tables[$table_name]['TABLE_NAME']
678 =& $each_tables[$table_name]['Name'];
679 $each_tables[$table_name]['ENGINE']
680 =& $each_tables[$table_name]['Engine'];
681 $each_tables[$table_name]['VERSION']
682 =& $each_tables[$table_name]['Version'];
683 $each_tables[$table_name]['ROW_FORMAT']
684 =& $each_tables[$table_name]['Row_format'];
685 $each_tables[$table_name]['TABLE_ROWS']
686 =& $each_tables[$table_name]['Rows'];
687 $each_tables[$table_name]['AVG_ROW_LENGTH']
688 =& $each_tables[$table_name]['Avg_row_length'];
689 $each_tables[$table_name]['DATA_LENGTH']
690 =& $each_tables[$table_name]['Data_length'];
691 $each_tables[$table_name]['MAX_DATA_LENGTH']
692 =& $each_tables[$table_name]['Max_data_length'];
693 $each_tables[$table_name]['INDEX_LENGTH']
694 =& $each_tables[$table_name]['Index_length'];
695 $each_tables[$table_name]['DATA_FREE']
696 =& $each_tables[$table_name]['Data_free'];
697 $each_tables[$table_name]['AUTO_INCREMENT']
698 =& $each_tables[$table_name]['Auto_increment'];
699 $each_tables[$table_name]['CREATE_TIME']
700 =& $each_tables[$table_name]['Create_time'];
701 $each_tables[$table_name]['UPDATE_TIME']
702 =& $each_tables[$table_name]['Update_time'];
703 $each_tables[$table_name]['CHECK_TIME']
704 =& $each_tables[$table_name]['Check_time'];
705 $each_tables[$table_name]['TABLE_COLLATION']
706 =& $each_tables[$table_name]['Collation'];
707 $each_tables[$table_name]['CHECKSUM']
708 =& $each_tables[$table_name]['Checksum'];
709 $each_tables[$table_name]['CREATE_OPTIONS']
710 =& $each_tables[$table_name]['Create_options'];
711 $each_tables[$table_name]['TABLE_COMMENT']
712 =& $each_tables[$table_name]['Comment'];
714 if (strtoupper($each_tables[$table_name]['Comment']) === 'VIEW'
715 && $each_tables[$table_name]['Engine'] == null
717 $each_tables[$table_name]['TABLE_TYPE'] = 'VIEW';
718 } elseif ($each_database == 'information_schema') {
719 $each_tables[$table_name]['TABLE_TYPE'] = 'SYSTEM VIEW';
720 } else {
722 * @todo difference between 'TEMPORARY' and 'BASE TABLE'
723 * but how to detect?
725 $each_tables[$table_name]['TABLE_TYPE'] = 'BASE TABLE';
729 $tables[$each_database] = $each_tables;
733 // cache table data
734 // so PMA_Table does not require to issue SHOW TABLE STATUS again
735 $this->_cacheTableData($tables, $table);
737 if (is_array($database)) {
738 return $tables;
741 if (isset($tables[$database])) {
742 return $tables[$database];
745 if (isset($tables[/*overload*/mb_strtolower($database)])) {
746 // on windows with lower_case_table_names = 1
747 // MySQL returns
748 // with SHOW DATABASES or information_schema.SCHEMATA: `Test`
749 // but information_schema.TABLES gives `test`
750 // bug #2036
751 // https://sourceforge.net/p/phpmyadmin/bugs/2036/
752 return $tables[/*overload*/mb_strtolower($database)];
755 // one database but inexact letter case match
756 // as Drizzle is always case insensitive,
757 // we can safely return the only result
758 if (!PMA_DRIZZLE || !count($tables) == 1) {
759 return $tables;
762 $keys = array_keys($tables);
763 if (/*overload*/mb_strlen(array_pop($keys)) == /*overload*/mb_strlen($database)) {
764 return array_pop($tables);
766 return $tables;
770 * Copies the table properties to the set of property names used by PMA.
772 * @param array $tables array of table properties
773 * @param string $database database name
775 * @return array array with added properties
777 public function copyTableProperties($tables, $database)
779 foreach ($tables as $table_name => $each_table) {
780 if (! isset($tables[$table_name]['Type'])
781 && isset($tables[$table_name]['Engine'])
783 // pma BC, same parts of PMA still uses 'Type'
784 $tables[$table_name]['Type']
785 =& $tables[$table_name]['Engine'];
786 } elseif (! isset($tables[$table_name]['Engine'])
787 && isset($tables[$table_name]['Type'])
789 // old MySQL reports Type, newer MySQL reports Engine
790 $tables[$table_name]['Engine']
791 =& $tables[$table_name]['Type'];
794 // MySQL forward compatibility
795 // so pma could use this array as if every server
796 // is of version >5.0
797 // todo : remove and check usage in the rest of the code,
798 // MySQL 5.0 is required by current PMA version
799 $tables[$table_name]['TABLE_SCHEMA']
800 = $database;
801 $tables[$table_name]['TABLE_NAME']
802 =& $tables[$table_name]['Name'];
803 $tables[$table_name]['ENGINE']
804 =& $tables[$table_name]['Engine'];
805 $tables[$table_name]['VERSION']
806 =& $tables[$table_name]['Version'];
807 $tables[$table_name]['ROW_FORMAT']
808 =& $tables[$table_name]['Row_format'];
809 $tables[$table_name]['TABLE_ROWS']
810 =& $tables[$table_name]['Rows'];
811 $tables[$table_name]['AVG_ROW_LENGTH']
812 =& $tables[$table_name]['Avg_row_length'];
813 $tables[$table_name]['DATA_LENGTH']
814 =& $tables[$table_name]['Data_length'];
815 $tables[$table_name]['MAX_DATA_LENGTH']
816 =& $tables[$table_name]['Max_data_length'];
817 $tables[$table_name]['INDEX_LENGTH']
818 =& $tables[$table_name]['Index_length'];
819 $tables[$table_name]['DATA_FREE']
820 =& $tables[$table_name]['Data_free'];
821 $tables[$table_name]['AUTO_INCREMENT']
822 =& $tables[$table_name]['Auto_increment'];
823 $tables[$table_name]['CREATE_TIME']
824 =& $tables[$table_name]['Create_time'];
825 $tables[$table_name]['UPDATE_TIME']
826 =& $tables[$table_name]['Update_time'];
827 $tables[$table_name]['CHECK_TIME']
828 =& $tables[$table_name]['Check_time'];
829 $tables[$table_name]['TABLE_COLLATION']
830 =& $tables[$table_name]['Collation'];
831 $tables[$table_name]['CHECKSUM']
832 =& $tables[$table_name]['Checksum'];
833 $tables[$table_name]['CREATE_OPTIONS']
834 =& $tables[$table_name]['Create_options'];
835 $tables[$table_name]['TABLE_COMMENT']
836 =& $tables[$table_name]['Comment'];
838 $commentUpper = /*overload*/mb_strtoupper(
839 $tables[$table_name]['Comment']
841 if ($commentUpper === 'VIEW'
842 && $tables[$table_name]['Engine'] == null
844 $tables[$table_name]['TABLE_TYPE'] = 'VIEW';
845 } else {
847 * @todo difference between 'TEMPORARY' and 'BASE TABLE'
848 * but how to detect?
850 $tables[$table_name]['TABLE_TYPE'] = 'BASE TABLE';
853 return $tables;
857 * Get VIEWs in a particular database
859 * @param string $db Database name to look in
861 * @return array $views Set of VIEWs inside the database
863 public function getVirtualTables($db)
866 $tables_full = $this->getTablesFull($db);
867 $views = array();
869 foreach ($tables_full as $table=>$tmp) {
871 $_table = $this->getTable($db, $table);
872 if ($_table->isView()) {
873 $views[] = $table;
878 return $views;
884 * returns array with databases containing extended infos about them
886 * @param string $database database
887 * @param boolean $force_stats retrieve stats also for MySQL < 5
888 * @param object $link mysql link
889 * @param string $sort_by column to order by
890 * @param string $sort_order ASC or DESC
891 * @param integer $limit_offset starting offset for LIMIT
892 * @param bool|int $limit_count row count for LIMIT or true
893 * for $GLOBALS['cfg']['MaxDbList']
895 * @todo move into PMA_List_Database?
897 * @return array $databases
899 public function getDatabasesFull($database = null, $force_stats = false,
900 $link = null, $sort_by = 'SCHEMA_NAME', $sort_order = 'ASC',
901 $limit_offset = 0, $limit_count = false
903 $sort_order = strtoupper($sort_order);
905 if (true === $limit_count) {
906 $limit_count = $GLOBALS['cfg']['MaxDbList'];
909 $apply_limit_and_order_manual = true;
911 if (! $GLOBALS['cfg']['Server']['DisableIS']) {
913 * if $GLOBALS['cfg']['NaturalOrder'] is enabled, we cannot use LIMIT
914 * cause MySQL does not support natural ordering,
915 * we have to do it afterward
917 $limit = '';
918 if (! $GLOBALS['cfg']['NaturalOrder']) {
919 if ($limit_count) {
920 $limit = ' LIMIT ' . $limit_count . ' OFFSET ' . $limit_offset;
923 $apply_limit_and_order_manual = false;
926 // get table information from information_schema
927 if (! empty($database)) {
928 $sql_where_schema = 'WHERE `SCHEMA_NAME` LIKE \''
929 . PMA_Util::sqlAddSlashes($database) . '\'';
930 } else {
931 $sql_where_schema = '';
934 if (PMA_DRIZZLE) {
935 // data_dictionary.table_cache may not contain any data for some
936 // tables, it's just a table cache
937 $sql = 'SELECT
938 s.SCHEMA_NAME,
939 s.DEFAULT_COLLATION_NAME';
940 if ($force_stats) {
941 // no TABLE_CACHE data, stable results are better than
942 // constantly changing
943 $sql .= ',
944 COUNT(t.TABLE_SCHEMA) AS SCHEMA_TABLES,
945 SUM(stat.NUM_ROWS) AS SCHEMA_TABLE_ROWS';
947 $sql .= '
948 FROM data_dictionary.SCHEMAS s';
949 if ($force_stats) {
950 $stats_join = $this->_getDrizzeStatsJoin();
952 $sql .= "
953 LEFT JOIN data_dictionary.TABLES t
954 ON t.TABLE_SCHEMA = s.SCHEMA_NAME
955 $stats_join";
957 $sql .= $sql_where_schema . '
958 GROUP BY s.SCHEMA_NAME, s.DEFAULT_COLLATION_NAME
959 ORDER BY ' . PMA_Util::backquote($sort_by) . ' ' . $sort_order
960 . $limit;
961 } else {
962 $sql = 'SELECT *,
963 CAST(BIN_NAME AS CHAR CHARACTER SET utf8) AS SCHEMA_NAME
964 FROM (';
965 $sql .= 'SELECT
966 BINARY s.SCHEMA_NAME AS BIN_NAME,
967 s.DEFAULT_COLLATION_NAME';
968 if ($force_stats) {
969 $sql .= ',
970 COUNT(t.TABLE_SCHEMA) AS SCHEMA_TABLES,
971 SUM(t.TABLE_ROWS) AS SCHEMA_TABLE_ROWS,
972 SUM(t.DATA_LENGTH) AS SCHEMA_DATA_LENGTH,
973 SUM(t.MAX_DATA_LENGTH) AS SCHEMA_MAX_DATA_LENGTH,
974 SUM(t.INDEX_LENGTH) AS SCHEMA_INDEX_LENGTH,
975 SUM(t.DATA_LENGTH + t.INDEX_LENGTH)
976 AS SCHEMA_LENGTH,
977 SUM(t.DATA_FREE) AS SCHEMA_DATA_FREE';
979 $sql .= '
980 FROM `information_schema`.SCHEMATA s';
981 if ($force_stats) {
982 $sql .= '
983 LEFT JOIN `information_schema`.TABLES t
984 ON BINARY t.TABLE_SCHEMA = BINARY s.SCHEMA_NAME';
986 $sql .= $sql_where_schema . '
987 GROUP BY BINARY s.SCHEMA_NAME, s.DEFAULT_COLLATION_NAME
988 ORDER BY ';
989 if ($sort_by == 'SCHEMA_NAME'
990 || $sort_by == 'DEFAULT_COLLATION_NAME'
992 $sql .= 'BINARY ';
994 $sql .= PMA_Util::backquote($sort_by)
995 . ' ' . $sort_order
996 . $limit;
997 $sql .= ') a';
1000 $databases = $this->fetchResult($sql, 'SCHEMA_NAME', null, $link);
1002 $mysql_error = $this->getError($link);
1003 if (! count($databases) && $GLOBALS['errno']) {
1004 PMA_Util::mysqlDie($mysql_error, $sql);
1007 // display only databases also in official database list
1008 // f.e. to apply hide_db and only_db
1009 $drops = array_diff(
1010 array_keys($databases), (array) $GLOBALS['pma']->databases
1012 foreach ($drops as $drop) {
1013 unset($databases[$drop]);
1015 } else {
1016 $databases = array();
1017 foreach ($GLOBALS['pma']->databases as $database_name) {
1018 // MySQL forward compatibility
1019 // so pma could use this array as if every server is of version >5.0
1020 // todo : remove and check the rest of the code for usage,
1021 // MySQL 5.0 or higher is required for current PMA version
1022 $databases[$database_name]['SCHEMA_NAME'] = $database_name;
1024 include_once './libraries/mysql_charsets.inc.php';
1025 $databases[$database_name]['DEFAULT_COLLATION_NAME']
1026 = PMA_getDbCollation($database_name);
1028 if (!$force_stats) {
1029 continue;
1032 // get additional info about tables
1033 $databases[$database_name]['SCHEMA_TABLES'] = 0;
1034 $databases[$database_name]['SCHEMA_TABLE_ROWS'] = 0;
1035 $databases[$database_name]['SCHEMA_DATA_LENGTH'] = 0;
1036 $databases[$database_name]['SCHEMA_MAX_DATA_LENGTH'] = 0;
1037 $databases[$database_name]['SCHEMA_INDEX_LENGTH'] = 0;
1038 $databases[$database_name]['SCHEMA_LENGTH'] = 0;
1039 $databases[$database_name]['SCHEMA_DATA_FREE'] = 0;
1041 $res = $this->query(
1042 'SHOW TABLE STATUS FROM '
1043 . PMA_Util::backquote($database_name) . ';'
1046 if ($res === false) {
1047 unset($res);
1048 continue;
1051 while ($row = $this->fetchAssoc($res)) {
1052 $databases[$database_name]['SCHEMA_TABLES']++;
1053 $databases[$database_name]['SCHEMA_TABLE_ROWS']
1054 += $row['Rows'];
1055 $databases[$database_name]['SCHEMA_DATA_LENGTH']
1056 += $row['Data_length'];
1057 $databases[$database_name]['SCHEMA_MAX_DATA_LENGTH']
1058 += $row['Max_data_length'];
1059 $databases[$database_name]['SCHEMA_INDEX_LENGTH']
1060 += $row['Index_length'];
1062 // for InnoDB, this does not contain the number of
1063 // overhead bytes but the total free space
1064 if ('InnoDB' != $row['Engine']) {
1065 $databases[$database_name]['SCHEMA_DATA_FREE']
1066 += $row['Data_free'];
1068 $databases[$database_name]['SCHEMA_LENGTH']
1069 += $row['Data_length'] + $row['Index_length'];
1071 $this->freeResult($res);
1072 unset($res);
1077 * apply limit and order manually now
1078 * (caused by older MySQL < 5 or $GLOBALS['cfg']['NaturalOrder'])
1080 if ($apply_limit_and_order_manual) {
1081 $GLOBALS['callback_sort_order'] = $sort_order;
1082 $GLOBALS['callback_sort_by'] = $sort_by;
1083 usort(
1084 $databases,
1085 array('PMA_DatabaseInterface', '_usortComparisonCallback')
1087 unset($GLOBALS['callback_sort_order'], $GLOBALS['callback_sort_by']);
1090 * now apply limit
1092 if ($limit_count) {
1093 $databases = array_slice($databases, $limit_offset, $limit_count);
1097 return $databases;
1102 * Generates JOIN part for the Drizzle query to get database/table stats.
1104 * @return string
1106 private function _getDrizzeStatsJoin()
1108 $engine_info = PMA_Util::cacheGet('drizzle_engines');
1109 $stats_join = "LEFT JOIN (SELECT 0 NUM_ROWS) AS stat ON false";
1110 if (isset($engine_info['InnoDB'])
1111 && $engine_info['InnoDB']['module_library'] == 'innobase'
1113 $stats_join
1114 = "LEFT JOIN data_dictionary.INNODB_SYS_TABLESTATS stat"
1115 . " ON (t.ENGINE = 'InnoDB' AND stat.NAME"
1116 . " = (t.TABLE_SCHEMA || '/') || t.TABLE_NAME)";
1118 return $stats_join;
1123 * usort comparison callback
1125 * @param string $a first argument to sort
1126 * @param string $b second argument to sort
1128 * @return integer a value representing whether $a should be before $b in the
1129 * sorted array or not
1131 * @access private
1133 private static function _usortComparisonCallback($a, $b)
1135 if ($GLOBALS['cfg']['NaturalOrder']) {
1136 $sorter = 'strnatcasecmp';
1137 } else {
1138 $sorter = 'strcasecmp';
1140 /* No sorting when key is not present */
1141 if (! isset($a[$GLOBALS['callback_sort_by']])
1142 || ! isset($b[$GLOBALS['callback_sort_by']])
1144 return 0;
1146 // produces f.e.:
1147 // return -1 * strnatcasecmp($a["SCHEMA_TABLES"], $b["SCHEMA_TABLES"])
1148 return ($GLOBALS['callback_sort_order'] == 'ASC' ? 1 : -1) * $sorter(
1149 $a[$GLOBALS['callback_sort_by']], $b[$GLOBALS['callback_sort_by']]
1151 } // end of the '_usortComparisonCallback()' method
1154 * returns detailed array with all columns for sql
1156 * @param string $sql_query target SQL query to get columns
1157 * @param array $view_columns alias for columns
1159 * @return array
1161 public function getColumnMapFromSql($sql_query, $view_columns = array())
1163 $result = $this->tryQuery($sql_query);
1165 if ($result === false) {
1166 return array();
1169 $meta = $this->getFieldsMeta(
1170 $result
1173 $nbFields = count($meta);
1174 if ($nbFields <= 0) {
1175 return array();
1178 $column_map = array();
1179 $nbColumns = count($view_columns);
1181 for ($i=0; $i < $nbFields; $i++) {
1183 $map = array();
1184 $map['table_name'] = $meta[$i]->table;
1185 $map['refering_column'] = $meta[$i]->name;
1187 if ($nbColumns > 1) {
1188 $map['real_column'] = $view_columns[$i];
1191 $column_map[] = $map;
1194 return $column_map;
1198 * returns detailed array with all columns for given table in database,
1199 * or all tables/databases
1201 * @param string $database name of database
1202 * @param string $table name of table to retrieve columns from
1203 * @param string $column name of specific column
1204 * @param mixed $link mysql link resource
1206 * @return array
1208 public function getColumnsFull($database = null, $table = null,
1209 $column = null, $link = null
1211 if (! $GLOBALS['cfg']['Server']['DisableIS']) {
1212 $sql_wheres = array();
1213 $array_keys = array();
1215 // get columns information from information_schema
1216 if (null !== $database) {
1217 $sql_wheres[] = '`TABLE_SCHEMA` = \''
1218 . PMA_Util::sqlAddSlashes($database) . '\' ';
1219 } else {
1220 $array_keys[] = 'TABLE_SCHEMA';
1222 if (null !== $table) {
1223 $sql_wheres[] = '`TABLE_NAME` = \''
1224 . PMA_Util::sqlAddSlashes($table) . '\' ';
1225 } else {
1226 $array_keys[] = 'TABLE_NAME';
1228 if (null !== $column) {
1229 $sql_wheres[] = '`COLUMN_NAME` = \''
1230 . PMA_Util::sqlAddSlashes($column) . '\' ';
1231 } else {
1232 $array_keys[] = 'COLUMN_NAME';
1235 // for PMA bc:
1236 // `[SCHEMA_FIELD_NAME]` AS `[SHOW_FULL_COLUMNS_FIELD_NAME]`
1237 if (PMA_DRIZZLE) {
1238 $sql = "SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME,
1239 column_name AS `Field`,
1240 (CASE
1241 WHEN character_maximum_length > 0
1242 THEN concat(lower(data_type), '(', character_maximum_length, ')')
1243 WHEN numeric_precision > 0 OR numeric_scale > 0
1244 THEN concat(lower(data_type), '(', numeric_precision,
1245 ',', numeric_scale, ')')
1246 WHEN enum_values IS NOT NULL
1247 THEN concat(lower(data_type), '(', enum_values, ')')
1248 ELSE lower(data_type) END)
1249 AS `Type`,
1250 collation_name AS `Collation`,
1251 (CASE is_nullable
1252 WHEN 1 THEN 'YES'
1253 ELSE 'NO' END) AS `Null`,
1254 (CASE
1255 WHEN is_used_in_primary THEN 'PRI'
1256 ELSE '' END) AS `Key`,
1257 column_default AS `Default`,
1258 (CASE
1259 WHEN is_auto_increment THEN 'auto_increment'
1260 WHEN column_default_update
1261 THEN 'on update ' || column_default_update
1262 ELSE '' END) AS `Extra`,
1263 NULL AS `Privileges`,
1264 column_comment AS `Comment`
1265 FROM data_dictionary.columns";
1266 } else {
1267 $sql = '
1268 SELECT *,
1269 `COLUMN_NAME` AS `Field`,
1270 `COLUMN_TYPE` AS `Type`,
1271 `COLLATION_NAME` AS `Collation`,
1272 `IS_NULLABLE` AS `Null`,
1273 `COLUMN_KEY` AS `Key`,
1274 `COLUMN_DEFAULT` AS `Default`,
1275 `EXTRA` AS `Extra`,
1276 `PRIVILEGES` AS `Privileges`,
1277 `COLUMN_COMMENT` AS `Comment`
1278 FROM `information_schema`.`COLUMNS`';
1280 if (count($sql_wheres)) {
1281 $sql .= "\n" . ' WHERE ' . implode(' AND ', $sql_wheres);
1283 return $this->fetchResult($sql, $array_keys, null, $link);
1284 } else {
1285 $columns = array();
1286 if (null === $database) {
1287 foreach ($GLOBALS['pma']->databases as $database) {
1288 $columns[$database] = $this->getColumnsFull(
1289 $database, null, null, $link
1292 return $columns;
1293 } elseif (null === $table) {
1294 $tables = $this->getTables($database);
1295 foreach ($tables as $table) {
1296 $columns[$table] = $this->getColumnsFull(
1297 $database, $table, null, $link
1300 return $columns;
1302 $sql = 'SHOW FULL COLUMNS FROM '
1303 . PMA_Util::backquote($database) . '.' . PMA_Util::backquote($table);
1304 if (null !== $column) {
1305 $sql .= " LIKE '" . PMA_Util::sqlAddSlashes($column, true) . "'";
1308 $columns = $this->fetchResult($sql, 'Field', null, $link);
1309 $ordinal_position = 1;
1310 foreach ($columns as $column_name => $each_column) {
1312 // MySQL forward compatibility
1313 // so pma could use this array as if every server is of version >5.0
1314 // todo : remove and check the rest of the code for usage,
1315 // MySQL 5.0 or higher is required for current PMA version
1316 $columns[$column_name]['COLUMN_NAME']
1317 =& $columns[$column_name]['Field'];
1318 $columns[$column_name]['COLUMN_TYPE']
1319 =& $columns[$column_name]['Type'];
1320 $columns[$column_name]['COLLATION_NAME']
1321 =& $columns[$column_name]['Collation'];
1322 $columns[$column_name]['IS_NULLABLE']
1323 =& $columns[$column_name]['Null'];
1324 $columns[$column_name]['COLUMN_KEY']
1325 =& $columns[$column_name]['Key'];
1326 $columns[$column_name]['COLUMN_DEFAULT']
1327 =& $columns[$column_name]['Default'];
1328 $columns[$column_name]['EXTRA']
1329 =& $columns[$column_name]['Extra'];
1330 $columns[$column_name]['PRIVILEGES']
1331 =& $columns[$column_name]['Privileges'];
1332 $columns[$column_name]['COLUMN_COMMENT']
1333 =& $columns[$column_name]['Comment'];
1335 $columns[$column_name]['TABLE_CATALOG'] = null;
1336 $columns[$column_name]['TABLE_SCHEMA'] = $database;
1337 $columns[$column_name]['TABLE_NAME'] = $table;
1338 $columns[$column_name]['ORDINAL_POSITION'] = $ordinal_position;
1339 $columns[$column_name]['DATA_TYPE']
1340 = substr(
1341 $columns[$column_name]['COLUMN_TYPE'],
1343 strpos($columns[$column_name]['COLUMN_TYPE'], '(')
1346 * @todo guess CHARACTER_MAXIMUM_LENGTH from COLUMN_TYPE
1348 $columns[$column_name]['CHARACTER_MAXIMUM_LENGTH'] = null;
1350 * @todo guess CHARACTER_OCTET_LENGTH from CHARACTER_MAXIMUM_LENGTH
1352 $columns[$column_name]['CHARACTER_OCTET_LENGTH'] = null;
1353 $columns[$column_name]['NUMERIC_PRECISION'] = null;
1354 $columns[$column_name]['NUMERIC_SCALE'] = null;
1355 $columns[$column_name]['CHARACTER_SET_NAME']
1356 = substr(
1357 $columns[$column_name]['COLLATION_NAME'],
1359 strpos($columns[$column_name]['COLLATION_NAME'], '_')
1362 $ordinal_position++;
1365 if (null !== $column) {
1366 reset($columns);
1367 $columns = current($columns);
1370 return $columns;
1375 * Returns SQL query for fetching columns for a table
1377 * The 'Key' column is not calculated properly, use $GLOBALS['dbi']->getColumns()
1378 * to get correct values.
1380 * @param string $database name of database
1381 * @param string $table name of table to retrieve columns from
1382 * @param string $column name of column, null to show all columns
1383 * @param boolean $full whether to return full info or only column names
1385 * @see getColumns()
1387 * @return string
1389 public function getColumnsSql($database, $table, $column = null, $full = false)
1391 if (defined('PMA_DRIZZLE') && PMA_DRIZZLE) {
1392 // `Key` column:
1393 // * used in primary key => PRI
1394 // * unique one-column => UNI
1395 // * indexed, one-column or first in multi-column => MUL
1396 // Promotion of UNI to PRI in case no primary index exists
1397 // is done after query is executed
1398 $sql = "SELECT
1399 column_name AS `Field`,
1400 (CASE
1401 WHEN character_maximum_length > 0
1402 THEN concat(
1403 lower(data_type), '(', character_maximum_length, ')'
1405 WHEN numeric_precision > 0 OR numeric_scale > 0
1406 THEN concat(lower(data_type), '(', numeric_precision,
1407 ',', numeric_scale, ')')
1408 WHEN enum_values IS NOT NULL
1409 THEN concat(lower(data_type), '(', enum_values, ')')
1410 ELSE lower(data_type) END)
1411 AS `Type`,
1412 " . ($full ? "
1413 collation_name AS `Collation`," : '') . "
1414 (CASE is_nullable
1415 WHEN 1 THEN 'YES'
1416 ELSE 'NO' END) AS `Null`,
1417 (CASE
1418 WHEN is_used_in_primary THEN 'PRI'
1419 WHEN is_unique AND NOT is_multi THEN 'UNI'
1420 WHEN is_indexed
1421 AND (NOT is_multi OR is_first_in_multi) THEN 'MUL'
1422 ELSE '' END) AS `Key`,
1423 column_default AS `Default`,
1424 (CASE
1425 WHEN is_auto_increment THEN 'auto_increment'
1426 WHEN column_default_update <> ''
1427 THEN 'on update ' || column_default_update
1428 ELSE '' END) AS `Extra`
1429 " . ($full ? " ,
1430 NULL AS `Privileges`,
1431 column_comment AS `Comment`" : '') . "
1432 FROM data_dictionary.columns
1433 WHERE table_schema = '" . PMA_Util::sqlAddSlashes($database) . "'
1434 AND table_name = '" . PMA_Util::sqlAddSlashes($table) . "'
1435 " . (
1436 ($column !== null)
1438 AND column_name = '" . PMA_Util::sqlAddSlashes($column) . "'"
1439 : ''
1441 // ORDER BY ordinal_position
1442 } else {
1443 $sql = 'SHOW ' . ($full ? 'FULL' : '') . ' COLUMNS FROM '
1444 . PMA_Util::backquote($database) . '.' . PMA_Util::backquote($table)
1445 . (($column !== null) ? "LIKE '"
1446 . PMA_Util::sqlAddSlashes($column, true) . "'" : '');
1448 return $sql;
1452 * Returns descriptions of columns in given table (all or given by $column)
1454 * @param string $database name of database
1455 * @param string $table name of table to retrieve columns from
1456 * @param string $column name of column, null to show all columns
1457 * @param boolean $full whether to return full info or only column names
1458 * @param mixed $link mysql link resource
1460 * @return array array indexed by column names or,
1461 * if $column is given, flat array description
1463 public function getColumns($database, $table, $column = null, $full = false,
1464 $link = null
1466 $sql = $this->getColumnsSql($database, $table, $column, $full);
1467 $fields = $this->fetchResult($sql, 'Field', null, $link);
1468 if (! is_array($fields) || count($fields) == 0) {
1469 return array();
1471 // Check if column is a part of multiple-column index and set its 'Key'.
1472 $indexes = PMA_Index::getFromTable($table, $database);
1473 foreach ($fields as $field => $field_data) {
1474 if (!empty($field_data['Key'])) {
1475 continue;
1478 foreach ($indexes as $index) {
1479 /** @var PMA_Index $index */
1480 if (!$index->hasColumn($field)) {
1481 continue;
1484 $index_columns = $index->getColumns();
1485 if ($index_columns[$field]->getSeqInIndex() > 1) {
1486 if ($index->isUnique()) {
1487 $fields[$field]['Key'] = 'UNI';
1488 } else {
1489 $fields[$field]['Key'] = 'MUL';
1494 if (PMA_DRIZZLE) {
1495 // fix Key column, it's much simpler in PHP than in SQL
1496 $has_pk = false;
1497 $has_pk_candidates = false;
1498 foreach ($fields as $f) {
1499 if ($f['Key'] == 'PRI') {
1500 $has_pk = true;
1501 break;
1502 } else if ($f['Null'] == 'NO'
1503 && ($f['Key'] == 'MUL'
1504 || $f['Key'] == 'UNI')
1506 $has_pk_candidates = true;
1509 if (! $has_pk && $has_pk_candidates) {
1510 $secureDatabase = PMA_Util::sqlAddSlashes($database);
1511 // check whether we can promote some unique index to PRI
1512 $sql = "
1513 SELECT i.index_name, p.column_name
1514 FROM data_dictionary.indexes i
1515 JOIN data_dictionary.index_parts p
1516 USING (table_schema, table_name)
1517 WHERE i.table_schema = '" . $secureDatabase . "'
1518 AND i.table_name = '" . PMA_Util::sqlAddSlashes($table) . "'
1519 AND i.is_unique
1520 AND NOT i.is_nullable";
1521 $result = $this->fetchResult($sql, 'index_name', null, $link);
1522 $result = $result ? array_shift($result) : array();
1523 foreach ($result as $f) {
1524 $fields[$f]['Key'] = 'PRI';
1529 return ($column != null) ? array_shift($fields) : $fields;
1533 * Returns all column names in given table
1535 * @param string $database name of database
1536 * @param string $table name of table to retrieve columns from
1537 * @param mixed $link mysql link resource
1539 * @return null|array
1541 public function getColumnNames($database, $table, $link = null)
1543 $sql = $this->getColumnsSql($database, $table);
1544 // We only need the 'Field' column which contains the table's column names
1545 $fields = array_keys($this->fetchResult($sql, 'Field', null, $link));
1547 if (! is_array($fields) || count($fields) == 0) {
1548 return null;
1550 return $fields;
1554 * Returns SQL for fetching information on table indexes (SHOW INDEXES)
1556 * @param string $database name of database
1557 * @param string $table name of the table whose indexes are to be retrieved
1558 * @param string $where additional conditions for WHERE
1560 * @return string SQL for getting indexes
1562 public function getTableIndexesSql($database, $table, $where = null)
1564 if (PMA_DRIZZLE) {
1565 $sql = "SELECT
1566 ip.table_name AS `Table`,
1567 (NOT ip.is_unique) AS Non_unique,
1568 ip.index_name AS Key_name,
1569 ip.sequence_in_index+1 AS Seq_in_index,
1570 ip.column_name AS Column_name,
1571 (CASE
1572 WHEN i.index_type = 'BTREE' THEN 'A'
1573 ELSE NULL END) AS Collation,
1574 NULL AS Cardinality,
1575 compare_length AS Sub_part,
1576 NULL AS Packed,
1577 ip.is_nullable AS `Null`,
1578 i.index_type AS Index_type,
1579 NULL AS Comment,
1580 i.index_comment AS Index_comment
1581 FROM data_dictionary.index_parts ip
1582 LEFT JOIN data_dictionary.indexes i
1583 USING (table_schema, table_name, index_name)
1584 WHERE table_schema = '" . PMA_Util::sqlAddSlashes($database) . "'
1585 AND table_name = '" . PMA_Util::sqlAddSlashes($table) . "'
1587 if ($where) {
1588 $sql = "SELECT * FROM (" . $sql . ") A WHERE (" . $where . ")";
1590 } else {
1591 $sql = 'SHOW INDEXES FROM ' . PMA_Util::backquote($database) . '.'
1592 . PMA_Util::backquote($table);
1593 if ($where) {
1594 $sql .= ' WHERE (' . $where . ')';
1597 return $sql;
1601 * Returns indexes of a table
1603 * @param string $database name of database
1604 * @param string $table name of the table whose indexes are to be retrieved
1605 * @param mixed $link mysql link resource
1607 * @return array $indexes
1609 public function getTableIndexes($database, $table, $link = null)
1611 $sql = $this->getTableIndexesSql($database, $table);
1612 $indexes = $this->fetchResult($sql, null, null, $link);
1614 if (! is_array($indexes) || count($indexes) < 1) {
1615 return array();
1617 return $indexes;
1621 * returns value of given mysql server variable
1623 * @param string $var mysql server variable name
1624 * @param int $type PMA_DatabaseInterface::GETVAR_SESSION |
1625 * PMA_DatabaseInterface::GETVAR_GLOBAL
1626 * @param mixed $link mysql link resource|object
1628 * @return mixed value for mysql server variable
1630 public function getVariable(
1631 $var, $type = self::GETVAR_SESSION, $link = null
1633 $link = $this->getLink($link);
1634 if ($link === false) {
1635 return false;
1638 switch ($type) {
1639 case self::GETVAR_SESSION:
1640 $modifier = ' SESSION';
1641 break;
1642 case self::GETVAR_GLOBAL:
1643 $modifier = ' GLOBAL';
1644 break;
1645 default:
1646 $modifier = '';
1648 return $this->fetchValue(
1649 'SHOW' . $modifier . ' VARIABLES LIKE \'' . $var . '\';', 0, 1, $link
1654 * Sets new value for a variable if it is different from the current value
1656 * @param string $var variable name
1657 * @param string $value value to set
1658 * @param mixed $link mysql link resource|object
1660 * @return bool whether query was a successful
1662 public function setVariable($var, $value, $link = null)
1664 $link = $this->getLink($link);
1665 if ($link === false) {
1666 return false;
1668 $current_value = $GLOBALS['dbi']->getVariable(
1669 $var, self::GETVAR_SESSION, $link
1671 if ($current_value == $value) {
1672 return true;
1675 return $this->query("SET " . $var . " = " . $value . ';', $link);
1679 * Function called just after a connection to the MySQL database server has
1680 * been established. It sets the connection collation, and determines the
1681 * version of MySQL which is running.
1683 * @param mixed $link mysql link resource|object
1685 * @return void
1687 public function postConnect($link)
1689 if (! defined('PMA_MYSQL_INT_VERSION')) {
1690 if (PMA_Util::cacheExists('PMA_MYSQL_INT_VERSION')) {
1691 define(
1692 'PMA_MYSQL_INT_VERSION',
1693 PMA_Util::cacheGet('PMA_MYSQL_INT_VERSION')
1695 define(
1696 'PMA_MYSQL_MAJOR_VERSION',
1697 PMA_Util::cacheGet('PMA_MYSQL_MAJOR_VERSION')
1699 define(
1700 'PMA_MYSQL_STR_VERSION',
1701 PMA_Util::cacheGet('PMA_MYSQL_STR_VERSION')
1703 define(
1704 'PMA_MYSQL_VERSION_COMMENT',
1705 PMA_Util::cacheGet('PMA_MYSQL_VERSION_COMMENT')
1707 define(
1708 'PMA_MARIADB',
1709 PMA_Util::cacheGet('PMA_MARIADB')
1711 define(
1712 'PMA_DRIZZLE',
1713 PMA_Util::cacheGet('PMA_DRIZZLE')
1715 } else {
1716 $version = $this->fetchSingleRow(
1717 'SELECT @@version, @@version_comment',
1718 'ASSOC',
1719 $link
1722 if ($version) {
1723 $match = explode('.', $version['@@version']);
1724 define('PMA_MYSQL_MAJOR_VERSION', (int)$match[0]);
1725 define(
1726 'PMA_MYSQL_INT_VERSION',
1727 (int) sprintf(
1728 '%d%02d%02d', $match[0], $match[1], intval($match[2])
1731 define('PMA_MYSQL_STR_VERSION', $version['@@version']);
1732 define(
1733 'PMA_MYSQL_VERSION_COMMENT',
1734 $version['@@version_comment']
1736 } else {
1737 define('PMA_MYSQL_INT_VERSION', 50501);
1738 define('PMA_MYSQL_MAJOR_VERSION', 5);
1739 define('PMA_MYSQL_STR_VERSION', '5.05.01');
1740 define('PMA_MYSQL_VERSION_COMMENT', '');
1742 PMA_Util::cacheSet(
1743 'PMA_MYSQL_INT_VERSION',
1744 PMA_MYSQL_INT_VERSION
1746 PMA_Util::cacheSet(
1747 'PMA_MYSQL_MAJOR_VERSION',
1748 PMA_MYSQL_MAJOR_VERSION
1750 PMA_Util::cacheSet(
1751 'PMA_MYSQL_STR_VERSION',
1752 PMA_MYSQL_STR_VERSION
1754 PMA_Util::cacheSet(
1755 'PMA_MYSQL_VERSION_COMMENT',
1756 PMA_MYSQL_VERSION_COMMENT
1758 /* Detect MariaDB */
1759 if (mb_strpos(PMA_MYSQL_STR_VERSION, 'MariaDB') !== false) {
1760 define('PMA_MARIADB', true);
1761 } else {
1762 define('PMA_MARIADB', false);
1764 PMA_Util::cacheSet(
1765 'PMA_MARIADB',
1766 PMA_MARIADB
1769 /* Detect Drizzle - it does not support charsets */
1770 $charset_result = $this->query(
1771 // The following does not work in MySQL 5.7:
1772 //"SHOW VARIABLES LIKE 'character_set_results'",
1773 // so a workaround was implemented:
1774 "SELECT @@character_set_results",
1775 $link
1777 if ($this->numRows($charset_result) == 0) {
1778 define('PMA_DRIZZLE', true);
1779 } else {
1780 define('PMA_DRIZZLE', false);
1782 $this->freeResult($charset_result);
1784 PMA_Util::cacheSet(
1785 'PMA_DRIZZLE',
1786 PMA_DRIZZLE
1791 // Skip charsets for Drizzle
1792 if (!PMA_DRIZZLE) {
1793 if (PMA_MYSQL_INT_VERSION > 50503) {
1794 $default_charset = 'utf8mb4';
1795 $default_collation = 'utf8mb4_general_ci';
1796 } else {
1797 $default_charset = 'utf8';
1798 $default_collation = 'utf8_general_ci';
1800 if (! empty($GLOBALS['collation_connection'])) {
1801 $this->query(
1802 "SET CHARACTER SET '$default_charset';",
1803 $link,
1804 self::QUERY_STORE
1806 /* Automatically adjust collation to mb4 variant */
1807 if ($default_charset == 'utf8mb4'
1808 && strncmp('utf8_', $GLOBALS['collation_connection'], 5) == 0
1810 $GLOBALS['collation_connection'] = 'utf8mb4_' . substr(
1811 $GLOBALS['collation_connection'],
1815 $result = $this->tryQuery(
1816 "SET collation_connection = '"
1817 . PMA_Util::sqlAddSlashes($GLOBALS['collation_connection'])
1818 . "';",
1819 $link,
1820 self::QUERY_STORE
1822 if ($result === false) {
1823 trigger_error(
1824 __('Failed to set configured collation connection!'),
1825 E_USER_WARNING
1827 $this->query(
1828 "SET collation_connection = '"
1829 . PMA_Util::sqlAddSlashes($default_collation)
1830 . "';",
1831 $link,
1832 self::QUERY_STORE
1835 } else {
1836 $this->query(
1837 "SET NAMES '$default_charset' COLLATE '$default_collation';",
1838 $link,
1839 self::QUERY_STORE
1844 // Cache plugin list for Drizzle
1845 if (PMA_DRIZZLE && !PMA_Util::cacheExists('drizzle_engines')) {
1846 $sql = "SELECT p.plugin_name, m.module_library
1847 FROM data_dictionary.plugins p
1848 JOIN data_dictionary.modules m USING (module_name)
1849 WHERE p.plugin_type = 'StorageEngine'
1850 AND p.plugin_name NOT IN ('FunctionEngine', 'schema')
1851 AND p.is_active = 'YES'";
1852 $engines = $this->fetchResult($sql, 'plugin_name', null, $link);
1853 PMA_Util::cacheSet('drizzle_engines', $engines);
1858 * returns a single value from the given result or query,
1859 * if the query or the result has more than one row or field
1860 * the first field of the first row is returned
1862 * <code>
1863 * $sql = 'SELECT `name` FROM `user` WHERE `id` = 123';
1864 * $user_name = $GLOBALS['dbi']->fetchValue($sql);
1865 * // produces
1866 * // $user_name = 'John Doe'
1867 * </code>
1869 * @param string $query The query to execute
1870 * @param integer $row_number row to fetch the value from,
1871 * starting at 0, with 0 being default
1872 * @param integer|string $field field to fetch the value from,
1873 * starting at 0, with 0 being default
1874 * @param object $link mysql link
1876 * @return mixed value of first field in first row from result
1877 * or false if not found
1879 public function fetchValue($query, $row_number = 0, $field = 0, $link = null)
1881 $value = false;
1883 $result = $this->tryQuery(
1884 $query,
1885 $link,
1886 self::QUERY_STORE,
1887 false
1889 if ($result === false) {
1890 return false;
1893 // return false if result is empty or false
1894 // or requested row is larger than rows in result
1895 if ($this->numRows($result) < ($row_number + 1)) {
1896 return $value;
1899 // if $field is an integer use non associative mysql fetch function
1900 if (is_int($field)) {
1901 $fetch_function = 'fetchRow';
1902 } else {
1903 $fetch_function = 'fetchAssoc';
1906 // get requested row
1907 for ($i = 0; $i <= $row_number; $i++) {
1908 $row = $this->$fetch_function($result);
1910 $this->freeResult($result);
1912 // return requested field
1913 if (isset($row[$field])) {
1914 $value = $row[$field];
1917 return $value;
1921 * returns only the first row from the result
1923 * <code>
1924 * $sql = 'SELECT * FROM `user` WHERE `id` = 123';
1925 * $user = $GLOBALS['dbi']->fetchSingleRow($sql);
1926 * // produces
1927 * // $user = array('id' => 123, 'name' => 'John Doe')
1928 * </code>
1930 * @param string $query The query to execute
1931 * @param string $type NUM|ASSOC|BOTH returned array should either
1932 * numeric associative or both
1933 * @param object $link mysql link
1935 * @return array|boolean first row from result
1936 * or false if result is empty
1938 public function fetchSingleRow($query, $type = 'ASSOC', $link = null)
1940 $result = $this->tryQuery(
1941 $query,
1942 $link,
1943 self::QUERY_STORE,
1944 false
1946 if ($result === false) {
1947 return false;
1950 // return false if result is empty or false
1951 if (! $this->numRows($result)) {
1952 return false;
1955 switch ($type) {
1956 case 'NUM' :
1957 $fetch_function = 'fetchRow';
1958 break;
1959 case 'ASSOC' :
1960 $fetch_function = 'fetchAssoc';
1961 break;
1962 case 'BOTH' :
1963 default :
1964 $fetch_function = 'fetchArray';
1965 break;
1968 $row = $this->$fetch_function($result);
1969 $this->freeResult($result);
1970 return $row;
1974 * Returns row or element of a row
1976 * @param array $row Row to process
1977 * @param string|null $value Which column to return
1979 * @return mixed
1981 private function _fetchValue($row, $value)
1983 if (is_null($value)) {
1984 return $row;
1985 } else {
1986 return $row[$value];
1991 * returns all rows in the resultset in one array
1993 * <code>
1994 * $sql = 'SELECT * FROM `user`';
1995 * $users = $GLOBALS['dbi']->fetchResult($sql);
1996 * // produces
1997 * // $users[] = array('id' => 123, 'name' => 'John Doe')
1999 * $sql = 'SELECT `id`, `name` FROM `user`';
2000 * $users = $GLOBALS['dbi']->fetchResult($sql, 'id');
2001 * // produces
2002 * // $users['123'] = array('id' => 123, 'name' => 'John Doe')
2004 * $sql = 'SELECT `id`, `name` FROM `user`';
2005 * $users = $GLOBALS['dbi']->fetchResult($sql, 0);
2006 * // produces
2007 * // $users['123'] = array(0 => 123, 1 => 'John Doe')
2009 * $sql = 'SELECT `id`, `name` FROM `user`';
2010 * $users = $GLOBALS['dbi']->fetchResult($sql, 'id', 'name');
2011 * // or
2012 * $users = $GLOBALS['dbi']->fetchResult($sql, 0, 1);
2013 * // produces
2014 * // $users['123'] = 'John Doe'
2016 * $sql = 'SELECT `name` FROM `user`';
2017 * $users = $GLOBALS['dbi']->fetchResult($sql);
2018 * // produces
2019 * // $users[] = 'John Doe'
2021 * $sql = 'SELECT `group`, `name` FROM `user`'
2022 * $users = $GLOBALS['dbi']->fetchResult($sql, array('group', null), 'name');
2023 * // produces
2024 * // $users['admin'][] = 'John Doe'
2026 * $sql = 'SELECT `group`, `name` FROM `user`'
2027 * $users = $GLOBALS['dbi']->fetchResult($sql, array('group', 'name'), 'id');
2028 * // produces
2029 * // $users['admin']['John Doe'] = '123'
2030 * </code>
2032 * @param string $query query to execute
2033 * @param string|integer|array $key field-name or offset
2034 * used as key for array
2035 * or array of those
2036 * @param string|integer $value value-name or offset
2037 * used as value for array
2038 * @param object $link mysql link
2039 * @param integer $options query options
2041 * @return array resultrows or values indexed by $key
2043 public function fetchResult($query, $key = null, $value = null,
2044 $link = null, $options = 0
2046 $resultrows = array();
2048 $result = $this->tryQuery($query, $link, $options, false);
2050 // return empty array if result is empty or false
2051 if ($result === false) {
2052 return $resultrows;
2055 $fetch_function = 'fetchAssoc';
2057 // no nested array if only one field is in result
2058 if (null === $key && 1 === $this->numFields($result)) {
2059 $value = 0;
2060 $fetch_function = 'fetchRow';
2063 // if $key is an integer use non associative mysql fetch function
2064 if (is_int($key)) {
2065 $fetch_function = 'fetchRow';
2068 if (null === $key) {
2069 while ($row = $this->$fetch_function($result)) {
2070 $resultrows[] = $this->_fetchValue($row, $value);
2072 } else {
2073 if (is_array($key)) {
2074 while ($row = $this->$fetch_function($result)) {
2075 $result_target =& $resultrows;
2076 foreach ($key as $key_index) {
2077 if (null === $key_index) {
2078 $result_target =& $result_target[];
2079 continue;
2082 if (! isset($result_target[$row[$key_index]])) {
2083 $result_target[$row[$key_index]] = array();
2085 $result_target =& $result_target[$row[$key_index]];
2087 $result_target = $this->_fetchValue($row, $value);
2089 } else {
2090 while ($row = $this->$fetch_function($result)) {
2091 $resultrows[$row[$key]] = $this->_fetchValue($row, $value);
2096 $this->freeResult($result);
2097 return $resultrows;
2101 * Get supported SQL compatibility modes
2103 * @return array supported SQL compatibility modes
2105 public function getCompatibilities()
2107 // Drizzle doesn't support compatibility modes
2108 if (PMA_DRIZZLE) {
2109 return array();
2112 $compats = array('NONE');
2113 $compats[] = 'ANSI';
2114 $compats[] = 'DB2';
2115 $compats[] = 'MAXDB';
2116 $compats[] = 'MYSQL323';
2117 $compats[] = 'MYSQL40';
2118 $compats[] = 'MSSQL';
2119 $compats[] = 'ORACLE';
2120 // removed; in MySQL 5.0.33, this produces exports that
2121 // can't be read by POSTGRESQL (see our bug #1596328)
2122 //$compats[] = 'POSTGRESQL';
2123 $compats[] = 'TRADITIONAL';
2125 return $compats;
2129 * returns warnings for last query
2131 * @param object $link mysql link resource
2133 * @return array warnings
2135 public function getWarnings($link = null)
2137 $link = $this->getLink($link);
2138 if ($link === false) {
2139 return false;
2142 return $this->fetchResult('SHOW WARNINGS', null, null, $link);
2146 * returns an array of PROCEDURE or FUNCTION names for a db
2148 * @param string $db db name
2149 * @param string $which PROCEDURE | FUNCTION
2150 * @param object $link mysql link
2152 * @return array the procedure names or function names
2154 public function getProceduresOrFunctions($db, $which, $link = null)
2156 if (PMA_DRIZZLE) {
2157 // Drizzle doesn't support functions and procedures
2158 return array();
2160 $shows = $this->fetchResult(
2161 'SHOW ' . $which . ' STATUS;', null, null, $link
2163 $result = array();
2164 foreach ($shows as $one_show) {
2165 if ($one_show['Db'] == $db && $one_show['Type'] == $which) {
2166 $result[] = $one_show['Name'];
2169 return($result);
2173 * returns the definition of a specific PROCEDURE, FUNCTION, EVENT or VIEW
2175 * @param string $db db name
2176 * @param string $which PROCEDURE | FUNCTION | EVENT | VIEW
2177 * @param string $name the procedure|function|event|view name
2179 * @return string the definition
2181 public function getDefinition($db, $which, $name)
2183 $returned_field = array(
2184 'PROCEDURE' => 'Create Procedure',
2185 'FUNCTION' => 'Create Function',
2186 'EVENT' => 'Create Event',
2187 'VIEW' => 'Create View'
2189 $query = 'SHOW CREATE ' . $which . ' '
2190 . PMA_Util::backquote($db) . '.'
2191 . PMA_Util::backquote($name);
2192 return($this->fetchValue($query, 0, $returned_field[$which]));
2196 * returns details about the PROCEDUREs or FUNCTIONs for a specific database
2197 * or details about a specific routine
2199 * @param string $db db name
2200 * @param string $which PROCEDURE | FUNCTION or null for both
2201 * @param string $name name of the routine (to fetch a specific routine)
2203 * @return array information about ROCEDUREs or FUNCTIONs
2205 public function getRoutines($db, $which = null, $name = '')
2207 if (PMA_DRIZZLE) {
2208 // Drizzle doesn't support functions and procedures
2209 return array();
2212 $routines = array();
2213 if (! $GLOBALS['cfg']['Server']['DisableIS']) {
2214 $query = "SELECT"
2215 . " `ROUTINE_SCHEMA` AS `Db`,"
2216 . " `SPECIFIC_NAME` AS `Name`,"
2217 . " `ROUTINE_TYPE` AS `Type`,"
2218 . " `DEFINER` AS `Definer`,"
2219 . " `LAST_ALTERED` AS `Modified`,"
2220 . " `CREATED` AS `Created`,"
2221 . " `SECURITY_TYPE` AS `Security_type`,"
2222 . " `ROUTINE_COMMENT` AS `Comment`,"
2223 . " `CHARACTER_SET_CLIENT` AS `character_set_client`,"
2224 . " `COLLATION_CONNECTION` AS `collation_connection`,"
2225 . " `DATABASE_COLLATION` AS `Database Collation`,"
2226 . " `DTD_IDENTIFIER`"
2227 . " FROM `information_schema`.`ROUTINES`"
2228 . " WHERE `ROUTINE_SCHEMA` " . PMA_Util::getCollateForIS()
2229 . " = '" . PMA_Util::sqlAddSlashes($db) . "'";
2230 if (PMA_isValid($which, array('FUNCTION','PROCEDURE'))) {
2231 $query .= " AND `ROUTINE_TYPE` = '" . $which . "'";
2233 if (! empty($name)) {
2234 $query .= " AND `SPECIFIC_NAME`"
2235 . " = '" . PMA_Util::sqlAddSlashes($name) . "'";
2237 $result = $this->fetchResult($query);
2238 if (!empty($result)) {
2239 $routines = $result;
2241 } else {
2242 if ($which == 'FUNCTION' || $which == null) {
2243 $query = "SHOW FUNCTION STATUS"
2244 . " WHERE `Db` = '" . PMA_Util::sqlAddSlashes($db) . "'";
2245 if (! empty($name)) {
2246 $query .= " AND `Name` = '"
2247 . PMA_Util::sqlAddSlashes($name) . "'";
2249 $result = $this->fetchResult($query);
2250 if (!empty($result)) {
2251 $routines = array_merge($routines, $result);
2254 if ($which == 'PROCEDURE' || $which == null) {
2255 $query = "SHOW PROCEDURE STATUS"
2256 . " WHERE `Db` = '" . PMA_Util::sqlAddSlashes($db) . "'";
2257 if (! empty($name)) {
2258 $query .= " AND `Name` = '"
2259 . PMA_Util::sqlAddSlashes($name) . "'";
2261 $result = $this->fetchResult($query);
2262 if (!empty($result)) {
2263 $routines = array_merge($routines, $result);
2268 $ret = array();
2269 foreach ($routines as $routine) {
2270 $one_result = array();
2271 $one_result['db'] = $routine['Db'];
2272 $one_result['name'] = $routine['Name'];
2273 $one_result['type'] = $routine['Type'];
2274 $one_result['definer'] = $routine['Definer'];
2275 $one_result['returns'] = isset($routine['DTD_IDENTIFIER'])
2276 ? $routine['DTD_IDENTIFIER'] : "";
2277 $ret[] = $one_result;
2280 // Sort results by name
2281 $name = array();
2282 foreach ($ret as $value) {
2283 $name[] = $value['name'];
2285 array_multisort($name, SORT_ASC, $ret);
2287 return($ret);
2291 * returns details about the EVENTs for a specific database
2293 * @param string $db db name
2294 * @param string $name event name
2296 * @return array information about EVENTs
2298 public function getEvents($db, $name = '')
2300 if (PMA_DRIZZLE) {
2301 // Drizzle doesn't support events
2302 return array();
2305 if (! $GLOBALS['cfg']['Server']['DisableIS']) {
2306 $query = "SELECT"
2307 . " `EVENT_SCHEMA` AS `Db`,"
2308 . " `EVENT_NAME` AS `Name`,"
2309 . " `DEFINER` AS `Definer`,"
2310 . " `TIME_ZONE` AS `Time zone`,"
2311 . " `EVENT_TYPE` AS `Type`,"
2312 . " `EXECUTE_AT` AS `Execute at`,"
2313 . " `INTERVAL_VALUE` AS `Interval value`,"
2314 . " `INTERVAL_FIELD` AS `Interval field`,"
2315 . " `STARTS` AS `Starts`,"
2316 . " `ENDS` AS `Ends`,"
2317 . " `STATUS` AS `Status`,"
2318 . " `ORIGINATOR` AS `Originator`,"
2319 . " `CHARACTER_SET_CLIENT` AS `character_set_client`,"
2320 . " `COLLATION_CONNECTION` AS `collation_connection`, "
2321 . "`DATABASE_COLLATION` AS `Database Collation`"
2322 . " FROM `information_schema`.`EVENTS`"
2323 . " WHERE `EVENT_SCHEMA` " . PMA_Util::getCollateForIS()
2324 . " = '" . PMA_Util::sqlAddSlashes($db) . "'";
2325 if (! empty($name)) {
2326 $query .= " AND `EVENT_NAME`"
2327 . " = '" . PMA_Util::sqlAddSlashes($name) . "'";
2329 } else {
2330 $query = "SHOW EVENTS FROM " . PMA_Util::backquote($db);
2331 if (! empty($name)) {
2332 $query .= " AND `Name` = '"
2333 . PMA_Util::sqlAddSlashes($name) . "'";
2337 $result = array();
2338 if ($events = $this->fetchResult($query)) {
2339 foreach ($events as $event) {
2340 $one_result = array();
2341 $one_result['name'] = $event['Name'];
2342 $one_result['type'] = $event['Type'];
2343 $one_result['status'] = $event['Status'];
2344 $result[] = $one_result;
2348 // Sort results by name
2349 $name = array();
2350 foreach ($result as $value) {
2351 $name[] = $value['name'];
2353 array_multisort($name, SORT_ASC, $result);
2355 return $result;
2359 * returns details about the TRIGGERs for a specific table or database
2361 * @param string $db db name
2362 * @param string $table table name
2363 * @param string $delimiter the delimiter to use (may be empty)
2365 * @return array information about triggers (may be empty)
2367 public function getTriggers($db, $table = '', $delimiter = '//')
2369 if (PMA_DRIZZLE) {
2370 // Drizzle doesn't support triggers
2371 return array();
2374 $result = array();
2375 if (! $GLOBALS['cfg']['Server']['DisableIS']) {
2376 $query = 'SELECT TRIGGER_SCHEMA, TRIGGER_NAME, EVENT_MANIPULATION'
2377 . ', EVENT_OBJECT_TABLE, ACTION_TIMING, ACTION_STATEMENT'
2378 . ', EVENT_OBJECT_SCHEMA, EVENT_OBJECT_TABLE, DEFINER'
2379 . ' FROM information_schema.TRIGGERS'
2380 . ' WHERE EVENT_OBJECT_SCHEMA ' . PMA_Util::getCollateForIS() . '='
2381 . ' \'' . PMA_Util::sqlAddSlashes($db) . '\'';
2383 if (! empty($table)) {
2384 $query .= " AND EVENT_OBJECT_TABLE " . PMA_Util::getCollateForIS()
2385 . " = '" . PMA_Util::sqlAddSlashes($table) . "';";
2387 } else {
2388 $query = "SHOW TRIGGERS FROM " . PMA_Util::backquote($db);
2389 if (! empty($table)) {
2390 $query .= " LIKE '" . PMA_Util::sqlAddSlashes($table, true) . "';";
2394 if ($triggers = $this->fetchResult($query)) {
2395 foreach ($triggers as $trigger) {
2396 if ($GLOBALS['cfg']['Server']['DisableIS']) {
2397 $trigger['TRIGGER_NAME'] = $trigger['Trigger'];
2398 $trigger['ACTION_TIMING'] = $trigger['Timing'];
2399 $trigger['EVENT_MANIPULATION'] = $trigger['Event'];
2400 $trigger['EVENT_OBJECT_TABLE'] = $trigger['Table'];
2401 $trigger['ACTION_STATEMENT'] = $trigger['Statement'];
2402 $trigger['DEFINER'] = $trigger['Definer'];
2404 $one_result = array();
2405 $one_result['name'] = $trigger['TRIGGER_NAME'];
2406 $one_result['table'] = $trigger['EVENT_OBJECT_TABLE'];
2407 $one_result['action_timing'] = $trigger['ACTION_TIMING'];
2408 $one_result['event_manipulation'] = $trigger['EVENT_MANIPULATION'];
2409 $one_result['definition'] = $trigger['ACTION_STATEMENT'];
2410 $one_result['definer'] = $trigger['DEFINER'];
2412 // do not prepend the schema name; this way, importing the
2413 // definition into another schema will work
2414 $one_result['full_trigger_name'] = PMA_Util::backquote(
2415 $trigger['TRIGGER_NAME']
2417 $one_result['drop'] = 'DROP TRIGGER IF EXISTS '
2418 . $one_result['full_trigger_name'];
2419 $one_result['create'] = 'CREATE TRIGGER '
2420 . $one_result['full_trigger_name'] . ' '
2421 . $trigger['ACTION_TIMING'] . ' '
2422 . $trigger['EVENT_MANIPULATION']
2423 . ' ON ' . PMA_Util::backquote($trigger['EVENT_OBJECT_TABLE'])
2424 . "\n" . ' FOR EACH ROW '
2425 . $trigger['ACTION_STATEMENT'] . "\n" . $delimiter . "\n";
2427 $result[] = $one_result;
2431 // Sort results by name
2432 $name = array();
2433 foreach ($result as $value) {
2434 $name[] = $value['name'];
2436 array_multisort($name, SORT_ASC, $result);
2438 return($result);
2442 * Formats database error message in a friendly way.
2443 * This is needed because some errors messages cannot
2444 * be obtained by mysql_error().
2446 * @param int $error_number Error code
2447 * @param string $error_message Error message as returned by server
2449 * @return string HML text with error details
2451 public function formatError($error_number, $error_message)
2453 $error_message = htmlspecialchars($error_message);
2455 $error = '#' . ((string) $error_number);
2457 if ($error_number == 2002) {
2458 $error .= ' - ' . $error_message;
2459 $error .= '<br />';
2460 $error .= __(
2461 'The server is not responding (or the local server\'s socket'
2462 . ' is not correctly configured).'
2464 } elseif ($error_number == 2003) {
2465 $error .= ' - ' . $error_message;
2466 $error .= '<br />' . __('The server is not responding.');
2467 } elseif ($error_number == 1005) {
2468 if (strpos($error_message, 'errno: 13') !== false) {
2469 $error .= ' - ' . $error_message;
2470 $error .= '<br />'
2471 . __('Please check privileges of directory containing database.');
2472 } else {
2473 /* InnoDB constraints, see
2474 * http://dev.mysql.com/doc/refman/5.0/en/
2475 * innodb-foreign-key-constraints.html
2477 $error .= ' - ' . $error_message .
2478 ' (<a href="server_engines.php' .
2479 PMA_URL_getCommon(
2480 array('engine' => 'InnoDB', 'page' => 'Status')
2481 ) . '">' . __('Details…') . '</a>)';
2483 } else {
2484 $error .= ' - ' . $error_message;
2487 return $error;
2491 * gets the current user with host
2493 * @return string the current user i.e. user@host
2495 public function getCurrentUser()
2497 if (PMA_Util::cacheExists('mysql_cur_user')) {
2498 return PMA_Util::cacheGet('mysql_cur_user');
2500 $user = $GLOBALS['dbi']->fetchValue('SELECT USER();');
2501 if ($user !== false) {
2502 PMA_Util::cacheSet('mysql_cur_user', $user);
2503 return PMA_Util::cacheGet('mysql_cur_user');
2505 return '';
2509 * Checks if current user is superuser
2511 * @return bool Whether user is a superuser
2513 public function isSuperuser()
2515 return self::isUserType('super');
2519 * Checks if current user has global create user/grant privilege
2520 * or is a superuser (i.e. SELECT on mysql.users)
2521 * while caching the result in session.
2523 * @param string $type type of user to check for
2524 * i.e. 'create', 'grant', 'super'
2526 * @return bool Whether user is a given type of user
2528 public function isUserType($type)
2530 if (PMA_Util::cacheExists('is_' . $type . 'user')) {
2531 return PMA_Util::cacheGet('is_' . $type . 'user');
2534 // when connection failed we don't have a $userlink
2535 if (! isset($GLOBALS['userlink'])) {
2536 PMA_Util::cacheSet('is_' . $type . 'user', false);
2537 return PMA_Util::cacheGet('is_' . $type . 'user');
2540 if (PMA_DRIZZLE) {
2541 // Drizzle has no authorization by default, so when no plugin is
2542 // enabled everyone is a superuser
2543 // Known authorization libraries: regex_policy, simple_user_policy
2544 // Plugins limit object visibility (dbs, tables, processes), we can
2545 // safely assume we always deal with superuser
2546 PMA_Util::cacheSet('is_' . $type . 'user', true);
2547 return PMA_Util::cacheGet('is_' . $type . 'user');
2550 if (! $GLOBALS['cfg']['Server']['DisableIS'] || $type === 'super') {
2551 // Prepare query for each user type check
2552 $query = '';
2553 if ($type === 'super') {
2554 $query = 'SELECT 1 FROM mysql.user LIMIT 1';
2555 } elseif ($type === 'create') {
2556 list($user, $host) = $this->_getCurrentUserAndHost();
2557 $query = "SELECT 1 FROM `INFORMATION_SCHEMA`.`USER_PRIVILEGES` "
2558 . "WHERE `PRIVILEGE_TYPE` = 'CREATE USER' AND "
2559 . "'''" . $user . "''@''" . $host . "''' LIKE `GRANTEE` LIMIT 1";
2560 } elseif ($type === 'grant') {
2561 list($user, $host) = $this->_getCurrentUserAndHost();
2562 $query = "SELECT 1 FROM ("
2563 . "SELECT `GRANTEE`, `IS_GRANTABLE` FROM "
2564 . "`INFORMATION_SCHEMA`.`COLUMN_PRIVILEGES` UNION "
2565 . "SELECT `GRANTEE`, `IS_GRANTABLE` FROM "
2566 . "`INFORMATION_SCHEMA`.`TABLE_PRIVILEGES` UNION "
2567 . "SELECT `GRANTEE`, `IS_GRANTABLE` FROM "
2568 . "`INFORMATION_SCHEMA`.`SCHEMA_PRIVILEGES` UNION "
2569 . "SELECT `GRANTEE`, `IS_GRANTABLE` FROM "
2570 . "`INFORMATION_SCHEMA`.`USER_PRIVILEGES`) t "
2571 . "WHERE `IS_GRANTABLE` = 'YES' AND "
2572 . "'''" . $user . "''@''" . $host . "''' LIKE `GRANTEE` LIMIT 1";
2575 $is = false;
2576 $result = $GLOBALS['dbi']->tryQuery(
2577 $query,
2578 $GLOBALS['userlink'],
2579 self::QUERY_STORE
2581 if ($result) {
2582 $is = (bool) $GLOBALS['dbi']->numRows($result);
2584 $GLOBALS['dbi']->freeResult($result);
2586 PMA_Util::cacheSet('is_' . $type . 'user', $is);
2587 } else {
2588 $is = false;
2589 $grants = $GLOBALS['dbi']->fetchResult(
2590 "SHOW GRANTS FOR CURRENT_USER();",
2591 null,
2592 null,
2593 $GLOBALS['userlink'],
2594 self::QUERY_STORE
2596 if ($grants) {
2597 foreach ($grants as $grant) {
2598 if ($type === 'create') {
2599 if (strpos($grant, "ALL PRIVILEGES ON *.*") !== false
2600 || strpos($grant, "CREATE USER") !== false
2602 $is = true;
2603 break;
2605 } elseif ($type === 'grant') {
2606 if (strpos($grant, "WITH GRANT OPTION") !== false) {
2607 $is = true;
2608 break;
2614 PMA_Util::cacheSet('is_' . $type . 'user', $is);
2617 return PMA_Util::cacheGet('is_' . $type . 'user');
2621 * Get the current user and host
2623 * @return array array of username and hostname
2625 private function _getCurrentUserAndHost()
2627 $user = $GLOBALS['dbi']->fetchValue("SELECT CURRENT_USER();");
2628 return explode("@", $user);
2632 * Get the list of system schemas
2634 * @return array list of system schemas
2636 public function getSystemSchemas()
2638 $schemas = array(
2639 'information_schema', 'performance_schema', 'data_dictionary', 'mysql',
2640 'sys'
2642 $systemSchemas = array();
2643 foreach ($schemas as $schema) {
2644 if ($this->isSystemSchema($schema, true)) {
2645 $systemSchemas[] = $schema;
2648 return $systemSchemas;
2652 * Checks whether given schema is a system schema: information_schema
2653 * (MySQL and Drizzle) or data_dictionary (Drizzle)
2655 * @param string $schema_name Name of schema (database) to test
2656 * @param bool $testForMysqlSchema Whether 'mysql' schema should
2657 * be treated the same as IS and DD
2659 * @return bool
2661 public function isSystemSchema($schema_name, $testForMysqlSchema = false)
2663 $schema_name = strtolower($schema_name);
2664 return $schema_name == 'information_schema'
2665 || (!PMA_DRIZZLE && $schema_name == 'performance_schema')
2666 || (!PMA_DRIZZLE && $schema_name == 'mysql' && $testForMysqlSchema)
2667 || (!PMA_DRIZZLE && $schema_name == 'sys')
2668 || ( PMA_DRIZZLE && $schema_name == 'data_dictionary');
2672 * connects to the database server
2674 * @param string $user user name
2675 * @param string $password user password
2676 * @param bool $is_controluser whether this is a control user connection
2677 * @param array $server host/port/socket/persistent
2678 * @param bool $auxiliary_connection (when true, don't go back to login if
2679 * connection fails)
2681 * @return mixed false on error or a connection object on success
2683 public function connect(
2684 $user, $password, $is_controluser = false, $server = null,
2685 $auxiliary_connection = false
2687 $result = $this->_extension->connect(
2688 $user, $password, $is_controluser, $server, $auxiliary_connection
2691 if ($result) {
2692 if (! $auxiliary_connection && ! $is_controluser) {
2693 $GLOBALS['dbi']->postConnect($result);
2695 return $result;
2698 if ($is_controluser) {
2699 trigger_error(
2701 'Connection for controluser as defined in your '
2702 . 'configuration failed.'
2704 E_USER_WARNING
2706 return false;
2709 // we could be calling $GLOBALS['dbi']->connect() to connect to another
2710 // server, for example in the Synchronize feature, so do not
2711 // go back to main login if it fails
2712 if ($auxiliary_connection) {
2713 return false;
2716 PMA_logUser($user, 'mysql-denied');
2717 $GLOBALS['auth_plugin']->authFails();
2719 return $result;
2723 * selects given database
2725 * @param string $dbname database name to select
2726 * @param object $link connection object
2728 * @return boolean
2730 public function selectDb($dbname, $link = null)
2732 $link = $this->getLink($link);
2733 if ($link === false) {
2734 return false;
2736 return $this->_extension->selectDb($dbname, $link);
2740 * returns array of rows with associative and numeric keys from $result
2742 * @param object $result result set identifier
2744 * @return array
2746 public function fetchArray($result)
2748 return $this->_extension->fetchArray($result);
2752 * returns array of rows with associative keys from $result
2754 * @param object $result result set identifier
2756 * @return array
2758 public function fetchAssoc($result)
2760 return $this->_extension->fetchAssoc($result);
2764 * returns array of rows with numeric keys from $result
2766 * @param object $result result set identifier
2768 * @return array
2770 public function fetchRow($result)
2772 return $this->_extension->fetchRow($result);
2776 * Adjusts the result pointer to an arbitrary row in the result
2778 * @param object $result database result
2779 * @param integer $offset offset to seek
2781 * @return bool true on success, false on failure
2783 public function dataSeek($result, $offset)
2785 return $this->_extension->dataSeek($result, $offset);
2789 * Frees memory associated with the result
2791 * @param object $result database result
2793 * @return void
2795 public function freeResult($result)
2797 $this->_extension->freeResult($result);
2801 * Check if there are any more query results from a multi query
2803 * @param object $link the connection object
2805 * @return bool true or false
2807 public function moreResults($link = null)
2809 $link = $this->getLink($link);
2810 if ($link === false) {
2811 return false;
2813 return $this->_extension->moreResults($link);
2817 * Prepare next result from multi_query
2819 * @param object $link the connection object
2821 * @return bool true or false
2823 public function nextResult($link = null)
2825 $link = $this->getLink($link);
2826 if ($link === false) {
2827 return false;
2829 return $this->_extension->nextResult($link);
2833 * Store the result returned from multi query
2835 * @param object $link the connection object
2837 * @return mixed false when empty results / result set when not empty
2839 public function storeResult($link = null)
2841 $link = $this->getLink($link);
2842 if ($link === false) {
2843 return false;
2845 return $this->_extension->storeResult($link);
2849 * Returns a string representing the type of connection used
2851 * @param object $link mysql link
2853 * @return string type of connection used
2855 public function getHostInfo($link = null)
2857 $link = $this->getLink($link);
2858 if ($link === false) {
2859 return false;
2861 return $this->_extension->getHostInfo($link);
2865 * Returns the version of the MySQL protocol used
2867 * @param object $link mysql link
2869 * @return integer version of the MySQL protocol used
2871 public function getProtoInfo($link = null)
2873 $link = $this->getLink($link);
2874 if ($link === false) {
2875 return false;
2877 return $this->_extension->getProtoInfo($link);
2881 * returns a string that represents the client library version
2883 * @return string MySQL client library version
2885 public function getClientInfo()
2887 return $this->_extension->getClientInfo();
2891 * returns last error message or false if no errors occurred
2893 * @param object $link connection link
2895 * @return string|bool $error or false
2897 public function getError($link = null)
2899 $link = $this->getLink($link);
2900 return $this->_extension->getError($link);
2904 * returns the number of rows returned by last query
2906 * @param object $result result set identifier
2908 * @return string|int
2910 public function numRows($result)
2912 return $this->_extension->numRows($result);
2916 * returns last inserted auto_increment id for given $link
2917 * or $GLOBALS['userlink']
2919 * @param object $link the connection object
2921 * @return int|boolean
2923 public function insertId($link = null)
2925 $link = $this->getLink($link);
2926 if ($link === false) {
2927 return false;
2929 // If the primary key is BIGINT we get an incorrect result
2930 // (sometimes negative, sometimes positive)
2931 // and in the present function we don't know if the PK is BIGINT
2932 // so better play safe and use LAST_INSERT_ID()
2934 // When no controluser is defined, using mysqli_insert_id($link)
2935 // does not always return the last insert id due to a mixup with
2936 // the tracking mechanism, but this works:
2937 return $GLOBALS['dbi']->fetchValue('SELECT LAST_INSERT_ID();', 0, 0, $link);
2941 * returns the number of rows affected by last query
2943 * @param object $link the connection object
2944 * @param bool $get_from_cache whether to retrieve from cache
2946 * @return int|boolean
2948 public function affectedRows($link = null, $get_from_cache = true)
2950 $link = $this->getLink($link);
2951 if ($link === false) {
2952 return false;
2955 if ($get_from_cache) {
2956 return $GLOBALS['cached_affected_rows'];
2957 } else {
2958 return $this->_extension->affectedRows($link);
2963 * returns metainfo for fields in $result
2965 * @param object $result result set identifier
2967 * @return array meta info for fields in $result
2969 public function getFieldsMeta($result)
2971 return $this->_extension->getFieldsMeta($result);
2975 * return number of fields in given $result
2977 * @param object $result result set identifier
2979 * @return int field count
2981 public function numFields($result)
2983 return $this->_extension->numFields($result);
2987 * returns the length of the given field $i in $result
2989 * @param object $result result set identifier
2990 * @param int $i field
2992 * @return int length of field
2994 public function fieldLen($result, $i)
2996 return $this->_extension->fieldLen($result, $i);
3000 * returns name of $i. field in $result
3002 * @param object $result result set identifier
3003 * @param int $i field
3005 * @return string name of $i. field in $result
3007 public function fieldName($result, $i)
3009 return $this->_extension->fieldName($result, $i);
3013 * returns concatenated string of human readable field flags
3015 * @param object $result result set identifier
3016 * @param int $i field
3018 * @return string field flags
3020 public function fieldFlags($result, $i)
3022 return $this->_extension->fieldFlags($result, $i);
3026 * Gets server connection port
3028 * @param array|null $server host/port/socket/persistent
3030 * @return null|integer
3032 public function getServerPort($server = null)
3034 if (is_null($server)) {
3035 $server = &$GLOBALS['cfg']['Server'];
3038 if (empty($server['port'])) {
3039 return null;
3040 } else {
3041 return intval($server['port']);
3046 * Gets server connection socket
3048 * @param array|null $server host/port/socket/persistent
3050 * @return null|string
3052 public function getServerSocket($server = null)
3054 if (is_null($server)) {
3055 $server = &$GLOBALS['cfg']['Server'];
3058 if (empty($server['socket'])) {
3059 return null;
3060 } else {
3061 return $server['socket'];
3066 * Gets correct link object.
3068 * @param object $link optional database link to use
3070 * @return object|boolean
3072 public function getLink($link = null)
3074 if (! is_null($link) && $link !== false) {
3075 return $link;
3078 if (isset($GLOBALS['userlink']) && !is_null($GLOBALS['userlink'])) {
3079 return $GLOBALS['userlink'];
3080 } else {
3081 return false;
3086 * Checks if this database server is running on Amazon RDS.
3088 * @return boolean
3090 public function isAmazonRds()
3092 if (PMA_Util::cacheExists('is_amazon_rds')) {
3093 return PMA_Util::cacheGet('is_amazon_rds');
3095 $sql = 'SELECT @@basedir';
3096 $result = $this->fetchResult($sql);
3097 $rds = ($result[0] == '/rdsdbbin/mysql/');
3098 PMA_Util::cacheSet('is_amazon_rds', $rds);
3100 return $rds;
3104 * Gets SQL for killing a process.
3106 * @param int $process Process ID
3108 * @return string
3110 public function getKillQuery($process)
3112 if ($this->isAmazonRds()) {
3113 return 'CALL mysql.rds_kill(' . $process . ');';
3114 } else {
3115 return 'KILL ' . $process . ';';
3120 * Get the phpmyadmin database manager
3122 * @return PMA\SystemDatabase
3124 public function getSystemDatabase()
3126 return new PMA\SystemDatabase($this);
3130 * Get a table with database name and table name
3132 * @param string $db_name DB name
3133 * @param string $table_name Table name
3135 * @return PMA_Table
3137 public function getTable($db_name, $table_name)
3139 return new PMA_Table($table_name, $db_name, $this);