Update edih_x12file_class.php (#295)
[openemr.git] / phpmyadmin / libraries / controllers / TableStructureController.class.php
blob1e4429d71d16555a7e9409f73202302cee8c7ccd
1 <?php
2 /* vim: set expandtab sw=4 ts=4 sts=4: */
4 /**
5 * Holds the PMA\TableStructureController
7 * @package PMA
8 */
10 namespace PMA\Controllers;
12 use PMA\Template;
13 use PMA_Index;
14 use PMA_Partition;
15 use PMA_Table;
16 use PMA_Message;
17 use PMA_PageSettings;
18 use PMA_Util;
19 use PMA\Util;
20 use SqlParser;
22 require_once 'libraries/Index.class.php';
23 require_once 'libraries/Partition.class.php';
24 require_once 'libraries/mysql_charsets.inc.php';
25 require_once 'libraries/config/page_settings.class.php';
26 require_once 'libraries/transformations.lib.php';
27 require_once 'libraries/Template.class.php';
28 require_once 'libraries/util.lib.php';
29 require_once 'libraries/controllers/TableController.class.php';
31 /**
32 * Handles table structure logic
34 * @package PhpMyAdmin
36 class TableStructureController extends TableController
38 /**
39 * @var PMA_Table The table object
41 protected $_table_obj;
42 /**
43 * @var string The URL query string
45 protected $_url_query;
46 /**
47 * @var bool DB is information_schema
49 protected $_db_is_system_schema;
50 /**
51 * @var bool Table is a view
53 protected $_tbl_is_view;
54 /**
55 * @var string Table storage engine
57 protected $_tbl_storage_engine;
58 /**
59 * @var int Number of rows
61 protected $_table_info_num_rows;
62 /**
63 * @var string Table collation
65 protected $_tbl_collation;
66 /**
67 * @var array Show table info
69 protected $_showtable;
71 /**
72 * TableStructureController constructor
74 * @param string $type Indicate the db_structure or tbl_structure
75 * @param string $db DB name
76 * @param string $table Table name
77 * @param string $url_query URL query
78 * @param int $num_tables Number of tables
79 * @param int $pos Current position in the list
80 * @param bool $db_is_system_schema DB is information_schema
81 * @param int $total_num_tables Number of tables
82 * @param array $tables Tables in the DB
83 * @param bool $is_show_stats Whether stats show or not
84 * @param bool $tbl_is_view Table is a view
85 * @param string $tbl_storage_engine Table storage engine
86 * @param int $table_info_num_rows Number of rows
87 * @param string $tbl_collation Table collation
88 * @param array $showtable Show table info
90 public function __construct(
91 $type, $db, $table, $url_query, $num_tables, $pos, $db_is_system_schema,
92 $total_num_tables, $tables, $is_show_stats, $tbl_is_view,
93 $tbl_storage_engine, $table_info_num_rows, $tbl_collation, $showtable
94 ) {
95 parent::__construct();
97 $this->_db_is_system_schema = $db_is_system_schema;
98 $this->_url_query = $url_query;
99 $this->_tbl_is_view = $tbl_is_view;
100 $this->_tbl_storage_engine = $tbl_storage_engine;
101 $this->_table_info_num_rows = $table_info_num_rows;
102 $this->_tbl_collation = $tbl_collation;
103 $this->_showtable = $showtable;
104 $this->table_obj = $this->dbi->getTable($this->db, $this->table);
108 * Index action
110 * @return void
112 public function indexAction()
114 PMA_PageSettings::showGroup('TableStructure');
117 * Function implementations for this script
119 include_once 'libraries/check_user_privileges.lib.php';
120 include_once 'libraries/index.lib.php';
121 include_once 'libraries/sql.lib.php';
122 include_once 'libraries/bookmark.lib.php';
124 $this->response->getHeader()->getScripts()->addFiles(
125 array(
126 'tbl_structure.js',
127 'indexes.js'
132 * Handle column moving
134 if (isset($_REQUEST['move_columns'])
135 && is_array($_REQUEST['move_columns'])
136 && $this->response->isAjax()
138 $this->moveColumns();
139 return;
143 * handle MySQL reserved words columns check
145 if (isset($_REQUEST['reserved_word_check'])) {
146 if ($GLOBALS['cfg']['ReservedWordDisableWarning'] === false) {
147 $columns_names = $_REQUEST['field_name'];
148 $reserved_keywords_names = array();
149 foreach ($columns_names as $column) {
150 if (SqlParser\Context::isKeyword(trim($column), true)) {
151 $reserved_keywords_names[] = trim($column);
154 if (SqlParser\Context::isKeyword(trim($this->table), true)) {
155 $reserved_keywords_names[] = trim($this->table);
157 if (count($reserved_keywords_names) == 0) {
158 $this->response->isSuccess(false);
160 $this->response->addJSON(
161 'message', sprintf(
162 _ngettext(
163 'The name \'%s\' is a MySQL reserved keyword.',
164 'The names \'%s\' are MySQL reserved keywords.',
165 count($reserved_keywords_names)
167 implode(',', $reserved_keywords_names)
170 } else {
171 $this->response->isSuccess(false);
173 return;
176 * A click on Change has been made for one column
178 if (isset($_REQUEST['change_column'])) {
179 $this->displayHtmlForColumnChange(null, 'tbl_structure.php');
180 return;
184 * handle multiple field commands if required
186 * submit_mult_*_x comes from IE if <input type="img" ...> is used
188 $submit_mult = $this->getMultipleFieldCommandType();
190 if (! empty($submit_mult)) {
191 if (isset($_REQUEST['selected_fld'])) {
192 if ($submit_mult == 'browse') {
193 // browsing the table displaying only selected columns
194 $this->displayTableBrowseForSelectedColumns(
195 $GLOBALS['goto'], $GLOBALS['pmaThemeImage']
197 } else {
198 // handle multiple field commands
199 // handle confirmation of deleting multiple columns
200 $action = 'tbl_structure.php';
201 $GLOBALS['selected'] = $_REQUEST['selected_fld'];
202 list(
203 $what_ret, $query_type_ret, $is_unset_submit_mult,
204 $mult_btn_ret, $centralColsError
206 = $this->getDataForSubmitMult(
207 $submit_mult, $_REQUEST['selected_fld'], $action
209 //update the existing variables
210 // todo: refactor mult_submits.inc.php such as
211 // below globals are not needed anymore
212 if (isset($what_ret)) {
213 $GLOBALS['what'] = $what_ret;
214 global $what;
216 if (isset($query_type_ret)) {
217 $GLOBALS['query_type'] = $query_type_ret;
218 global $query_type;
220 if ($is_unset_submit_mult) {
221 unset($submit_mult);
223 if (isset($mult_btn_ret)) {
224 $GLOBALS['mult_btn'] = $mult_btn_ret;
225 global $mult_btn;
227 include 'libraries/mult_submits.inc.php';
229 * if $submit_mult == 'change', execution will have stopped
230 * at this point
232 if (empty($message)) {
233 $message = PMA_Message::success();
235 $this->response->addHTML(
236 PMA_Util::getMessage($message, $sql_query)
239 } else {
240 $this->response->isSuccess(false);
241 $this->response->addJSON('message', __('No column selected.'));
245 // display secondary level tabs if necessary
246 $engine = $this->table_obj->getStatusInfo('ENGINE');
247 $this->response->addHTML(
248 Template::get('table/secondary_tabs')->render(
249 array(
250 'url_params' => array(
251 'db' => $this->db,
252 'table' => $this->table
254 'engine' => $engine
258 $this->response->addHTML('<div id="structure_content">');
261 * Modifications have been submitted -> updates the table
263 if (isset($_REQUEST['do_save_data'])) {
264 $regenerate = $this->updateColumns();
265 if ($regenerate) {
266 // This happens when updating failed
267 // @todo: do something appropriate
268 } else {
269 // continue to show the table's structure
270 unset($_REQUEST['selected']);
275 * Adding indexes
277 if (isset($_REQUEST['add_key'])
278 || isset($_REQUEST['partition_maintenance'])
280 //todo: set some variables for sql.php include, to be eliminated
281 //after refactoring sql.php
282 $db = $this->db;
283 $table = $this->table;
284 $cfg = $GLOBALS['cfg'];
285 $is_superuser = $GLOBALS['dbi']->isSuperuser();
286 $pmaThemeImage = $GLOBALS['pmaThemeImage'];
287 include 'sql.php';
288 $GLOBALS['reload'] = true;
292 * Gets the relation settings
294 $cfgRelation = PMA_getRelationsParam();
297 * Runs common work
299 // set db, table references, for require_once that follows
300 // got to be eliminated in long run
301 $db = &$this->db;
302 $table = &$this->table;
303 include_once 'libraries/tbl_common.inc.php';
304 $this->_db_is_system_schema = $db_is_system_schema;
305 $this->_url_query = $url_query
306 . '&amp;goto=tbl_structure.php&amp;back=tbl_structure.php';
307 $url_params['goto'] = 'tbl_structure.php';
308 $url_params['back'] = 'tbl_structure.php';
311 * Gets tables information
313 include_once 'libraries/tbl_info.inc.php';
315 include_once 'libraries/Index.class.php';
317 // 2. Gets table keys and retains them
318 // @todo should be: $server->db($db)->table($table)->primary()
319 $primary = PMA_Index::getPrimary($this->table, $this->db);
320 $columns_with_index = $this->dbi
321 ->getTable($this->db, $this->table)
322 ->getColumnsWithIndex(
323 PMA_Index::UNIQUE | PMA_Index::INDEX | PMA_Index::SPATIAL
324 | PMA_Index::FULLTEXT
326 $columns_with_unique_index = $this->dbi
327 ->getTable($this->db, $this->table)
328 ->getColumnsWithIndex(PMA_Index::UNIQUE);
330 // 3. Get fields
331 $fields = (array)$this->dbi->getColumns(
332 $this->db, $this->table, null, true
335 // Get more complete field information
336 // For now, this is done just for MySQL 4.1.2+ new TIMESTAMP options
337 // but later, if the analyser returns more information, it
338 // could be executed for any MySQL version and replace
339 // the info given by SHOW FULL COLUMNS FROM.
341 // We also need this to correctly learn if a TIMESTAMP is NOT NULL, since
342 // SHOW FULL COLUMNS or INFORMATION_SCHEMA incorrectly says NULL
343 // and SHOW CREATE TABLE says NOT NULL (tested
344 // in MySQL 4.0.25 and 5.0.21, http://bugs.mysql.com/20910).
346 $show_create_table = $this->table_obj->showCreate();
347 $parser = new SqlParser\Parser($show_create_table);
350 * @var CreateStatement $stmt
352 $stmt = $parser->statements[0];
354 $create_table_fields = SqlParser\Utils\Table::getFields($stmt);
356 //display table structure
357 $this->response->addHTML(
358 $this->displayStructure(
359 $cfgRelation, $columns_with_unique_index, $url_params, $primary,
360 $fields, $columns_with_index, $create_table_fields
364 $this->response->addHTML('</div>');
368 * Moves columns in the table's structure based on $_REQUEST
370 * @return void
372 protected function moveColumns()
374 $this->dbi->selectDb($this->db);
377 * load the definitions for all columns
379 $columns = $this->dbi->getColumnsFull($this->db, $this->table);
380 $column_names = array_keys($columns);
381 $changes = array();
383 // move columns from first to last
384 for ($i = 0, $l = count($_REQUEST['move_columns']); $i < $l; $i++) {
385 $column = $_REQUEST['move_columns'][$i];
386 // is this column already correctly placed?
387 if ($column_names[$i] == $column) {
388 continue;
391 // it is not, let's move it to index $i
392 $data = $columns[$column];
393 $extracted_columnspec = PMA_Util::extractColumnSpec($data['Type']);
394 if (isset($data['Extra'])
395 && $data['Extra'] == 'on update CURRENT_TIMESTAMP'
397 $extracted_columnspec['attribute'] = $data['Extra'];
398 unset($data['Extra']);
400 $current_timestamp = ($data['Type'] == 'timestamp'
401 || $data['Type'] == 'datetime')
402 && $data['Default'] == 'CURRENT_TIMESTAMP';
404 if ($data['Null'] === 'YES' && $data['Default'] === null) {
405 $default_type = 'NULL';
406 } elseif ($current_timestamp) {
407 $default_type = 'CURRENT_TIMESTAMP';
408 } elseif ($data['Default'] === null) {
409 $default_type = 'NONE';
410 } else {
411 $default_type = 'USER_DEFINED';
414 $virtual = array(
415 'VIRTUAL', 'PERSISTENT', 'VIRTUAL GENERATED', 'STORED GENERATED'
417 $data['Virtuality'] = '';
418 $data['Expression'] = '';
419 if (isset($data['Extra']) && in_array($data['Extra'], $virtual)) {
420 $data['Virtuality'] = str_replace(' GENERATED', '', $data['Extra']);
421 $expressions = $this->table->getColumnGenerationExpression($column);
422 $data['Expression'] = $expressions[$column];
425 $changes[] = 'CHANGE ' . PMA_Table::generateAlter(
426 $column,
427 $column,
428 /*overload*/mb_strtoupper($extracted_columnspec['type']),
429 $extracted_columnspec['spec_in_brackets'],
430 $extracted_columnspec['attribute'],
431 isset($data['Collation']) ? $data['Collation'] : '',
432 $data['Null'] === 'YES' ? 'NULL' : 'NOT NULL',
433 $default_type,
434 $current_timestamp ? '' : $data['Default'],
435 isset($data['Extra']) && $data['Extra'] !== '' ? $data['Extra']
436 : false,
437 isset($data['COLUMN_COMMENT']) && $data['COLUMN_COMMENT'] !== ''
438 ? $data['COLUMN_COMMENT'] : false,
439 $data['Virtuality'],
440 $data['Expression'],
441 $i === 0 ? '-first' : $column_names[$i - 1]
443 // update current column_names array, first delete old position
444 for ($j = 0, $ll = count($column_names); $j < $ll; $j++) {
445 if ($column_names[$j] == $column) {
446 unset($column_names[$j]);
449 // insert moved column
450 array_splice($column_names, $i, 0, $column);
452 if (empty($changes)) { // should never happen
453 $this->response->isSuccess(false);
454 return;
456 // move columns
457 $this->dbi->tryQuery(
458 sprintf(
459 'ALTER TABLE %s %s',
460 PMA_Util::backquote($this->table),
461 implode(', ', $changes)
464 $tmp_error = $this->dbi->getError();
465 if ($tmp_error) {
466 $this->response->isSuccess(false);
467 $this->response->addJSON('message', PMA_Message::error($tmp_error));
468 } else {
469 $message = PMA_Message::success(
470 __('The columns have been moved successfully.')
472 $this->response->addJSON('message', $message);
473 $this->response->addJSON('columns', $column_names);
478 * Displays HTML for changing one or more columns
480 * @param array $selected the selected columns
481 * @param string $action target script to call
483 * @return boolean $regenerate true if error occurred
486 protected function displayHtmlForColumnChange($selected, $action)
488 // $selected comes from mult_submits.inc.php
489 if (empty($selected)) {
490 $selected[] = $_REQUEST['field'];
491 $selected_cnt = 1;
492 } else { // from a multiple submit
493 $selected_cnt = count($selected);
497 * @todo optimize in case of multiple fields to modify
499 $fields_meta = array();
500 for ($i = 0; $i < $selected_cnt; $i++) {
501 $fields_meta[] = $this->dbi->getColumns(
502 $this->db, $this->table, $selected[$i], true
505 $num_fields = count($fields_meta);
506 // set these globals because tbl_columns_definition_form.inc.php
507 // verifies them
508 // @todo: refactor tbl_columns_definition_form.inc.php so that it uses
509 // protected function params
510 $GLOBALS['action'] = $action;
511 $GLOBALS['num_fields'] = $num_fields;
514 * Form for changing properties.
516 include_once 'libraries/check_user_privileges.lib.php';
517 include 'libraries/tbl_columns_definition_form.inc.php';
521 * Function to get the type of command for multiple field handling
523 * @return string
525 protected function getMultipleFieldCommandType()
527 $types = array(
528 'change', 'drop', 'primary',
529 'index', 'unique', 'spatial',
530 'fulltext', 'browse'
533 foreach ($types as $type) {
534 if (isset($_REQUEST['submit_mult_' . $type . '_x'])) {
535 return $type;
539 if (isset($_REQUEST['submit_mult'])) {
540 return $_REQUEST['submit_mult'];
541 } elseif (isset($_REQUEST['mult_btn'])
542 && $_REQUEST['mult_btn'] == __('Yes')
544 if (isset($_REQUEST['selected'])) {
545 $_REQUEST['selected_fld'] = $_REQUEST['selected'];
547 return 'row_delete';
550 return null;
554 * Function to display table browse for selected columns
556 * @param string $goto goto page url
557 * @param string $pmaThemeImage URI of the pma theme image
559 * @return void
561 protected function displayTableBrowseForSelectedColumns($goto, $pmaThemeImage)
563 $GLOBALS['active_page'] = 'sql.php';
564 $fields = array();
565 foreach ($_REQUEST['selected_fld'] as $sval) {
566 $fields[] = PMA_Util::backquote($sval);
568 $sql_query = sprintf(
569 'SELECT %s FROM %s.%s',
570 implode(', ', $fields),
571 PMA_Util::backquote($this->db),
572 PMA_Util::backquote($this->table)
575 // Parse and analyze the query
576 // @todo Refactor parse_analyze.inc to protected function
577 $db = &$this->db;
578 include_once 'libraries/parse_analyze.inc.php';
580 include_once 'libraries/sql.lib.php';
582 $this->response->addHTML(
583 PMA_executeQueryAndGetQueryResponse(
584 isset($analyzed_sql_results) ? $analyzed_sql_results : '',
585 false, // is_gotofile
586 $this->db, // db
587 $this->table, // table
588 null, // find_real_end
589 null, // sql_query_for_bookmark
590 null, // extra_data
591 null, // message_to_show
592 null, // message
593 null, // sql_data
594 $goto, // goto
595 $pmaThemeImage, // pmaThemeImage
596 null, // disp_query
597 null, // disp_message
598 null, // query_type
599 $sql_query, // sql_query
600 null, // selectedTables
601 null // complete_query
607 * Update the table's structure based on $_REQUEST
609 * @return boolean $regenerate true if error occurred
612 protected function updateColumns()
614 $err_url = 'tbl_structure.php' . PMA_URL_getCommon(
615 array(
616 'db' => $this->db, 'table' => $this->table
619 $regenerate = false;
620 $field_cnt = count($_REQUEST['field_name']);
621 $changes = array();
622 $adjust_privileges = array();
624 for ($i = 0; $i < $field_cnt; $i++) {
625 if (!$this->columnNeedsAlterTable($i)) {
626 continue;
629 $changes[] = 'CHANGE ' . PMA_Table::generateAlter(
630 Util\get($_REQUEST, "field_orig.${i}", ''),
631 $_REQUEST['field_name'][$i],
632 $_REQUEST['field_type'][$i],
633 $_REQUEST['field_length'][$i],
634 $_REQUEST['field_attribute'][$i],
635 Util\get($_REQUEST, "field_collation.${i}", ''),
636 Util\get($_REQUEST, "field_null.${i}", 'NOT NULL'),
637 $_REQUEST['field_default_type'][$i],
638 $_REQUEST['field_default_value'][$i],
639 Util\get($_REQUEST, "field_extra.${i}", false),
640 Util\get($_REQUEST, "field_comments.${i}", ''),
641 Util\get($_REQUEST, "field_virtuality.${i}", ''),
642 Util\get($_REQUEST, "field_expression.${i}", ''),
643 Util\get($_REQUEST, "field_move_to.${i}", '')
646 // find the remembered sort expression
647 $sorted_col = $this->table_obj->getUiProp(
648 PMA_Table::PROP_SORTED_COLUMN
650 // if the old column name is part of the remembered sort expression
651 if (/*overload*/mb_strpos(
652 $sorted_col,
653 PMA_Util::backquote($_REQUEST['field_orig'][$i])
654 ) !== false) {
655 // delete the whole remembered sort expression
656 $this->table_obj->removeUiProp(PMA_Table::PROP_SORTED_COLUMN);
659 if (isset($_REQUEST['field_adjust_privileges'][$i])
660 && ! empty($_REQUEST['field_adjust_privileges'][$i])
661 && $_REQUEST['field_orig'][$i] != $_REQUEST['field_name'][$i]
663 $adjust_privileges[$_REQUEST['field_orig'][$i]]
664 = $_REQUEST['field_name'][$i];
666 } // end for
668 if (count($changes) > 0 || isset($_REQUEST['preview_sql'])) {
669 // Builds the primary keys statements and updates the table
670 $key_query = '';
672 * this is a little bit more complex
674 * @todo if someone selects A_I when altering a column we need to check:
675 * - no other column with A_I
676 * - the column has an index, if not create one
680 // To allow replication, we first select the db to use
681 // and then run queries on this db.
682 if (!$this->dbi->selectDb($this->db)) {
683 PMA_Util::mysqlDie(
684 $this->dbi->getError(),
685 'USE ' . PMA_Util::backquote($this->db) . ';',
686 false,
687 $err_url
690 $sql_query = 'ALTER TABLE ' . PMA_Util::backquote($this->table) . ' ';
691 $sql_query .= implode(', ', $changes) . $key_query;
692 $sql_query .= ';';
694 // If there is a request for SQL previewing.
695 if (isset($_REQUEST['preview_sql'])) {
696 PMA_previewSQL(count($changes) > 0 ? $sql_query : '');
699 $columns_with_index = $this->dbi
700 ->getTable($this->db, $this->table)
701 ->getColumnsWithIndex(
702 PMA_Index::PRIMARY | PMA_Index::UNIQUE | PMA_Index::INDEX
703 | PMA_Index::SPATIAL | PMA_Index::FULLTEXT
706 $changedToBlob = array();
707 // While changing the Column Collation
708 // First change to BLOB
709 for ($i = 0; $i < $field_cnt; $i++ ) {
710 if (isset($_REQUEST['field_collation'][$i])
711 && isset($_REQUEST['field_collation_orig'][$i])
712 && $_REQUEST['field_collation'][$i] !== $_REQUEST['field_collation_orig'][$i]
713 && ! in_array($_REQUEST['field_orig'][$i], $columns_with_index)
715 $secondary_query = 'ALTER TABLE ' . PMA_Util::backquote(
716 $this->table
718 . ' CHANGE ' . PMA_Util::backquote(
719 $_REQUEST['field_orig'][$i]
721 . ' ' . PMA_Util::backquote($_REQUEST['field_orig'][$i])
722 . ' BLOB;';
723 $this->dbi->query($secondary_query);
724 $changedToBlob[$i] = true;
725 } else {
726 $changedToBlob[$i] = false;
730 // Then make the requested changes
731 $result = $this->dbi->tryQuery($sql_query);
733 if ($result !== false) {
734 $changed_privileges = $this->adjustColumnPrivileges(
735 $adjust_privileges
738 if ($changed_privileges) {
739 $message = PMA_Message::success(
741 'Table %1$s has been altered successfully. Privileges ' .
742 'have been adjusted.'
745 } else {
746 $message = PMA_Message::success(
747 __('Table %1$s has been altered successfully.')
750 $message->addParam($this->table);
752 $this->response->addHTML(
753 PMA_Util::getMessage($message, $sql_query, 'success')
755 } else {
756 // An error happened while inserting/updating a table definition
758 // Save the Original Error
759 $orig_error = $this->dbi->getError();
760 $changes_revert = array();
762 // Change back to Orignal Collation and data type
763 for ($i = 0; $i < $field_cnt; $i++) {
764 if ($changedToBlob[$i]) {
765 $changes_revert[] = 'CHANGE ' . PMA_Table::generateAlter(
766 Util\get($_REQUEST, "field_orig.${i}", ''),
767 $_REQUEST['field_name'][$i],
768 $_REQUEST['field_type_orig'][$i],
769 $_REQUEST['field_length_orig'][$i],
770 $_REQUEST['field_attribute_orig'][$i],
771 Util\get($_REQUEST, "field_collation_orig.${i}", ''),
772 Util\get($_REQUEST, "field_null_orig.${i}", 'NOT NULL'),
773 $_REQUEST['field_default_type_orig'][$i],
774 $_REQUEST['field_default_value_orig'][$i],
775 Util\get($_REQUEST, "field_extra_orig.${i}", false),
776 Util\get($_REQUEST, "field_comments_orig.${i}", ''),
777 Util\get($_REQUEST, "field_virtuality_orig.${i}", ''),
778 Util\get($_REQUEST, "field_expression_orig.${i}", ''),
779 Util\get($_REQUEST, "field_move_to_orig.${i}", '')
784 $revert_query = 'ALTER TABLE ' . PMA_Util::backquote($this->table)
785 . ' ';
786 $revert_query .= implode(', ', $changes_revert) . '';
787 $revert_query .= ';';
789 // Column reverted back to original
790 $this->dbi->query($revert_query);
792 $this->response->isSuccess(false);
793 $this->response->addJSON(
794 'message',
795 PMA_Message::rawError(
796 __('Query error') . ':<br />' . $orig_error
799 $regenerate = true;
803 // update field names in relation
804 if (isset($_REQUEST['field_orig']) && is_array($_REQUEST['field_orig'])) {
805 foreach ($_REQUEST['field_orig'] as $fieldindex => $fieldcontent) {
806 if ($_REQUEST['field_name'][$fieldindex] != $fieldcontent) {
807 PMA_REL_renameField(
808 $this->db, $this->table, $fieldcontent,
809 $_REQUEST['field_name'][$fieldindex]
815 // update mime types
816 if (isset($_REQUEST['field_mimetype'])
817 && is_array($_REQUEST['field_mimetype'])
818 && $GLOBALS['cfg']['BrowseMIME']
820 foreach ($_REQUEST['field_mimetype'] as $fieldindex => $mimetype) {
821 if (isset($_REQUEST['field_name'][$fieldindex])
822 && /*overload*/mb_strlen(
823 $_REQUEST['field_name'][$fieldindex]
826 PMA_setMIME(
827 $this->db, $this->table,
828 $_REQUEST['field_name'][$fieldindex],
829 $mimetype,
830 $_REQUEST['field_transformation'][$fieldindex],
831 $_REQUEST['field_transformation_options'][$fieldindex],
832 $_REQUEST['field_input_transformation'][$fieldindex],
833 $_REQUEST['field_input_transformation_options'][$fieldindex]
838 return $regenerate;
842 * Adjusts the Privileges for all the columns whose names have changed
844 * @param array $adjust_privileges assoc array of old col names mapped to new
845 * cols
847 * @return boolean $changed boolean whether at least one column privileges
848 * adjusted
850 protected function adjustColumnPrivileges($adjust_privileges)
852 $changed = false;
854 if ((!defined('PMA_DRIZZLE') || !PMA_DRIZZLE)
855 && Util\get($GLOBALS, 'col_priv', false)
856 && Util\get($GLOBALS, 'is_reload_priv', false)
858 $this->dbi->selectDb('mysql');
860 // For Column specific privileges
861 foreach ($adjust_privileges as $oldCol => $newCol) {
863 $this->dbi->query(
864 sprintf(
865 'UPDATE %s SET Column_name = "%s"
866 WHERE Db = "%s"
867 AND Table_name = "%s"
868 AND Column_name = "%s";',
869 PMA_Util::backquote('columns_priv'),
870 $newCol, $this->db, $this->table, $oldCol
874 // i.e. if atleast one column privileges adjusted
875 $changed = true;
878 if ($changed) {
879 // Finally FLUSH the new privileges
880 $this->dbi->query("FLUSH PRIVILEGES;");
884 return $changed;
888 * Verifies if some elements of a column have changed
890 * @param integer $i column index in the request
892 * @return boolean $alterTableNeeded true if we need to generate ALTER TABLE
895 protected function columnNeedsAlterTable($i)
897 // these two fields are checkboxes so might not be part of the
898 // request; therefore we define them to avoid notices below
899 if (! isset($_REQUEST['field_null'][$i])) {
900 $_REQUEST['field_null'][$i] = 'NO';
902 if (! isset($_REQUEST['field_extra'][$i])) {
903 $_REQUEST['field_extra'][$i] = '';
906 // field_name does not follow the convention (corresponds to field_orig)
907 if ($_REQUEST['field_name'][$i] != $_REQUEST['field_orig'][$i]) {
908 return true;
911 $fields = array(
912 'field_attribute', 'field_collation', 'field_comments',
913 'field_default_value', 'field_default_type', 'field_extra',
914 'field_length', 'field_null', 'field_type'
916 foreach ($fields as $field) {
917 if ($_REQUEST[$field][$i] != $_REQUEST[$field . '_orig'][$i]) {
918 return true;
921 return !empty($_REQUEST['field_move_to'][$i]);
925 * Displays the table structure ('show table' works correct since 3.23.03)
927 * @param array $cfgRelation current relation parameters
928 * @param array $columns_with_unique_index Columns with unique index
929 * @param mixed $url_params Contains an associative
930 * array with url params
931 * @param PMA_Index|false $primary_index primary index or false if
932 * no one exists
933 * @param array $fields Fields
934 * @param array $columns_with_index Columns with index
935 * @param array $create_table_fields Fields of the table.
937 * @return string
939 protected function displayStructure(
940 $cfgRelation, $columns_with_unique_index, $url_params, $primary_index,
941 $fields, $columns_with_index, $create_table_fields
943 /* TABLE INFORMATION */
944 $HideStructureActions = '';
945 if ($GLOBALS['cfg']['HideStructureActions'] === true) {
946 $HideStructureActions .= ' HideStructureActions';
949 // prepare comments
950 $comments_map = array();
951 $mime_map = array();
953 if ($GLOBALS['cfg']['ShowPropertyComments']) {
954 include_once 'libraries/transformations.lib.php';
955 $comments_map = PMA_getComments($this->db, $this->table);
956 if ($cfgRelation['mimework'] && $GLOBALS['cfg']['BrowseMIME']) {
957 $mime_map = PMA_getMIME($this->db, $this->table, true);
960 include_once 'libraries/central_columns.lib.php';
961 $central_list = PMA_getCentralColumnsFromTable($this->db, $this->table);
962 $columns_list = array();
964 $titles = array(
965 'Change' => PMA_Util::getIcon('b_edit.png', __('Change')),
966 'Drop' => PMA_Util::getIcon('b_drop.png', __('Drop')),
967 'NoDrop' => PMA_Util::getIcon('b_drop.png', __('Drop')),
968 'Primary' => PMA_Util::getIcon('b_primary.png', __('Primary')),
969 'Index' => PMA_Util::getIcon('b_index.png', __('Index')),
970 'Unique' => PMA_Util::getIcon('b_unique.png', __('Unique')),
971 'Spatial' => PMA_Util::getIcon('b_spatial.png', __('Spatial')),
972 'IdxFulltext' => PMA_Util::getIcon('b_ftext.png', __('Fulltext')),
973 'NoPrimary' => PMA_Util::getIcon('bd_primary.png', __('Primary')),
974 'NoIndex' => PMA_Util::getIcon('bd_index.png', __('Index')),
975 'NoUnique' => PMA_Util::getIcon('bd_unique.png', __('Unique')),
976 'NoSpatial' => PMA_Util::getIcon('bd_spatial.png', __('Spatial')),
977 'NoIdxFulltext' => PMA_Util::getIcon('bd_ftext.png', __('Fulltext')),
978 'DistinctValues' => PMA_Util::getIcon(
979 'b_browse.png',
980 __('Distinct values')
985 * Work on the table
987 if ($this->_tbl_is_view && ! $this->_db_is_system_schema) {
988 $item = $this->dbi->fetchSingleRow(
989 sprintf(
990 "SELECT `VIEW_DEFINITION`, `CHECK_OPTION`, `DEFINER`,
991 `SECURITY_TYPE`
992 FROM `INFORMATION_SCHEMA`.`VIEWS`
993 WHERE TABLE_SCHEMA='%s'
994 AND TABLE_NAME='%s';",
995 PMA_Util::sqlAddSlashes($this->db),
996 PMA_Util::sqlAddSlashes($this->table)
1000 $createView = $this->dbi->getTable($this->db, $this->table)
1001 ->showCreate();
1002 // get algorithm from $createView of the form
1003 // CREATE ALGORITHM=<ALGORITHM> DE...
1004 $parts = explode(" ", substr($createView, 17));
1005 $item['ALGORITHM'] = $parts[0];
1007 $view = array(
1008 'operation' => 'alter',
1009 'definer' => $item['DEFINER'],
1010 'sql_security' => $item['SECURITY_TYPE'],
1011 'name' => $this->table,
1012 'as' => $item['VIEW_DEFINITION'],
1013 'with' => $item['CHECK_OPTION'],
1014 'algorithm' => $item['ALGORITHM'],
1017 $edit_view_url = 'view_create.php'
1018 . PMA_URL_getCommon($url_params) . '&amp;'
1019 . implode(
1020 '&amp;',
1021 array_map(
1022 function ($key, $val) {
1023 return 'view[' . urlencode($key) . ']=' . urlencode(
1024 $val
1027 array_keys($view), $view
1033 * Displays Space usage and row statistics
1035 // BEGIN - Calc Table Space
1036 // Get valid statistics whatever is the table type
1037 if ($GLOBALS['cfg']['ShowStats']) {
1038 //get table stats in HTML format
1039 $tablestats = $this->getTableStats();
1040 //returning the response in JSON format to be used by Ajax
1041 $this->response->addJSON('tableStat', $tablestats);
1043 // END - Calc Table Space
1045 return Template::get('table/structure/display_structure')->render(
1046 array(
1047 'HideStructureActions' => $HideStructureActions,
1048 'db' => $this->db,
1049 'table' => $this->table,
1050 'db_is_system_schema' => $this->_db_is_system_schema,
1051 'tbl_is_view' => $this->_tbl_is_view,
1052 'mime_map' => $mime_map,
1053 'url_query' => $this->_url_query,
1054 'titles' => $titles,
1055 'tbl_storage_engine' => $this->_tbl_storage_engine,
1056 'primary' => $primary_index,
1057 'columns_with_unique_index' => $columns_with_unique_index,
1058 'edit_view_url' => isset($edit_view_url) ? $edit_view_url : null,
1059 'columns_list' => $columns_list,
1060 'tablestats' => isset($tablestats) ? $tablestats : null,
1061 'fields' => $fields,
1062 'columns_with_index' => $columns_with_index,
1063 'central_list' => $central_list,
1064 'create_table_fields' => $create_table_fields,
1065 'comments_map' => $comments_map
1071 * Get HTML snippet for display table statistics
1073 * @return string $html_output
1075 protected function getTableStats()
1077 if (empty($this->_showtable)) {
1078 $this->_showtable = $this->dbi->getTable(
1079 $this->db, $this->table
1080 )->getStatusInfo(null, true);
1083 if (empty($this->_showtable['Data_length'])) {
1084 $this->_showtable['Data_length'] = 0;
1086 if (empty($this->_showtable['Index_length'])) {
1087 $this->_showtable['Index_length'] = 0;
1090 $is_innodb = (isset($this->_showtable['Type'])
1091 && $this->_showtable['Type'] == 'InnoDB');
1093 $mergetable = $this->table_obj->isMerge();
1095 // this is to display for example 261.2 MiB instead of 268k KiB
1096 $max_digits = 3;
1097 $decimals = 1;
1098 list($data_size, $data_unit) = PMA_Util::formatByteDown(
1099 $this->_showtable['Data_length'], $max_digits, $decimals
1101 if ($mergetable == false) {
1102 list($index_size, $index_unit) = PMA_Util::formatByteDown(
1103 $this->_showtable['Index_length'], $max_digits, $decimals
1106 // InnoDB returns a huge value in Data_free, do not use it
1107 if (! $is_innodb && isset($this->_showtable['Data_free'])
1108 && $this->_showtable['Data_free'] > 0
1110 list($free_size, $free_unit) = PMA_Util::formatByteDown(
1111 $this->_showtable['Data_free'], $max_digits, $decimals
1113 list($effect_size, $effect_unit) = PMA_Util::formatByteDown(
1114 $this->_showtable['Data_length']
1115 + $this->_showtable['Index_length']
1116 - $this->_showtable['Data_free'],
1117 $max_digits, $decimals
1119 } else {
1120 list($effect_size, $effect_unit) = PMA_Util::formatByteDown(
1121 $this->_showtable['Data_length']
1122 + $this->_showtable['Index_length'],
1123 $max_digits, $decimals
1126 list($tot_size, $tot_unit) = PMA_Util::formatByteDown(
1127 $this->_showtable['Data_length'] + $this->_showtable['Index_length'],
1128 $max_digits, $decimals
1130 if ($this->_table_info_num_rows > 0) {
1131 list($avg_size, $avg_unit) = PMA_Util::formatByteDown(
1132 ($this->_showtable['Data_length']
1133 + $this->_showtable['Index_length'])
1134 / $this->_showtable['Rows'],
1138 } else {
1139 $avg_size = $avg_unit = '';
1142 return Template::get('table/structure/display_table_stats')->render(
1143 array(
1144 'showtable' => $this->_showtable,
1145 'table_info_num_rows' => $this->_table_info_num_rows,
1146 'tbl_is_view' => $this->_tbl_is_view,
1147 'db_is_system_schema' => $this->_db_is_system_schema,
1148 'tbl_storage_engine' => $this->_tbl_storage_engine,
1149 'url_query' => $this->_url_query,
1150 'tbl_collation' => $this->_tbl_collation,
1151 'is_innodb' => $is_innodb,
1152 'mergetable' => $mergetable,
1153 'avg_size' => isset($avg_size) ? $avg_size : null,
1154 'avg_unit' => isset($avg_unit) ? $avg_unit : null,
1155 'data_size' => $data_size,
1156 'data_unit' => $data_unit,
1157 'index_size' => isset($index_size) ? $index_size : null,
1158 'index_unit' => isset($index_unit) ? $index_unit : null,
1159 'free_size' => isset($free_size) ? $free_size : null,
1160 'free_unit' => isset($free_unit) ? $free_unit : null,
1161 'effect_size' => $effect_size,
1162 'effect_unit' => $effect_unit,
1163 'tot_size' => $tot_size,
1164 'tot_unit' => $tot_unit
1170 * Gets table primary key
1172 * @return string
1174 protected function getKeyForTablePrimary()
1176 $this->dbi->selectDb($this->db);
1177 $result = $this->dbi->query(
1178 'SHOW KEYS FROM ' . PMA_Util::backquote($this->table) . ';'
1180 $primary = '';
1181 while ($row = $this->dbi->fetchAssoc($result)) {
1182 // Backups the list of primary keys
1183 if ($row['Key_name'] == 'PRIMARY') {
1184 $primary .= $row['Column_name'] . ', ';
1186 } // end while
1187 $this->dbi->freeResult($result);
1189 return $primary;
1193 * Get List of information for Submit Mult
1195 * @param string $submit_mult mult_submit type
1196 * @param array $selected the selected columns
1197 * @param string $action action type
1199 * @return array
1201 protected function getDataForSubmitMult($submit_mult, $selected, $action)
1203 $what = null;
1204 $query_type = null;
1205 $is_unset_submit_mult = false;
1206 $mult_btn = null;
1207 $centralColsError = null;
1208 switch ($submit_mult) {
1209 case 'drop':
1210 $what = 'drop_fld';
1211 break;
1212 case 'primary':
1213 // Gets table primary key
1214 $primary = $this->getKeyForTablePrimary();
1215 if (empty($primary)) {
1216 // no primary key, so we can safely create new
1217 $is_unset_submit_mult = true;
1218 $query_type = 'primary_fld';
1219 $mult_btn = __('Yes');
1220 } else {
1221 // primary key exists, so lets as user
1222 $what = 'primary_fld';
1224 break;
1225 case 'index':
1226 $is_unset_submit_mult = true;
1227 $query_type = 'index_fld';
1228 $mult_btn = __('Yes');
1229 break;
1230 case 'unique':
1231 $is_unset_submit_mult = true;
1232 $query_type = 'unique_fld';
1233 $mult_btn = __('Yes');
1234 break;
1235 case 'spatial':
1236 $is_unset_submit_mult = true;
1237 $query_type = 'spatial_fld';
1238 $mult_btn = __('Yes');
1239 break;
1240 case 'ftext':
1241 $is_unset_submit_mult = true;
1242 $query_type = 'fulltext_fld';
1243 $mult_btn = __('Yes');
1244 break;
1245 case 'add_to_central_columns':
1246 include_once 'libraries/central_columns.lib.php';
1247 $centralColsError = PMA_syncUniqueColumns($selected, false);
1248 break;
1249 case 'remove_from_central_columns':
1250 include_once 'libraries/central_columns.lib.php';
1251 $centralColsError = PMA_deleteColumnsFromList($selected, false);
1252 break;
1253 case 'change':
1254 $this->displayHtmlForColumnChange($selected, $action);
1255 // execution stops here but PMA_Response correctly finishes
1256 // the rendering
1257 exit;
1258 case 'browse':
1259 // this should already be handled by tbl_structure.php
1262 return array(
1263 $what, $query_type, $is_unset_submit_mult, $mult_btn,
1264 $centralColsError