/** * Validate whether the table exists. * * @return void */ protected function validateTableAndLoadFields() { $sql = 'DESCRIBE ' . PMA_Util::backquote($this->tableName); $result = $GLOBALS['dbi']->tryQuery($sql, null, PMA_DatabaseInterface::QUERY_STORE); if (!$result || !$GLOBALS['dbi']->numRows($result)) { $this->showMissingTableError(); } if ($this->showKeys) { $indexes = PMA_Index::getFromTable($this->tableName, $this->db); $all_columns = array(); foreach ($indexes as $index) { $all_columns = array_merge($all_columns, array_flip(array_keys($index->getColumns()))); } $this->fields = array_keys($all_columns); } else { while ($row = $GLOBALS['dbi']->fetchRow($result)) { $this->fields[] = $row[0]; } } }
/** * Displays the headers of the results table * * @uses $_SESSION['tmp_user_values']['disp_direction'] * @uses $_SESSION['tmp_user_values']['repeat_cells'] * @uses $_SESSION['tmp_user_values']['max_rows'] * @uses $_SESSION['tmp_user_values']['display_text'] * @uses $_SESSION['tmp_user_values']['display_binary'] * @uses $_SESSION['tmp_user_values']['display_binary_as_hex'] * @param array which elements to display * @param array the list of fields properties * @param integer the total number of fields returned by the SQL query * @param array the analyzed query * * @return boolean $clause_is_unique * * @global string $db the database name * @global string $table the table name * @global string $goto the URL to go back in case of errors * @global string $sql_query the SQL query * @global integer $num_rows the total number of rows returned by the * SQL query * @global array $vertical_display informations used with vertical display * mode * * @access private * * @see PMA_displayTable() */ function PMA_displayTableHeaders(&$is_display, &$fields_meta, $fields_cnt = 0, $analyzed_sql = '', $sort_expression, $sort_expression_nodirection, $sort_direction) { global $db, $table, $goto; global $sql_query, $num_rows; global $vertical_display, $highlight_columns; if ($analyzed_sql == '') { $analyzed_sql = array(); } // can the result be sorted? if ($is_display['sort_lnk'] == '1') { // Just as fallback $unsorted_sql_query = $sql_query; if (isset($analyzed_sql[0]['unsorted_query'])) { $unsorted_sql_query = $analyzed_sql[0]['unsorted_query']; } // Handles the case of multiple clicks on a column's header // which would add many spaces before "ORDER BY" in the // generated query. $unsorted_sql_query = trim($unsorted_sql_query); // sorting by indexes, only if it makes sense (only one table ref) if (isset($analyzed_sql) && isset($analyzed_sql[0]) && isset($analyzed_sql[0]['querytype']) && $analyzed_sql[0]['querytype'] == 'SELECT' && isset($analyzed_sql[0]['table_ref']) && count($analyzed_sql[0]['table_ref']) == 1) { // grab indexes data: $indexes = PMA_Index::getFromTable($table, $db); // do we have any index? if ($indexes) { if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontal' || $_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped') { $span = $fields_cnt; if ($is_display['edit_lnk'] != 'nn') { $span++; } if ($is_display['del_lnk'] != 'nn') { $span++; } if ($is_display['del_lnk'] != 'kp' && $is_display['del_lnk'] != 'nn') { $span++; } } else { $span = $num_rows + floor($num_rows / $_SESSION['tmp_user_values']['repeat_cells']) + 1; } echo '<form action="sql.php" method="post">' . "\n"; echo PMA_generate_common_hidden_inputs($db, $table); echo $GLOBALS['strSortByKey'] . ': <select name="sql_query" onchange="this.form.submit();">' . "\n"; $used_index = false; $local_order = isset($sort_expression) ? $sort_expression : ''; foreach ($indexes as $index) { $asc_sort = '`' . implode('` ASC, `', array_keys($index->getColumns())) . '` ASC'; $desc_sort = '`' . implode('` DESC, `', array_keys($index->getColumns())) . '` DESC'; $used_index = $used_index || $local_order == $asc_sort || $local_order == $desc_sort; echo '<option value="' . htmlspecialchars($unsorted_sql_query . ' ORDER BY ' . $asc_sort) . '"' . ($local_order == $asc_sort ? ' selected="selected"' : '') . '>' . htmlspecialchars($index->getName()) . ' (' . $GLOBALS['strAscending'] . ')</option>'; echo '<option value="' . htmlspecialchars($unsorted_sql_query . ' ORDER BY ' . $desc_sort) . '"' . ($local_order == $desc_sort ? ' selected="selected"' : '') . '>' . htmlspecialchars($index->getName()) . ' (' . $GLOBALS['strDescending'] . ')</option>'; } echo '<option value="' . htmlspecialchars($unsorted_sql_query) . '"' . ($used_index ? '' : ' selected="selected"') . '>' . $GLOBALS['strNone'] . '</option>'; echo '</select>' . "\n"; echo '<noscript><input type="submit" value="' . $GLOBALS['strGo'] . '" /></noscript>'; echo '</form>' . "\n"; } } } $vertical_display['emptypre'] = 0; $vertical_display['emptyafter'] = 0; $vertical_display['textbtn'] = ''; // Display options (if we are not in print view) if (!(isset($GLOBALS['printview']) && $GLOBALS['printview'] == '1')) { echo '<form method="post" action="sql.php" name="displayOptionsForm" id="displayOptionsForm">'; $url_params = array('db' => $db, 'table' => $table, 'sql_query' => $sql_query, 'goto' => $goto, 'display_options_form' => 1); echo PMA_generate_common_hidden_inputs($url_params); echo '<br />'; PMA_generate_slider_effect('displayoptions', $GLOBALS['strOptions']); echo '<fieldset>'; echo '<div class="formelement">'; $choices = array('P' => $GLOBALS['strPartialText'], 'F' => $GLOBALS['strFullText']); PMA_display_html_radio('display_text', $choices, $_SESSION['tmp_user_values']['display_text']); echo '</div>'; // prepare full/partial text button or link if ($_SESSION['tmp_user_values']['display_text'] == 'F') { // currently in fulltext mode so show the opposite link $tmp_image_file = $GLOBALS['pmaThemeImage'] . 's_partialtext.png'; $tmp_txt = $GLOBALS['strPartialText']; $url_params['display_text'] = 'P'; } else { $tmp_image_file = $GLOBALS['pmaThemeImage'] . 's_fulltext.png'; $tmp_txt = $GLOBALS['strFullText']; $url_params['display_text'] = 'F'; } $tmp_image = '<img class="fulltext" width="50" height="20" src="' . $tmp_image_file . '" alt="' . $tmp_txt . '" title="' . $tmp_txt . '" />'; $tmp_url = 'sql.php' . PMA_generate_common_url($url_params); $full_or_partial_text_link = PMA_linkOrButton($tmp_url, $tmp_image, array(), false); unset($tmp_image_file, $tmp_txt, $tmp_url, $tmp_image); if ($GLOBALS['cfgRelation']['relwork'] && $GLOBALS['cfgRelation']['displaywork']) { echo '<div class="formelement">'; $choices = array('K' => $GLOBALS['strRelationalKey'], 'D' => $GLOBALS['strRelationalDisplayField']); PMA_display_html_radio('relational_display', $choices, $_SESSION['tmp_user_values']['relational_display']); echo '</div>'; } echo '<div class="formelement">'; PMA_display_html_checkbox('display_binary', $GLOBALS['strShowBinaryContents'], !empty($_SESSION['tmp_user_values']['display_binary']), false); echo '<br />'; PMA_display_html_checkbox('display_blob', $GLOBALS['strShowBLOBContents'], !empty($_SESSION['tmp_user_values']['display_blob']), false); echo '<br />'; PMA_display_html_checkbox('display_binary_as_hex', $GLOBALS['strShowBinaryContentsAsHex'], !empty($_SESSION['tmp_user_values']['display_binary_as_hex']), false); echo '</div>'; // I would have preferred to name this "display_transformation". // This is the only way I found to be able to keep this setting sticky // per SQL query, and at the same time have a default that displays // the transformations. echo '<div class="formelement">'; PMA_display_html_checkbox('hide_transformation', $GLOBALS['strHide'] . ' ' . $GLOBALS['strMIME_transformation'], !empty($_SESSION['tmp_user_values']['hide_transformation']), false); echo '</div>'; echo '<div class="clearfloat"></div>'; echo '</fieldset>'; echo '<fieldset class="tblFooters">'; echo '<input type="submit" value="' . $GLOBALS['strGo'] . '" />'; echo '</fieldset>'; echo '</div>'; echo '</form>'; } // Start of form for multi-rows edit/delete/export if ($is_display['del_lnk'] == 'dr' || $is_display['del_lnk'] == 'kp') { echo '<form method="post" action="tbl_row_action.php" name="rowsDeleteForm" id="rowsDeleteForm">' . "\n"; echo PMA_generate_common_hidden_inputs($db, $table, 1); echo '<input type="hidden" name="goto" value="sql.php" />' . "\n"; } echo '<table id="table_results" class="data">' . "\n"; if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontal' || $_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped') { echo '<thead><tr>' . "\n"; } // 1. Displays the full/partial text button (part 1)... if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontal' || $_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped') { $colspan = $is_display['edit_lnk'] != 'nn' && $is_display['del_lnk'] != 'nn' ? ' colspan="3"' : ''; } else { $rowspan = $is_display['edit_lnk'] != 'nn' && $is_display['del_lnk'] != 'nn' ? ' rowspan="3"' : ''; } // ... before the result table if ($is_display['edit_lnk'] == 'nn' && $is_display['del_lnk'] == 'nn' && $is_display['text_btn'] == '1') { $vertical_display['emptypre'] = $is_display['edit_lnk'] != 'nn' && $is_display['del_lnk'] != 'nn' ? 3 : 0; if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontal' || $_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped') { ?> <th colspan="<?php echo $fields_cnt; ?> "></th> </tr> <tr> <?php } else { ?> <tr> <th colspan="<?php echo $num_rows + floor($num_rows / $_SESSION['tmp_user_values']['repeat_cells']) + 1; ?> "></th> </tr> <?php } // end vertical mode } elseif ($GLOBALS['cfg']['ModifyDeleteAtLeft'] && $is_display['text_btn'] == '1') { $vertical_display['emptypre'] = $is_display['edit_lnk'] != 'nn' && $is_display['del_lnk'] != 'nn' ? 3 : 0; if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontal' || $_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped') { ?> <th <?php echo $colspan; ?> ><?php echo $full_or_partial_text_link; ?> </th> <?php } else { $vertical_display['textbtn'] = ' <th ' . $rowspan . ' valign="middle">' . "\n" . ' ' . "\n" . ' </th>' . "\n"; } // end vertical mode } elseif ($GLOBALS['cfg']['ModifyDeleteAtLeft'] && ($is_display['edit_lnk'] != 'nn' || $is_display['del_lnk'] != 'nn')) { $vertical_display['emptypre'] = $is_display['edit_lnk'] != 'nn' && $is_display['del_lnk'] != 'nn' ? 3 : 0; if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontal' || $_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped') { ?> <td<?php echo $colspan; ?> ></td> <?php } else { $vertical_display['textbtn'] = ' <td' . $rowspan . '></td>' . "\n"; } // end vertical mode } // 2. Displays the fields' name // 2.0 If sorting links should be used, checks if the query is a "JOIN" // statement (see 2.1.3) // 2.0.1 Prepare Display column comments if enabled ($GLOBALS['cfg']['ShowBrowseComments']). // Do not show comments, if using horizontalflipped mode, because of space usage if ($GLOBALS['cfg']['ShowBrowseComments'] && $_SESSION['tmp_user_values']['disp_direction'] != 'horizontalflipped') { $comments_map = array(); if (isset($analyzed_sql[0]) && is_array($analyzed_sql[0])) { foreach ($analyzed_sql[0]['table_ref'] as $tbl) { $tb = $tbl['table_true_name']; $comments_map[$tb] = PMA_getComments($db, $tb); unset($tb); } } } if ($GLOBALS['cfgRelation']['commwork'] && $GLOBALS['cfgRelation']['mimework'] && $GLOBALS['cfg']['BrowseMIME'] && !$_SESSION['tmp_user_values']['hide_transformation']) { require_once './libraries/transformations.lib.php'; $GLOBALS['mime_map'] = PMA_getMIME($db, $table); } if ($is_display['sort_lnk'] == '1') { $select_expr = $analyzed_sql[0]['select_expr_clause']; } // garvin: See if we have to highlight any header fields of a WHERE query. // Uses SQL-Parser results. $highlight_columns = array(); if (isset($analyzed_sql) && isset($analyzed_sql[0]) && isset($analyzed_sql[0]['where_clause_identifiers'])) { $wi = 0; if (isset($analyzed_sql[0]['where_clause_identifiers']) && is_array($analyzed_sql[0]['where_clause_identifiers'])) { foreach ($analyzed_sql[0]['where_clause_identifiers'] as $wci_nr => $wci) { $highlight_columns[$wci] = 'true'; } } } for ($i = 0; $i < $fields_cnt; $i++) { // garvin: See if this column should get highlight because it's used in the // where-query. if (isset($highlight_columns[$fields_meta[$i]->name]) || isset($highlight_columns[PMA_backquote($fields_meta[$i]->name)])) { $condition_field = true; } else { $condition_field = false; } // 2.0 Prepare comment-HTML-wrappers for each row, if defined/enabled. if (isset($comments_map) && isset($comments_map[$fields_meta[$i]->table]) && isset($comments_map[$fields_meta[$i]->table][$fields_meta[$i]->name])) { $comments = '<span class="tblcomment">' . htmlspecialchars($comments_map[$fields_meta[$i]->table][$fields_meta[$i]->name]) . '</span>'; } else { $comments = ''; } // 2.1 Results can be sorted if ($is_display['sort_lnk'] == '1') { // 2.1.1 Checks if the table name is required; it's the case // for a query with a "JOIN" statement and if the column // isn't aliased, or in queries like // SELECT `1`.`master_field` , `2`.`master_field` // FROM `PMA_relation` AS `1` , `PMA_relation` AS `2` if (isset($fields_meta[$i]->table) && strlen($fields_meta[$i]->table)) { $sort_tbl = PMA_backquote($fields_meta[$i]->table) . '.'; } else { $sort_tbl = ''; } // 2.1.2 Checks if the current column is used to sort the // results // the orgname member does not exist for all MySQL versions // but if found, it's the one on which to sort $name_to_use_in_sort = $fields_meta[$i]->name; if (isset($fields_meta[$i]->orgname) && strlen($fields_meta[$i]->orgname)) { $name_to_use_in_sort = $fields_meta[$i]->orgname; } // $name_to_use_in_sort might contain a space due to // formatting of function expressions like "COUNT(name )" // so we remove the space in this situation $name_to_use_in_sort = str_replace(' )', ')', $name_to_use_in_sort); if (empty($sort_expression)) { $is_in_sort = false; } else { // Field name may be preceded by a space, or any number // of characters followed by a dot (tablename.fieldname) // so do a direct comparison for the sort expression; // this avoids problems with queries like // "SELECT id, count(id)..." and clicking to sort // on id or on count(id). // Another query to test this: // SELECT p.*, FROM_UNIXTIME(p.temps) FROM mytable AS p // (and try clicking on each column's header twice) if (!empty($sort_tbl) && strpos($sort_expression_nodirection, $sort_tbl) === false && strpos($sort_expression_nodirection, '(') === false) { $sort_expression_nodirection = $sort_tbl . $sort_expression_nodirection; } $is_in_sort = str_replace('`', '', $sort_tbl) . $name_to_use_in_sort == str_replace('`', '', $sort_expression_nodirection) ? true : false; } // 2.1.3 Check the field name for a bracket. // If it contains one, it's probably a function column // like 'COUNT(`field`)' if (strpos($name_to_use_in_sort, '(') !== false) { $sort_order = ' ORDER BY ' . $name_to_use_in_sort . ' '; } else { $sort_order = ' ORDER BY ' . $sort_tbl . PMA_backquote($name_to_use_in_sort) . ' '; } unset($name_to_use_in_sort); // 2.1.4 Do define the sorting URL if (!$is_in_sort) { // loic1: patch #455484 ("Smart" order) $GLOBALS['cfg']['Order'] = strtoupper($GLOBALS['cfg']['Order']); if ($GLOBALS['cfg']['Order'] === 'SMART') { $sort_order .= preg_match('@time|date@i', $fields_meta[$i]->type) ? 'DESC' : 'ASC'; } else { $sort_order .= $GLOBALS['cfg']['Order']; } $order_img = ''; } elseif ('DESC' == $sort_direction) { $sort_order .= ' ASC'; $order_img = ' <img class="icon" src="' . $GLOBALS['pmaThemeImage'] . 's_desc.png" width="11" height="9" alt="' . $GLOBALS['strDescending'] . '" title="' . $GLOBALS['strDescending'] . '" id="soimg' . $i . '" />'; } else { $sort_order .= ' DESC'; $order_img = ' <img class="icon" src="' . $GLOBALS['pmaThemeImage'] . 's_asc.png" width="11" height="9" alt="' . $GLOBALS['strAscending'] . '" title="' . $GLOBALS['strAscending'] . '" id="soimg' . $i . '" />'; } if (preg_match('@(.*)([[:space:]](LIMIT (.*)|PROCEDURE (.*)|FOR UPDATE|LOCK IN SHARE MODE))@i', $unsorted_sql_query, $regs3)) { $sorted_sql_query = $regs3[1] . $sort_order . $regs3[2]; } else { $sorted_sql_query = $unsorted_sql_query . $sort_order; } $_url_params = array('db' => $db, 'table' => $table, 'sql_query' => $sorted_sql_query); $order_url = 'sql.php' . PMA_generate_common_url($_url_params); // 2.1.5 Displays the sorting URL // added 20004-06-09: Michael Keck <*****@*****.**> // enable sort order swapping for image $order_link_params = array(); if (isset($order_img) && $order_img != '') { if (strstr($order_img, 'asc')) { $order_link_params['onmouseover'] = 'if(document.getElementById(\'soimg' . $i . '\')){ document.getElementById(\'soimg' . $i . '\').src=\'' . $GLOBALS['pmaThemeImage'] . 's_desc.png\'; }'; $order_link_params['onmouseout'] = 'if(document.getElementById(\'soimg' . $i . '\')){ document.getElementById(\'soimg' . $i . '\').src=\'' . $GLOBALS['pmaThemeImage'] . 's_asc.png\'; }'; } elseif (strstr($order_img, 'desc')) { $order_link_params['onmouseover'] = 'if(document.getElementById(\'soimg' . $i . '\')){ document.getElementById(\'soimg' . $i . '\').src=\'' . $GLOBALS['pmaThemeImage'] . 's_asc.png\'; }'; $order_link_params['onmouseout'] = 'if(document.getElementById(\'soimg' . $i . '\')){ document.getElementById(\'soimg' . $i . '\').src=\'' . $GLOBALS['pmaThemeImage'] . 's_desc.png\'; }'; } } if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped' && $GLOBALS['cfg']['HeaderFlipType'] == 'css') { $order_link_params['style'] = 'direction: ltr; writing-mode: tb-rl;'; } $order_link_params['title'] = $GLOBALS['strSort']; $order_link_content = $_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped' && $GLOBALS['cfg']['HeaderFlipType'] == 'fake' ? PMA_flipstring(htmlspecialchars($fields_meta[$i]->name), "<br />\n") : htmlspecialchars($fields_meta[$i]->name); $order_link = PMA_linkOrButton($order_url, $order_link_content . $order_img, $order_link_params, false, true); if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontal' || $_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped') { echo '<th'; if ($condition_field) { echo ' class="condition"'; } if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped') { echo ' valign="bottom"'; } echo '>' . $order_link . $comments . '</th>'; } $vertical_display['desc'][] = ' <th ' . ($condition_field ? ' class="condition"' : '') . '>' . "\n" . $order_link . $comments . ' </th>' . "\n"; } else { if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontal' || $_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped') { echo '<th'; if ($condition_field) { echo ' class="condition"'; } if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped') { echo ' valign="bottom"'; } if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped' && $GLOBALS['cfg']['HeaderFlipType'] == 'css') { echo ' style="direction: ltr; writing-mode: tb-rl;"'; } echo '>'; if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped' && $GLOBALS['cfg']['HeaderFlipType'] == 'fake') { echo PMA_flipstring(htmlspecialchars($fields_meta[$i]->name), '<br />'); } else { echo htmlspecialchars($fields_meta[$i]->name); } echo "\n" . $comments . '</th>'; } $vertical_display['desc'][] = ' <th ' . ($condition_field ? ' class="condition"' : '') . '>' . "\n" . ' ' . htmlspecialchars($fields_meta[$i]->name) . "\n" . $comments . ' </th>'; } // end else (2.2) } // end for // 3. Displays the needed checkboxes at the right // column of the result table header if possible and required... if ($GLOBALS['cfg']['ModifyDeleteAtRight'] && ($is_display['edit_lnk'] != 'nn' || $is_display['del_lnk'] != 'nn') && $is_display['text_btn'] == '1') { $vertical_display['emptyafter'] = $is_display['edit_lnk'] != 'nn' && $is_display['del_lnk'] != 'nn' ? 3 : 1; if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontal' || $_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped') { echo "\n"; ?> <th <?php echo $colspan; ?> ><?php echo $full_or_partial_text_link; ?> </th> <?php } else { $vertical_display['textbtn'] = ' <th ' . $rowspan . ' valign="middle">' . "\n" . ' ' . "\n" . ' </th>' . "\n"; } // end vertical mode } elseif ($GLOBALS['cfg']['ModifyDeleteAtRight'] && ($is_display['edit_lnk'] == 'nn' && $is_display['del_lnk'] == 'nn') && !$GLOBALS['is_header_sent']) { $vertical_display['emptyafter'] = $is_display['edit_lnk'] != 'nn' && $is_display['del_lnk'] != 'nn' ? 3 : 1; if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontal' || $_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped') { echo "\n"; ?> <td<?php echo $colspan; ?> ></td> <?php } else { $vertical_display['textbtn'] = ' <td' . $rowspan . '></td>' . "\n"; } // end vertical mode } if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontal' || $_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped') { ?> </tr> </thead> <?php } return true; }
/** * Returns all indices * * @param bool $unique_only whether to include only unique ones * * @return array indices */ function PMA_getAllKeys($unique_only = false) { include_once './libraries/Index.class.php'; $keys = array(); foreach ($GLOBALS['PMD']['TABLE_NAME_SMALL'] as $I => $table) { $schema = $GLOBALS['PMD']['OWNER'][$I]; // for now, take into account only the first index segment foreach (PMA_Index::getFromTable($table, $schema) as $index) { if ($unique_only && !$index->isUnique()) { continue; } $columns = $index->getColumns(); foreach ($columns as $column_name => $dummy) { $keys[$schema . '.' . $table . '.' . $column_name] = 1; } } } return $keys; }
/** * Prepare unsorted sql query and sort by key drop down * * @param array $analyzed_sql_results analyzed sql results * @param string $sort_expression sort expression * * @return array two element array - $unsorted_sql_query, $drop_down_html * * @access private * * @see _getTableHeaders() */ private function _getUnsortedSqlAndSortByKeyDropDown($analyzed_sql_results, $sort_expression) { $drop_down_html = ''; $unsorted_sql_query = SqlParser\Utils\Query::replaceClause($analyzed_sql_results['statement'], $analyzed_sql_results['parser']->list, 'ORDER BY', ''); // Data is sorted by indexes only if it there is only one table. if ($this->_isSelect($analyzed_sql_results)) { // grab indexes data: $indexes = PMA_Index::getFromTable($this->__get('table'), $this->__get('db')); // do we have any index? if (!empty($indexes)) { $drop_down_html = $this->_getSortByKeyDropDown($indexes, $sort_expression, $unsorted_sql_query); } } return array($unsorted_sql_query, $drop_down_html); }
*/ $columns = $GLOBALS['dbi']->getColumns($GLOBALS['db'], $GLOBALS['table']); /** * Displays the page */ $response->addHTML('<div id="boxContainer" data-box-width="300">'); /** * Order the table */ $hideOrderTable = false; // `ALTER TABLE ORDER BY` does not make sense for InnoDB tables that contain // a user-defined clustered index (PRIMARY KEY or NOT NULL UNIQUE index). // InnoDB always orders table rows according to such an index if one is present. if ($tbl_storage_engine == 'INNODB') { include_once 'libraries/Index.class.php'; $indexes = PMA_Index::getFromTable($GLOBALS['table'], $GLOBALS['db']); foreach ($indexes as $name => $idx) { if ($name == 'PRIMARY') { $hideOrderTable = true; break; } elseif (!$idx->getNonUnique()) { $notNull = true; foreach ($idx->getColumns() as $column) { if ($column->getNull()) { $notNull = false; break; } } if ($notNull) { $hideOrderTable = true; break;
/** * The "Table_Stats" constructor * * @param string $tableName The table name * @param integer $fontSize The font size * @param integer $pageNumber The current page number (from the * $cfg['Servers'][$i]['table_coords'] table) * @param integer &$sameWideWidth The max. with among tables * @param boolean $showKeys Whether to display keys or not * @param boolean $showInfo Whether to display table position or not * * @global object The current PDF document * @global array The relations settings * @global string The current db name * * @return void * * @see PMA_Schema_PDF, Table_Stats::Table_Stats_setWidth, * Table_Stats::Table_Stats_setHeight */ function __construct($tableName, $fontSize, $pageNumber, &$sameWideWidth, $showKeys = false, $showInfo = false) { global $pdf, $cfgRelation, $db; $this->_tableName = $tableName; $sql = 'DESCRIBE ' . PMA_backquote($tableName); $result = PMA_DBI_try_query($sql, null, PMA_DBI_QUERY_STORE); if (!$result || !PMA_DBI_num_rows($result)) { $pdf->Error(sprintf(__('The %s table doesn\'t exist!'), $tableName)); } // load fields //check to see if it will load all fields or only the foreign keys if ($showKeys) { $indexes = PMA_Index::getFromTable($this->_tableName, $db); $all_columns = array(); foreach ($indexes as $index) { $all_columns = array_merge($all_columns, array_flip(array_keys($index->getColumns()))); } $this->fields = array_keys($all_columns); } else { while ($row = PMA_DBI_fetch_row($result)) { $this->fields[] = $row[0]; } } $this->_showInfo = $showInfo; $this->_setHeight(); /* * setWidth must me after setHeight, because title * can include table height which changes table width */ $this->_setWidth($fontSize); if ($sameWideWidth < $this->width) { $sameWideWidth = $this->width; } $sql = 'SELECT x, y FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' WHERE db_name = \'' . PMA_sqlAddSlashes($db) . '\'' . ' AND table_name = \'' . PMA_sqlAddSlashes($tableName) . '\'' . ' AND pdf_page_number = ' . $pageNumber; $result = PMA_query_as_controluser($sql, false, PMA_DBI_QUERY_STORE); if (!$result || !PMA_DBI_num_rows($result)) { $pdf->Error(sprintf(__('Please configure the coordinates for table %s'), $tableName)); } list($this->x, $this->y) = PMA_DBI_fetch_row($result); $this->x = (double) $this->x; $this->y = (double) $this->y; /* * displayfield */ $this->displayfield = PMA_getDisplayField($db, $tableName); /* * index */ $result = PMA_DBI_query('SHOW INDEX FROM ' . PMA_backquote($tableName) . ';', null, PMA_DBI_QUERY_STORE); if (PMA_DBI_num_rows($result) > 0) { while ($row = PMA_DBI_fetch_assoc($result)) { if ($row['Key_name'] == 'PRIMARY') { $this->primary[] = $row['Column_name']; } } } }
/** * Get columns with unique index * * @param string $db database name * @param string $table tablename * * @return array $columns_with_unique_index An array of columns with unique index, * with $column name as the array key */ function PMA_getColumnsWithUniqueIndex($db, $table) { $columns_with_unique_index = array(); foreach (PMA_Index::getFromTable($table, $db) as $index) { if ($index->isUnique() && $index->getChoice() == 'UNIQUE') { $columns = $index->getColumns(); foreach ($columns as $column_name => $dummy) { $columns_with_unique_index[$column_name] = 1; } } } return $columns_with_unique_index; }
/** * The "Table_Stats_Svg" constructor * * @param string $tableName The table name * @param string $font Font face * @param integer $fontSize The font size * @param integer $pageNumber Page number * @param integer &$same_wide_width The max. with among tables * @param boolean $showKeys Whether to display keys or not * @param boolean $showInfo Whether to display table position or not * * @global object $svg The current SVG image document * @global integer The current page number (from the * $cfg['Servers'][$i]['table_coords'] table) * @global array $cfgRelation The relations settings * @global string $db The current db name * * @access private * * @see PMA_SVG, Table_Stats_Svg::Table_Stats_setWidth, * Table_Stats_Svg::Table_Stats_setHeight */ function __construct($tableName, $font, $fontSize, $pageNumber, &$same_wide_width, $showKeys = false, $showInfo = false) { global $svg, $cfgRelation, $db; $this->_tableName = $tableName; $sql = 'DESCRIBE ' . PMA_Util::backquote($tableName); $result = $GLOBALS['dbi']->tryQuery($sql, null, PMA_DatabaseInterface::QUERY_STORE); if (!$result || !$GLOBALS['dbi']->numRows($result)) { $svg->dieSchema($pageNumber, "SVG", sprintf(__('The %s table doesn\'t exist!'), $tableName)); } /* * load fields * check to see if it will load all fields or only the foreign keys */ if ($showKeys) { $indexes = PMA_Index::getFromTable($this->_tableName, $db); $all_columns = array(); foreach ($indexes as $index) { $all_columns = array_merge($all_columns, array_flip(array_keys($index->getColumns()))); } $this->fields = array_keys($all_columns); } else { while ($row = $GLOBALS['dbi']->fetchRow($result)) { $this->fields[] = $row[0]; } } $this->_showInfo = $showInfo; // height and width $this->_setHeightTable($fontSize); // setWidth must me after setHeight, because title // can include table height which changes table width $this->_setWidthTable($font, $fontSize); if ($same_wide_width < $this->width) { $same_wide_width = $this->width; } // x and y $sql = 'SELECT x, y FROM ' . PMA_Util::backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_Util::backquote($cfgRelation['table_coords']) . ' WHERE db_name = \'' . PMA_Util::sqlAddSlashes($db) . '\'' . ' AND table_name = \'' . PMA_Util::sqlAddSlashes($tableName) . '\'' . ' AND pdf_page_number = ' . $pageNumber; $result = PMA_queryAsControlUser($sql, false, PMA_DatabaseInterface::QUERY_STORE); if (!$result || !$GLOBALS['dbi']->numRows($result)) { $svg->dieSchema($pageNumber, "SVG", sprintf(__('Please configure the coordinates for table %s'), $tableName)); } list($this->x, $this->y) = $GLOBALS['dbi']->fetchRow($result); $this->x = (double) $this->x; $this->y = (double) $this->y; // displayfield $this->displayfield = PMA_getDisplayField($db, $tableName); // index $result = $GLOBALS['dbi']->query('SHOW INDEX FROM ' . PMA_Util::backquote($tableName) . ';', null, PMA_DatabaseInterface::QUERY_STORE); if ($GLOBALS['dbi']->numRows($result) > 0) { while ($row = $GLOBALS['dbi']->fetchAssoc($result)) { if ($row['Key_name'] == 'PRIMARY') { $this->primary[] = $row['Column_name']; } } } }
echo '</td>' . "\n"; } echo ' <td>'; if (isset($comments[$column_name])) { echo htmlspecialchars($comments[$column_name]); } echo '</td>' . "\n"; if ($cfgRelation['mimework']) { $mime_map = PMA_getMIME($db, $table, true); echo ' <td>'; if (isset($mime_map[$column_name])) { echo htmlspecialchars(str_replace('_', '/', $mime_map[$column_name]['mimetype'])); } echo '</td>' . "\n"; } echo '</tr>'; } // end foreach $count++; echo '</table>'; // display indexes information if (count(PMA_Index::getFromTable($table, $db)) > 0) { echo PMA_Index::getView($table, $db, true); } echo '</div>'; } //ends main while /** * Displays the footer */ echo PMA_Util::getButton();
/** * The "PMA_RT_Table" constructor * * @param string $ The table name * @param integer $ The font size * @param integer $ The max. with among tables * @global object The current PDF document * @global integer The current page number (from the * $cfg['Servers'][$i]['table_coords'] table) * @global array The relations settings * @global string The current db name * @access private * @see PMA_PDF, PMA_RT_Table::PMA_RT_Table_setWidth, PMA_RT_Table::PMA_RT_Table_setHeight */ function __construct($table_name, $ff, &$same_wide_width, $show_keys) { global $pdf, $pdf_page_number, $cfgRelation, $db; $this->table_name = $table_name; $sql = 'DESCRIBE ' . PMA_backquote($table_name); $result = PMA_DBI_try_query($sql, null, PMA_DBI_QUERY_STORE); if (!$result || !PMA_DBI_num_rows($result)) { $pdf->PMA_PDF_die(sprintf($GLOBALS['strPdfInvalidTblName'], $table_name)); } // load fields //check to see if it will load all fields or only the foreign keys if ($show_keys) { $indexes = PMA_Index::getFromTable($this->table_name, $db); $all_columns = array(); foreach ($indexes as $index) { $all_columns = array_merge($all_columns, array_flip(array_keys($index->getColumns()))); } $this->fields = array_keys($all_columns); } else { while ($row = PMA_DBI_fetch_row($result)) { $this->fields[] = $row[0]; } } // height and width $this->PMA_RT_Table_setWidth($ff); $this->PMA_RT_Table_setHeight(); if ($same_wide_width < $this->width) { $same_wide_width = $this->width; } // x and y $sql = 'SELECT x, y FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' . ' AND table_name = \'' . PMA_sqlAddslashes($table_name) . '\'' . ' AND pdf_page_number = ' . $pdf_page_number; $result = PMA_query_as_cu($sql, false, PMA_DBI_QUERY_STORE); if (!$result || !PMA_DBI_num_rows($result)) { $pdf->PMA_PDF_die(sprintf($GLOBALS['strConfigureTableCoord'], $table_name)); } list($this->x, $this->y) = PMA_DBI_fetch_row($result); $this->x = (double) $this->x; $this->y = (double) $this->y; // displayfield $this->displayfield = PMA_getDisplayField($db, $table_name); // index $result = PMA_DBI_query('SHOW INDEX FROM ' . PMA_backquote($table_name) . ';', null, PMA_DBI_QUERY_STORE); if (PMA_DBI_num_rows($result) > 0) { while ($row = PMA_DBI_fetch_assoc($result)) { if ($row['Key_name'] == 'PRIMARY') { $this->primary[] = $row['Column_name']; } } } // end if }
/** * Function to check over array of indexes and look for common problems * * @uses is_string() * @uses is_array() * @uses count() * @uses array_pop() * @uses reset() * @uses current() * @access public * @param string name of table * @return string Output HTML */ public static function findDuplicates($table, $schema) { $indexes = PMA_Index::getFromTable($table, $schema); $output = ''; // count($indexes) < 2: // there is no need to check if there less than two indexes if (count($indexes) < 2) { return $output; } // remove last index from stack and ... while ($while_index = array_pop($indexes)) { // ... compare with every remaining index in stack foreach ($indexes as $each_index) { if ($each_index->getCompareData() !== $while_index->getCompareData()) { continue; } // did not find any difference // so it makes no sense to have this two equal indexes $message = PMA_Message::error(__('The indexes %1$s and %2$s seem to be equal and one of them could possibly be removed.')); $message->addParam($each_index->getName()); $message->addParam($while_index->getName()); $output .= $message->getDisplay(); // there is no need to check any further indexes if we have already // found that this one has a duplicate continue 2; } } return $output; }
* Prepares the table structure display */ /** * Gets tables informations */ require_once './libraries/tbl_info.inc.php'; /** * Displays top menu links */ require_once './libraries/tbl_links.inc.php'; require_once './libraries/Index.class.php'; // 2. Gets table keys and retains them // @todo should be: $server->db($db)->table($table)->primary() $primary = PMA_Index::getPrimary($table, $db); $columns_with_unique_index = array(); foreach (PMA_Index::getFromTable($table, $db) as $index) { if ($index->isUnique() && $index->getChoice() == 'UNIQUE') { $columns = $index->getColumns(); foreach ($columns as $column_name => $dummy) { $columns_with_unique_index[$column_name] = 1; } } } unset($index, $columns, $column_name, $dummy); // 3. Get fields $fields_rs = PMA_DRIZZLE ? PMA_DBI_query('SHOW COLUMNS FROM ' . PMA_backquote($table) . ';', null, PMA_DBI_QUERY_STORE) : PMA_DBI_query('SHOW FULL FIELDS FROM ' . PMA_backquote($table) . ';', null, PMA_DBI_QUERY_STORE); $fields_cnt = PMA_DBI_num_rows($fields_rs); // Get more complete field information // For now, this is done just for MySQL 4.1.2+ new TIMESTAMP options // but later, if the analyser returns more information, it // could be executed for any MySQL version and replace
/** * Displays the headers of the results table * * @param array &$is_display which elements to display * @param array &$fields_meta the list of fields properties * @param integer $fields_cnt the total number of fields returned by the SQL query * @param array $analyzed_sql the analyzed query * @param string $sort_expression sort expression * @param string $sort_expression_nodirection sort expression without direction * @param string $sort_direction sort direction * * @return boolean $clause_is_unique * * @global string $db the database name * @global string $table the table name * @global string $goto the URL to go back in case of errors * @global string $sql_query the SQL query * @global integer $num_rows the total number of rows returned by the * SQL query * @global array $vertical_display informations used with vertical display * mode * * @access private * * @see PMA_displayTable() */ function PMA_displayTableHeaders(&$is_display, &$fields_meta, $fields_cnt = 0, $analyzed_sql = '', $sort_expression, $sort_expression_nodirection, $sort_direction) { global $db, $table, $goto; global $sql_query, $num_rows; global $vertical_display, $highlight_columns; // required to generate sort links that will remember whether the // "Show all" button has been clicked $sql_md5 = md5($GLOBALS['sql_query']); $session_max_rows = $_SESSION['tmp_user_values']['query'][$sql_md5]['max_rows']; if ($analyzed_sql == '') { $analyzed_sql = array(); } // can the result be sorted? if ($is_display['sort_lnk'] == '1') { // Just as fallback $unsorted_sql_query = $sql_query; if (isset($analyzed_sql[0]['unsorted_query'])) { $unsorted_sql_query = $analyzed_sql[0]['unsorted_query']; } // Handles the case of multiple clicks on a column's header // which would add many spaces before "ORDER BY" in the // generated query. $unsorted_sql_query = trim($unsorted_sql_query); // sorting by indexes, only if it makes sense (only one table ref) if (isset($analyzed_sql) && isset($analyzed_sql[0]) && isset($analyzed_sql[0]['querytype']) && $analyzed_sql[0]['querytype'] == 'SELECT' && isset($analyzed_sql[0]['table_ref']) && count($analyzed_sql[0]['table_ref']) == 1) { // grab indexes data: $indexes = PMA_Index::getFromTable($table, $db); // do we have any index? if ($indexes) { if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontal' || $_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped') { $span = $fields_cnt; if ($is_display['edit_lnk'] != 'nn') { $span++; } if ($is_display['del_lnk'] != 'nn') { $span++; } if ($is_display['del_lnk'] != 'kp' && $is_display['del_lnk'] != 'nn') { $span++; } } else { $span = $num_rows + floor($num_rows / $_SESSION['tmp_user_values']['repeat_cells']) + 1; } echo '<form action="sql.php" method="post">' . "\n"; echo PMA_generate_common_hidden_inputs($db, $table); echo __('Sort by key') . ': <select name="sql_query" class="autosubmit">' . "\n"; $used_index = false; $local_order = isset($sort_expression) ? $sort_expression : ''; foreach ($indexes as $index) { $asc_sort = '`' . implode('` ASC, `', array_keys($index->getColumns())) . '` ASC'; $desc_sort = '`' . implode('` DESC, `', array_keys($index->getColumns())) . '` DESC'; $used_index = $used_index || $local_order == $asc_sort || $local_order == $desc_sort; if (preg_match('@(.*)([[:space:]](LIMIT (.*)|PROCEDURE (.*)|FOR UPDATE|LOCK IN SHARE MODE))@is', $unsorted_sql_query, $my_reg)) { $unsorted_sql_query_first_part = $my_reg[1]; $unsorted_sql_query_second_part = $my_reg[2]; } else { $unsorted_sql_query_first_part = $unsorted_sql_query; $unsorted_sql_query_second_part = ''; } echo '<option value="' . htmlspecialchars($unsorted_sql_query_first_part . "\n" . ' ORDER BY ' . $asc_sort . $unsorted_sql_query_second_part) . '"' . ($local_order == $asc_sort ? ' selected="selected"' : '') . '>' . htmlspecialchars($index->getName()) . ' (' . __('Ascending') . ')</option>'; echo '<option value="' . htmlspecialchars($unsorted_sql_query_first_part . "\n" . ' ORDER BY ' . $desc_sort . $unsorted_sql_query_second_part) . '"' . ($local_order == $desc_sort ? ' selected="selected"' : '') . '>' . htmlspecialchars($index->getName()) . ' (' . __('Descending') . ')</option>'; } echo '<option value="' . htmlspecialchars($unsorted_sql_query) . '"' . ($used_index ? '' : ' selected="selected"') . '>' . __('None') . '</option>'; echo '</select>' . "\n"; echo '<noscript><input type="submit" value="' . __('Go') . '" /></noscript>'; echo '</form>' . "\n"; } } } // Output data needed for grid editing echo '<input id="save_cells_at_once" type="hidden" value="' . $GLOBALS['cfg']['SaveCellsAtOnce'] . '" />'; echo '<div class="common_hidden_inputs">'; echo PMA_generate_common_hidden_inputs($db, $table); echo '</div>'; // Output data needed for column reordering and show/hide column if (PMA_isSelect()) { // generate the column order, if it is set $pmatable = new PMA_Table($GLOBALS['table'], $GLOBALS['db']); $col_order = $pmatable->getUiProp(PMA_Table::PROP_COLUMN_ORDER); if ($col_order) { echo '<input id="col_order" type="hidden" value="' . implode(',', $col_order) . '" />'; } $col_visib = $pmatable->getUiProp(PMA_Table::PROP_COLUMN_VISIB); if ($col_visib) { echo '<input id="col_visib" type="hidden" value="' . implode(',', $col_visib) . '" />'; } // generate table create time if (!PMA_Table::isView($GLOBALS['table'], $GLOBALS['db'])) { echo '<input id="table_create_time" type="hidden" value="' . PMA_Table::sGetStatusInfo($GLOBALS['db'], $GLOBALS['table'], 'Create_time') . '" />'; } } $vertical_display['emptypre'] = 0; $vertical_display['emptyafter'] = 0; $vertical_display['textbtn'] = ''; // Display options (if we are not in print view) if (!(isset($GLOBALS['printview']) && $GLOBALS['printview'] == '1')) { echo '<form method="post" action="sql.php" name="displayOptionsForm" id="displayOptionsForm"'; if ($GLOBALS['cfg']['AjaxEnable']) { echo ' class="ajax" '; } echo '>'; $url_params = array('db' => $db, 'table' => $table, 'sql_query' => $sql_query, 'goto' => $goto, 'display_options_form' => 1); echo PMA_generate_common_hidden_inputs($url_params); echo '<br />'; PMA_generate_slider_effect('displayoptions', __('Options')); echo '<fieldset>'; echo '<div class="formelement">'; $choices = array('P' => __('Partial texts'), 'F' => __('Full texts')); PMA_display_html_radio('display_text', $choices, $_SESSION['tmp_user_values']['display_text']); echo '</div>'; // prepare full/partial text button or link $url_params_full_text = array('db' => $db, 'table' => $table, 'sql_query' => $sql_query, 'goto' => $goto, 'full_text_button' => 1); if ($_SESSION['tmp_user_values']['display_text'] == 'F') { // currently in fulltext mode so show the opposite link $tmp_image_file = $GLOBALS['pmaThemeImage'] . 's_partialtext.png'; $tmp_txt = __('Partial texts'); $url_params_full_text['display_text'] = 'P'; } else { $tmp_image_file = $GLOBALS['pmaThemeImage'] . 's_fulltext.png'; $tmp_txt = __('Full texts'); $url_params_full_text['display_text'] = 'F'; } $tmp_image = '<img class="fulltext" src="' . $tmp_image_file . '" alt="' . $tmp_txt . '" title="' . $tmp_txt . '" />'; $tmp_url = 'sql.php' . PMA_generate_common_url($url_params_full_text); $full_or_partial_text_link = PMA_linkOrButton($tmp_url, $tmp_image, array(), false); unset($tmp_image_file, $tmp_txt, $tmp_url, $tmp_image); if ($GLOBALS['cfgRelation']['relwork'] && $GLOBALS['cfgRelation']['displaywork']) { echo '<div class="formelement">'; $choices = array('K' => __('Relational key'), 'D' => __('Relational display column')); PMA_display_html_radio('relational_display', $choices, $_SESSION['tmp_user_values']['relational_display']); echo '</div>'; } echo '<div class="formelement">'; PMA_display_html_checkbox('display_binary', __('Show binary contents'), !empty($_SESSION['tmp_user_values']['display_binary']), false); echo '<br />'; PMA_display_html_checkbox('display_blob', __('Show BLOB contents'), !empty($_SESSION['tmp_user_values']['display_blob']), false); echo '<br />'; PMA_display_html_checkbox('display_binary_as_hex', __('Show binary contents as HEX'), !empty($_SESSION['tmp_user_values']['display_binary_as_hex']), false); echo '</div>'; // I would have preferred to name this "display_transformation". // This is the only way I found to be able to keep this setting sticky // per SQL query, and at the same time have a default that displays // the transformations. echo '<div class="formelement">'; PMA_display_html_checkbox('hide_transformation', __('Hide browser transformation'), !empty($_SESSION['tmp_user_values']['hide_transformation']), false); echo '</div>'; if (!PMA_DRIZZLE) { echo '<div class="formelement">'; $choices = array('GEOM' => __('Geometry'), 'WKT' => __('Well Known Text'), 'WKB' => __('Well Known Binary')); PMA_display_html_radio('geometry_display', $choices, $_SESSION['tmp_user_values']['geometry_display']); echo '</div>'; } echo '<div class="clearfloat"></div>'; echo '</fieldset>'; echo '<fieldset class="tblFooters">'; echo '<input type="submit" value="' . __('Go') . '" />'; echo '</fieldset>'; echo '</div>'; echo '</form>'; } // Start of form for multi-rows edit/delete/export if ($is_display['del_lnk'] == 'dr' || $is_display['del_lnk'] == 'kp') { echo '<form method="post" action="tbl_row_action.php" name="resultsForm" id="resultsForm"'; if ($GLOBALS['cfg']['AjaxEnable']) { echo ' class="ajax" '; } echo '>' . "\n"; echo PMA_generate_common_hidden_inputs($db, $table, 1); echo '<input type="hidden" name="goto" value="sql.php" />' . "\n"; } echo '<table id="table_results" class="data'; if ($GLOBALS['cfg']['AjaxEnable']) { echo ' ajax'; } echo '">' . "\n"; if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontal' || $_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped') { echo '<thead><tr>' . "\n"; } // 1. Displays the full/partial text button (part 1)... if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontal' || $_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped') { $colspan = $is_display['edit_lnk'] != 'nn' && $is_display['del_lnk'] != 'nn' ? ' colspan="4"' : ''; } else { $rowspan = $is_display['edit_lnk'] != 'nn' && $is_display['del_lnk'] != 'nn' ? ' rowspan="4"' : ''; } // ... before the result table if ($is_display['edit_lnk'] == 'nn' && $is_display['del_lnk'] == 'nn' && $is_display['text_btn'] == '1') { $vertical_display['emptypre'] = $is_display['edit_lnk'] != 'nn' && $is_display['del_lnk'] != 'nn' ? 4 : 0; if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontal' || $_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped') { ?> <th colspan="<?php echo $fields_cnt; ?> "></th> </tr> <tr> <?php // end horizontal/horizontalflipped mode } else { ?> <tr> <th colspan="<?php echo $num_rows + floor($num_rows / $_SESSION['tmp_user_values']['repeat_cells']) + 1; ?> "></th> </tr> <?php } // end vertical mode } elseif (($GLOBALS['cfg']['RowActionLinks'] == 'left' || $GLOBALS['cfg']['RowActionLinks'] == 'both') && $is_display['text_btn'] == '1') { $vertical_display['emptypre'] = $is_display['edit_lnk'] != 'nn' && $is_display['del_lnk'] != 'nn' ? 4 : 0; if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontal' || $_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped') { ?> <th <?php echo $colspan; ?> ><?php echo $full_or_partial_text_link; ?> </th> <?php // end horizontal/horizontalflipped mode } else { $vertical_display['textbtn'] = ' <th ' . $rowspan . ' valign="middle">' . "\n" . ' ' . "\n" . ' </th>' . "\n"; } // end vertical mode } elseif (($GLOBALS['cfg']['RowActionLinks'] == 'left' || $GLOBALS['cfg']['RowActionLinks'] == 'both') && ($is_display['edit_lnk'] != 'nn' || $is_display['del_lnk'] != 'nn')) { $vertical_display['emptypre'] = $is_display['edit_lnk'] != 'nn' && $is_display['del_lnk'] != 'nn' ? 4 : 0; if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontal' || $_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped') { ?> <td<?php echo $colspan; ?> ></td> <?php // end horizontal/horizontalfipped mode } else { $vertical_display['textbtn'] = ' <td' . $rowspan . '></td>' . "\n"; } // end vertical mode } elseif ($GLOBALS['cfg']['RowActionLinks'] == 'none' && ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontal' || $_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped')) { echo '<th></th>'; } // 2. Displays the fields' name // 2.0 If sorting links should be used, checks if the query is a "JOIN" // statement (see 2.1.3) // 2.0.1 Prepare Display column comments if enabled ($GLOBALS['cfg']['ShowBrowseComments']). // Do not show comments, if using horizontalflipped mode, because of space usage if ($GLOBALS['cfg']['ShowBrowseComments'] && $_SESSION['tmp_user_values']['disp_direction'] != 'horizontalflipped') { $comments_map = array(); if (isset($analyzed_sql[0]) && is_array($analyzed_sql[0])) { foreach ($analyzed_sql[0]['table_ref'] as $tbl) { $tb = $tbl['table_true_name']; $comments_map[$tb] = PMA_getComments($db, $tb); unset($tb); } } } if ($GLOBALS['cfgRelation']['commwork'] && $GLOBALS['cfgRelation']['mimework'] && $GLOBALS['cfg']['BrowseMIME'] && !$_SESSION['tmp_user_values']['hide_transformation']) { include_once './libraries/transformations.lib.php'; $GLOBALS['mime_map'] = PMA_getMIME($db, $table); } // See if we have to highlight any header fields of a WHERE query. // Uses SQL-Parser results. $highlight_columns = array(); if (isset($analyzed_sql) && isset($analyzed_sql[0]) && isset($analyzed_sql[0]['where_clause_identifiers'])) { $wi = 0; if (isset($analyzed_sql[0]['where_clause_identifiers']) && is_array($analyzed_sql[0]['where_clause_identifiers'])) { foreach ($analyzed_sql[0]['where_clause_identifiers'] as $wci_nr => $wci) { $highlight_columns[$wci] = 'true'; } } } if (PMA_isSelect()) { // prepare to get the column order, if available $pmatable = new PMA_Table($GLOBALS['table'], $GLOBALS['db']); $col_order = $pmatable->getUiProp(PMA_Table::PROP_COLUMN_ORDER); $col_visib = $pmatable->getUiProp(PMA_Table::PROP_COLUMN_VISIB); } else { $col_order = false; $col_visib = false; } for ($j = 0; $j < $fields_cnt; $j++) { // assign $i with appropriate column order $i = $col_order ? $col_order[$j] : $j; // See if this column should get highlight because it's used in the // where-query. if (isset($highlight_columns[$fields_meta[$i]->name]) || isset($highlight_columns[PMA_backquote($fields_meta[$i]->name)])) { $condition_field = true; } else { $condition_field = false; } // 2.0 Prepare comment-HTML-wrappers for each row, if defined/enabled. if (isset($comments_map) && isset($comments_map[$fields_meta[$i]->table]) && isset($comments_map[$fields_meta[$i]->table][$fields_meta[$i]->name])) { $comments = '<span class="tblcomment">' . htmlspecialchars($comments_map[$fields_meta[$i]->table][$fields_meta[$i]->name]) . '</span>'; } else { $comments = ''; } // 2.1 Results can be sorted if ($is_display['sort_lnk'] == '1') { // 2.1.1 Checks if the table name is required; it's the case // for a query with a "JOIN" statement and if the column // isn't aliased, or in queries like // SELECT `1`.`master_field` , `2`.`master_field` // FROM `PMA_relation` AS `1` , `PMA_relation` AS `2` if (isset($fields_meta[$i]->table) && strlen($fields_meta[$i]->table)) { $sort_tbl = PMA_backquote($fields_meta[$i]->table) . '.'; } else { $sort_tbl = ''; } // 2.1.2 Checks if the current column is used to sort the // results // the orgname member does not exist for all MySQL versions // but if found, it's the one on which to sort $name_to_use_in_sort = $fields_meta[$i]->name; $is_orgname = false; if (isset($fields_meta[$i]->orgname) && strlen($fields_meta[$i]->orgname)) { $name_to_use_in_sort = $fields_meta[$i]->orgname; $is_orgname = true; } // $name_to_use_in_sort might contain a space due to // formatting of function expressions like "COUNT(name )" // so we remove the space in this situation $name_to_use_in_sort = str_replace(' )', ')', $name_to_use_in_sort); if (empty($sort_expression)) { $is_in_sort = false; } else { // Field name may be preceded by a space, or any number // of characters followed by a dot (tablename.fieldname) // so do a direct comparison for the sort expression; // this avoids problems with queries like // "SELECT id, count(id)..." and clicking to sort // on id or on count(id). // Another query to test this: // SELECT p.*, FROM_UNIXTIME(p.temps) FROM mytable AS p // (and try clicking on each column's header twice) if (!empty($sort_tbl) && strpos($sort_expression_nodirection, $sort_tbl) === false && strpos($sort_expression_nodirection, '(') === false) { $sort_expression_nodirection = $sort_tbl . $sort_expression_nodirection; } $is_in_sort = str_replace('`', '', $sort_tbl) . $name_to_use_in_sort == str_replace('`', '', $sort_expression_nodirection) ? true : false; } // 2.1.3 Check the field name for a bracket. // If it contains one, it's probably a function column // like 'COUNT(`field`)' // It still might be a column name of a view. See bug #3383711 // Check is_orgname. if (strpos($name_to_use_in_sort, '(') !== false && !$is_orgname) { $sort_order = "\n" . 'ORDER BY ' . $name_to_use_in_sort . ' '; } else { $sort_order = "\n" . 'ORDER BY ' . $sort_tbl . PMA_backquote($name_to_use_in_sort) . ' '; } unset($name_to_use_in_sort); unset($is_orgname); // 2.1.4 Do define the sorting URL if (!$is_in_sort) { // patch #455484 ("Smart" order) $GLOBALS['cfg']['Order'] = strtoupper($GLOBALS['cfg']['Order']); if ($GLOBALS['cfg']['Order'] === 'SMART') { $sort_order .= preg_match('@time|date@i', $fields_meta[$i]->type) ? 'DESC' : 'ASC'; } else { $sort_order .= $GLOBALS['cfg']['Order']; } $order_img = ''; } elseif ('DESC' == $sort_direction) { $sort_order .= ' ASC'; $order_img = ' ' . PMA_getImage('s_desc.png', __('Descending'), array('class' => "soimg{$i}", 'title' => '')); $order_img .= ' ' . PMA_getImage('s_asc.png', __('Ascending'), array('class' => "soimg{$i} hide", 'title' => '')); } else { $sort_order .= ' DESC'; $order_img = ' ' . PMA_getImage('s_asc.png', __('Ascending'), array('class' => "soimg{$i}", 'title' => '')); $order_img .= ' ' . PMA_getImage('s_desc.png', __('Descending'), array('class' => "soimg{$i} hide", 'title' => '')); } if (preg_match('@(.*)([[:space:]](LIMIT (.*)|PROCEDURE (.*)|FOR UPDATE|LOCK IN SHARE MODE))@is', $unsorted_sql_query, $regs3)) { $sorted_sql_query = $regs3[1] . $sort_order . $regs3[2]; } else { $sorted_sql_query = $unsorted_sql_query . $sort_order; } $_url_params = array('db' => $db, 'table' => $table, 'sql_query' => $sorted_sql_query, 'session_max_rows' => $session_max_rows); $order_url = 'sql.php' . PMA_generate_common_url($_url_params); // 2.1.5 Displays the sorting URL // enable sort order swapping for image $order_link_params = array(); if (isset($order_img) && $order_img != '') { if (strstr($order_img, 'asc')) { $order_link_params['onmouseover'] = "\$('.soimg{$i}').toggle()"; $order_link_params['onmouseout'] = "\$('.soimg{$i}').toggle()"; } elseif (strstr($order_img, 'desc')) { $order_link_params['onmouseover'] = "\$('.soimg{$i}').toggle()"; $order_link_params['onmouseout'] = "\$('.soimg{$i}').toggle()"; } } if ($GLOBALS['cfg']['HeaderFlipType'] == 'auto') { if (PMA_USR_BROWSER_AGENT == 'IE') { $GLOBALS['cfg']['HeaderFlipType'] = 'css'; } else { $GLOBALS['cfg']['HeaderFlipType'] = 'fake'; } } if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped' && $GLOBALS['cfg']['HeaderFlipType'] == 'css') { $order_link_params['style'] = 'direction: ltr; writing-mode: tb-rl;'; } $order_link_params['title'] = __('Sort'); $order_link_content = $_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped' && $GLOBALS['cfg']['HeaderFlipType'] == 'fake' ? PMA_flipstring(htmlspecialchars($fields_meta[$i]->name), "<br />\n") : htmlspecialchars($fields_meta[$i]->name); $order_link = PMA_linkOrButton($order_url, $order_link_content . $order_img, $order_link_params, false, true); if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontal' || $_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped') { echo '<th'; $th_class = array(); $th_class[] = 'draggable'; if ($col_visib && !$col_visib[$j]) { $th_class[] = 'hide'; } if ($condition_field) { $th_class[] = 'condition'; } $th_class[] = 'column_heading'; if ($GLOBALS['cfg']['BrowsePointerEnable'] == true) { $th_class[] = 'pointer'; } if ($GLOBALS['cfg']['BrowseMarkerEnable'] == true) { $th_class[] = 'marker'; } echo ' class="' . implode(' ', $th_class) . '"'; if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped') { echo ' valign="bottom"'; } echo '>' . $order_link . $comments . '</th>'; } $vertical_display['desc'][] = ' <th ' . 'class="draggable' . ($condition_field ? ' condition' : '') . '">' . "\n" . $order_link . $comments . ' </th>' . "\n"; } else { if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontal' || $_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped') { echo '<th'; $th_class = array(); $th_class[] = 'draggable'; if ($col_visib && !$col_visib[$j]) { $th_class[] = 'hide'; } if ($condition_field) { $th_class[] = 'condition'; } echo ' class="' . implode(' ', $th_class) . '"'; if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped') { echo ' valign="bottom"'; } if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped' && $GLOBALS['cfg']['HeaderFlipType'] == 'css') { echo ' style="direction: ltr; writing-mode: tb-rl;"'; } echo '>'; if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped' && $GLOBALS['cfg']['HeaderFlipType'] == 'fake') { echo PMA_flipstring(htmlspecialchars($fields_meta[$i]->name), '<br />'); } else { echo htmlspecialchars($fields_meta[$i]->name); } echo "\n" . $comments . '</th>'; } $vertical_display['desc'][] = ' <th ' . 'class="draggable' . ($condition_field ? ' condition"' : '') . '">' . "\n" . ' ' . htmlspecialchars($fields_meta[$i]->name) . "\n" . $comments . ' </th>'; } // end else (2.2) } // end for // 3. Displays the needed checkboxes at the right // column of the result table header if possible and required... if (($GLOBALS['cfg']['RowActionLinks'] == 'right' || $GLOBALS['cfg']['RowActionLinks'] == 'both') && ($is_display['edit_lnk'] != 'nn' || $is_display['del_lnk'] != 'nn') && $is_display['text_btn'] == '1') { $vertical_display['emptyafter'] = $is_display['edit_lnk'] != 'nn' && $is_display['del_lnk'] != 'nn' ? 4 : 1; if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontal' || $_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped') { echo "\n"; ?> <th <?php echo $colspan; ?> ><?php echo $full_or_partial_text_link; ?> </th> <?php // end horizontal/horizontalflipped mode } else { $vertical_display['textbtn'] = ' <th ' . $rowspan . ' valign="middle">' . "\n" . ' ' . "\n" . ' </th>' . "\n"; } // end vertical mode } elseif (($GLOBALS['cfg']['RowActionLinks'] == 'left' || $GLOBALS['cfg']['RowActionLinks'] == 'both') && ($is_display['edit_lnk'] == 'nn' && $is_display['del_lnk'] == 'nn') && !$GLOBALS['is_header_sent']) { $vertical_display['emptyafter'] = $is_display['edit_lnk'] != 'nn' && $is_display['del_lnk'] != 'nn' ? 4 : 1; if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontal' || $_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped') { echo "\n"; ?> <td<?php echo $colspan; ?> ></td> <?php // end horizontal/horizontalflipped mode } else { $vertical_display['textbtn'] = ' <td' . $rowspan . '></td>' . "\n"; } // end vertical mode } if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontal' || $_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped') { ?> </tr> </thead> <?php } return true; }
/** * The "Table_Stats" constructor * * @param string $tableName The table name * @param integer $pageNumber The current page number (from the * $cfg['Servers'][$i]['table_coords'] table) * @param boolean $showKeys Whether to display ONLY keys or not * * @return void * * @global object The current dia document * @global array The relations settings * @global string The current db name * * @see PMA_DIA */ function __construct($tableName, $pageNumber, $showKeys = false) { global $dia, $cfgRelation, $db; $this->tableName = $tableName; $sql = 'DESCRIBE ' . PMA_Util::backquote($tableName); $result = PMA_DBI_try_query($sql, null, PMA_DBI_QUERY_STORE); if (!$result || !PMA_DBI_num_rows($result)) { $dia->dieSchema($pageNumber, "DIA", sprintf(__('The %s table doesn\'t exist!'), $tableName)); } /* * load fields * check to see if it will load all fields or only the foreign keys */ if ($showKeys) { $indexes = PMA_Index::getFromTable($this->tableName, $db); $all_columns = array(); foreach ($indexes as $index) { $all_columns = array_merge($all_columns, array_flip(array_keys($index->getColumns()))); } $this->fields = array_keys($all_columns); } else { while ($row = PMA_DBI_fetch_row($result)) { $this->fields[] = $row[0]; } } $sql = 'SELECT x, y FROM ' . PMA_Util::backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_Util::backquote($cfgRelation['table_coords']) . ' WHERE db_name = \'' . PMA_Util::sqlAddSlashes($db) . '\'' . ' AND table_name = \'' . PMA_Util::sqlAddSlashes($tableName) . '\'' . ' AND pdf_page_number = ' . $pageNumber; $result = PMA_queryAsControlUser($sql, false, PMA_DBI_QUERY_STORE); if (!$result || !PMA_DBI_num_rows($result)) { $dia->dieSchema($pageNumber, "DIA", sprintf(__('Please configure the coordinates for table %s'), $tableName)); } list($this->x, $this->y) = PMA_DBI_fetch_row($result); $this->x = (double) $this->x; $this->y = (double) $this->y; /* * displayfield */ $this->displayfield = PMA_getDisplayField($db, $tableName); /* * index */ $result = PMA_DBI_query('SHOW INDEX FROM ' . PMA_Util::backquote($tableName) . ';', null, PMA_DBI_QUERY_STORE); if (PMA_DBI_num_rows($result) > 0) { while ($row = PMA_DBI_fetch_assoc($result)) { if ($row['Key_name'] == 'PRIMARY') { $this->primary[] = $row['Column_name']; } } } /** * Every object in Dia document needs an ID to identify * so, we used a static variable to keep the things unique */ PMA_Dia_Relation_Schema::$objectId += 1; $this->tableId = PMA_Dia_Relation_Schema::$objectId; }
/** * Verify whether the result set contains all the columns * of at least one unique key * * @param string $db database name * @param string $table table name * @param array $fields_meta meta fields * * @return boolean whether the result set contains a unique key */ function PMA_resultSetContainsUniqueKey($db, $table, $fields_meta) { $resultSetColumnNames = array(); foreach ($fields_meta as $oneMeta) { $resultSetColumnNames[] = $oneMeta->name; } foreach (PMA_Index::getFromTable($table, $db) as $index) { if ($index->isUnique()) { $indexColumns = $index->getColumns(); $numberFound = 0; foreach ($indexColumns as $indexColumnName => $dummy) { if (in_array($indexColumnName, $resultSetColumnNames)) { $numberFound++; } } if ($numberFound == count($indexColumns)) { return true; } } } return false; }
/** * Returns descriptions of columns in given table (all or given by $column) * * @param string $database name of database * @param string $table name of table to retrieve columns from * @param string $column name of column, null to show all columns * @param boolean $full whether to return full info or only column names * @param mixed $link mysql link resource * * @return array array indexed by column names or, * if $column is given, flat array description */ public function getColumns($database, $table, $column = null, $full = false, $link = null) { $sql = $this->getColumnsSql($database, $table, $column, $full); $fields = $this->fetchResult($sql, 'Field', null, $link); if (!is_array($fields) || count($fields) == 0) { return array(); } // Check if column is a part of multiple-column index and set its 'Key'. $indexes = PMA_Index::getFromTable($table, $database); foreach ($fields as $field => $field_data) { if (!empty($field_data['Key'])) { continue; } foreach ($indexes as $index) { /** @var PMA_Index $index */ if (!$index->hasColumn($field)) { continue; } $index_columns = $index->getColumns(); if ($index_columns[$field]->getSeqInIndex() > 1) { if ($index->isUnique()) { $fields[$field]['Key'] = 'UNI'; } else { $fields[$field]['Key'] = 'MUL'; } } } } if (PMA_DRIZZLE) { // fix Key column, it's much simpler in PHP than in SQL $has_pk = false; $has_pk_candidates = false; foreach ($fields as $f) { if ($f['Key'] == 'PRI') { $has_pk = true; break; } else { if ($f['Null'] == 'NO' && ($f['Key'] == 'MUL' || $f['Key'] == 'UNI')) { $has_pk_candidates = true; } } } if (!$has_pk && $has_pk_candidates) { $secureDatabase = PMA_Util::sqlAddSlashes($database); // check whether we can promote some unique index to PRI $sql = "\n SELECT i.index_name, p.column_name\n FROM data_dictionary.indexes i\n JOIN data_dictionary.index_parts p\n USING (table_schema, table_name)\n WHERE i.table_schema = '" . $secureDatabase . "'\n AND i.table_name = '" . PMA_Util::sqlAddSlashes($table) . "'\n AND i.is_unique\n AND NOT i.is_nullable"; $result = $this->fetchResult($sql, 'index_name', null, $link); $result = $result ? array_shift($result) : array(); foreach ($result as $f) { $fields[$f]['Key'] = 'PRI'; } } } return $column != null ? array_shift($fields) : $fields; }
/** * Prepare unsorted sql query and sort by key drop down * * @param array $analyzed_sql the analyzed query * @param string $sort_expression sort expression * * @return array two element array - $unsorted_sql_query, $drop_down_html * * @access private * * @see _getTableHeaders() */ private function _getUnsortedSqlAndSortByKeyDropDown($analyzed_sql, $sort_expression) { $drop_down_html = ''; // Just as fallback $unsorted_sql_query = $this->__get('sql_query'); if (isset($analyzed_sql[0]['unsorted_query'])) { $unsorted_sql_query = $analyzed_sql[0]['unsorted_query']; } // Handles the case of multiple clicks on a column's header // which would add many spaces before "ORDER BY" in the // generated query. $unsorted_sql_query = trim($unsorted_sql_query); // sorting by indexes, only if it makes sense (only one table ref) if (isset($analyzed_sql) && isset($analyzed_sql[0]) && isset($analyzed_sql[0]['querytype']) && $analyzed_sql[0]['querytype'] == self::QUERY_TYPE_SELECT && isset($analyzed_sql[0]['table_ref']) && count($analyzed_sql[0]['table_ref']) == 1) { // grab indexes data: $indexes = PMA_Index::getFromTable($this->__get('table'), $this->__get('db')); // do we have any index? if ($indexes) { $drop_down_html = $this->_getSortByKeyDropDown($indexes, $sort_expression, $unsorted_sql_query); } } return array($unsorted_sql_query, $drop_down_html); }
/** * Returns descriptions of columns in given table (all or given by $column) * * @param string $database name of database * @param string $table name of table to retrieve columns from * @param string $column name of column, null to show all columns * @param boolean $full whether to return full info or only column names * @param mixed $link mysql link resource * * @return array array indexed by column names or, * if $column is given, flat array description */ public function getColumns($database, $table, $column = null, $full = false, $link = null) { $sql = $this->getColumnsSql($database, $table, $column, $full); $fields = $this->fetchResult($sql, 'Field', null, $link); if (!is_array($fields) || count($fields) == 0) { return array(); } // Check if column is a part of multiple-column index and set its 'Key'. $indexes = PMA_Index::getFromTable($table, $database); foreach ($fields as $field => $field_data) { if (!empty($field_data['Key'])) { continue; } foreach ($indexes as $index) { /** @var PMA_Index $index */ if (!$index->hasColumn($field)) { continue; } $index_columns = $index->getColumns(); if ($index_columns[$field]->getSeqInIndex() > 1) { if ($index->isUnique()) { $fields[$field]['Key'] = 'UNI'; } else { $fields[$field]['Key'] = 'MUL'; } } } } return $column != null ? array_shift($fields) : $fields; }