Merge pull request #472 from scnakandala/gsoc_2013
Refactoring SQL executor
This commit is contained in:
commit
a3980f549d
@ -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,
|
||||
|
||||
@ -1113,23 +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 $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, $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);
|
||||
@ -1144,7 +1134,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 +1237,229 @@ 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 dropped column if any
|
||||
* @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 $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,
|
||||
$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, $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
|
||||
);
|
||||
}
|
||||
?>
|
||||
|
||||
192
sql.php
192
sql.php
@ -196,181 +196,29 @@ if (PMA_isAppendLimitClause($analyzed_sql_results)) {
|
||||
);
|
||||
}
|
||||
|
||||
// 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;
|
||||
} else {
|
||||
$is_procedure = false;
|
||||
}
|
||||
|
||||
$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, $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) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user