From 8045c7ef6c576a3692da8fd848a34d1763151da9 Mon Sep 17 00:00:00 2001 From: Spun Nakandala Date: Sun, 30 Jun 2013 21:14:04 +0530 Subject: [PATCH 1/5] Added methods PMA_cleanupRelations, PMA_countQueryResults, PMA_executeTheQuery --- libraries/parse_analyze.inc.php | 1 + libraries/sql.lib.php | 234 +++++++++++++++++++++++++++++++- sql.php | 191 +++----------------------- 3 files changed, 249 insertions(+), 177 deletions(-) diff --git a/libraries/parse_analyze.inc.php b/libraries/parse_analyze.inc.php index 75b965218d..c8dab441a3 100644 --- a/libraries/parse_analyze.inc.php +++ b/libraries/parse_analyze.inc.php @@ -76,6 +76,7 @@ $is_select = isset($analyzed_sql[0]['queryflags']['select_from']); // aggregates all the results into one array $analyzed_sql_results = array( + "parsed_sql" => $parsed_sql, "analyzed_sql" => $analyzed_sql, "reload" => $reload, "drop_database" => $drop_database, diff --git a/libraries/sql.lib.php b/libraries/sql.lib.php index 3946efe3b5..89afc8137b 100644 --- a/libraries/sql.lib.php +++ b/libraries/sql.lib.php @@ -1115,14 +1115,11 @@ function PMA_getDefaultSqlQueryForBrowse($db, $table) * @param boolean $is_gotofile whether goto file or not * @param String $goto goto page url * @param String $table current table - * @param String $active_page active page url * @param String $error error after executing the query * * @return void */ -function PMA_handleQueryExecuteError($is_gotofile, $goto, $table, $active_page, - $error -) { +function PMA_handleQueryExecuteError($is_gotofile, $goto, $table, $error) { if ($is_gotofile) { if (strpos($goto, 'db_') === 0 && strlen($table)) { $table = ''; @@ -1144,7 +1141,7 @@ function PMA_handleQueryExecuteError($is_gotofile, $goto, $table, $active_page, * @param String $bkm_user the bookmarking user * @param String $import_text import text * @param String $bkm_label bookmark label - * @param boolean $bkm_replace whether to rep;ace existing bookmarks + * @param boolean $bkm_replace whether to replace existing bookmarks * * @return void */ @@ -1247,4 +1244,231 @@ function PMA_hasCurrentDbChanged($db) return $reload; } + +/** + * If a table, database or column gets dropped, clean comments. + * + * @param String $db current database + * @param String $table current table + * @param String $dropped_column current table + * @param bool $purge + * @param array $extra_data + * + * @return array $extra_data + */ +function PMA_cleanupRelations($db, $table, $dropped_column, $purge, $extra_data) +{ + include_once 'libraries/relation_cleanup.lib.php'; + + if (isset($purge) && $purge == 1) { + if (strlen($table) && strlen($db)) { + PMA_relationsCleanupTable($db, $table); + } elseif (strlen($db)) { + PMA_relationsCleanupDatabase($db); + } + } + + if (isset($dropped_column) + && !empty($dropped_column) + && strlen($db) + && strlen($table) + ) { + PMA_relationsCleanupColumn($db, $table, $dropped_column); + if(isset($extra_data)) { + // to refresh the list of indexes (Ajax mode) + $extra_data['indexes_list'] = PMA_Index::getView($table, $db); + } + } + + return $extra_data; +} + +/** + * Function to count the total number of rows for the same 'SELECT' query without + * the 'LIMIT' clause that may have been programatically added + * + * @param int $num_rows number of rows affected/changed by the query + * @param bool $is_select whether the query is SELECT or not + * @param bool $justBrowsing whether just browsing or not + * @param string $db the current database + * @param string $table the current table + * @param array $parsed_sql parsed sql + * @param array $analyzed_sql_results the analyzed query and other varibles set + * after analyzing the query + * + * @return int $unlim_num_rows unlimited number of rows + */ +function PMA_countQueryResults($num_rows, $is_select, $justBrowsing, + $db, $table, $parsed_sql, $analyzed_sql_results +) { + if (PMA_isAppendLimitClause($analyzed_sql_results)) + { + // if we did not append a limit, set this to get a correct + // "Showing rows..." message + // $_SESSION['tmp_user_values']['max_rows'] = 'all'; + $unlim_num_rows = $num_rows; + } elseif ($is_select) { + // c o u n t q u e r y + + // If we are "just browsing", there is only one table, + // and no WHERE clause (or just 'WHERE 1 '), + // we do a quick count (which uses MaxExactCount) because + // SQL_CALC_FOUND_ROWS is not quick on large InnoDB tables + + // However, do not count again if we did it previously + // due to $find_real_end == true + if ($justBrowsing) { + $unlim_num_rows = PMA_Table::countRecords( + $db, + $table, + $force_exact = true + ); + + } else { + // add select expression after the SQL_CALC_FOUND_ROWS + + // for UNION, just adding SQL_CALC_FOUND_ROWS + // after the first SELECT works. + + // take the left part, could be: + // SELECT + // (SELECT + + $analyzed_sql = $analyzed_sql_results['analyzed_sql']; + + $count_query = PMA_SQP_format( + $parsed_sql, + 'query_only', + 0, + $analyzed_sql[0]['position_of_first_select'] + 1 + ); + $count_query .= ' SQL_CALC_FOUND_ROWS '; + // add everything that was after the first SELECT + $count_query .= PMA_SQP_format( + $parsed_sql, + 'query_only', + $analyzed_sql[0]['position_of_first_select'] + 1 + ); + // ensure there is no semicolon at the end of the + // count query because we'll probably add + // a LIMIT 1 clause after it + $count_query = rtrim($count_query); + $count_query = rtrim($count_query, ';'); + + // if using SQL_CALC_FOUND_ROWS, add a LIMIT to avoid + // long delays. Returned count will be complete anyway. + // (but a LIMIT would disrupt results in an UNION) + + if (! isset($analyzed_sql[0]['queryflags']['union'])) { + $count_query .= ' LIMIT 1'; + } + + // run the count query + + $GLOBALS['dbi']->tryQuery($count_query); + // if (mysql_error()) { + // void. + // I tried the case + // (SELECT `User`, `Host`, `Db`, `Select_priv` FROM `db`) + // UNION (SELECT `User`, `Host`, "%" AS "Db", + // `Select_priv` + // FROM `user`) ORDER BY `User`, `Host`, `Db`; + // and although the generated count_query is wrong + // the SELECT FOUND_ROWS() work! (maybe it gets the + // count from the latest query that worked) + // + // another case where the count_query is wrong: + // SELECT COUNT(*), f1 from t1 group by f1 + // and you click to sort on count(*) + // } + $unlim_num_rows = $GLOBALS['dbi']->fetchValue('SELECT FOUND_ROWS()'); + } // end else "just browsing" + } else {// not $is_select + $unlim_num_rows = 0; + } + + return $unlim_num_rows; +} + +/** + * Function to handle all aspects relating to executing the query + * + * @param array $analyzed_sql_results + * @param String $full_sql_query full sql query + * @param boolean $is_gotofile whether to go to a file + * @param String $goto goto page url + * @param String $db current database + * @param String $table current table + * @param boolean $find_real_end whether to find the real end + * @param String $import_text sql command + * @param String $bkm_user bookmarking user + * + * @return mixed + */ +function PMA_executeTheQuery($analyzed_sql_results, $full_sql_query, $is_gotofile, + $goto, $db, $table, $find_real_end, $import_text, $bkm_user, $extra_data +) { + // Only if we ask to see the php code + if (isset($GLOBALS['show_as_php']) || ! empty($GLOBALS['validatequery'])) { + $result = null; + $num_rows = 0; + $unlim_num_rows = 0; + } else { // If we don't ask to see the php code + if (isset($_SESSION['profiling']) && PMA_Util::profilingSupported()) { + $GLOBALS['dbi']->query('SET PROFILING=1;'); + } + + $result = PMA_executeQueryAndStoreResults($full_sql_query); + + // Displays an error message if required and stop parsing the script + $error = $GLOBALS['dbi']->getError(); + if ($error) { + PMA_handleQueryExecuteError($is_gotofile, $goto, $table, $error + ); + } + + // If there are no errors and bookmarklabel was given, + // store the query as a bookmark + if (! empty($_POST['bkm_label']) && ! empty($import_text)) { + PMA_storeTheQueryAsBookmark($db, $bkm_user, + $import_text, $_POST['bkm_label'], + isset($_POST['bkm_replace']) ? $_POST['bkm_replace'] : null + ); + } // end store bookmarks + + // Gets the number of rows affected/returned + // (This must be done immediately after the query because + // mysql_affected_rows() reports about the last query done) + $num_rows = PMA_getNumberOfRowsAffectedOrChanged( + $analyzed_sql_results['is_affected'], $result, + isset($num_rows) ? $num_rows : null + ); + + // Grabs the profiling results + if (isset($_SESSION['profiling']) && PMA_Util::profilingSupported()) { + $profiling_results = $GLOBALS['dbi']->fetchResult('SHOW PROFILE;'); + } + + $justBrowsing = PMA_isJustBrowsing( + $analyzed_sql_results,isset($find_real_end) ? $find_real_end : null + ); + + $unlim_num_rows = PMA_countQueryResults($num_rows, + $analyzed_sql_results['is_select'], $justBrowsing, $db, + $table, $analyzed_sql_results['parsed_sql'], $analyzed_sql_results + ); + + $extra_data = PMA_cleanupRelations( + isset($db) ? $db : '', isset($table) ? $table : '', + isset($_REQUEST['dropped_column']) ? $_REQUEST['dropped_column'] : null, + isset($_REQUEST['purge']) ? $_REQUEST['purge'] : null, + isset($extra_data) ? $extra_data : null + ); + } + + return array($result, $num_rows, $unlim_num_rows, + isset($profiling_results) ? $profiling_results : null, + isset($justBrowsing) ? $justBrowsing : null, $extra_data + ); +} ?> diff --git a/sql.php b/sql.php index e149618cea..38eca62396 100644 --- a/sql.php +++ b/sql.php @@ -196,181 +196,28 @@ if (PMA_isAppendLimitClause($analyzed_sql_results)) { ); } +$is_procedure = false; +// Since multiple query execution is anyway handled, +// ignore the WHERE clause of the first sql statement +// which might contain a phrase like 'call ' +if (preg_match("/\bcall\b/i", $full_sql_query) + && empty($analyzed_sql[0]['where_clause']) +) { + $is_procedure = true; +} + $reload = PMA_hasCurrentDbChanged($db); -// E x e c u t e t h e q u e r y +// Execute the query +list($result, $num_rows, $unlim_num_rows, $profiling_results, + $justBrowsing, $extra_data +) = PMA_executeTheQuery( + $analyzed_sql_results, $full_sql_query, $is_gotofile, $goto, $db, $table, + isset($find_real_end) ? $find_real_end : null, + isset($import_text) ? $import_text : null, $cfg['Bookmark']['user'], + isset($extra_data) ? $extra_data : null +); -// Only if we didn't ask to see the php code -if (isset($GLOBALS['show_as_php']) || ! empty($GLOBALS['validatequery'])) { - unset($result); - $num_rows = 0; - $unlim_num_rows = 0; -} else { - if (isset($_SESSION['profiling']) && PMA_Util::profilingSupported()) { - $GLOBALS['dbi']->query('SET PROFILING=1;'); - } - - $result = PMA_executeQueryAndStoreResults($full_sql_query); - - $is_procedure = false; - // Since multiple query execution is anyway handled, - // ignore the WHERE clause of the first sql statement - // which might contain a phrase like 'call ' - if (preg_match("/\bcall\b/i", $full_sql_query) - && empty($analyzed_sql[0]['where_clause']) - ) { - $is_procedure = true; - } - - // Displays an error message if required and stop parsing the script - $error = $GLOBALS['dbi']->getError(); - if ($error) { - PMA_handleQueryExecuteError($is_gotofile, $goto, $table, $active_page, - $error - ); - } - unset($error); - - // If there are no errors and bookmarklabel was given, - // store the query as a bookmark - if (! empty($bkm_label) && ! empty($import_text)) { - PMA_storeTheQueryAsBookmark($db, $cfg['Bookmark']['user'], - $import_text, $bkm_label, isset($bkm_replace) ? $bkm_replace : null - ); - $bookmark_created = true; - } // end store bookmarks - - // Gets the number of rows affected/returned - // (This must be done immediately after the query because - // mysql_affected_rows() reports about the last query done) - $num_rows = PMA_getNumberOfRowsAffectedOrChanged($is_affected, $result, - isset($num_rows) ? $num_rows : null - ); - - // Grabs the profiling results - if (isset($_SESSION['profiling']) && PMA_Util::profilingSupported()) { - $profiling_results = $GLOBALS['dbi']->fetchResult('SHOW PROFILE;'); - } - - - // Counts the total number of rows for the same 'SELECT' query without the - // 'LIMIT' clause that may have been programatically added - $justBrowsing = false; - if (empty($sql_limit_to_append)) { - $unlim_num_rows = $num_rows; - // if we did not append a limit, set this to get a correct - // "Showing rows..." message - // $_SESSION['tmp_user_values']['max_rows'] = 'all'; - } elseif ($is_select) { - - // c o u n t q u e r y - - // If we are "just browsing", there is only one table, - // and no WHERE clause (or just 'WHERE 1 '), - // we do a quick count (which uses MaxExactCount) because - // SQL_CALC_FOUND_ROWS is not quick on large InnoDB tables - - // However, do not count again if we did it previously - // due to $find_real_end == true - if (PMA_isJustBrowsing( - $analyzed_sql_results,isset($find_real_end) ? $find_real_end : null) - ) { - $justBrowsing = true; - $unlim_num_rows = PMA_Table::countRecords( - $db, - $table, - $force_exact = true - ); - - } else { - // add select expression after the SQL_CALC_FOUND_ROWS - - // for UNION, just adding SQL_CALC_FOUND_ROWS - // after the first SELECT works. - - // take the left part, could be: - // SELECT - // (SELECT - $count_query = PMA_SQP_format( - $parsed_sql, - 'query_only', - 0, - $analyzed_sql[0]['position_of_first_select'] + 1 - ); - $count_query .= ' SQL_CALC_FOUND_ROWS '; - // add everything that was after the first SELECT - $count_query .= PMA_SQP_format( - $parsed_sql, - 'query_only', - $analyzed_sql[0]['position_of_first_select'] + 1 - ); - // ensure there is no semicolon at the end of the - // count query because we'll probably add - // a LIMIT 1 clause after it - $count_query = rtrim($count_query); - $count_query = rtrim($count_query, ';'); - - // if using SQL_CALC_FOUND_ROWS, add a LIMIT to avoid - // long delays. Returned count will be complete anyway. - // (but a LIMIT would disrupt results in an UNION) - - if (! isset($analyzed_sql[0]['queryflags']['union'])) { - $count_query .= ' LIMIT 1'; - } - - // run the count query - - $GLOBALS['dbi']->tryQuery($count_query); - // if (mysql_error()) { - // void. - // I tried the case - // (SELECT `User`, `Host`, `Db`, `Select_priv` FROM `db`) - // UNION (SELECT `User`, `Host`, "%" AS "Db", - // `Select_priv` - // FROM `user`) ORDER BY `User`, `Host`, `Db`; - // and although the generated count_query is wrong - // the SELECT FOUND_ROWS() work! (maybe it gets the - // count from the latest query that worked) - // - // another case where the count_query is wrong: - // SELECT COUNT(*), f1 from t1 group by f1 - // and you click to sort on count(*) - // } - $unlim_num_rows = $GLOBALS['dbi']->fetchValue('SELECT FOUND_ROWS()'); - } // end else "just browsing" - - } else { // not $is_select - $unlim_num_rows = 0; - } // end rows total count - - // if a table or database gets dropped, check column comments. - if (isset($purge) && $purge == '1') { - /** - * Cleanup relations. - */ - include_once 'libraries/relation_cleanup.lib.php'; - - if (strlen($table) && strlen($db)) { - PMA_relationsCleanupTable($db, $table); - } elseif (strlen($db)) { - PMA_relationsCleanupDatabase($db); - } else { - // VOID. No DB/Table gets deleted. - } // end if relation-stuff - } // end if ($purge) - - // If a column gets dropped, do relation magic. - if (isset($dropped_column) - && strlen($db) - && strlen($table) - && ! empty($dropped_column) - ) { - include_once 'libraries/relation_cleanup.lib.php'; - PMA_relationsCleanupColumn($db, $table, $dropped_column); - // to refresh the list of indexes (Ajax mode) - $extra_data['indexes_list'] = PMA_Index::getView($table, $db); - } // end if column was dropped -} // end else "didn't ask to see php code" // No rows returned -> move back to the calling page if ((0 == $num_rows && 0 == $unlim_num_rows) || $is_affected) { From 7cdf9ebeb80a373d129e9eca81a355e9541bd2d3 Mon Sep 17 00:00:00 2001 From: Spun Nakandala Date: Mon, 1 Jul 2013 19:56:37 +0530 Subject: [PATCH 2/5] removed unused parameters and code --- libraries/sql.lib.php | 15 +++------------ sql.php | 2 +- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/libraries/sql.lib.php b/libraries/sql.lib.php index 89afc8137b..d36ef7376b 100644 --- a/libraries/sql.lib.php +++ b/libraries/sql.lib.php @@ -1113,20 +1113,13 @@ function PMA_getDefaultSqlQueryForBrowse($db, $table) * Responds an error when an error happens when executing the query * * @param boolean $is_gotofile whether goto file or not - * @param String $goto goto page url - * @param String $table current table * @param String $error error after executing the query * * @return void */ -function PMA_handleQueryExecuteError($is_gotofile, $goto, $table, $error) { +function PMA_handleQueryExecuteError($is_gotofile, $error) { if ($is_gotofile) { - if (strpos($goto, 'db_') === 0 && strlen($table)) { - $table = ''; - } - $active_page = $goto; $message = PMA_Message::rawError($error); - $response = PMA_Response::getInstance(); $response->isSuccess(false); $response->addJSON('message', $message); @@ -1396,7 +1389,6 @@ function PMA_countQueryResults($num_rows, $is_select, $justBrowsing, * @param array $analyzed_sql_results * @param String $full_sql_query full sql query * @param boolean $is_gotofile whether to go to a file - * @param String $goto goto page url * @param String $db current database * @param String $table current table * @param boolean $find_real_end whether to find the real end @@ -1406,7 +1398,7 @@ function PMA_countQueryResults($num_rows, $is_select, $justBrowsing, * @return mixed */ function PMA_executeTheQuery($analyzed_sql_results, $full_sql_query, $is_gotofile, - $goto, $db, $table, $find_real_end, $import_text, $bkm_user, $extra_data + $db, $table, $find_real_end, $import_text, $bkm_user, $extra_data ) { // Only if we ask to see the php code if (isset($GLOBALS['show_as_php']) || ! empty($GLOBALS['validatequery'])) { @@ -1423,8 +1415,7 @@ function PMA_executeTheQuery($analyzed_sql_results, $full_sql_query, $is_gotofil // Displays an error message if required and stop parsing the script $error = $GLOBALS['dbi']->getError(); if ($error) { - PMA_handleQueryExecuteError($is_gotofile, $goto, $table, $error - ); + PMA_handleQueryExecuteError($is_gotofile, $error); } // If there are no errors and bookmarklabel was given, diff --git a/sql.php b/sql.php index 38eca62396..6f7b98316d 100644 --- a/sql.php +++ b/sql.php @@ -212,7 +212,7 @@ $reload = PMA_hasCurrentDbChanged($db); list($result, $num_rows, $unlim_num_rows, $profiling_results, $justBrowsing, $extra_data ) = PMA_executeTheQuery( - $analyzed_sql_results, $full_sql_query, $is_gotofile, $goto, $db, $table, + $analyzed_sql_results, $full_sql_query, $is_gotofile, $db, $table, isset($find_real_end) ? $find_real_end : null, isset($import_text) ? $import_text : null, $cfg['Bookmark']['user'], isset($extra_data) ? $extra_data : null From 102350fd03d979e8084429376fed07cd377f91b5 Mon Sep 17 00:00:00 2001 From: Spun Nakandala Date: Mon, 1 Jul 2013 20:00:03 +0530 Subject: [PATCH 3/5] comment corrected --- libraries/sql.lib.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/sql.lib.php b/libraries/sql.lib.php index d36ef7376b..b3a7f82a85 100644 --- a/libraries/sql.lib.php +++ b/libraries/sql.lib.php @@ -1243,7 +1243,7 @@ function PMA_hasCurrentDbChanged($db) * * @param String $db current database * @param String $table current table - * @param String $dropped_column current table + * @param String $dropped_column dropped column if any * @param bool $purge * @param array $extra_data * From b267d590126cfcef2d686cea531cbe064bfaca0e Mon Sep 17 00:00:00 2001 From: Spun Nakandala Date: Mon, 1 Jul 2013 20:02:00 +0530 Subject: [PATCH 4/5] statement transferred to the else condition --- sql.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sql.php b/sql.php index 6f7b98316d..d04e2b5d97 100644 --- a/sql.php +++ b/sql.php @@ -196,7 +196,6 @@ if (PMA_isAppendLimitClause($analyzed_sql_results)) { ); } -$is_procedure = false; // Since multiple query execution is anyway handled, // ignore the WHERE clause of the first sql statement // which might contain a phrase like 'call ' @@ -204,6 +203,8 @@ if (preg_match("/\bcall\b/i", $full_sql_query) && empty($analyzed_sql[0]['where_clause']) ) { $is_procedure = true; +} else { + $is_procedure = false; } $reload = PMA_hasCurrentDbChanged($db); From f17221af8e2e2117600b103cb97f24af307c0c2a Mon Sep 17 00:00:00 2001 From: Spun Nakandala Date: Mon, 1 Jul 2013 20:19:13 +0530 Subject: [PATCH 5/5] invalid $unlim_num_rows error corrected --- libraries/sql.lib.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/sql.lib.php b/libraries/sql.lib.php index b3a7f82a85..d873f63fd0 100644 --- a/libraries/sql.lib.php +++ b/libraries/sql.lib.php @@ -1294,7 +1294,7 @@ function PMA_cleanupRelations($db, $table, $dropped_column, $purge, $extra_data) function PMA_countQueryResults($num_rows, $is_select, $justBrowsing, $db, $table, $parsed_sql, $analyzed_sql_results ) { - if (PMA_isAppendLimitClause($analyzed_sql_results)) + if (!PMA_isAppendLimitClause($analyzed_sql_results)) { // if we did not append a limit, set this to get a correct // "Showing rows..." message