/** * Calls the parser on a query * * @param string $sql_query the query to parse * @param string $db the current database * * @return array * * @access public */ function PMA_parseAnalyze($sql_query, $db) { // @todo: move to returned results (also in all the calling chain) $GLOBALS['unparsed_sql'] = $sql_query; // Get details about the SQL query. $analyzed_sql_results = SqlParser\Utils\Query::getAll($sql_query); extract($analyzed_sql_results); $table = ''; // If the targeted table (and database) are different than the ones that is // currently browsed, edit `$db` and `$table` to match them so other elements // (page headers, links, navigation panel) can be updated properly. if (!empty($analyzed_sql_results['select_tables'])) { // Previous table and database name is stored to check if it changed. $prev_db = $db; if (count($analyzed_sql_results['select_tables']) > 1) { /** * @todo if there are more than one table name in the Select: * - do not extract the first table name * - do not show a table name in the page header * - do not display the sub-pages links) */ $table = ''; } else { $table = $analyzed_sql_results['select_tables'][0][0]; if (!empty($analyzed_sql_results['select_tables'][0][1])) { $db = $analyzed_sql_results['select_tables'][0][1]; } } // There is no point checking if a reload is required if we already decided // to reload. Also, no reload is required for AJAX requests. $response = Response::getInstance(); if (empty($reload) && !$response->isAjax()) { // NOTE: Database names are case-insensitive. $reload = strcasecmp($db, $prev_db) != 0; } // Updating the array. $analyzed_sql_results['reload'] = $reload; } return array($analyzed_sql_results, $db, $table); }
/** * Get url sql query without conditions to shorten URLs * * @param array $analyzed_sql_results analyzed sql results * * @return string $url_sql analyzed sql query * * @access private * * @see _getTableBody() */ private function _getUrlSqlQuery($analyzed_sql_results) { if ($analyzed_sql_results['querytype'] != 'SELECT' || mb_strlen($this->__get('sql_query')) < 200) { return $this->__get('sql_query'); } $query = 'SELECT ' . SqlParser\Utils\Query::getClause($analyzed_sql_results['statement'], $analyzed_sql_results['parser']->list, 'SELECT'); $from_clause = SqlParser\Utils\Query::getClause($analyzed_sql_results['statement'], $analyzed_sql_results['parser']->list, 'FROM'); if (!empty($from_clause)) { $query .= ' FROM ' . $from_clause; } return $query; }
/** * 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 $justBrowsing whether just browsing or not * @param string $db the current database * @param string $table the current table * @param array $analyzed_sql_results the analyzed query and other variables set * after analyzing the query * * @return int $unlim_num_rows unlimited number of rows */ function PMA_countQueryResults($num_rows, $justBrowsing, $db, $table, $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['tmpval']['max_rows'] = 'all'; $unlim_num_rows = $num_rows; } elseif ($analyzed_sql_results['querytype'] == 'SELECT' || $analyzed_sql_results['is_subquery']) { // 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) { // Get row count (is approximate for InnoDB) $unlim_num_rows = $GLOBALS['dbi']->getTable($db, $table)->countRecords(); /** * @todo Can we know at this point that this is InnoDB, * (in this case there would be no need for getting * an exact count)? */ if ($unlim_num_rows < $GLOBALS['cfg']['MaxExactCount']) { // Get the exact count if approximate count // is less than MaxExactCount /** * @todo In countRecords(), MaxExactCount is also verified, * so can we avoid checking it twice? */ $unlim_num_rows = $GLOBALS['dbi']->getTable($db, $table)->countRecords(true); } } else { // The SQL_CALC_FOUND_ROWS option of the SELECT statement is used. // For UNION statements, only a SQL_CALC_FOUND_ROWS is required // after the first SELECT. $count_query = SqlParser\Utils\Query::replaceClause($analyzed_sql_results['statement'], $analyzed_sql_results['parser']->list, 'SELECT SQL_CALC_FOUND_ROWS', null, true); // Another LIMIT clause is added to avoid long delays. // A complete result will be returned anyway, but the LIMIT would // stop the query as soon as the result that is required has been // computed. if (empty($analyzed_sql_results['union'])) { $count_query .= ' LIMIT 1'; } // Running the count query. $GLOBALS['dbi']->tryQuery($count_query); $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; }
foreach ($parser->statements[0]->from as $from) { if (!empty($from->table) && !empty($from->alias)) { $aliases[$from->alias] = $from->table; // We remove the alias of the table because they are going to // be replaced anyway. $from->alias = null; $from->expr = null; // Force rebuild. } } // Rebuilding the SELECT and FROM clauses. $replaces = array(array('FROM', 'FROM ' . SqlParser\Components\ExpressionArray::build($parser->statements[0]->from))); // Checking if the WHERE clause has to be replaced. if (!empty($where_clause) && is_array($where_clause)) { $replaces[] = array('WHERE', 'WHERE (' . implode(') OR (', $where_clause) . ')'); } // Preparing to remove the LIMIT clause. $replaces[] = array('LIMIT', ''); // Replacing the clauses. $sql_query = SqlParser\Utils\Query::replaceClauses($parser->statements[0], $parser->list, $replaces); // Removing the aliases by finding the alias followed by a dot. $tokens = SqlParser\Lexer::getTokens($sql_query); foreach ($aliases as $alias => $table) { $tokens = SqlParser\Utils\Tokens::replaceTokens($tokens, array(array('value_str' => $alias), array('type' => SqlParser\Token::TYPE_OPERATOR, 'value_str' => '.')), array()); } $sql_query = SqlParser\TokensList::build($tokens); } echo PMA_Util::getMessage(PMA_Message::success()); } $export_type = 'table'; require_once 'libraries/display_export.inc.php';
/** * Checks if ROLLBACK is possible for a SQL query or not. * * @param string $sql_query SQL query * * @return bool */ function PMA_checkIfRollbackPossible($sql_query) { $parser = new SqlParser\Parser($sql_query); if (empty($parser->statements[0])) { return false; } $statement = $parser->statements[0]; // Check if query is supported. if (!($statement instanceof SqlParser\Statements\InsertStatement || $statement instanceof SqlParser\Statements\UpdateStatement || $statement instanceof SqlParser\Statements\DeleteStatement || $statement instanceof SqlParser\Statements\ReplaceStatement)) { return false; } // Get table_references from the query. $tables = SqlParser\Utils\Query::getTables($statement); // Check if each table is 'InnoDB'. foreach ($tables as $table) { if (!PMA_isTableTransactional($table)) { return false; } } return true; }
<?php /* vim: set expandtab sw=4 ts=4 sts=4: */ /** * Parse and analyse a SQL query * * @package PhpMyAdmin */ if (!defined('PHPMYADMIN')) { exit; } $GLOBALS['unparsed_sql'] = $sql_query; // Get details about the SQL query. $analyzed_sql_results = SqlParser\Utils\Query::getAll($sql_query); // TODO: Refactor this. extract($analyzed_sql_results); // If the targeted table (and database) are different than the ones that is // currently browsed, edit `$db` and `$table` to match them so other elements // (page headers, links, navigation panel) can be updated properly. if (!empty($analyzed_sql_results['select_tables'])) { // Previous table and database name is stored to check if it changed. $prev_db = $db; if (count($analyzed_sql_results['select_tables']) > 1) { /** * @todo if there are more than one table name in the Select: * - do not extract the first table name * - do not show a table name in the page header * - do not display the sub-pages links) */ $table = ''; } else {