Removing old documentation
[openemr.git] / phpmyadmin / libraries / Table.class.php
blob40d9ceea05b83b54ebaf21d0df9335eac6a62973
1 <?php
2 /* vim: set expandtab sw=4 ts=4 sts=4: */
3 /**
4 * Holds the PMA_Table class
6 * @package PhpMyAdmin
7 */
8 if (! defined('PHPMYADMIN')) {
9 exit;
12 /**
13 * Handles everything related to tables
15 * @todo make use of PMA_Message and PMA_Error
16 * @package PhpMyAdmin
18 class PMA_Table
20 /**
21 * UI preferences properties
23 const PROP_SORTED_COLUMN = 'sorted_col';
24 const PROP_COLUMN_ORDER = 'col_order';
25 const PROP_COLUMN_VISIB = 'col_visib';
27 /**
28 * @var string engine (innodb, myisam, bdb, ...)
30 var $engine = '';
32 /**
33 * @var string type (view, base table, system view)
35 var $type = '';
37 /**
38 * @var array UI preferences
40 var $uiprefs;
42 /**
43 * @var array errors occurred
45 var $errors = array();
47 /**
48 * @var array messages
50 var $messages = array();
52 /**
53 * @var string table name
55 protected $_name = '';
57 /**
58 * @var string database name
60 protected $_db_name = '';
62 /**
63 * @var PMA_DatabaseInterface
65 protected $_dbi;
67 /**
68 * Constructor
70 * @param string $table_name table name
71 * @param string $db_name database name
72 * @param PMA_DatabaseInterface $dbi database interface for the table
74 function __construct($table_name, $db_name, PMA_DatabaseInterface $dbi = null)
76 if (empty($dbi)) {
77 $dbi = $GLOBALS['dbi'];
79 $this->_dbi = $dbi;
80 $this->_name = $table_name;
81 $this->_db_name = $db_name;
84 /**
85 * returns table name
87 * @see PMA_Table::getName()
88 * @return string table name
90 function __toString()
92 return $this->getName();
95 /**
96 * return the last error
98 * @return string the last error
100 function getLastError()
102 return end($this->errors);
106 * return the last message
108 * @return string the last message
110 function getLastMessage()
112 return end($this->messages);
116 * returns table name
118 * @param boolean $backquoted whether to quote name with backticks ``
120 * @return string table name
122 function getName($backquoted = false)
124 if ($backquoted) {
125 return PMA_Util::backquote($this->_name);
127 return $this->_name;
131 * returns database name for this table
133 * @param boolean $backquoted whether to quote name with backticks ``
135 * @return string database name for this table
137 function getDbName($backquoted = false)
139 if ($backquoted) {
140 return PMA_Util::backquote($this->_db_name);
142 return $this->_db_name;
146 * returns full name for table, including database name
148 * @param boolean $backquoted whether to quote name with backticks ``
150 * @return string
152 function getFullName($backquoted = false)
154 return $this->getDbName($backquoted) . '.'
155 . $this->getName($backquoted);
159 * returns whether the table is actually a view
161 * @return boolean whether the given is a view
163 public function isView()
165 $db = $this->_db_name;
166 $table = $this->_name;
167 if (empty($db) || empty($table)) {
168 return false;
171 // use cached data or load information with SHOW command
172 if ($this->_dbi->getCachedTableContent(array($db, $table)) != null
173 || $GLOBALS['cfg']['Server']['DisableIS']
175 $type = $this->getStatusInfo('TABLE_TYPE');
176 return $type == 'VIEW' || $type == 'SYSTEM VIEW';
179 // information_schema tables are 'SYSTEM VIEW's
180 if ($db == 'information_schema') {
181 return true;
184 // query information_schema
185 $result = $this->_dbi->fetchResult(
186 "SELECT TABLE_NAME
187 FROM information_schema.VIEWS
188 WHERE TABLE_SCHEMA = '" . PMA_Util::sqlAddSlashes($db) . "'
189 AND TABLE_NAME = '" . PMA_Util::sqlAddSlashes($table) . "'"
191 return $result ? true : false;
195 * Returns whether the table is actually an updatable view
197 * @return boolean whether the given is an updatable view
199 public function isUpdatableView()
201 if (empty($this->_db_name) || empty($this->_name)) {
202 return false;
205 $result = $this->_dbi->fetchResult(
206 "SELECT TABLE_NAME
207 FROM information_schema.VIEWS
208 WHERE TABLE_SCHEMA = '" . PMA_Util::sqlAddSlashes($this->_db_name) . "'
209 AND TABLE_NAME = '" . PMA_Util::sqlAddSlashes($this->_name) . "'
210 AND IS_UPDATABLE = 'YES'"
212 return $result ? true : false;
216 * Returns the analysis of 'SHOW CREATE TABLE' query for the table.
217 * In case of a view, the values are taken from the information_schema.
219 * @return array analysis of 'SHOW CREATE TABLE' query for the table
221 public function analyzeStructure()
223 if (empty($this->_db_name) || empty($this->_name)) {
224 return false;
227 $analyzed_sql = array();
228 if ($this->isView()) {
229 // For a view, 'SHOW CREATE TABLE' returns the definition,
230 // but the structure of the view. So, we try to mock
231 // the result of analyzing 'SHOW CREATE TABLE' query.
232 $analyzed_sql[0] = array();
233 $analyzed_sql[0]['create_table_fields'] = array();
235 $results = $this->_dbi->fetchResult(
236 "SELECT COLUMN_NAME, DATA_TYPE
237 FROM information_schema.COLUMNS
238 WHERE TABLE_SCHEMA = '" . PMA_Util::sqlAddSlashes($this->_db_name)
239 . " AND TABLE_NAME = '" . PMA_Util::sqlAddSlashes($this->_name) . "'"
242 foreach ($results as $result) {
243 $analyzed_sql[0]['create_table_fields'][$result['COLUMN_NAME']]
244 = array(
245 'type' => /*overload*/mb_strtoupper($result['DATA_TYPE'])
248 } else {
249 $show_create_table = $this->_dbi->fetchValue(
250 'SHOW CREATE TABLE '
251 . PMA_Util::backquote($this->_db_name)
252 . '.' . PMA_Util::backquote($this->_name),
256 $analyzed_sql = PMA_SQP_analyze(PMA_SQP_parse($show_create_table));
258 return $analyzed_sql;
262 * Checks if this is a merge table
264 * If the ENGINE of the table is MERGE or MRG_MYISAM (alias),
265 * this is a merge table.
268 * @return boolean true if it is a merge table
270 public function isMerge()
272 $engine = null;
273 // if called static, with parameters
274 if (! empty($this->_db_name) && ! empty($this->_name)) {
275 $engine = $this->getStatusInfo('ENGINE', null, true);
278 // did we get engine?
279 if (empty($engine)) {
280 return false;
283 // any of known merge engines?
284 return in_array(
285 /*overload*/mb_strtoupper($engine),
286 array('MERGE', 'MRG_MYISAM')
291 * Returns full table status info, or specific if $info provided
292 * this info is collected from information_schema
294 * @param string $info specific information to be fetched
295 * @param boolean $force_read read new rather than serving from cache
296 * @param boolean $disable_error if true, disables error message
298 * @todo DatabaseInterface::getTablesFull needs to be merged
299 * somehow into this class or at least better documented
301 * @return mixed
303 public function getStatusInfo(
304 $info = null,
305 $force_read = false,
306 $disable_error = false
308 $db = $this->_db_name;
309 $table = $this->_name;
311 if (! empty($_SESSION['is_multi_query'])) {
312 $disable_error = true;
315 // sometimes there is only one entry (ExactRows) so
316 // we have to get the table's details
317 if ($this->_dbi->getCachedTableContent(array($db, $table)) == null
318 || $force_read
319 || count($this->_dbi->getCachedTableContent(array($db, $table))) == 1
321 $this->_dbi->getTablesFull($db, $table);
324 if ($this->_dbi->getCachedTableContent(array($db, $table)) == null) {
325 // happens when we enter the table creation dialog
326 // or when we really did not get any status info, for example
327 // when $table == 'TABLE_NAMES' after the user tried SHOW TABLES
328 return '';
331 if (null === $info) {
332 return $this->_dbi->getCachedTableContent(array($db, $table));
335 // array_key_exists allows for null values
336 if (!array_key_exists(
337 $info, $this->_dbi->getCachedTableContent(array($db, $table))
340 if (! $disable_error) {
341 trigger_error(
342 __('Unknown table status:') . ' ' . $info,
343 E_USER_WARNING
346 return false;
349 return $this->_dbi->getCachedTableContent(array($db, $table, $info));
353 * generates column specification for ALTER or CREATE TABLE syntax
355 * @param string $name name
356 * @param string $type type ('INT', 'VARCHAR', 'BIT', ...)
357 * @param string $length length ('2', '5,2', '', ...)
358 * @param string $attribute attribute
359 * @param string $collation collation
360 * @param bool|string $null with 'NULL' or 'NOT NULL'
361 * @param string $default_type whether default is CURRENT_TIMESTAMP,
362 * NULL, NONE, USER_DEFINED
363 * @param string $default_value default value for USER_DEFINED
364 * default type
365 * @param string $extra 'AUTO_INCREMENT'
366 * @param string $comment field comment
367 * @param string $virtuality virtuality of the column
368 * @param string $expression expression for the virtual column
369 * @param string $move_to new position for column
371 * @todo move into class PMA_Column
372 * @todo on the interface, some js to clear the default value when the
373 * default current_timestamp is checked
375 * @return string field specification
377 static function generateFieldSpec($name, $type, $length = '',
378 $attribute = '', $collation = '', $null = false,
379 $default_type = 'USER_DEFINED', $default_value = '', $extra = '',
380 $comment = '', $virtuality = '', $expression = '', $move_to = ''
382 $is_timestamp = /*overload*/mb_strpos(
383 /*overload*/mb_strtoupper($type),
384 'TIMESTAMP'
385 ) !== false;
387 $query = PMA_Util::backquote($name) . ' ' . $type;
389 // allow the possibility of a length for TIME, DATETIME and TIMESTAMP
390 // (will work on MySQL >= 5.6.4)
392 // MySQL permits a non-standard syntax for FLOAT and DOUBLE,
393 // see http://dev.mysql.com/doc/refman/5.5/en/floating-point-types.html
395 $pattern = '@^(DATE|TINYBLOB|TINYTEXT|BLOB|TEXT|'
396 . 'MEDIUMBLOB|MEDIUMTEXT|LONGBLOB|LONGTEXT|SERIAL|BOOLEAN|UUID)$@i';
397 if ($length != '' && ! preg_match($pattern, $type)) {
398 $query .= '(' . $length . ')';
401 if ($virtuality) {
402 $query .= ' AS (' . $expression . ') ' . $virtuality;
403 } else {
404 if ($attribute != '') {
405 $query .= ' ' . $attribute;
408 $matches = preg_match(
409 '@^(TINYTEXT|TEXT|MEDIUMTEXT|LONGTEXT|VARCHAR|CHAR|ENUM|SET)$@i',
410 $type
412 if (! empty($collation) && $collation != 'NULL' && $matches) {
413 $query .= PMA_generateCharsetQueryPart($collation, true);
416 if ($null !== false) {
417 if ($null == 'NULL') {
418 $query .= ' NULL';
419 } else {
420 $query .= ' NOT NULL';
424 switch ($default_type) {
425 case 'USER_DEFINED' :
426 if ($is_timestamp && $default_value === '0') {
427 // a TIMESTAMP does not accept DEFAULT '0'
428 // but DEFAULT 0 works
429 $query .= ' DEFAULT 0';
430 } elseif ($type == 'BIT') {
431 $query .= ' DEFAULT b\''
432 . preg_replace('/[^01]/', '0', $default_value)
433 . '\'';
434 } elseif ($type == 'BOOLEAN') {
435 if (preg_match('/^1|T|TRUE|YES$/i', $default_value)) {
436 $query .= ' DEFAULT TRUE';
437 } elseif (preg_match('/^0|F|FALSE|NO$/i', $default_value)) {
438 $query .= ' DEFAULT FALSE';
439 } else {
440 // Invalid BOOLEAN value
441 $query .= ' DEFAULT \''
442 . PMA_Util::sqlAddSlashes($default_value) . '\'';
444 } elseif ($type == 'BINARY' || $type == 'VARBINARY') {
445 $query .= ' DEFAULT 0x' . $default_value;
446 } else {
447 $query .= ' DEFAULT \''
448 . PMA_Util::sqlAddSlashes($default_value) . '\'';
450 break;
451 /** @noinspection PhpMissingBreakStatementInspection */
452 case 'NULL' :
453 // If user uncheck null checkbox and not change default value null,
454 // default value will be ignored.
455 if ($null !== false && $null !== 'NULL') {
456 break;
458 // else fall-through intended, no break here
459 case 'CURRENT_TIMESTAMP' :
460 $query .= ' DEFAULT ' . $default_type;
461 break;
462 case 'NONE' :
463 default :
464 break;
467 if (!empty($extra)) {
468 $query .= ' ' . $extra;
471 if (!empty($comment)) {
472 $query .= " COMMENT '" . PMA_Util::sqlAddSlashes($comment) . "'";
475 // move column
476 if ($move_to == '-first') { // dash can't appear as part of column name
477 $query .= ' FIRST';
478 } elseif ($move_to != '') {
479 $query .= ' AFTER ' . PMA_Util::backquote($move_to);
481 return $query;
482 } // end function
485 * Counts and returns (or displays) the number of records in a table
487 * @param bool $force_exact whether to force an exact count
489 * @return mixed the number of records if "retain" param is true,
490 * otherwise true
492 public function countRecords($force_exact = false)
494 $is_view = $this->isView();
495 $db = $this->_db_name;
496 $table = $this->_name;
498 if ($this->_dbi->getCachedTableContent(array($db, $table, 'ExactRows')) != null) {
499 $row_count = $this->_dbi->getCachedTableContent(
500 array($db, $table, 'ExactRows')
502 return $row_count;
504 $row_count = false;
506 if (! $force_exact) {
507 if (($this->_dbi->getCachedTableContent(array($db, $table, 'Rows')) == null)
508 && !$is_view
510 $tmp_tables = $this->_dbi->getTablesFull($db, $table);
511 if (isset($tmp_tables[$table])) {
512 $this->_dbi->cacheTableContent(
513 array($db, $table),
514 $tmp_tables[$table]
518 if ($this->_dbi->getCachedTableContent(array($db, $table, 'Rows')) != null) {
519 $row_count = $this->_dbi->getCachedTableContent(
520 array($db, $table, 'Rows')
522 } else {
523 $row_count = false;
526 // for a VIEW, $row_count is always false at this point
527 if (false !== $row_count
528 && $row_count >= $GLOBALS['cfg']['MaxExactCount']
530 return $row_count;
533 // Make an exception for views in I_S and D_D schema in
534 // Drizzle, as these map to in-memory data and should execute
535 // fast enough
536 if (! $is_view
537 || (PMA_DRIZZLE && $this->_dbi->isSystemSchema($db))
539 $row_count = $this->_dbi->fetchValue(
540 'SELECT COUNT(*) FROM ' . PMA_Util::backquote($db) . '.'
541 . PMA_Util::backquote($table)
543 } else {
544 // For complex views, even trying to get a partial record
545 // count could bring down a server, so we offer an
546 // alternative: setting MaxExactCountViews to 0 will bypass
547 // completely the record counting for views
549 if ($GLOBALS['cfg']['MaxExactCountViews'] == 0) {
550 $row_count = 0;
551 } else {
552 // Counting all rows of a VIEW could be too long,
553 // so use a LIMIT clause.
554 // Use try_query because it can fail (when a VIEW is
555 // based on a table that no longer exists)
556 $result = $this->_dbi->tryQuery(
557 'SELECT 1 FROM ' . PMA_Util::backquote($db) . '.'
558 . PMA_Util::backquote($table) . ' LIMIT '
559 . $GLOBALS['cfg']['MaxExactCountViews'],
560 null,
561 PMA_DatabaseInterface::QUERY_STORE
563 if (!$this->_dbi->getError()) {
564 $row_count = $this->_dbi->numRows($result);
565 $this->_dbi->freeResult($result);
569 if ($row_count) {
570 $this->_dbi->cacheTableContent(array($db, $table, 'ExactRows'), $row_count);
573 return $row_count;
574 } // end of the 'PMA_Table::countRecords()' function
577 * Generates column specification for ALTER syntax
579 * @param string $oldcol old column name
580 * @param string $newcol new column name
581 * @param string $type type ('INT', 'VARCHAR', 'BIT', ...)
582 * @param string $length length ('2', '5,2', '', ...)
583 * @param string $attribute attribute
584 * @param string $collation collation
585 * @param bool|string $null with 'NULL' or 'NOT NULL'
586 * @param string $default_type whether default is CURRENT_TIMESTAMP,
587 * NULL, NONE, USER_DEFINED
588 * @param string $default_value default value for USER_DEFINED default
589 * type
590 * @param string $extra 'AUTO_INCREMENT'
591 * @param string $comment field comment
592 * @param string $virtuality virtuality of the column
593 * @param string $expression expression for the virtual column
594 * @param string $move_to new position for column
596 * @see PMA_Table::generateFieldSpec()
598 * @return string field specification
600 static public function generateAlter($oldcol, $newcol, $type, $length,
601 $attribute, $collation, $null, $default_type, $default_value,
602 $extra, $comment, $virtuality, $expression, $move_to
604 return PMA_Util::backquote($oldcol) . ' '
605 . PMA_Table::generateFieldSpec(
606 $newcol, $type, $length, $attribute,
607 $collation, $null, $default_type, $default_value, $extra,
608 $comment, $virtuality, $expression, $move_to
610 } // end function
613 * Inserts existing entries in a PMA_* table by reading a value from an old
614 * entry
616 * @param string $work The array index, which Relation feature to
617 * check ('relwork', 'commwork', ...)
618 * @param string $pma_table The array index, which PMA-table to update
619 * ('bookmark', 'relation', ...)
620 * @param array $get_fields Which fields will be SELECT'ed from the old entry
621 * @param array $where_fields Which fields will be used for the WHERE query
622 * (array('FIELDNAME' => 'FIELDVALUE'))
623 * @param array $new_fields Which fields will be used as new VALUES.
624 * These are the important keys which differ
625 * from the old entry
626 * (array('FIELDNAME' => 'NEW FIELDVALUE'))
628 * @global relation variable
630 * @return int|true
632 static public function duplicateInfo($work, $pma_table, $get_fields,
633 $where_fields, $new_fields
635 $last_id = -1;
637 if (!isset($GLOBALS['cfgRelation']) || !$GLOBALS['cfgRelation'][$work]) {
638 return true;
641 $select_parts = array();
642 $row_fields = array();
643 foreach ($get_fields as $get_field) {
644 $select_parts[] = PMA_Util::backquote($get_field);
645 $row_fields[$get_field] = 'cc';
648 $where_parts = array();
649 foreach ($where_fields as $_where => $_value) {
650 $where_parts[] = PMA_Util::backquote($_where) . ' = \''
651 . PMA_Util::sqlAddSlashes($_value) . '\'';
654 $new_parts = array();
655 $new_value_parts = array();
656 foreach ($new_fields as $_where => $_value) {
657 $new_parts[] = PMA_Util::backquote($_where);
658 $new_value_parts[] = PMA_Util::sqlAddSlashes($_value);
661 $table_copy_query = '
662 SELECT ' . implode(', ', $select_parts) . '
663 FROM ' . PMA_Util::backquote($GLOBALS['cfgRelation']['db']) . '.'
664 . PMA_Util::backquote($GLOBALS['cfgRelation'][$pma_table]) . '
665 WHERE ' . implode(' AND ', $where_parts);
667 // must use PMA_DatabaseInterface::QUERY_STORE here, since we execute
668 // another query inside the loop
669 $table_copy_rs = PMA_queryAsControlUser(
670 $table_copy_query, true, PMA_DatabaseInterface::QUERY_STORE
673 while ($table_copy_row = @$GLOBALS['dbi']->fetchAssoc($table_copy_rs)) {
674 $value_parts = array();
675 foreach ($table_copy_row as $_key => $_val) {
676 if (isset($row_fields[$_key]) && $row_fields[$_key] == 'cc') {
677 $value_parts[] = PMA_Util::sqlAddSlashes($_val);
681 $new_table_query = 'INSERT IGNORE INTO '
682 . PMA_Util::backquote($GLOBALS['cfgRelation']['db'])
683 . '.' . PMA_Util::backquote($GLOBALS['cfgRelation'][$pma_table])
684 . ' (' . implode(', ', $select_parts) . ', '
685 . implode(', ', $new_parts) . ') VALUES (\''
686 . implode('\', \'', $value_parts) . '\', \''
687 . implode('\', \'', $new_value_parts) . '\')';
689 PMA_queryAsControlUser($new_table_query);
690 $last_id = $GLOBALS['dbi']->insertId();
691 } // end while
693 $GLOBALS['dbi']->freeResult($table_copy_rs);
695 return $last_id;
696 } // end of 'PMA_Table::duplicateInfo()' function
699 * Copies or renames table
701 * @param string $source_db source database
702 * @param string $source_table source table
703 * @param string $target_db target database
704 * @param string $target_table target table
705 * @param string $what what to be moved or copied (data, dataonly)
706 * @param bool $move whether to move
707 * @param string $mode mode
709 * @return bool true if success, false otherwise
711 static public function moveCopy($source_db, $source_table, $target_db,
712 $target_table, $what, $move, $mode
715 global $err_url;
717 // Try moving the tables directly, using native `RENAME` statement.
718 if ($move && $what == 'data') {
719 $tbl = new PMA_Table($source_table, $source_db);
720 if ($tbl->rename($target_table, $target_db)) {
721 $GLOBALS['message'] = $tbl->getLastMessage();
722 return true;
726 // Setting required export settings.
727 $GLOBALS['sql_backquotes'] = 1;
728 $GLOBALS['asfile'] = 1;
730 // Ensuring the target database is valid.
731 if (! $GLOBALS['pma']->databases->exists($source_db, $target_db)) {
732 if (! $GLOBALS['pma']->databases->exists($source_db)) {
733 $GLOBALS['message'] = PMA_Message::rawError(
734 sprintf(
735 __('Source database `%s` was not found!'),
736 htmlspecialchars($source_db)
740 if (! $GLOBALS['pma']->databases->exists($target_db)) {
741 $GLOBALS['message'] = PMA_Message::rawError(
742 sprintf(
743 __('Target database `%s` was not found!'),
744 htmlspecialchars($target_db)
748 return false;
752 * The full name of source table, quoted.
753 * @var string $source
755 $source = PMA_Util::backquote($source_db)
756 . '.' . PMA_Util::backquote($source_table);
758 // If the target database is not specified, the operation is taking
759 // place in the same database.
760 if (! isset($target_db) || ! /*overload*/mb_strlen($target_db)) {
761 $target_db = $source_db;
764 // Selecting the database could avoid some problems with replicated
765 // databases, when moving table from replicated one to not replicated one.
766 $GLOBALS['dbi']->selectDb($target_db);
769 * The full name of target table, quoted.
770 * @var string $target
772 $target = PMA_Util::backquote($target_db)
773 . '.' . PMA_Util::backquote($target_table);
775 // No table is created when this is a data-only operation.
776 if ($what != 'dataonly') {
778 include_once "libraries/plugin_interface.lib.php";
781 * Instance used for exporting the current structure of the table.
783 * @var ExportSql
785 $export_sql_plugin = PMA_getPlugin(
786 "export",
787 "sql",
788 'libraries/plugins/export/',
789 array(
790 'export_type' => 'table',
791 'single_table' => false,
795 $no_constraints_comments = true;
796 $GLOBALS['sql_constraints_query'] = '';
797 // set the value of global sql_auto_increment variable
798 if (isset($_POST['sql_auto_increment'])) {
799 $GLOBALS['sql_auto_increment'] = $_POST['sql_auto_increment'];
803 * The old structure of the table..
804 * @var string $sql_structure
806 $sql_structure = $export_sql_plugin->getTableDef(
807 $source_db, $source_table, "\n", $err_url, false, false
810 unset($no_constraints_comments);
812 // -----------------------------------------------------------------
813 // Phase 0: Preparing structures used.
816 * The destination where the table is moved or copied to.
817 * @var SqlParser\Components\Expression
819 $destination = new SqlParser\Components\Expression(
820 $target_db, $target_table, ''
823 // Find server's SQL mode so the builder can generate correct
824 // queries.
825 // One of the options that alters the behaviour is `ANSI_QUOTES`.
826 // This is not availabile for Drizzle.
827 if (!PMA_DRIZZLE) {
828 SqlParser\Context::setMode(
829 $GLOBALS['dbi']->fetchValue("SELECT @@sql_mode")
833 // -----------------------------------------------------------------
834 // Phase 1: Dropping existent element of the same name (if exists
835 // and required).
837 if (isset($_REQUEST['drop_if_exists'])
838 && $_REQUEST['drop_if_exists'] == 'true'
842 * Drop statement used for building the query.
843 * @var SqlParser\Statements\DropStatement $statement
845 $statement = new SqlParser\Statements\DropStatement();
847 $tbl = new PMA_Table($target_db, $target_table);
849 $statement->options = new SqlParser\Components\OptionsArray(
850 array(
851 $tbl->isView() ? 'VIEW' : 'TABLE',
852 'IF EXISTS',
856 $statement->fields = array($destination);
858 // Building the query.
859 $drop_query = $statement->build() . ';';
861 // Executing it.
862 $GLOBALS['dbi']->query($drop_query);
863 $GLOBALS['sql_query'] .= "\n" . $drop_query;
865 // If an existing table gets deleted, maintain any entries for
866 // the PMA_* tables.
867 $maintain_relations = true;
870 // -----------------------------------------------------------------
871 // Phase 2: Generating the new query of this structure.
874 * The parser responsible for parsing the old queries.
875 * @var SqlParser\Parser $parser
877 $parser = new SqlParser\Parser($sql_structure);
879 if (!empty($parser->statements[0])) {
882 * The CREATE statement of this structure.
883 * @var SqlParser\Statements\CreateStatement $statement
885 $statement = $parser->statements[0];
887 // Changing the destination.
888 $statement->name = $destination;
890 // Building back the query.
891 $sql_structure = $statement->build() . ';';
893 // Executing it.
894 $GLOBALS['dbi']->query($sql_structure);
895 $GLOBALS['sql_query'] .= "\n" . $sql_structure;
898 // -----------------------------------------------------------------
899 // Phase 3: Adding constraints.
900 // All constraint names are removed because they must be unique.
902 if (($move || isset($GLOBALS['add_constraints']))
903 && !empty($GLOBALS['sql_constraints_query'])
906 $parser = new SqlParser\Parser($GLOBALS['sql_constraints_query']);
909 * The ALTER statement that generates the constraints.
910 * @var SqlParser\Statements\AlterStatement $statement
912 $statement = $parser->statements[0];
914 // Changing the altered table to the destination.
915 $statement->table = $destination;
917 // Removing the name of the constraints.
918 foreach ($statement->altered as $idx => $altered) {
919 // All constraint names are removed because they must be unique.
920 if ($altered->options->has('CONSTRAINT')) {
921 $altered->field = null;
925 // Building back the query.
926 $GLOBALS['sql_constraints_query'] = $statement->build() . ';';
928 // Executing it.
929 if ($mode == 'one_table') {
930 $GLOBALS['dbi']->query($GLOBALS['sql_constraints_query']);
932 $GLOBALS['sql_query'] .= "\n" . $GLOBALS['sql_constraints_query'];
933 if ($mode == 'one_table') {
934 unset($GLOBALS['sql_constraints_query']);
938 // -----------------------------------------------------------------
939 // Phase 4: Adding indexes.
940 // View phase 3.
942 if (!empty($GLOBALS['sql_indexes'])) {
944 $parser = new SqlParser\Parser($GLOBALS['sql_indexes']);
947 * The ALTER statement that generates the indexes.
948 * @var SqlParser\Statements\AlterStatement $statement
950 $statement = $parser->statements[0];
952 // Changing the altered table to the destination.
953 $statement->table = $destination;
955 // Removing the name of the constraints.
956 foreach ($statement->altered as $idx => $altered) {
957 // All constraint names are removed because they must be unique.
958 if ($altered->options->has('CONSTRAINT')) {
959 $altered->field = null;
963 // Building back the query.
964 $GLOBALS['sql_indexes'] = $statement->build() . ';';
966 // Executing it.
967 if ($mode == 'one_table' || $mode == 'db_copy') {
968 $GLOBALS['dbi']->query($GLOBALS['sql_indexes']);
970 $GLOBALS['sql_query'] .= "\n" . $GLOBALS['sql_indexes'];
971 if ($mode == 'one_table' || $mode == 'db_copy') {
972 unset($GLOBALS['sql_indexes']);
976 // -----------------------------------------------------------------
977 // Phase 5: Adding AUTO_INCREMENT.
979 if (! empty($GLOBALS['sql_auto_increments'])) {
980 if ($mode == 'one_table' || $mode == 'db_copy') {
982 $parser = new SqlParser\Parser($GLOBALS['sql_auto_increments']);
985 * The ALTER statement that alters the AUTO_INCREMENT value.
986 * @var SqlParser\Statements\AlterStatement $statement
988 $statement = $parser->statements[0];
990 // Changing the altered table to the destination.
991 $statement->table = $destination;
993 // Building back the query.
994 $GLOBALS['sql_auto_increments'] = $statement->build() . ';';
996 // Executing it.
997 $GLOBALS['dbi']->query($GLOBALS['sql_auto_increments']);
998 $GLOBALS['sql_query'] .= "\n" . $GLOBALS['sql_auto_increments'];
999 unset($GLOBALS['sql_auto_increments']);
1002 } else {
1003 $GLOBALS['sql_query'] = '';
1006 $_table = new PMA_Table($target_table, $target_db);
1007 // Copy the data unless this is a VIEW
1008 if (($what == 'data' || $what == 'dataonly')
1009 && ! $_table->isView()
1011 if (! PMA_DRIZZLE) {
1012 $sql_set_mode = "SET SQL_MODE='NO_AUTO_VALUE_ON_ZERO'";
1013 $GLOBALS['dbi']->query($sql_set_mode);
1014 $GLOBALS['sql_query'] .= "\n\n" . $sql_set_mode . ';';
1017 $sql_insert_data = 'INSERT INTO ' . $target
1018 . ' SELECT * FROM ' . $source;
1019 $GLOBALS['dbi']->query($sql_insert_data);
1020 $GLOBALS['sql_query'] .= "\n\n" . $sql_insert_data . ';';
1023 PMA_getRelationsParam();
1025 // Drops old table if the user has requested to move it
1026 if ($move) {
1028 // This could avoid some problems with replicated databases, when
1029 // moving table from replicated one to not replicated one
1030 $GLOBALS['dbi']->selectDb($source_db);
1032 $_source_table = new PMA_Table($source_table, $source_db);
1033 if ($_source_table->isView()) {
1034 $sql_drop_query = 'DROP VIEW';
1035 } else {
1036 $sql_drop_query = 'DROP TABLE';
1038 $sql_drop_query .= ' ' . $source;
1039 $GLOBALS['dbi']->query($sql_drop_query);
1041 // Renable table in configuration storage
1042 PMA_REL_renameTable(
1043 $source_db, $target_db,
1044 $source_table, $target_table
1047 $GLOBALS['sql_query'] .= "\n\n" . $sql_drop_query . ';';
1048 // end if ($move)
1049 return true;
1052 // we are copying
1053 // Create new entries as duplicates from old PMA DBs
1054 if ($what == 'dataonly' || isset($maintain_relations)) {
1055 return true;
1058 if ($GLOBALS['cfgRelation']['commwork']) {
1059 // Get all comments and MIME-Types for current table
1060 $comments_copy_rs = PMA_queryAsControlUser(
1061 'SELECT column_name, comment'
1062 . ($GLOBALS['cfgRelation']['mimework']
1063 ? ', mimetype, transformation, transformation_options'
1064 : '')
1065 . ' FROM '
1066 . PMA_Util::backquote($GLOBALS['cfgRelation']['db'])
1067 . '.'
1068 . PMA_Util::backquote($GLOBALS['cfgRelation']['column_info'])
1069 . ' WHERE '
1070 . ' db_name = \''
1071 . PMA_Util::sqlAddSlashes($source_db) . '\''
1072 . ' AND '
1073 . ' table_name = \''
1074 . PMA_Util::sqlAddSlashes($source_table) . '\''
1077 // Write every comment as new copied entry. [MIME]
1078 while ($comments_copy_row
1079 = $GLOBALS['dbi']->fetchAssoc($comments_copy_rs)) {
1080 $new_comment_query = 'REPLACE INTO '
1081 . PMA_Util::backquote($GLOBALS['cfgRelation']['db'])
1082 . '.' . PMA_Util::backquote(
1083 $GLOBALS['cfgRelation']['column_info']
1085 . ' (db_name, table_name, column_name, comment'
1086 . ($GLOBALS['cfgRelation']['mimework']
1087 ? ', mimetype, transformation, transformation_options'
1088 : '')
1089 . ') ' . ' VALUES(' . '\'' . PMA_Util::sqlAddSlashes($target_db)
1090 . '\',\'' . PMA_Util::sqlAddSlashes($target_table) . '\',\''
1091 . PMA_Util::sqlAddSlashes($comments_copy_row['column_name'])
1092 . '\''
1093 . ($GLOBALS['cfgRelation']['mimework']
1094 ? ',\'' . PMA_Util::sqlAddSlashes(
1095 $comments_copy_row['comment']
1097 . '\',' . '\'' . PMA_Util::sqlAddSlashes(
1098 $comments_copy_row['mimetype']
1100 . '\',' . '\'' . PMA_Util::sqlAddSlashes(
1101 $comments_copy_row['transformation']
1103 . '\',' . '\'' . PMA_Util::sqlAddSlashes(
1104 $comments_copy_row['transformation_options']
1106 . '\''
1107 : '')
1108 . ')';
1109 PMA_queryAsControlUser($new_comment_query);
1110 } // end while
1111 $GLOBALS['dbi']->freeResult($comments_copy_rs);
1112 unset($comments_copy_rs);
1115 // duplicating the bookmarks must not be done here, but
1116 // just once per db
1118 $get_fields = array('display_field');
1119 $where_fields = array(
1120 'db_name' => $source_db,
1121 'table_name' => $source_table
1123 $new_fields = array(
1124 'db_name' => $target_db,
1125 'table_name' => $target_table
1127 PMA_Table::duplicateInfo(
1128 'displaywork',
1129 'table_info',
1130 $get_fields,
1131 $where_fields,
1132 $new_fields
1136 * @todo revise this code when we support cross-db relations
1138 $get_fields = array(
1139 'master_field',
1140 'foreign_table',
1141 'foreign_field'
1143 $where_fields = array(
1144 'master_db' => $source_db,
1145 'master_table' => $source_table
1147 $new_fields = array(
1148 'master_db' => $target_db,
1149 'foreign_db' => $target_db,
1150 'master_table' => $target_table
1152 PMA_Table::duplicateInfo(
1153 'relwork',
1154 'relation',
1155 $get_fields,
1156 $where_fields,
1157 $new_fields
1160 $get_fields = array(
1161 'foreign_field',
1162 'master_table',
1163 'master_field'
1165 $where_fields = array(
1166 'foreign_db' => $source_db,
1167 'foreign_table' => $source_table
1169 $new_fields = array(
1170 'master_db' => $target_db,
1171 'foreign_db' => $target_db,
1172 'foreign_table' => $target_table
1174 PMA_Table::duplicateInfo(
1175 'relwork',
1176 'relation',
1177 $get_fields,
1178 $where_fields,
1179 $new_fields
1183 * @todo Can't get duplicating PDFs the right way. The
1184 * page numbers always get screwed up independently from
1185 * duplication because the numbers do not seem to be stored on a
1186 * per-database basis. Would the author of pdf support please
1187 * have a look at it?
1189 $get_fields = array('page_descr');
1190 $where_fields = array('db_name' => $source_db);
1191 $new_fields = array('db_name' => $target_db);
1192 $last_id = PMA_Table::duplicateInfo(
1193 'pdfwork',
1194 'pdf_pages',
1195 $get_fields,
1196 $where_fields,
1197 $new_fields
1200 if (isset($last_id) && $last_id >= 0) {
1201 $get_fields = array('x', 'y');
1202 $where_fields = array(
1203 'db_name' => $source_db,
1204 'table_name' => $source_table
1206 $new_fields = array(
1207 'db_name' => $target_db,
1208 'table_name' => $target_table,
1209 'pdf_page_number' => $last_id
1211 PMA_Table::duplicateInfo(
1212 'pdfwork',
1213 'table_coords',
1214 $get_fields,
1215 $where_fields,
1216 $new_fields
1221 return true;
1225 * checks if given name is a valid table name,
1226 * currently if not empty, trailing spaces, '.', '/' and '\'
1228 * @param string $table_name name to check
1230 * @todo add check for valid chars in filename on current system/os
1231 * @see http://dev.mysql.com/doc/refman/5.0/en/legal-names.html
1233 * @return boolean whether the string is valid or not
1235 static function isValidName($table_name)
1237 if ($table_name !== trim($table_name)) {
1238 // trailing spaces
1239 return false;
1242 if (! /*overload*/mb_strlen($table_name)) {
1243 // zero length
1244 return false;
1247 if (preg_match('/[.\/\\\\]+/i', $table_name)) {
1248 // illegal char . / \
1249 return false;
1252 return true;
1256 * renames table
1258 * @param string $new_name new table name
1259 * @param string $new_db new database name
1261 * @return bool success
1263 function rename($new_name, $new_db = null)
1265 $lowerCaseTableNames = PMA_Util::cacheGet(
1266 'lower_case_table_names',
1267 function () {
1268 return $GLOBALS['dbi']->fetchValue(
1269 "SELECT @@lower_case_table_names"
1273 if ($lowerCaseTableNames) {
1274 $new_name = strtolower($new_name);
1277 if (null !== $new_db && $new_db !== $this->getDbName()) {
1278 // Ensure the target is valid
1279 if (! $GLOBALS['pma']->databases->exists($new_db)) {
1280 $this->errors[] = __('Invalid database:') . ' ' . $new_db;
1281 return false;
1283 } else {
1284 $new_db = $this->getDbName();
1287 $new_table = new PMA_Table($new_name, $new_db);
1289 if ($this->getFullName() === $new_table->getFullName()) {
1290 return true;
1293 if (! PMA_Table::isValidName($new_name)) {
1294 $this->errors[] = __('Invalid table name:') . ' '
1295 . $new_table->getFullName();
1296 return false;
1299 // If the table is moved to a different database drop its triggers first
1300 $triggers = $this->_dbi->getTriggers(
1301 $this->getDbName(), $this->getName(), ''
1303 $handle_triggers = $this->getDbName() != $new_db && $triggers;
1304 if ($handle_triggers) {
1305 foreach ($triggers as $trigger) {
1306 $sql = 'DROP TRIGGER IF EXISTS '
1307 . PMA_Util::backquote($this->getDbName())
1308 . '.' . PMA_Util::backquote($trigger['name']) . ';';
1309 $this->_dbi->query($sql);
1314 * tested also for a view, in MySQL 5.0.92, 5.1.55 and 5.5.13
1316 $GLOBALS['sql_query'] = '
1317 RENAME TABLE ' . $this->getFullName(true) . '
1318 TO ' . $new_table->getFullName(true) . ';';
1319 // I don't think a specific error message for views is necessary
1320 if (! $this->_dbi->query($GLOBALS['sql_query'])) {
1321 // Restore triggers in the old database
1322 if ($handle_triggers) {
1323 $this->_dbi->selectDb($this->getDbName());
1324 foreach ($triggers as $trigger) {
1325 $this->_dbi->query($trigger['create']);
1328 $this->errors[] = sprintf(
1329 __('Failed to rename table %1$s to %2$s!'),
1330 $this->getFullName(),
1331 $new_table->getFullName()
1333 return false;
1336 $old_name = $this->getName();
1337 $old_db = $this->getDbName();
1338 $this->_name = $new_name;
1339 $this->_db_name = $new_db;
1341 // Renable table in configuration storage
1342 PMA_REL_renameTable(
1343 $old_db, $new_db,
1344 $old_name, $new_name
1347 $this->messages[] = sprintf(
1348 __('Table %1$s has been renamed to %2$s.'),
1349 htmlspecialchars($old_name),
1350 htmlspecialchars($new_name)
1352 return true;
1356 * Get all unique columns
1358 * returns an array with all columns with unique content, in fact these are
1359 * all columns being single indexed in PRIMARY or UNIQUE
1361 * e.g.
1362 * - PRIMARY(id) // id
1363 * - UNIQUE(name) // name
1364 * - PRIMARY(fk_id1, fk_id2) // NONE
1365 * - UNIQUE(x,y) // NONE
1367 * @param bool $backquoted whether to quote name with backticks ``
1368 * @param bool $fullName whether to include full name of the table as a prefix
1370 * @return array
1372 public function getUniqueColumns($backquoted = true, $fullName = true)
1374 $sql = $this->_dbi->getTableIndexesSql(
1375 $this->getDbName(),
1376 $this->getName(),
1377 'Non_unique = 0'
1379 $uniques = $this->_dbi->fetchResult(
1380 $sql,
1381 array('Key_name', null),
1382 'Column_name'
1385 $return = array();
1386 foreach ($uniques as $index) {
1387 if (count($index) > 1) {
1388 continue;
1390 if ($fullName) {
1391 $possible_column = $this->getFullName($backquoted) . '.';
1392 } else {
1393 $possible_column = '';
1395 if ($backquoted) {
1396 $possible_column .= PMA_Util::backquote($index[0]);
1397 } else {
1398 $possible_column .= $index[0];
1400 // a column might have a primary and an unique index on it
1401 if (! in_array($possible_column, $return)) {
1402 $return[] = $possible_column;
1406 return $return;
1410 * Get all indexed columns
1412 * returns an array with all columns that make use of an index
1414 * e.g. index(col1, col2) would return col1, col2
1416 * @param bool $backquoted whether to quote name with backticks ``
1417 * @param bool $fullName whether to include full name of the table as a prefix
1419 * @return array
1421 public function getIndexedColumns($backquoted = true, $fullName = true)
1423 $sql = $this->_dbi->getTableIndexesSql(
1424 $this->getDbName(),
1425 $this->getName(),
1428 $indexed = $this->_dbi->fetchResult($sql, 'Column_name', 'Column_name');
1430 $return = array();
1431 foreach ($indexed as $column) {
1432 $return[] = ($fullName ? $this->getFullName($backquoted) . '.' : '')
1433 . ($backquoted ? PMA_Util::backquote($column) : $column);
1436 return $return;
1440 * Get all columns
1442 * returns an array with all columns
1444 * @param bool $backquoted whether to quote name with backticks ``
1445 * @param bool $fullName whether to include full name of the table as a prefix
1447 * @return array
1449 public function getColumns($backquoted = true, $fullName = true)
1451 $sql = 'SHOW COLUMNS FROM ' . $this->getFullName(true);
1452 $indexed = $this->_dbi->fetchResult($sql, 'Field', 'Field');
1454 $return = array();
1455 foreach ($indexed as $column) {
1456 $return[] = ($fullName ? $this->getFullName($backquoted) . '.' : '')
1457 . ($backquoted ? PMA_Util::backquote($column) : $column);
1460 return $return;
1464 * Get meta info for fields in table
1466 * @return mixed
1468 public function getColumnsMeta()
1470 $move_columns_sql_query = sprintf(
1471 'SELECT * FROM %s.%s LIMIT 1',
1472 PMA_Util::backquote($this->_db_name),
1473 PMA_Util::backquote($this->_name)
1475 $move_columns_sql_result = $this->_dbi->tryQuery($move_columns_sql_query);
1476 if ($move_columns_sql_result !== false) {
1477 return $this->_dbi->getFieldsMeta($move_columns_sql_result);
1478 } else {
1479 // unsure how to reproduce but it was seen on the reporting server
1480 return array();
1485 * Return UI preferences for this table from phpMyAdmin database.
1487 * @return array
1489 protected function getUiPrefsFromDb()
1491 $cfgRelation = PMA_getRelationsParam();
1492 $pma_table = PMA_Util::backquote($cfgRelation['db']) . "."
1493 . PMA_Util::backquote($cfgRelation['table_uiprefs']);
1495 // Read from phpMyAdmin database
1496 $sql_query = " SELECT `prefs` FROM " . $pma_table
1497 . " WHERE `username` = '" . $GLOBALS['cfg']['Server']['user'] . "'"
1498 . " AND `db_name` = '" . PMA_Util::sqlAddSlashes($this->_db_name) . "'"
1499 . " AND `table_name` = '" . PMA_Util::sqlAddSlashes($this->_name) . "'";
1501 $row = $this->_dbi->fetchArray(PMA_queryAsControlUser($sql_query));
1502 if (isset($row[0])) {
1503 return json_decode($row[0], true);
1504 } else {
1505 return array();
1510 * Save this table's UI preferences into phpMyAdmin database.
1512 * @return true|PMA_Message
1514 protected function saveUiPrefsToDb()
1516 $cfgRelation = PMA_getRelationsParam();
1517 $pma_table = PMA_Util::backquote($cfgRelation['db']) . "."
1518 . PMA_Util::backquote($cfgRelation['table_uiprefs']);
1520 $secureDbName = PMA_Util::sqlAddSlashes($this->_db_name);
1522 $username = $GLOBALS['cfg']['Server']['user'];
1523 $sql_query = " REPLACE INTO " . $pma_table
1524 . " (username, db_name, table_name, prefs) VALUES ('"
1525 . $username . "', '" . $secureDbName
1526 . "', '" . PMA_Util::sqlAddSlashes($this->_name) . "', '"
1527 . PMA_Util::sqlAddSlashes(json_encode($this->uiprefs)) . "')";
1529 $success = $this->_dbi->tryQuery($sql_query, $GLOBALS['controllink']);
1531 if (!$success) {
1532 $message = PMA_Message::error(
1533 __('Could not save table UI preferences!')
1535 $message->addMessage('<br /><br />');
1536 $message->addMessage(
1537 PMA_Message::rawError(
1538 $this->_dbi->getError($GLOBALS['controllink'])
1541 return $message;
1544 // Remove some old rows in table_uiprefs if it exceeds the configured
1545 // maximum rows
1546 $sql_query = 'SELECT COUNT(*) FROM ' . $pma_table;
1547 $rows_count = $this->_dbi->fetchValue($sql_query);
1548 $max_rows = $GLOBALS['cfg']['Server']['MaxTableUiprefs'];
1549 if ($rows_count > $max_rows) {
1550 $num_rows_to_delete = $rows_count - $max_rows;
1551 $sql_query
1552 = ' DELETE FROM ' . $pma_table .
1553 ' ORDER BY last_update ASC' .
1554 ' LIMIT ' . $num_rows_to_delete;
1555 $success = $this->_dbi->tryQuery(
1556 $sql_query, $GLOBALS['controllink']
1559 if (!$success) {
1560 $message = PMA_Message::error(
1561 sprintf(
1563 'Failed to cleanup table UI preferences (see ' .
1564 '$cfg[\'Servers\'][$i][\'MaxTableUiprefs\'] %s)'
1566 PMA_Util::showDocu('config', 'cfg_Servers_MaxTableUiprefs')
1569 $message->addMessage('<br /><br />');
1570 $message->addMessage(
1571 PMA_Message::rawError(
1572 $this->_dbi->getError($GLOBALS['controllink'])
1575 print_r($message);
1576 return $message;
1580 return true;
1584 * Loads the UI preferences for this table.
1585 * If pmadb and table_uiprefs is set, it will load the UI preferences from
1586 * phpMyAdmin database.
1588 * @return void
1590 protected function loadUiPrefs()
1592 $cfgRelation = PMA_getRelationsParam();
1593 $server_id = $GLOBALS['server'];
1595 // set session variable if it's still undefined
1596 if (!isset($_SESSION['tmpval']['table_uiprefs'][$server_id][$this->_db_name][$this->_name])) {
1597 // check whether we can get from pmadb
1598 $_SESSION['tmpval']['table_uiprefs'][$server_id][$this->_db_name]
1599 [$this->_name] = $cfgRelation['uiprefswork']
1600 ? $this->getUiPrefsFromDb()
1601 : array();
1603 $this->uiprefs =& $_SESSION['tmpval']['table_uiprefs'][$server_id]
1604 [$this->_db_name][$this->_name];
1608 * Get a property from UI preferences.
1609 * Return false if the property is not found.
1610 * Available property:
1611 * - PROP_SORTED_COLUMN
1612 * - PROP_COLUMN_ORDER
1613 * - PROP_COLUMN_VISIB
1615 * @param string $property property
1617 * @return mixed
1619 public function getUiProp($property)
1621 if (! isset($this->uiprefs)) {
1622 $this->loadUiPrefs();
1625 // do checking based on property
1626 if ($property == self::PROP_SORTED_COLUMN) {
1627 if (!isset($this->uiprefs[$property])) {
1628 return false;
1631 if (!isset($_REQUEST['discard_remembered_sort'])) {
1632 // check if the column name exists in this table
1633 $tmp = explode(' ', $this->uiprefs[$property]);
1634 $colname = $tmp[0];
1635 //remove backquoting from colname
1636 $colname = str_replace('`', '', $colname);
1637 //get the available column name without backquoting
1638 $avail_columns = $this->getColumns(false);
1640 foreach ($avail_columns as $each_col) {
1641 // check if $each_col ends with $colname
1642 if (substr_compare(
1643 $each_col,
1644 $colname,
1645 /*overload*/mb_strlen($each_col)
1646 - /*overload*/mb_strlen($colname)
1647 ) === 0
1649 return $this->uiprefs[$property];
1653 // remove the property, since it no longer exists in database
1654 $this->removeUiProp(self::PROP_SORTED_COLUMN);
1655 return false;
1658 if ($property == self::PROP_COLUMN_ORDER
1659 || $property == self::PROP_COLUMN_VISIB
1661 if ($this->isView() || !isset($this->uiprefs[$property])) {
1662 return false;
1665 // check if the table has not been modified
1666 if ($this->getStatusInfo('Create_time') == $this->uiprefs['CREATE_TIME']
1668 return $this->uiprefs[$property];
1671 // remove the property, since the table has been modified
1672 $this->removeUiProp(self::PROP_COLUMN_ORDER);
1673 return false;
1676 // default behaviour for other property:
1677 return isset($this->uiprefs[$property]) ? $this->uiprefs[$property] : false;
1681 * Set a property from UI preferences.
1682 * If pmadb and table_uiprefs is set, it will save the UI preferences to
1683 * phpMyAdmin database.
1684 * Available property:
1685 * - PROP_SORTED_COLUMN
1686 * - PROP_COLUMN_ORDER
1687 * - PROP_COLUMN_VISIB
1689 * @param string $property Property
1690 * @param mixed $value Value for the property
1691 * @param string $table_create_time Needed for PROP_COLUMN_ORDER
1692 * and PROP_COLUMN_VISIB
1694 * @return boolean|PMA_Message
1696 public function setUiProp($property, $value, $table_create_time = null)
1698 if (! isset($this->uiprefs)) {
1699 $this->loadUiPrefs();
1701 // we want to save the create time if the property is PROP_COLUMN_ORDER
1702 if (! $this->isView()
1703 && ($property == self::PROP_COLUMN_ORDER
1704 || $property == self::PROP_COLUMN_VISIB)
1706 $curr_create_time = $this->getStatusInfo('CREATE_TIME');
1707 if (isset($table_create_time)
1708 && $table_create_time == $curr_create_time
1710 $this->uiprefs['CREATE_TIME'] = $curr_create_time;
1711 } else {
1712 // there is no $table_create_time, or
1713 // supplied $table_create_time is older than current create time,
1714 // so don't save
1715 return PMA_Message::error(
1716 sprintf(
1718 'Cannot save UI property "%s". The changes made will ' .
1719 'not be persistent after you refresh this page. ' .
1720 'Please check if the table structure has been changed.'
1722 $property
1727 // save the value
1728 $this->uiprefs[$property] = $value;
1730 // check if pmadb is set
1731 $cfgRelation = PMA_getRelationsParam();
1732 if ($cfgRelation['uiprefswork']) {
1733 return $this->saveUiprefsToDb();
1735 return true;
1739 * Remove a property from UI preferences.
1741 * @param string $property the property
1743 * @return true|PMA_Message
1745 public function removeUiProp($property)
1747 if (! isset($this->uiprefs)) {
1748 $this->loadUiPrefs();
1750 if (isset($this->uiprefs[$property])) {
1751 unset($this->uiprefs[$property]);
1753 // check if pmadb is set
1754 $cfgRelation = PMA_getRelationsParam();
1755 if ($cfgRelation['uiprefswork']) {
1756 return $this->saveUiprefsToDb();
1759 return true;
1763 * Get all column names which are MySQL reserved words
1765 * @return array
1766 * @access public
1768 public function getReservedColumnNames()
1770 $columns = $this->getColumns(false);
1771 $return = array();
1772 foreach ($columns as $column) {
1773 $temp = explode('.', $column);
1774 $column_name = $temp[2];
1775 if (SqlParser\Context::isKeyword($column_name, true)) {
1776 $return[] = $column_name;
1779 return $return;
1783 * Function to get the name and type of the columns of a table
1785 * @return array
1787 public function getNameAndTypeOfTheColumns()
1789 $columns = array();
1790 foreach ($this->_dbi->getColumnsFull(
1791 $this->_db_name, $this->_name
1792 ) as $row) {
1793 if (preg_match('@^(set|enum)\((.+)\)$@i', $row['Type'], $tmp)) {
1794 $tmp[2] = /*overload*/
1795 mb_substr(
1796 preg_replace('@([^,])\'\'@', '\\1\\\'', ',' . $tmp[2]), 1
1798 $columns[$row['Field']] = $tmp[1] . '('
1799 . str_replace(',', ', ', $tmp[2]) . ')';
1800 } else {
1801 $columns[$row['Field']] = $row['Type'];
1804 return $columns;
1808 * Get index with index name
1810 * @param string $index Index name
1812 * @return PMA_Index
1814 public function getIndex($index)
1816 return PMA_Index::singleton($this->_db_name, $this->_name, $index);
1820 * Function to get the sql query for index creation or edit
1822 * @param PMA_Index $index current index
1823 * @param bool &$error whether error occurred or not
1825 * @return string
1827 public function getSqlQueryForIndexCreateOrEdit($index, &$error)
1829 // $sql_query is the one displayed in the query box
1830 $sql_query = sprintf(
1831 'ALTER TABLE %s.%s',
1832 PMA_Util::backquote($this->_db_name),
1833 PMA_Util::backquote($this->_name)
1836 // Drops the old index
1837 if (! empty($_REQUEST['old_index'])) {
1838 if ($_REQUEST['old_index'] == 'PRIMARY') {
1839 $sql_query .= ' DROP PRIMARY KEY,';
1840 } else {
1841 $sql_query .= sprintf(
1842 ' DROP INDEX %s,',
1843 PMA_Util::backquote($_REQUEST['old_index'])
1846 } // end if
1848 // Builds the new one
1849 switch ($index->getChoice()) {
1850 case 'PRIMARY':
1851 if ($index->getName() == '') {
1852 $index->setName('PRIMARY');
1853 } elseif ($index->getName() != 'PRIMARY') {
1854 $error = PMA_Message::error(
1855 __('The name of the primary key must be "PRIMARY"!')
1858 $sql_query .= ' ADD PRIMARY KEY';
1859 break;
1860 case 'FULLTEXT':
1861 case 'UNIQUE':
1862 case 'INDEX':
1863 case 'SPATIAL':
1864 if ($index->getName() == 'PRIMARY') {
1865 $error = PMA_Message::error(
1866 __('Can\'t rename index to PRIMARY!')
1869 $sql_query .= sprintf(
1870 ' ADD %s ',
1871 $index->getChoice()
1873 if ($index->getName()) {
1874 $sql_query .= PMA_Util::backquote($index->getName());
1876 break;
1877 } // end switch
1879 $index_fields = array();
1880 foreach ($index->getColumns() as $key => $column) {
1881 $index_fields[$key] = PMA_Util::backquote($column->getName());
1882 if ($column->getSubPart()) {
1883 $index_fields[$key] .= '(' . $column->getSubPart() . ')';
1885 } // end while
1887 if (empty($index_fields)) {
1888 $error = PMA_Message::error(__('No index parts defined!'));
1889 } else {
1890 $sql_query .= ' (' . implode(', ', $index_fields) . ')';
1893 $keyBlockSizes = $index->getKeyBlockSize();
1894 if (! empty($keyBlockSizes)) {
1895 $sql_query .= sprintf(
1896 ' KEY_BLOCK_SIZE = ',
1897 PMA_Util::sqlAddSlashes($keyBlockSizes)
1901 // specifying index type is allowed only for primary, unique and index only
1902 $type = $index->getType();
1903 if ($index->getChoice() != 'SPATIAL'
1904 && $index->getChoice() != 'FULLTEXT'
1905 && in_array($type, PMA_Index::getIndexTypes())
1907 $sql_query .= ' USING ' . $type;
1910 $parser = $index->getParser();
1911 if ($index->getChoice() == 'FULLTEXT' && ! empty($parser)) {
1912 $sql_query .= ' WITH PARSER ' . PMA_Util::sqlAddSlashes($parser);
1915 $comment = $index->getComment();
1916 if (! empty($comment)) {
1917 $sql_query .= sprintf(
1918 " COMMENT '%s'",
1919 PMA_Util::sqlAddSlashes($comment)
1923 $sql_query .= ';';
1925 return $sql_query;
1929 * Function to handle update for display field
1931 * @param string $disp current display field
1932 * @param string $display_field display field
1933 * @param array $cfgRelation configuration relation
1935 * @return boolean True on update succeed or False on failure
1937 public function updateDisplayField($disp, $display_field, $cfgRelation)
1939 $upd_query = false;
1940 if ($disp) {
1941 if ($display_field == '') {
1942 $upd_query = 'DELETE FROM '
1943 . PMA_Util::backquote($GLOBALS['cfgRelation']['db'])
1944 . '.' . PMA_Util::backquote($cfgRelation['table_info'])
1945 . ' WHERE db_name = \''
1946 . PMA_Util::sqlAddSlashes($this->_db_name) . '\''
1947 . ' AND table_name = \''
1948 . PMA_Util::sqlAddSlashes($this->_name) . '\'';
1949 } elseif ($disp != $display_field) {
1950 $upd_query = 'UPDATE '
1951 . PMA_Util::backquote($GLOBALS['cfgRelation']['db'])
1952 . '.' . PMA_Util::backquote($cfgRelation['table_info'])
1953 . ' SET display_field = \''
1954 . PMA_Util::sqlAddSlashes($display_field) . '\''
1955 . ' WHERE db_name = \''
1956 . PMA_Util::sqlAddSlashes($this->_db_name) . '\''
1957 . ' AND table_name = \''
1958 . PMA_Util::sqlAddSlashes($this->_name) . '\'';
1960 } elseif ($display_field != '') {
1961 $upd_query = 'INSERT INTO '
1962 . PMA_Util::backquote($GLOBALS['cfgRelation']['db'])
1963 . '.' . PMA_Util::backquote($cfgRelation['table_info'])
1964 . '(db_name, table_name, display_field) VALUES('
1965 . '\'' . PMA_Util::sqlAddSlashes($this->_db_name) . '\','
1966 . '\'' . PMA_Util::sqlAddSlashes($this->_name) . '\','
1967 . '\'' . PMA_Util::sqlAddSlashes($display_field) . '\')';
1970 if ($upd_query) {
1971 $this->_dbi->query(
1972 $upd_query,
1973 $GLOBALS['controllink'],
1975 false
1977 return true;
1979 return false;
1983 * Function to get update query for updating internal relations
1985 * @param array $multi_edit_columns_name multi edit column names
1986 * @param array $destination_db destination tables
1987 * @param array $destination_table destination tables
1988 * @param array $destination_column destination columns
1989 * @param array $cfgRelation configuration relation
1990 * @param array|null $existrel db, table, column
1992 * @return boolean
1994 public function updateInternalRelations($multi_edit_columns_name,
1995 $destination_db, $destination_table, $destination_column,
1996 $cfgRelation, $existrel
1998 $updated = false;
1999 foreach ($destination_db as $master_field_md5 => $foreign_db) {
2000 $upd_query = null;
2001 // Map the fieldname's md5 back to its real name
2002 $master_field = $multi_edit_columns_name[$master_field_md5];
2003 $foreign_table = $destination_table[$master_field_md5];
2004 $foreign_field = $destination_column[$master_field_md5];
2005 if (! empty($foreign_db)
2006 && ! empty($foreign_table)
2007 && ! empty($foreign_field)
2009 if (! isset($existrel[$master_field])) {
2010 $upd_query = 'INSERT INTO '
2011 . PMA_Util::backquote($GLOBALS['cfgRelation']['db'])
2012 . '.' . PMA_Util::backquote($cfgRelation['relation'])
2013 . '(master_db, master_table, master_field, foreign_db,'
2014 . ' foreign_table, foreign_field)'
2015 . ' values('
2016 . '\'' . PMA_Util::sqlAddSlashes($this->_db_name) . '\', '
2017 . '\'' . PMA_Util::sqlAddSlashes($this->_name) . '\', '
2018 . '\'' . PMA_Util::sqlAddSlashes($master_field) . '\', '
2019 . '\'' . PMA_Util::sqlAddSlashes($foreign_db) . '\', '
2020 . '\'' . PMA_Util::sqlAddSlashes($foreign_table) . '\','
2021 . '\'' . PMA_Util::sqlAddSlashes($foreign_field) . '\')';
2023 } elseif ($existrel[$master_field]['foreign_db'] != $foreign_db
2024 || $existrel[$master_field]['foreign_table'] != $foreign_table
2025 || $existrel[$master_field]['foreign_field'] != $foreign_field
2027 $upd_query = 'UPDATE '
2028 . PMA_Util::backquote($GLOBALS['cfgRelation']['db'])
2029 . '.' . PMA_Util::backquote($cfgRelation['relation'])
2030 . ' SET foreign_db = \''
2031 . PMA_Util::sqlAddSlashes($foreign_db) . '\', '
2032 . ' foreign_table = \''
2033 . PMA_Util::sqlAddSlashes($foreign_table) . '\', '
2034 . ' foreign_field = \''
2035 . PMA_Util::sqlAddSlashes($foreign_field) . '\' '
2036 . ' WHERE master_db = \''
2037 . PMA_Util::sqlAddSlashes($this->_db_name) . '\''
2038 . ' AND master_table = \''
2039 . PMA_Util::sqlAddSlashes($this->_name) . '\''
2040 . ' AND master_field = \''
2041 . PMA_Util::sqlAddSlashes($master_field) . '\'';
2042 } // end if... else....
2043 } elseif (isset($existrel[$master_field])) {
2044 $upd_query = 'DELETE FROM '
2045 . PMA_Util::backquote($GLOBALS['cfgRelation']['db'])
2046 . '.' . PMA_Util::backquote($cfgRelation['relation'])
2047 . ' WHERE master_db = \''
2048 . PMA_Util::sqlAddSlashes($this->_db_name) . '\''
2049 . ' AND master_table = \''
2050 . PMA_Util::sqlAddSlashes($this->_name) . '\''
2051 . ' AND master_field = \''
2052 . PMA_Util::sqlAddSlashes($master_field) . '\'';
2053 } // end if... else....
2055 if (isset($upd_query)) {
2056 $this->_dbi->query(
2057 $upd_query,
2058 $GLOBALS['controllink'],
2060 false
2062 $updated = true;
2065 return $updated;
2069 * Function to handle foreign key updates
2071 * @param array $destination_foreign_db destination foreign database
2072 * @param array $multi_edit_columns_name multi edit column names
2073 * @param array $destination_foreign_table destination foreign table
2074 * @param array $destination_foreign_column destination foreign column
2075 * @param array $options_array options array
2076 * @param string $table current table
2077 * @param array $existrel_foreign db, table, column
2079 * @return array
2081 public function updateForeignKeys($destination_foreign_db,
2082 $multi_edit_columns_name, $destination_foreign_table,
2083 $destination_foreign_column, $options_array, $table, $existrel_foreign
2085 $html_output = '';
2086 $preview_sql_data = '';
2087 $display_query = '';
2088 $seen_error = false;
2090 foreach ($destination_foreign_db as $master_field_md5 => $foreign_db) {
2091 $create = false;
2092 $drop = false;
2094 // Map the fieldname's md5 back to its real name
2095 $master_field = $multi_edit_columns_name[$master_field_md5];
2097 $foreign_table = $destination_foreign_table[$master_field_md5];
2098 $foreign_field = $destination_foreign_column[$master_field_md5];
2100 if (isset($existrel_foreign[$master_field_md5]['ref_db_name'])) {
2101 $ref_db_name = $existrel_foreign[$master_field_md5]['ref_db_name'];
2102 } else {
2103 $ref_db_name = $GLOBALS['db'];
2106 $empty_fields = false;
2107 foreach ($master_field as $key => $one_field) {
2108 if ((! empty($one_field) && empty($foreign_field[$key]))
2109 || (empty($one_field) && ! empty($foreign_field[$key]))
2111 $empty_fields = true;
2114 if (empty($one_field) && empty($foreign_field[$key])) {
2115 unset($master_field[$key]);
2116 unset($foreign_field[$key]);
2120 if (! empty($foreign_db)
2121 && ! empty($foreign_table)
2122 && ! $empty_fields
2124 if (isset($existrel_foreign[$master_field_md5])) {
2125 $constraint_name
2126 = $existrel_foreign[$master_field_md5]['constraint'];
2127 $on_delete = !empty(
2128 $existrel_foreign[$master_field_md5]['on_delete']
2130 ? $existrel_foreign[$master_field_md5]['on_delete']
2131 : 'RESTRICT';
2132 $on_update = ! empty(
2133 $existrel_foreign[$master_field_md5]['on_update']
2135 ? $existrel_foreign[$master_field_md5]['on_update']
2136 : 'RESTRICT';
2138 if ($ref_db_name != $foreign_db
2139 || $existrel_foreign[$master_field_md5]['ref_table_name'] != $foreign_table
2140 || $existrel_foreign[$master_field_md5]['ref_index_list'] != $foreign_field
2141 || $existrel_foreign[$master_field_md5]['index_list'] != $master_field
2142 || $_REQUEST['constraint_name'][$master_field_md5] != $constraint_name
2143 || ($_REQUEST['on_delete'][$master_field_md5] != $on_delete)
2144 || ($_REQUEST['on_update'][$master_field_md5] != $on_update)
2146 // another foreign key is already defined for this field
2147 // or an option has been changed for ON DELETE or ON UPDATE
2148 $drop = true;
2149 $create = true;
2150 } // end if... else....
2151 } else {
2152 // no key defined for this field(s)
2153 $create = true;
2155 } elseif (isset($existrel_foreign[$master_field_md5])) {
2156 $drop = true;
2157 } // end if... else....
2159 $tmp_error_drop = false;
2160 if ($drop) {
2161 $drop_query = 'ALTER TABLE ' . PMA_Util::backquote($table)
2162 . ' DROP FOREIGN KEY ' . PMA_Util::backquote($existrel_foreign[$master_field_md5]['constraint']) . ';';
2164 if (! isset($_REQUEST['preview_sql'])) {
2165 $display_query .= $drop_query . "\n";
2166 $this->_dbi->tryQuery($drop_query);
2167 $tmp_error_drop = $this->_dbi->getError();
2169 if (! empty($tmp_error_drop)) {
2170 $seen_error = true;
2171 $html_output .= PMA_Util::mysqlDie(
2172 $tmp_error_drop, $drop_query, false, '', false
2174 continue;
2176 } else {
2177 $preview_sql_data .= $drop_query . "\n";
2180 $tmp_error_create = false;
2181 if (!$create) {
2182 continue;
2185 $create_query = $this->_getSQLToCreateForeignKey(
2186 $table, $master_field, $foreign_db, $foreign_table, $foreign_field,
2187 $_REQUEST['constraint_name'][$master_field_md5],
2188 $options_array[$_REQUEST['on_delete'][$master_field_md5]],
2189 $options_array[$_REQUEST['on_update'][$master_field_md5]]
2192 if (! isset($_REQUEST['preview_sql'])) {
2193 $display_query .= $create_query . "\n";
2194 $this->_dbi->tryQuery($create_query);
2195 $tmp_error_create = $this->_dbi->getError();
2196 if (! empty($tmp_error_create)) {
2197 $seen_error = true;
2199 if (substr($tmp_error_create, 1, 4) == '1005') {
2200 $message = PMA_Message::error(
2202 'Error creating foreign key on %1$s (check data ' .
2203 'types)'
2206 $message->addParam(implode(', ', $master_field));
2207 $html_output .= $message->getDisplay();
2208 } else {
2209 $html_output .= PMA_Util::mysqlDie(
2210 $tmp_error_create, $create_query, false, '', false
2213 $html_output .= PMA_Util::showMySQLDocu(
2214 'InnoDB_foreign_key_constraints'
2215 ) . "\n";
2217 } else {
2218 $preview_sql_data .= $create_query . "\n";
2221 // this is an alteration and the old constraint has been dropped
2222 // without creation of a new one
2223 if ($drop && $create && empty($tmp_error_drop)
2224 && ! empty($tmp_error_create)
2226 // a rollback may be better here
2227 $sql_query_recreate = '# Restoring the dropped constraint...' . "\n";
2228 $sql_query_recreate .= $this->_getSQLToCreateForeignKey(
2229 $table,
2230 $master_field,
2231 $existrel_foreign[$master_field_md5]['ref_db_name'],
2232 $existrel_foreign[$master_field_md5]['ref_table_name'],
2233 $existrel_foreign[$master_field_md5]['ref_index_list'],
2234 $existrel_foreign[$master_field_md5]['constraint'],
2235 $options_array[$existrel_foreign[$master_field_md5]['on_delete']],
2236 $options_array[$existrel_foreign[$master_field_md5]['on_update']]
2238 if (! isset($_REQUEST['preview_sql'])) {
2239 $display_query .= $sql_query_recreate . "\n";
2240 $this->_dbi->tryQuery($sql_query_recreate);
2241 } else {
2242 $preview_sql_data .= $sql_query_recreate;
2245 } // end foreach
2247 return array(
2248 $html_output,
2249 $preview_sql_data,
2250 $display_query,
2251 $seen_error
2256 * Returns the SQL query for foreign key constraint creation
2258 * @param string $table table name
2259 * @param array $field field names
2260 * @param string $foreignDb foreign database name
2261 * @param string $foreignTable foreign table name
2262 * @param array $foreignField foreign field names
2263 * @param string $name name of the constraint
2264 * @param string $onDelete on delete action
2265 * @param string $onUpdate on update action
2267 * @return string SQL query for foreign key constraint creation
2269 private function _getSQLToCreateForeignKey(
2270 $table,
2271 $field,
2272 $foreignDb,
2273 $foreignTable,
2274 $foreignField,
2275 $name = null,
2276 $onDelete = null,
2277 $onUpdate = null
2279 $sql_query = 'ALTER TABLE ' . PMA_Util::backquote($table) . ' ADD ';
2280 // if user entered a constraint name
2281 if (! empty($name)) {
2282 $sql_query .= ' CONSTRAINT ' . PMA_Util::backquote($name);
2285 foreach ($field as $key => $one_field) {
2286 $field[$key] = PMA_Util::backquote($one_field);
2288 foreach ($foreignField as $key => $one_field) {
2289 $foreignField[$key] = PMA_Util::backquote($one_field);
2291 $sql_query .= ' FOREIGN KEY (' . implode(', ', $field) . ') REFERENCES '
2292 . ($this->_db_name != $foreignDb
2293 ? PMA_Util::backquote($foreignDb) . '.' : '')
2294 . PMA_Util::backquote($foreignTable)
2295 . '(' . implode(', ', $foreignField) . ')';
2297 if (! empty($onDelete)) {
2298 $sql_query .= ' ON DELETE ' . $onDelete;
2300 if (! empty($onUpdate)) {
2301 $sql_query .= ' ON UPDATE ' . $onUpdate;
2303 $sql_query .= ';';
2305 return $sql_query;
2309 * Returns the generation expression for virtual columns
2311 * @param string $column name of the column
2313 * @return array|boolean associative array of column name and their expressions
2314 * or false on failure
2316 public function getColumnGenerationExpression($column = null)
2318 $serverType = PMA_Util::getServerType();
2319 if ($serverType == 'MySQL'
2320 && PMA_MYSQL_INT_VERSION > 50705
2321 && ! $GLOBALS['cfg']['Server']['DisableIS']
2323 $sql
2324 = "SELECT
2325 `COLUMN_NAME` AS `Field`,
2326 `GENERATION_EXPRESSION` AS `Expression`
2327 FROM
2328 `information_schema`.`COLUMNS`
2329 WHERE
2330 `TABLE_SCHEMA` = '" . PMA_Util::sqlAddSlashes($this->_db_name) . "'
2331 AND `TABLE_NAME` = '" . PMA_Util::sqlAddSlashes($this->_name) . "'";
2332 if ($column != null) {
2333 $sql .= " AND `COLUMN_NAME` = '" . PMA_Util::sqlAddSlashes($column)
2334 . "'";
2336 $columns = $this->_dbi->fetchResult($sql, 'Field', 'Expression');
2337 return $columns;
2340 $createTable = $this->showCreate();
2341 if (!$createTable) {
2342 return false;
2345 $parser = new SqlParser\Parser($createTable);
2347 * @var SqlParser\Statements\CreateStatement $stmt
2349 $stmt = $parser->statements[0];
2350 $fields = SqlParser\Utils\Table::getFields($stmt);
2351 if ($column != null) {
2352 $expression = isset($fields[$column]['expr']) ?
2353 substr($fields[$column]['expr'], 1, -1) : '';
2354 return array($column => $expression);
2357 $ret = array();
2358 foreach ($fields as $field => $options) {
2359 if (isset($options['expr'])) {
2360 $ret[$field] = substr($options['expr'], 1, -1);
2363 return $ret;
2367 * Returns the CREATE statement for this table
2369 * @return mixed
2371 public function showCreate()
2373 return $this->_dbi->fetchValue(
2374 'SHOW CREATE TABLE ' . PMA_Util::backquote($this->_db_name) . '.'
2375 . PMA_Util::backquote($this->_name),
2376 0, 1
2381 * Returns the real row count for a table
2383 * @return number
2385 function getRealRowCountTable()
2387 // SQL query to get row count for a table.
2388 $result = $this->_dbi->fetchSingleRow(
2389 sprintf(
2390 'SELECT COUNT(*) AS %s FROM %s.%s',
2391 PMA_Util::backquote('row_count'),
2392 PMA_Util::backquote($this->_db_name),
2393 PMA_Util::backquote($this->_name)
2396 return $result['row_count'];
2400 * Get columns with indexes
2402 * @param int $types types bitmask
2404 * @return array an array of columns
2406 function getColumnsWithIndex($types)
2408 $columns_with_index = array();
2409 foreach (
2410 PMA_Index::getFromTableByChoice(
2411 $this->_name,
2412 $this->_db_name,
2413 $types
2414 ) as $index
2416 $columns = $index->getColumns();
2417 foreach ($columns as $column_name => $dummy) {
2418 $columns_with_index[] = $column_name;
2421 return $columns_with_index;