Merge pull request #431 from xmujay/0609_monitor
[phpmyadmin/aamir.git] / libraries / sql.lib.php
blobf92d15e34e2957e0fc1e990d84ff371a2aee5781
1 <?php
2 /* vim: set expandtab sw=4 ts=4 sts=4: */
3 /**
4 * set of functions for the sql executor
6 * @package PhpMyAdmin
7 */
8 if (!defined('PHPMYADMIN')) {
9 exit;
12 /**
13 * Get the database name inside a USE query
15 * @param string $sql SQL query
16 * @param array $databases array with all databases
18 * @return strin $db new database name
20 function PMA_getNewDatabase($sql, $databases)
22 $db = '';
23 // loop through all the databases
24 foreach ($databases as $database) {
25 if (strpos($sql, $database['SCHEMA_NAME']) !== false) {
26 $db = $database;
27 break;
30 return $db;
33 /**
34 * Get the table name in a sql query
35 * If there are several tables in the SQL query,
36 * first table wil lreturn
38 * @param string $sql SQL query
39 * @param array $tables array of names in current database
41 * @return string $table table name
43 function PMA_getTableNameBySQL($sql, $tables)
45 $table = '';
47 // loop through all the tables in the database
48 foreach ($tables as $tbl) {
49 if (strpos($sql, $tbl)) {
50 $table .= ' ' . $tbl;
54 if (count(explode(' ', trim($table))) > 1) {
55 $tmp_array = explode(' ', trim($table));
56 return $tmp_array[0];
59 return trim($table);
63 /**
64 * Generate table html when SQL statement have multiple queries
65 * which return displayable results
67 * @param object $displayResultsObject PMA_DisplayResults object
68 * @param string $db database name
69 * @param array $sql_data information about SQL statement
70 * @param string $goto URL to go back in case of errors
71 * @param string $pmaThemeImage path for theme images directory
72 * @param string $text_dir text direction
73 * @param string $printview whether printview is enabled
74 * @param string $url_query URL query
75 * @param array $disp_mode the display mode
76 * @param string $sql_limit_to_append limit clause
77 * @param bool $has_unique result contains a unique key
79 * @return string $table_html html content
81 function getTableHtmlForMultipleQueries(
82 $displayResultsObject, $db, $sql_data, $goto, $pmaThemeImage,
83 $text_dir, $printview, $url_query, $disp_mode, $sql_limit_to_append,
84 $editable
85 ) {
86 $table_html = '';
88 $tables_array = $GLOBALS['dbi']->getTables($db);
89 $databases_array = $GLOBALS['dbi']->getDatabasesFull();
90 $multi_sql = implode(";", $sql_data['valid_sql']);
91 $querytime_before = array_sum(explode(' ', microtime()));
93 // Assignment for variable is not needed since the results are
94 // looping using the connection
95 @$GLOBALS['dbi']->tryMultiQuery($multi_sql);
97 $querytime_after = array_sum(explode(' ', microtime()));
98 $querytime = $querytime_after - $querytime_before;
99 $sql_no = 0;
101 do {
102 $analyzed_sql = array();
103 $is_affected = false;
105 $result = $GLOBALS['dbi']->storeResult();
106 $fields_meta = ($result !== false)
107 ? $GLOBALS['dbi']->getFieldsMeta($result)
108 : array();
109 $fields_cnt = count($fields_meta);
111 // Initialize needed params related to each query in multiquery statement
112 if (isset($sql_data['valid_sql'][$sql_no])) {
113 // 'Use' query can change the database
114 if (stripos($sql_data['valid_sql'][$sql_no], "use ")) {
115 $db = PMA_getNewDatabase(
116 $sql_data['valid_sql'][$sql_no],
117 $databases_array
121 $table = PMA_getTableNameBySQL(
122 $sql_data['valid_sql'][$sql_no],
123 $tables_array
126 // for the use of the parse_analyze.inc.php
127 $sql_query = $sql_data['valid_sql'][$sql_no];
129 // Parse and analyze the query
130 include 'libraries/parse_analyze.inc.php';
132 $unlim_num_rows = PMA_Table::countRecords($db, $table, true);
133 $showtable = PMA_Table::sGetStatusInfo($db, $table, null, true);
134 $url_query = PMA_generate_common_url($db, $table);
136 // Handle remembered sorting order, only for single table query
137 if ($GLOBALS['cfg']['RememberSorting']
138 && ! ($is_count || $is_export || $is_func || $is_analyse)
139 && isset($analyzed_sql[0]['select_expr'])
140 && (count($analyzed_sql[0]['select_expr']) == 0)
141 && isset($analyzed_sql[0]['queryflags']['select_from'])
142 && count($analyzed_sql[0]['table_ref']) == 1
144 PMA_handleSortOrder(
145 $db,
146 $table,
147 $analyzed_sql,
148 $sql_data['valid_sql'][$sql_no]
152 // Do append a "LIMIT" clause?
153 if (($_SESSION['tmp_user_values']['max_rows'] != 'all')
154 && ! ($is_count || $is_export || $is_func || $is_analyse)
155 && isset($analyzed_sql[0]['queryflags']['select_from'])
156 && ! isset($analyzed_sql[0]['queryflags']['offset'])
157 && empty($analyzed_sql[0]['limit_clause'])
159 $sql_limit_to_append = ' LIMIT '
160 . $_SESSION['tmp_user_values']['pos']
161 . ', ' . $_SESSION['tmp_user_values']['max_rows'] . " ";
162 $sql_data['valid_sql'][$sql_no] = PMA_getSqlWithLimitClause(
163 $sql_data['valid_sql'][$sql_no],
164 $analyzed_sql,
165 $sql_limit_to_append
169 // Set the needed properties related to executing sql query
170 $displayResultsObject->__set('db', $db);
171 $displayResultsObject->__set('table', $table);
172 $displayResultsObject->__set('goto', $goto);
175 if (! $is_affected) {
176 $num_rows = ($result) ? @$GLOBALS['dbi']->numRows($result) : 0;
177 } elseif (! isset($num_rows)) {
178 $num_rows = @$GLOBALS['dbi']->affectedRows();
181 if (isset($sql_data['valid_sql'][$sql_no])) {
183 $displayResultsObject->__set(
184 'sql_query',
185 $sql_data['valid_sql'][$sql_no]
187 $displayResultsObject->setProperties(
188 $unlim_num_rows, $fields_meta, $is_count, $is_export, $is_func,
189 $is_analyse, $num_rows, $fields_cnt, $querytime, $pmaThemeImage,
190 $text_dir, $is_maint, $is_explain, $is_show, $showtable,
191 $printview, $url_query, $editable
195 if ($num_rows == 0) {
196 continue;
199 // With multiple results, operations are limied
200 $disp_mode = 'nnnn000000';
201 $is_limited_display = true;
203 // Collect the tables
204 $table_html .= $displayResultsObject->getTable(
205 $result, $disp_mode, $analyzed_sql, $is_limited_display
208 // Free the result to save the memory
209 $GLOBALS['dbi']->freeResult($result);
211 $sql_no++;
213 } while ($GLOBALS['dbi']->moreResults() && $GLOBALS['dbi']->nextResult());
215 return $table_html;
219 * Handle remembered sorting order, only for single table query
221 * @param string $db database name
222 * @param string $table table name
223 * @param array &$analyzed_sql the analyzed query
224 * @param string &$full_sql_query SQL query
226 * @return void
228 function PMA_handleSortOrder($db, $table, &$analyzed_sql, &$full_sql_query)
230 $pmatable = new PMA_Table($table, $db);
231 if (empty($analyzed_sql[0]['order_by_clause'])) {
232 $sorted_col = $pmatable->getUiProp(PMA_Table::PROP_SORTED_COLUMN);
233 if ($sorted_col) {
234 // retrieve the remembered sorting order for current table
235 $sql_order_to_append = ' ORDER BY ' . $sorted_col . ' ';
236 $full_sql_query = $analyzed_sql[0]['section_before_limit']
237 . $sql_order_to_append . $analyzed_sql[0]['limit_clause']
238 . ' ' . $analyzed_sql[0]['section_after_limit'];
240 // update the $analyzed_sql
241 $analyzed_sql[0]['section_before_limit'] .= $sql_order_to_append;
242 $analyzed_sql[0]['order_by_clause'] = $sorted_col;
244 } else {
245 // store the remembered table into session
246 $pmatable->setUiProp(
247 PMA_Table::PROP_SORTED_COLUMN,
248 $analyzed_sql[0]['order_by_clause']
254 * Append limit clause to SQL query
256 * @param string $full_sql_query SQL query
257 * @param array $analyzed_sql the analyzed query
258 * @param string $sql_limit_to_append clause to append
260 * @return string limit clause appended SQL query
262 function PMA_getSqlWithLimitClause($full_sql_query, $analyzed_sql,
263 $sql_limit_to_append
265 return $analyzed_sql[0]['section_before_limit'] . "\n"
266 . $sql_limit_to_append . $analyzed_sql[0]['section_after_limit'];
271 * Get column name from a drop SQL statement
273 * @param string $sql SQL query
275 * @return string $drop_column Name of the column
277 function PMA_getColumnNameInColumnDropSql($sql)
279 $tmpArray1 = explode('DROP', $sql);
280 $str_to_check = trim($tmpArray1[1]);
282 if (stripos($str_to_check, 'COLUMN') !== false) {
283 $tmpArray2 = explode('COLUMN', $str_to_check);
284 $str_to_check = trim($tmpArray2[1]);
287 $tmpArray3 = explode(' ', $str_to_check);
288 $str_to_check = trim($tmpArray3[0]);
290 $drop_column = str_replace(';', '', trim($str_to_check));
291 $drop_column = str_replace('`', '', $drop_column);
293 return $drop_column;
297 * Verify whether the result set contains all the columns
298 * of at least one unique key
300 * @param string $db database name
301 * @param string $table table name
302 * @param string $fields_meta meta fields
304 * @return boolean whether the result set contains a unique key
306 function PMA_resultSetContainsUniqueKey($db, $table, $fields_meta)
308 $resultSetColumnNames = array();
309 foreach ($fields_meta as $oneMeta) {
310 $resultSetColumnNames[] = $oneMeta->name;
312 foreach (PMA_Index::getFromTable($table, $db) as $index) {
313 if ($index->isUnique()) {
314 $indexColumns = $index->getColumns();
315 $numberFound = 0;
316 foreach ($indexColumns as $indexColumnName => $dummy) {
317 if (in_array($indexColumnName, $resultSetColumnNames)) {
318 $numberFound++;
321 if ($numberFound == count($indexColumns)) {
322 return true;
326 return false;
330 * Get the HTML for relational column dropdown
331 * During grid edit, if we have a relational field, returns the html for the
332 * dropdown
334 * @param string $db current database
335 * @param string $table current table
336 * @param string $column current column
337 * @param string $curr_value current selected value
339 * @return string $dropdown html for the dropdown
341 function PMA_getHtmlForRelationalColumnDropdown($db, $table, $column, $curr_value)
343 $foreigners = PMA_getForeigners($db, $table, $column);
345 $display_field = PMA_getDisplayField(
346 $foreigners[$column]['foreign_db'],
347 $foreigners[$column]['foreign_table']
350 $foreignData = PMA_getForeignData($foreigners, $column, false, '', '');
352 if ($foreignData['disp_row'] == null) {
353 //Handle the case when number of values
354 //is more than $cfg['ForeignKeyMaxLimit']
355 $_url_params = array(
356 'db' => $db,
357 'table' => $table,
358 'field' => $column
361 $dropdown = '<span class="curr_value">'
362 . htmlspecialchars($_REQUEST['curr_value'])
363 . '</span>'
364 . '<a href="browse_foreigners.php'
365 . PMA_generate_common_url($_url_params) . '"'
366 . ' target="_blank" class="browse_foreign" ' .'>'
367 . __('Browse foreign values')
368 . '</a>';
369 } else {
370 $dropdown = PMA_foreignDropdown(
371 $foreignData['disp_row'],
372 $foreignData['foreign_field'],
373 $foreignData['foreign_display'],
374 $curr_value,
375 $GLOBALS['cfg']['ForeignKeyMaxLimit']
377 $dropdown = '<select>' . $dropdown . '</select>';
380 return $dropdown;
384 * Get the HTML for the header of the page in print view
386 * @param string $db current database
387 * @param string $sql_query current sql query
388 * @param int $num_rows the number of rows in result
390 * @return string $header html for the header
392 function PMA_getHtmlForPrintViewHeader($db, $sql_query, $num_rows)
394 $hostname = '';
395 if ( $GLOBALS['cfg']['Server']['verbose']) {
396 $hostname = $GLOBALS['cfg']['Server']['verbose'];
397 } else {
398 $hostname = $GLOBALS['cfg']['Server']['host'];
399 if (! empty( $GLOBALS['cfg']['Server']['port'])) {
400 $hostname .= $GLOBALS['cfg']['Server']['port'];
404 $versions = "phpMyAdmin&nbsp;" . PMA_VERSION;
405 $versions .= "&nbsp;/&nbsp;";
406 $versions .= "MySQL&nbsp;" . PMA_MYSQL_STR_VERSION;
408 $header = '';
409 $header .= "<h1>" . __('SQL result') . "</h1>";
410 $header .= "<p>";
411 $header .= "<strong>" . __('Host:') . "</strong> $hostname<br />";
412 $header .= "<strong>" . __('Database:') . "</strong> "
413 . htmlspecialchars($db) . "<br />";
414 $header .= "<strong>" . __('Generation Time:') . "</strong> "
415 . PMA_Util::localisedDate() . "<br />";
416 $header .= "<strong>" . __('Generated by:') . "</strong> $versions<br />";
417 $header .= "<strong>" . __('SQL query:') . "</strong> "
418 . htmlspecialchars($sql_query) . ";";
419 if (isset($num_rows)) {
420 $header .= "<br />";
421 $header .= "<strong>" . __('Rows:') . "</strong> $num_rows";
423 $header .= "</p>";
425 return $header;
429 * Get the HTML for the profiling table and accompanying chart
431 * @param string $url_query the url query
432 * @param string $pma_token the pma token
433 * @param array $profiling_results array containing the profiling info
435 * @return string $profiling_table html for the profiling table and chart
437 function PMA_getHtmlForProfilingChart($url_query, $pma_token, $profiling_results)
439 $profiling_stats = array(
440 'total_time' => 0,
441 'states' => array(),
443 $profiling_table = '';
445 $profiling_table .= '<fieldset><legend>' . __('Profiling') . '</legend>' . "\n";
446 $profiling_table .= '<div style="float: left;">';
447 $profiling_table .= '<h3>' . __('Detailed profile') . '</h3>';
448 $profiling_table .= '<table id="profiletable"><thead>' . "\n";
449 $profiling_table .= ' <tr>' . "\n";
450 $profiling_table .= ' <th>' . __('Order')
451 . '<div class="sorticon"></div></th>' . "\n";
452 $profiling_table .= ' <th>' . __('State')
453 . PMA_Util::showMySQLDocu(
454 'general-thread-states', 'general-thread-states'
456 . '<div class="sorticon"></div></th>' . "\n";
457 $profiling_table .= ' <th>' . __('Time')
458 . '<div class="sorticon"></div></th>' . "\n";
459 $profiling_table .= ' </tr></thead><tbody>' . "\n";
461 $chart_json = Array();
462 $i = 1;
463 foreach ($profiling_results as $one_result) {
464 if (isset($profiling_stats['states'][ucwords($one_result['Status'])])) {
465 $profiling_stats['states'][ucwords($one_result['Status'])]['time'] += $one_result['Duration'];
466 $profiling_stats['states'][ucwords($one_result['Status'])]['calls']++;
467 } else {
468 $profiling_stats['states'][ucwords($one_result['Status'])] = array(
469 'total_time' => $one_result['Duration'],
470 'calls' => 1,
473 $profiling_stats['total_time'] += $one_result['Duration'];
475 $profiling_table .= ' <tr>' . "\n";
476 $profiling_table .= '<td>' . $i++ . '</td>' . "\n";
477 $profiling_table .= '<td>' . ucwords($one_result['Status']) . '</td>' . "\n";
478 $profiling_table .= '<td class="right">'
479 . (PMA_Util::formatNumber($one_result['Duration'], 3, 1))
480 . 's<span style="display:none;" class="rawvalue">'
481 . $one_result['Duration'] . '</span></td>' . "\n";
482 if (isset($chart_json[ucwords($one_result['Status'])])) {
483 $chart_json[ucwords($one_result['Status'])]
484 += $one_result['Duration'];
485 } else {
486 $chart_json[ucwords($one_result['Status'])]
487 = $one_result['Duration'];
491 $profiling_table .= '</tbody></table>' . "\n";
492 $profiling_table .= '</div>';
494 $profiling_table .= '<div style="float: left; margin-left:10px;">';
495 $profiling_table .= '<h3>' . __('Summary by state') . '</h3>';
496 $profiling_table .= '<table id="profilesummarytable"><thead>' . "\n";
497 $profiling_table .= ' <tr>' . "\n";
498 $profiling_table .= ' <th>' . __('State')
499 . PMA_Util::showMySQLDocu(
500 'general-thread-states', 'general-thread-states'
502 . '<div class="sorticon"></div></th>' . "\n";
503 $profiling_table .= ' <th>' . __('Total Time')
504 . '<div class="sorticon"></div></th>' . "\n";
505 $profiling_table .= ' <th>' . __('% Time')
506 . '<div class="sorticon"></div></th>' . "\n";
507 $profiling_table .= ' <th>' . __('Calls')
508 . '<div class="sorticon"></div></th>' . "\n";
509 $profiling_table .= ' <th>' . __('ΓΈ Time')
510 . '<div class="sorticon"></div></th>' . "\n";
511 $profiling_table .= ' </tr></thead><tbody>' . "\n";
512 foreach ($profiling_stats['states'] as $name => $stats) {
513 $profiling_table .= ' <tr>' . "\n";
514 $profiling_table .= '<td>' . $name . '</td>' . "\n";
515 $profiling_table .= '<td align="right">'
516 . PMA_Util::formatNumber($stats['total_time'], 3, 1)
517 . 's<span style="display:none;" class="rawvalue">'
518 . $stats['total_time'] . '</span></td>' . "\n";
519 $profiling_table .= '<td align="right">'
520 . PMA_Util::formatNumber(100 * ($stats['total_time'] / $profiling_stats['total_time']), 0, 2)
521 . '%</td>' . "\n";
522 $profiling_table .= '<td align="right">' . $stats['calls'] . '</td>' . "\n";
523 $profiling_table .= '<td align="right">'
524 . PMA_Util::formatNumber($stats['total_time'] / $stats['calls'], 3, 1)
525 . 's<span style="display:none;" class="rawvalue">'
526 . number_format($stats['total_time'] / $stats['calls'], 8, '.', '')
527 . '</span></td>' . "\n";
528 $profiling_table .= ' </tr>' . "\n";
531 $profiling_table .= '</tbody></table>' . "\n";
533 $profiling_table .= <<<EOT
534 <script type="text/javascript">
535 pma_token = '$pma_token';
536 url_query = '$url_query';
537 </script>
538 EOT;
539 $profiling_table .= "</div>";
541 //require_once 'libraries/chart.lib.php';
542 $profiling_table .= '<div id="profilingChartData" style="display:none;">';
543 $profiling_table .= json_encode($chart_json);
544 $profiling_table .= '</div>';
545 $profiling_table .= '<div id="profilingchart" style="display:none;">';
546 $profiling_table .= '</div>';
547 $profiling_table .= '<script type="text/javascript">';
548 $profiling_table .= 'makeProfilingChart();';
549 $profiling_table .= 'initProfilingTables();';
550 $profiling_table .= '</script>';
551 $profiling_table .= '</fieldset>' . "\n";
553 return $profiling_table;
557 * Get the HTML for the enum column dropdown
558 * During grid edit, if we have a enum field, returns the html for the
559 * dropdown
561 * @param string $db current database
562 * @param string $table current table
563 * @param string $column current column
564 * @param string $curr_value currently selected value
566 * @return string $dropdown html for the dropdown
568 function PMA_getHtmlForEnumColumnDropdown($db, $table, $column, $curr_value)
570 $values = PMA_getValuesForColumn($db, $table, $column);
571 $dropdown = '<option value="">&nbsp;</option>';
572 $dropdown .= PMA_getHtmlForOptionsList($values, array($curr_value));
573 $dropdown = '<select>' . $dropdown . '</select>';
574 return $dropdown;
578 * Get the HTML for the set column dropdown
579 * During grid edit, if we have a set field, returns the html for the
580 * dropdown
582 * @param string $db current database
583 * @param string $table current table
584 * @param string $column current column
585 * @param string $curr_value currently selected value
587 * @return string $dropdown html for the set column
589 function PMA_getHtmlForSetColumn($db, $table, $column, $curr_value)
591 $values = PMA_getValuesForColumn($db, $table, $column);
592 $dropdown = '';
594 //converts characters of $curr_value to HTML entities
595 $converted_curr_value = htmlentities(
596 $curr_value, ENT_COMPAT, "UTF-8"
599 $selected_values = explode(',', $converted_curr_value);
600 $dropdown .= PMA_getHtmlForOptionsList($values, $selected_values);
602 $select_size = (sizeof($values) > 10) ? 10 : sizeof($values);
603 $dropdown = '<select multiple="multiple" size="' . $select_size . '">'
604 . $dropdown . '</select>';
606 return $dropdown;
610 * Get all the values for a enum column or set column in a table
612 * @param string $db current database
613 * @param string $table current table
614 * @param string $column current column
616 * @return array $values array containing the value list for the column
618 function PMA_getValuesForColumn($db, $table, $column)
620 $field_info_query = $GLOBALS['dbi']->getColumnsSql($db, $table, $column);
622 $field_info_result = $GLOBALS['dbi']->fetchResult(
623 $field_info_query, null, null, null, PMA_DatabaseInterface::QUERY_STORE
626 $values = PMA_Util::parseEnumSetValues($field_info_result[0]['Type']);
628 return $values;
632 * Get HTML for options list
634 * @param array $values set of values
635 * @param array $selected_values currently selected values
637 * @return string $options HTML for options list
639 function PMA_getHtmlForOptionsList($values, $selected_values)
641 $options = '';
642 foreach ($values as $value) {
643 $options .= '<option value="' . $value . '"';
644 if (in_array($value, $selected_values, true)) {
645 $options .= ' selected="selected" ';
647 $options .= '>' . $value . '</option>';
649 return $options;