Merge remote-tracking branch 'origin/QA_4_0' into QA_4_0
[phpmyadmin.git] / tbl_structure.php
blob5e8e7ced5c59585f3f508a546f104da454c92320
1 <?php
2 /* vim: set expandtab sw=4 ts=4 sts=4: */
3 /**
4 * Displays table structure infos like fields/columns, indexes, size, rows
5 * and allows manipulation of indexes and columns/fields
7 * @package PhpMyAdmin
8 */
10 /**
13 require_once 'libraries/common.inc.php';
14 require_once 'libraries/mysql_charsets.lib.php';
16 /**
17 * Function implementations for this script
19 require_once 'libraries/structure.lib.php';
20 require_once 'libraries/index.lib.php';
22 $response = PMA_Response::getInstance();
23 $header = $response->getHeader();
24 $scripts = $header->getScripts();
25 $scripts->addFile('tbl_structure.js');
26 $scripts->addFile('indexes.js');
28 /**
29 * Handle column moving
31 if (isset($_REQUEST['move_columns'])
32 && is_array($_REQUEST['move_columns'])
33 && $response->isAjax()
34 ) {
35 PMA_moveColumns($db, $table);
36 exit;
39 /**
40 * A click on Change has been made for one column
42 if (isset($_REQUEST['change_column'])) {
43 PMA_displayHtmlForColumnChange($db, $table, null, 'tbl_structure.php');
44 exit;
46 /**
47 * Modifications have been submitted -> updates the table
49 if (isset($_REQUEST['do_save_data'])) {
50 $regenerate = PMA_updateColumns($db, $table);
51 if ($regenerate) {
52 // This happens when updating failed
53 // @todo: do something appropriate
54 } else {
55 // continue to show the table's structure
56 unset($_REQUEST['selected']);
57 unset($_REQUEST['true_selected']);
61 /**
62 * handle multiple field commands if required
64 * submit_mult_*_x comes from IE if <input type="img" ...> is used
66 if (isset($_REQUEST['submit_mult_change_x'])) {
67 $submit_mult = 'change';
68 } elseif (isset($_REQUEST['submit_mult_drop_x'])) {
69 $submit_mult = 'drop';
70 } elseif (isset($_REQUEST['submit_mult_primary_x'])) {
71 $submit_mult = 'primary';
72 } elseif (isset($_REQUEST['submit_mult_index_x'])) {
73 $submit_mult = 'index';
74 } elseif (isset($_REQUEST['submit_mult_unique_x'])) {
75 $submit_mult = 'unique';
76 } elseif (isset($_REQUEST['submit_mult_spatial_x'])) {
77 $submit_mult = 'spatial';
78 } elseif (isset($_REQUEST['submit_mult_fulltext_x'])) {
79 $submit_mult = 'ftext';
80 } elseif (isset($_REQUEST['submit_mult_browse_x'])) {
81 $submit_mult = 'browse';
82 } elseif (isset($_REQUEST['submit_mult'])) {
83 $submit_mult = $_REQUEST['submit_mult'];
84 } elseif (isset($_REQUEST['mult_btn']) && $_REQUEST['mult_btn'] == __('Yes')) {
85 $submit_mult = 'row_delete';
86 if (isset($_REQUEST['selected'])) {
87 $_REQUEST['selected_fld'] = $_REQUEST['selected'];
90 if (! empty($submit_mult)) {
91 if (isset($_REQUEST['selected_fld'])) {
92 $err_url = 'tbl_structure.php?' . PMA_generate_common_url($db, $table);
93 if ($submit_mult == 'browse') {
94 // browsing the table displaying only selected fields/columns
95 $GLOBALS['active_page'] = 'sql.php';
96 $sql_query = '';
97 foreach ($_REQUEST['selected_fld'] as $idx => $sval) {
98 if ($sql_query == '') {
99 $sql_query .= 'SELECT ' . PMA_Util::backquote($sval);
100 } else {
101 $sql_query .= ', ' . PMA_Util::backquote($sval);
104 $sql_query .= ' FROM ' . PMA_Util::backquote($db)
105 . '.' . PMA_Util::backquote($table);
106 include 'sql.php';
107 exit;
108 } else {
109 // handle multiple field commands
110 // handle confirmation of deleting multiple fields/columns
111 $action = 'tbl_structure.php';
112 include 'libraries/mult_submits.inc.php';
114 * if $submit_mult == 'change', execution will have stopped
115 * at this point
118 if (empty($message)) {
119 $message = PMA_Message::success();
122 } else {
123 $response = PMA_Response::getInstance();
124 $response->isSuccess(false);
125 $response->addJSON('message', __('No column selected.'));
130 * Gets the relation settings
132 $cfgRelation = PMA_getRelationsParam();
135 * Runs common work
137 require_once 'libraries/tbl_common.inc.php';
138 $url_query .= '&amp;goto=tbl_structure.php&amp;back=tbl_structure.php';
139 $url_params['goto'] = 'tbl_structure.php';
140 $url_params['back'] = 'tbl_structure.php';
142 // Check column names for MySQL reserved words
143 if ($cfg['ReservedWordDisableWarning'] === false) {
144 $pma_table = new PMA_Table($table, $db);
145 $columns = $pma_table->getReservedColumnNames();
146 if (! empty($columns)) {
147 foreach ($columns as $column) {
148 $msg = PMA_message::notice(
149 __('The column name \'%s\' is a MySQL reserved keyword.')
151 $msg->addParam($column);
152 $response->addHTML($msg);
158 * Prepares the table structure display
163 * Gets tables informations
165 require_once 'libraries/tbl_info.inc.php';
167 require_once 'libraries/Index.class.php';
169 // 2. Gets table keys and retains them
170 // @todo should be: $server->db($db)->table($table)->primary()
171 $primary = PMA_Index::getPrimary($table, $db);
173 $columns_with_unique_index = array();
174 foreach (PMA_Index::getFromTable($table, $db) as $index) {
175 if ($index->isUnique() && $index->getChoice() == 'UNIQUE') {
176 $columns = $index->getColumns();
177 foreach ($columns as $column_name => $dummy) {
178 $columns_with_unique_index[$column_name] = 1;
182 unset($index, $columns, $column_name, $dummy);
184 // 3. Get fields
185 $fields = (array) PMA_DBI_get_columns($db, $table, null, true);
187 // Get more complete field information
188 // For now, this is done just for MySQL 4.1.2+ new TIMESTAMP options
189 // but later, if the analyser returns more information, it
190 // could be executed for any MySQL version and replace
191 // the info given by SHOW FULL COLUMNS FROM.
193 // We also need this to correctly learn if a TIMESTAMP is NOT NULL, since
194 // SHOW FULL COLUMNS or INFORMATION_SCHEMA incorrectly says NULL
195 // and SHOW CREATE TABLE says NOT NULL (tested
196 // in MySQL 4.0.25 and 5.0.21, http://bugs.mysql.com/20910).
198 $show_create_table = PMA_DBI_fetch_value(
199 'SHOW CREATE TABLE ' . PMA_Util::backquote($db) . '.'
200 . PMA_Util::backquote($table),
201 0, 1
203 $analyzed_sql = PMA_SQP_analyze(PMA_SQP_parse($show_create_table));
206 * prepare table infos
208 // action titles (image or string)
209 $titles = PMA_getActionTitlesArray();
211 // hidden action titles (image and string)
212 $hidden_titles = PMA_getHiddenTitlesArray();
215 * Displays the table structure ('show table' works correct since 3.23.03)
217 /* TABLE INFORMATION */
218 // table header
221 $HideStructureActions = '';
222 if (in_array(
223 $GLOBALS['cfg']['ActionLinksMode'],
224 array('text', 'both')
226 && $GLOBALS['cfg']['HideStructureActions'] === true
228 $HideStructureActions .= ' HideStructureActions';
231 $html_form = '<form method="post" action="tbl_structure.php" name="fieldsForm" '
232 . 'id="fieldsForm" class="ajax' . $HideStructureActions . '">';
234 $response->addHTML($html_form);
235 $response->addHTML(PMA_generate_common_hidden_inputs($db, $table));
237 $tabletype = '<input type="hidden" name="table_type" value=';
238 if ($db_is_information_schema) {
239 $tabletype .= '"information_schema" />';
240 } else if ($tbl_is_view) {
241 $tabletype .= '"view" />';
242 } else {
243 $tabletype .= '"table" />';
245 $response->addHTML($tabletype);
247 $tablestructure = '<table id="tablestructure" class="data">';
248 $response->addHTML($tablestructure);
251 $response->addHTML(
252 PMA_getHtmlForTableStructureHeader(
253 $db_is_information_schema,
254 $tbl_is_view
258 $response->addHTML('<tbody>');
260 // table body
262 // prepare comments
263 $comments_map = array();
264 $mime_map = array();
266 if ($GLOBALS['cfg']['ShowPropertyComments']) {
267 include_once 'libraries/transformations.lib.php';
268 $comments_map = PMA_getComments($db, $table);
269 if ($cfgRelation['mimework'] && $cfg['BrowseMIME']) {
270 $mime_map = PMA_getMIME($db, $table, true);
274 $rownum = 0;
275 $columns_list = array();
276 $save_row = array();
277 $odd_row = true;
278 foreach ($fields as $row) {
279 $save_row[] = $row;
280 $rownum++;
281 $columns_list[] = $row['Field'];
283 $type = $row['Type'];
284 $extracted_columnspec = PMA_Util::extractColumnSpec($row['Type']);
286 if ('set' == $extracted_columnspec['type']
287 || 'enum' == $extracted_columnspec['type']
289 $type_nowrap = '';
290 } else {
291 $type_nowrap = ' class="nowrap"';
293 $type = $extracted_columnspec['print_type'];
294 if (empty($type)) {
295 $type = ' ';
298 $field_charset = '';
299 if ($extracted_columnspec['can_contain_collation']
300 && ! empty($row['Collation'])
302 $field_charset = $row['Collation'];
305 // Display basic mimetype [MIME]
306 if ($cfgRelation['commwork']
307 && $cfgRelation['mimework']
308 && $cfg['BrowseMIME']
309 && isset($mime_map[$row['Field']]['mimetype'])
311 $type_mime = '<br />MIME: '
312 . str_replace('_', '/', $mime_map[$row['Field']]['mimetype']);
313 } else {
314 $type_mime = '';
317 $attribute = $extracted_columnspec['attribute'];
319 // prepare a common variable to reuse below; however,
320 // in case of a VIEW, $analyzed_sql[0]['create_table_fields'] is empty
321 if (isset($analyzed_sql[0]['create_table_fields'][$row['Field']])) {
322 $tempField = $analyzed_sql[0]['create_table_fields'][$row['Field']];
323 } else {
324 $tempField = array();
327 // MySQL 4.1.2+ TIMESTAMP options
328 // (if on_update_current_timestamp is set, then it's TRUE)
329 if (isset($tempField['on_update_current_timestamp'])) {
330 $attribute = 'on update CURRENT_TIMESTAMP';
333 // here, we have a TIMESTAMP that SHOW FULL COLUMNS reports as having the
334 // NULL attribute, but SHOW CREATE TABLE says the contrary. Believe
335 // the latter.
336 if (! empty($tempField['type'])
337 && $tempField['type'] == 'TIMESTAMP'
338 && $tempField['timestamp_not_null']
340 $row['Null'] = '';
344 if (! isset($row['Default'])) {
345 if ($row['Null'] == 'YES') {
346 $row['Default'] = '<i>NULL</i>';
348 } else {
349 $row['Default'] = htmlspecialchars($row['Default']);
352 $field_encoded = urlencode($row['Field']);
353 $field_name = htmlspecialchars($row['Field']);
354 $displayed_field_name = $field_name;
356 // underline commented fields and display a hover-title (CSS only)
358 if (isset($comments_map[$row['Field']])) {
359 $displayed_field_name = '<span class="commented_column" title="'
360 . htmlspecialchars($comments_map[$row['Field']]) . '">'
361 . $field_name . '</span>';
364 if ($primary && $primary->hasColumn($field_name)) {
365 $displayed_field_name = '<u>' . $field_name . '</u>';
367 $response->addHTML(
368 '<tr class="' . ($odd_row ? 'odd': 'even') . '">'
370 $odd_row = !$odd_row;
372 $response->addHTML(
373 PMA_getHtmlTableStructureRow(
374 $row, $rownum, $displayed_field_name,
375 $type_nowrap, $extracted_columnspec, $type_mime,
376 $field_charset, $attribute, $tbl_is_view,
377 $db_is_information_schema, $url_query, $field_encoded, $titles, $table
381 if (! $tbl_is_view && ! $db_is_information_schema) {
382 $response->addHTML(
383 PMA_getHtmlForActionsInTableStructure(
384 $type, $tbl_storage_engine, $primary,
385 $field_name, $url_query, $titles, $row, $rownum,
386 $hidden_titles, $columns_with_unique_index
389 } // end if (! $tbl_is_view && ! $db_is_information_schema)
391 $response->addHTML('</tr>');
393 unset($field_charset);
394 } // end foreach
396 $response->addHTML('</tbody></table>');
398 $response->addHTML(
399 PMA_getHtmlForCheckAllTableColumn(
400 $pmaThemeImage, $text_dir, $tbl_is_view,
401 $db_is_information_schema, $tbl_storage_engine
405 $response->addHTML(
406 '</form><hr />'
408 $response->addHTML(
409 PMA_getHtmlDivForMoveColumnsDialog()
413 * Work on the table
416 if ($tbl_is_view) {
417 $response->addHTML(PMA_getHtmlForEditView($url_params));
419 $response->addHTML(
420 PMA_getHtmlForOptionalActionLinks(
421 $url_query, $tbl_is_view, $db_is_information_schema,
422 $tbl_storage_engine, $cfgRelation
426 if (! $tbl_is_view && ! $db_is_information_schema) {
427 $response->addHTML('<br />');
428 $response->addHTML(PMA_getHtmlForAddColumn($columns_list));
429 $response->addHTML(
430 '<div id="index_div" class="ajax" >'
435 * Displays indexes
438 if (! $tbl_is_view
439 && ! $db_is_information_schema
440 && 'ARCHIVE' != $tbl_storage_engine
442 //return the list of index
443 $response->addJSON('indexes_list', PMA_Index::getView($GLOBALS['table'], $GLOBALS['db']));
444 $response->addHTML(PMA_getHtmlForDisplayIndexes());
448 * Displays Space usage and row statistics
450 // BEGIN - Calc Table Space
451 // Get valid statistics whatever is the table type
452 if ($cfg['ShowStats']) {
453 //get table stats in HTML format
454 $tablestats = PMA_getHtmlForDisplayTableStats(
455 $showtable, $table_info_num_rows, $tbl_is_view,
456 $db_is_information_schema, $tbl_storage_engine,
457 $url_query, $tbl_collation
459 //returning the response in JSON format to be used by Ajax
460 $response->addJSON('tableStat', $tablestats);
461 $response->addHTML($tablestats);
463 // END - Calc Table Space
465 $response->addHTML(
466 '<div class="clearfloat"></div>'