relation = new Relation(); $this->transformations = new Transformations(); } /** * Parses and analyzes the given SQL query. * * @param string $sql_query SQL query * @param string $db DB name * * @return mixed */ public function parseAndAnalyze($sql_query, $db = null) { if (is_null($db) && isset($GLOBALS['db']) && strlen($GLOBALS['db'])) { $db = $GLOBALS['db']; } list($analyzed_sql_results,,) = ParseAnalyze::sqlQuery($sql_query, $db); return $analyzed_sql_results; } /** * Handle remembered sorting order, only for single table query * * @param string $db database name * @param string $table table name * @param array &$analyzed_sql_results the analyzed query results * @param string &$full_sql_query SQL query * * @return void */ private function handleSortOrder( $db, $table, array &$analyzed_sql_results, &$full_sql_query ) { $pmatable = new Table($table, $db); if (empty($analyzed_sql_results['order'])) { // Retrieving the name of the column we should sort after. $sortCol = $pmatable->getUiProp(Table::PROP_SORTED_COLUMN); if (empty($sortCol)) { return; } // Remove the name of the table from the retrieved field name. $sortCol = str_replace( Util::backquote($table) . '.', '', $sortCol ); // Create the new query. $full_sql_query = Query::replaceClause( $analyzed_sql_results['statement'], $analyzed_sql_results['parser']->list, 'ORDER BY ' . $sortCol ); // TODO: Avoid reparsing the query. $analyzed_sql_results = Query::getAll($full_sql_query); } else { // Store the remembered table into session. $pmatable->setUiProp( Table::PROP_SORTED_COLUMN, Query::getClause( $analyzed_sql_results['statement'], $analyzed_sql_results['parser']->list, 'ORDER BY' ) ); } } /** * Append limit clause to SQL query * * @param array &$analyzed_sql_results the analyzed query results * * @return string limit clause appended SQL query */ private function getSqlWithLimitClause(array &$analyzed_sql_results) { return Query::replaceClause( $analyzed_sql_results['statement'], $analyzed_sql_results['parser']->list, 'LIMIT ' . $_SESSION['tmpval']['pos'] . ', ' . $_SESSION['tmpval']['max_rows'] ); } /** * Verify whether the result set has columns from just one table * * @param array $fields_meta meta fields * * @return boolean whether the result set has columns from just one table */ private function resultSetHasJustOneTable(array $fields_meta) { $just_one_table = true; $prev_table = ''; foreach ($fields_meta as $one_field_meta) { if ($one_field_meta->table != '' && $prev_table != '' && $one_field_meta->table != $prev_table ) { $just_one_table = false; } if ($one_field_meta->table != '') { $prev_table = $one_field_meta->table; } } return $just_one_table && $prev_table != ''; } /** * 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 */ private function resultSetContainsUniqueKey($db, $table, array $fields_meta) { $resultSetColumnNames = []; foreach ($fields_meta as $oneMeta) { $resultSetColumnNames[] = $oneMeta->name; } foreach (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; } /** * Get the HTML for relational column dropdown * During grid edit, if we have a relational field, returns the html for the * dropdown * * @param string $db current database * @param string $table current table * @param string $column current column * @param string $curr_value current selected value * * @return string $dropdown html for the dropdown */ private function getHtmlForRelationalColumnDropdown($db, $table, $column, $curr_value) { $foreigners = $this->relation->getForeigners($db, $table, $column); $foreignData = $this->relation->getForeignData($foreigners, $column, false, '', ''); if ($foreignData['disp_row'] == null) { //Handle the case when number of values //is more than $cfg['ForeignKeyMaxLimit'] $_url_params = [ 'db' => $db, 'table' => $table, 'field' => $column ]; $dropdown = '' . htmlspecialchars($_REQUEST['curr_value']) . '' . '' . __('Browse foreign values') . ''; } else { $dropdown = $this->relation->foreignDropdown( $foreignData['disp_row'], $foreignData['foreign_field'], $foreignData['foreign_display'], $curr_value, $GLOBALS['cfg']['ForeignKeyMaxLimit'] ); $dropdown = ''; } return $dropdown; } /** * Get the HTML for the profiling table and accompanying chart if profiling is set. * Otherwise returns null * * @param string $url_query url query * @param string $db current database * @param array $profiling_results array containing the profiling info * * @return string $profiling_table html for the profiling table and chart */ private function getHtmlForProfilingChart($url_query, $db, $profiling_results) { if (! empty($profiling_results)) { $url_query = isset($url_query) ? $url_query : Url::getCommon(['db' => $db]); $profiling_table = ''; $profiling_table .= '
' . __('Profiling') . '' . "\n"; $profiling_table .= '
'; $profiling_table .= '

' . __('Detailed profile') . '

'; $profiling_table .= '' . "\n"; $profiling_table .= ' ' . "\n"; $profiling_table .= ' ' . "\n"; $profiling_table .= ' ' . "\n"; $profiling_table .= ' ' . "\n"; $profiling_table .= ' ' . "\n"; list($detailed_table, $chart_json, $profiling_stats) = $this->analyzeAndGetTableHtmlForProfilingResults($profiling_results); $profiling_table .= $detailed_table; $profiling_table .= '
' . __('Order') . '
' . __('State') . Util::showMySQLDocu('general-thread-states') . '
' . __('Time') . '
' . "\n"; $profiling_table .= '
'; $profiling_table .= '
'; $profiling_table .= '

' . __('Summary by state') . '

'; $profiling_table .= '' . "\n"; $profiling_table .= ' ' . "\n"; $profiling_table .= ' ' . "\n"; $profiling_table .= ' ' . "\n"; $profiling_table .= ' ' . "\n"; $profiling_table .= ' ' . "\n"; $profiling_table .= ' ' . "\n"; $profiling_table .= ' ' . "\n"; $profiling_table .= $this->getTableHtmlForProfilingSummaryByState( $profiling_stats ); $profiling_table .= '
' . __('State') . Util::showMySQLDocu('general-thread-states') . '
' . __('Total Time') . '
' . __('% Time') . '
' . __('Calls') . '
' . __('ΓΈ Time') . '
' . "\n"; $profiling_table .= << url_query = '$url_query'; EOT; $profiling_table .= "
"; $profiling_table .= "
"; //require_once 'libraries/chart.lib.php'; $profiling_table .= '
'; $profiling_table .= json_encode($chart_json); $profiling_table .= '
'; $profiling_table .= '
'; $profiling_table .= '
'; $profiling_table .= ''; $profiling_table .= '
' . "\n"; } else { $profiling_table = null; } return $profiling_table; } /** * Function to get HTML for detailed profiling results table, profiling stats, and * $chart_json for displaying the chart. * * @param array $profiling_results profiling results * * @return mixed */ private function analyzeAndGetTableHtmlForProfilingResults( $profiling_results ) { $profiling_stats = [ 'total_time' => 0, 'states' => [], ]; $chart_json = []; $i = 1; $table = ''; foreach ($profiling_results as $one_result) { if (isset($profiling_stats['states'][ucwords($one_result['Status'])])) { $states = $profiling_stats['states']; $states[ucwords($one_result['Status'])]['total_time'] += $one_result['Duration']; $states[ucwords($one_result['Status'])]['calls']++; } else { $profiling_stats['states'][ucwords($one_result['Status'])] = [ 'total_time' => $one_result['Duration'], 'calls' => 1, ]; } $profiling_stats['total_time'] += $one_result['Duration']; $table .= ' ' . "\n"; $table .= '' . $i++ . '' . "\n"; $table .= '' . ucwords($one_result['Status']) . '' . "\n"; $table .= '' . (Util::formatNumber($one_result['Duration'], 3, 1)) . 's' . $one_result['Duration'] . '' . "\n"; if (isset($chart_json[ucwords($one_result['Status'])])) { $chart_json[ucwords($one_result['Status'])] += $one_result['Duration']; } else { $chart_json[ucwords($one_result['Status'])] = $one_result['Duration']; } } return [$table, $chart_json, $profiling_stats]; } /** * Function to get HTML for summary by state table * * @param array $profiling_stats profiling stats * * @return string $table html for the table */ private function getTableHtmlForProfilingSummaryByState(array $profiling_stats) { $table = ''; foreach ($profiling_stats['states'] as $name => $stats) { $table .= ' ' . "\n"; $table .= '' . $name . '' . "\n"; $table .= '' . Util::formatNumber($stats['total_time'], 3, 1) . 's' . $stats['total_time'] . '' . "\n"; $table .= '' . Util::formatNumber( 100 * ($stats['total_time'] / $profiling_stats['total_time']), 0, 2 ) . '%' . "\n"; $table .= '' . $stats['calls'] . '' . "\n"; $table .= '' . Util::formatNumber( $stats['total_time'] / $stats['calls'], 3, 1 ) . 's' . number_format($stats['total_time'] / $stats['calls'], 8, '.', '') . '' . "\n"; $table .= ' ' . "\n"; } return $table; } /** * Get the HTML for the enum column dropdown * During grid edit, if we have a enum field, returns the html for the * dropdown * * @param string $db current database * @param string $table current table * @param string $column current column * @param string $curr_value currently selected value * * @return string $dropdown html for the dropdown */ private function getHtmlForEnumColumnDropdown($db, $table, $column, $curr_value) { $values = $this->getValuesForColumn($db, $table, $column); $dropdown = ''; $dropdown .= $this->getHtmlForOptionsList($values, [$curr_value]); $dropdown = ''; return $dropdown; } /** * Get value of a column for a specific row (marked by $where_clause) * * @param string $db current database * @param string $table current table * @param string $column current column * @param string $where_clause where clause to select a particular row * * @return string with value */ private function getFullValuesForSetColumn($db, $table, $column, $where_clause) { $result = $GLOBALS['dbi']->fetchSingleRow( "SELECT `$column` FROM `$db`.`$table` WHERE $where_clause" ); return $result[$column]; } /** * Get the HTML for the set column dropdown * During grid edit, if we have a set field, returns the html for the * dropdown * * @param string $db current database * @param string $table current table * @param string $column current column * @param string $curr_value currently selected value * * @return string $dropdown html for the set column */ private function getHtmlForSetColumn($db, $table, $column, $curr_value) { $values = $this->getValuesForColumn($db, $table, $column); $dropdown = ''; $full_values = isset($_REQUEST['get_full_values']) ? $_REQUEST['get_full_values'] : false; $where_clause = isset($_REQUEST['where_clause']) ? $_REQUEST['where_clause'] : null; // If the $curr_value was truncated, we should // fetch the correct full values from the table if ($full_values && ! empty($where_clause)) { $curr_value = $this->getFullValuesForSetColumn( $db, $table, $column, $where_clause ); } //converts characters of $curr_value to HTML entities $converted_curr_value = htmlentities( $curr_value, ENT_COMPAT, "UTF-8" ); $selected_values = explode(',', $converted_curr_value); $dropdown .= $this->getHtmlForOptionsList($values, $selected_values); $select_size = (sizeof($values) > 10) ? 10 : sizeof($values); $dropdown = ''; return $dropdown; } /** * Get all the values for a enum column or set column in a table * * @param string $db current database * @param string $table current table * @param string $column current column * * @return array $values array containing the value list for the column */ private function getValuesForColumn($db, $table, $column) { $field_info_query = $GLOBALS['dbi']->getColumnsSql($db, $table, $column); $field_info_result = $GLOBALS['dbi']->fetchResult( $field_info_query, null, null, DatabaseInterface::CONNECT_USER, DatabaseInterface::QUERY_STORE ); $values = Util::parseEnumSetValues($field_info_result[0]['Type']); return $values; } /** * Get HTML for options list * * @param array $values set of values * @param array $selected_values currently selected values * * @return string $options HTML for options list */ private function getHtmlForOptionsList(array $values, array $selected_values) { $options = ''; foreach ($values as $value) { $options .= '