diff options
author | Christian Foellmann <foellmann@foe-services.de> | 2014-12-23 14:48:13 +0300 |
---|---|---|
committer | Christian Foellmann <foellmann@foe-services.de> | 2014-12-23 14:48:13 +0300 |
commit | 2bfb20e57418ebf396149782be9f98e868fe8608 (patch) | |
tree | a5ec4011410970603d3f462a535a5a359057fa77 /libraries/import.lib.php | |
parent | 20f1bf77c8281efc675a14e0f6bf52f657dabd9a (diff) |
UPDATE 4.3.34.3.3
Diffstat (limited to 'libraries/import.lib.php')
-rw-r--r-- | libraries/import.lib.php | 695 |
1 files changed, 646 insertions, 49 deletions
diff --git a/libraries/import.lib.php b/libraries/import.lib.php index 7b56aa2c92..355dbd1c8b 100644 --- a/libraries/import.lib.php +++ b/libraries/import.lib.php @@ -104,11 +104,14 @@ function PMA_importRunQuery($sql = '', $full = '', $controluser = false, // USE query changes the database, son need to track // while running multiple queries $is_use_query - = (stripos($import_run_buffer['sql'], "use ") !== false) + = (/*overload*/mb_stripos($import_run_buffer['sql'], "use ") !== false) ? true : false; - $max_sql_len = max($max_sql_len, strlen($import_run_buffer['sql'])); + $max_sql_len = max( + $max_sql_len, + /*overload*/mb_strlen($import_run_buffer['sql']) + ); if (! $sql_query_disabled) { $sql_query .= $import_run_buffer['full']; } @@ -123,7 +126,6 @@ function PMA_importRunQuery($sql = '', $full = '', $controluser = false, ); $error = true; } else { - $executed_queries++; $pattern = '/^[\s]*(SELECT|SHOW|HANDLER)/i'; @@ -202,14 +204,9 @@ function PMA_importRunQuery($sql = '', $full = '', $controluser = false, ); } - if (($a_num_rows > 0) || $is_use_query) { - $sql_data['valid_sql'][] = $import_run_buffer['sql']; - if (! isset($sql_data['valid_queries'])) { - $sql_data['valid_queries'] = 0; - } - $sql_data['valid_queries']++; - } - + $sql_data = updateSqlData( + $sql_data, $a_num_rows, $is_use_query, $import_run_buffer + ); } if (! $sql_query_disabled) { $sql_query .= $msg . "\n"; @@ -250,7 +247,7 @@ function PMA_importRunQuery($sql = '', $full = '', $controluser = false, // the complete query in the textarea) if (! $go_sql && $run_query) { if (! empty($sql_query)) { - if (strlen($sql_query) > 50000 + if (/*overload*/mb_strlen($sql_query) > 50000 || $executed_queries > 50 || $max_sql_len > 1000 ) { @@ -262,6 +259,33 @@ function PMA_importRunQuery($sql = '', $full = '', $controluser = false, // Do we have something to push into buffer? $import_run_buffer = PMA_ImportRunQuery_post($import_run_buffer, $sql, $full); + + // In case of ROLLBACK, notify the user. + if (isset($_REQUEST['rollback_query'])) { + $msg .= __('[ROLLBACK occurred.]'); + } +} + +/** + * Update $sql_data + * + * @param array $sql_data SQL data + * @param int $a_num_rows Number of rows + * @param bool $is_use_query Query is used + * @param array $import_run_buffer Import buffer + * + * @return array + */ +function updateSqlData($sql_data, $a_num_rows, $is_use_query, $import_run_buffer) +{ + if (($a_num_rows > 0) || $is_use_query) { + $sql_data['valid_sql'][] = $import_run_buffer['sql']; + if (!isset($sql_data['valid_queries'])) { + $sql_data['valid_queries'] = 0; + } + $sql_data['valid_queries']++; + } + return $sql_data; } /** @@ -348,13 +372,14 @@ function PMA_importGetNextChunk($size = 32768) if ($GLOBALS['import_file'] == 'none') { // Well this is not yet supported and tested, // but should return content of textarea - if (strlen($GLOBALS['import_text']) < $size) { + if (/*overload*/mb_strlen($GLOBALS['import_text']) < $size) { $GLOBALS['finished'] = true; return $GLOBALS['import_text']; } else { - $r = substr($GLOBALS['import_text'], 0, $size); + $r = /*overload*/mb_substr($GLOBALS['import_text'], 0, $size); $GLOBALS['offset'] += $size; - $GLOBALS['import_text'] = substr($GLOBALS['import_text'], $size); + $GLOBALS['import_text'] = /*overload*/ + mb_substr($GLOBALS['import_text'], $size); return $r; } } @@ -369,8 +394,11 @@ function PMA_importGetNextChunk($size = 32768) $GLOBALS['finished'] = feof($import_handle); break; case 'application/zip': - $result = substr($GLOBALS['import_text'], 0, $size); - $GLOBALS['import_text'] = substr($GLOBALS['import_text'], $size); + $result = /*overload*/mb_substr($GLOBALS['import_text'], 0, $size); + $GLOBALS['import_text'] = /*overload*/mb_substr( + $GLOBALS['import_text'], + $size + ); $GLOBALS['finished'] = empty($GLOBALS['import_text']); break; case 'none': @@ -394,12 +422,12 @@ function PMA_importGetNextChunk($size = 32768) if ($GLOBALS['offset'] == $size) { // UTF-8 if (strncmp($result, "\xEF\xBB\xBF", 3) == 0) { - $result = substr($result, 3); + $result = /*overload*/mb_substr($result, 3); // UTF-16 BE, LE } elseif (strncmp($result, "\xFE\xFF", 2) == 0 || strncmp($result, "\xFF\xFE", 2) == 0 ) { - $result = substr($result, 2); + $result = /*overload*/mb_substr($result, 2); } } return $result; @@ -413,7 +441,7 @@ function PMA_importGetNextChunk($size = 32768) * The column number (1-26) is converted to the responding * ASCII character (A-Z) and returned. * - * If the column number is bigger than 26 (= num of letters in alfabet), + * If the column number is bigger than 26 (= num of letters in alphabet), * an extra character needs to be added. To find this extra character, * the number is divided by 26 and this value is passed to another instance * of the same function (hence recursion). In that new instance the number is @@ -422,7 +450,7 @@ function PMA_importGetNextChunk($size = 32768) * the number is smaller than 26. Then the last called function returns * the corresponding ASCII character to the function that called it. * Each time a called function ends an extra character is added to the column name. - * When the first function is reached, the last character is addded and the complete + * When the first function is reached, the last character is added and the complete * column name is returned. * * @param int $num the column number @@ -454,10 +482,10 @@ function PMA_getColumnAlphaName($num) if ($num == 0) { // use 'Z' if column number is 0, // this is necessary because A-Z has no 'zero' - $col_name .= chr(($A + 26) - 1); + $col_name .= /*overload*/mb_chr(($A + 26) - 1); } else { // convert column number to ASCII character - $col_name .= chr(($A + $num) - 1); + $col_name .= /*overload*/mb_chr(($A + $num) - 1); } return $col_name; @@ -467,7 +495,7 @@ function PMA_getColumnAlphaName($num) * Returns the column number based on the Excel name. * So "A" = 1, "Z" = 26, "AA" = 27, etc. * - * Basicly this is a base26 (A-Z) to base10 (0-9) conversion. + * Basically this is a base26 (A-Z) to base10 (0-9) conversion. * It iterates through all characters in the column name and * calculates the corresponding value, based on character value * (A = 1, ..., Z = 26) and position in the string. @@ -483,8 +511,8 @@ function PMA_getColumnNumberFromName($name) return 0; } - $name = strtoupper($name); - $num_chars = strlen($name); + $name = /*overload*/mb_strtoupper($name); + $num_chars = /*overload*/mb_strlen($name); $column_number = 0; for ($i = 0; $i < $num_chars; ++$i) { // read string from back to front @@ -494,7 +522,7 @@ function PMA_getColumnNumberFromName($name) // and subtract 64 to get corresponding decimal value // ASCII value of "A" is 65, "B" is 66, etc. // Decimal equivalent of "A" is 1, "B" is 2, etc. - $number = (ord($name[$char_pos]) - 64); + $number = (int)(/*overload*/mb_ord($name[$char_pos]) - 64); // base26 to base10 conversion : multiply each number // with corresponding value of the position, in this case @@ -541,7 +569,11 @@ define("FORMATTEDSQL", 2); */ function PMA_getDecimalPrecision($last_cumulative_size) { - return (int)substr($last_cumulative_size, 0, strpos($last_cumulative_size, ",")); + return (int)substr( + $last_cumulative_size, + 0, + strpos($last_cumulative_size, ",") + ); } /** @@ -555,7 +587,7 @@ function PMA_getDecimalPrecision($last_cumulative_size) */ function PMA_getDecimalScale($last_cumulative_size) { - return (int) substr( + return (int)substr( $last_cumulative_size, (strpos($last_cumulative_size, ",") + 1), (strlen($last_cumulative_size) - strpos($last_cumulative_size, ",")) @@ -573,8 +605,8 @@ function PMA_getDecimalScale($last_cumulative_size) */ function PMA_getDecimalSize($cell) { - $curr_size = strlen((string)$cell); - $decPos = strpos($cell, "."); + $curr_size = /*overload*/mb_strlen((string)$cell); + $decPos = /*overload*/mb_strpos($cell, "."); $decPrecision = ($curr_size - 1) - $decPos; $m = $curr_size - 1; @@ -601,7 +633,7 @@ function PMA_getDecimalSize($cell) function PMA_detectSize($last_cumulative_size, $last_cumulative_type, $curr_type, $cell ) { - $curr_size = strlen((string)$cell); + $curr_size = /*overload*/mb_strlen((string)$cell); /** * If the cell is NULL, don't treat it as a varchar @@ -739,7 +771,7 @@ function PMA_detectSize($last_cumulative_size, $last_cumulative_type, $oldM = PMA_getDecimalPrecision($last_cumulative_size); $oldD = PMA_getDecimalScale($last_cumulative_size); $oldInt = $oldM - $oldD; - $newInt = strlen((string)$cell); + $newInt = /*overload*/mb_strlen((string)$cell); /* See which has the larger integer length */ if ($oldInt >= $newInt) { @@ -812,22 +844,22 @@ function PMA_detectType($last_cumulative_type, $cell) return $last_cumulative_type; } - if (is_numeric($cell)) { - if ($cell == (string)(float)$cell - && strpos($cell, ".") !== false - && substr_count($cell, ".") == 1 - ) { - return DECIMAL; - } + if (!is_numeric($cell)) { + return VARCHAR; + } - if (abs($cell) > 2147483647) { - return BIGINT; - } + if ($cell == (string)(float)$cell + && /*overload*/mb_strpos($cell, ".") !== false + && /*overload*/mb_substr_count($cell, ".") == 1 + ) { + return DECIMAL; + } - return INT; + if (abs($cell) > 2147483647) { + return BIGINT; } - return VARCHAR; + return INT; } /** @@ -1049,7 +1081,6 @@ function PMA_buildSQL($db_name, &$tables, &$analyses = null, } /* Create SQL code to create the tables */ - $tempSQLStr = ""; $num_tables = count($tables); for ($i = 0; $i < $num_tables; ++$i) { $num_cols = count($tables[$i][COL_NAMES]); @@ -1115,7 +1146,7 @@ function PMA_buildSQL($db_name, &$tables, &$analyses = null, for ($k = 0; $k < $num_cols; ++$k) { // If fully formatted SQL, no need to enclose - // with aphostrophes, add shalshes etc. + // with apostrophes, add slashes etc. if ($analyses != null && isset($analyses[$i][FORMATTEDSQL][$col_count]) && $analyses[$i][FORMATTEDSQL][$col_count] == true @@ -1332,8 +1363,8 @@ function PMA_stopImport( PMA_Message $error_message ) if ($file_to_unlink != '') { unlink($file_to_unlink); } - - $_SESSION['Import_message']['message'] = $error_message->getDisplay(); + $msg = $error_message->getDisplay(); + $_SESSION['Import_message']['message'] = $msg; $response = PMA_Response::getInstance(); $response->isSuccess(false); @@ -1342,4 +1373,570 @@ function PMA_stopImport( PMA_Message $error_message ) exit; } +/** + * Handles request for Simulation of UPDATE/DELETE queries. + * + * @return void + */ +function PMA_handleSimulateDMLRequest() +{ + $response = PMA_Response::getInstance(); + $error = false; + $error_msg = __('Only single-table UPDATE and DELETE queries can be simulated.'); + $sql_delimiter = $_REQUEST['sql_delimiter']; + $sql_data = array(); + $queries = explode($sql_delimiter, $GLOBALS['sql_query']); + foreach ($queries as $sql_query) { + if (empty($sql_query)) { + continue; + } + + // Parse and Analyze the query. + $parsed_sql = PMA_SQP_parse($sql_query); + $analyzed_sql = PMA_SQP_analyze($parsed_sql); + $analyzed_sql_results = array( + 'parsed_sql' => $parsed_sql, + 'analyzed_sql' => $analyzed_sql + ); + + // Only UPDATE/DELETE queries accepted. + $query_type = $analyzed_sql_results['analyzed_sql'][0]['querytype']; + if ($query_type != 'UPDATE' && $query_type != 'DELETE') { + $error = $error_msg; + break; + } + + // Only single-table queries accepted. + $table_references = PMA_getTableReferences($analyzed_sql_results); + $table_references = $table_references ? $table_references : ''; + if (preg_match('/JOIN/i', $table_references)) { + $error = $error_msg; + break; + } else { + $tables = explode(',', $table_references); + if (count($tables) > 1) { + $error = $error_msg; + break; + } + } + + // Get the matched rows for the query. + $result = PMA_getMatchedRows($analyzed_sql_results); + if (! $error = $GLOBALS['dbi']->getError()) { + $sql_data[] = $result; + } else { + break; + } + } + + if ($error) { + $message = PMA_Message::rawError($error); + $response->addJSON('message', $message); + $response->addJSON('sql_data', false); + } else { + $response->addJSON('sql_data', $sql_data); + } +} + +/** + * Find the matching rows for UPDATE/DELETE query. + * + * @param array $analyzed_sql_results Analyzed SQL results from parser. + * + * @return mixed + */ +function PMA_getMatchedRows($analyzed_sql_results = array()) +{ + // Get the query type. + $query_type = (isset($analyzed_sql_results['analyzed_sql'][0]['querytype'])) + ? $analyzed_sql_results['analyzed_sql'][0]['querytype'] + : ''; + + $matched_row_query = ''; + if ($query_type == 'DELETE') { + $matched_row_query = PMA_getSimulatedDeleteQuery($analyzed_sql_results); + } else if ($query_type == 'UPDATE') { + $matched_row_query = PMA_getSimulatedUpdateQuery($analyzed_sql_results); + } + + // Execute the query and get the number of matched rows. + $matched_rows = PMA_executeMatchedRowQuery($matched_row_query); + // URL to matched rows. + $_url_params = array( + 'db' => $GLOBALS['db'], + 'sql_query' => $matched_row_query + ); + $matched_rows_url = 'sql.php' . PMA_URL_getCommon($_url_params); + + return array( + 'sql_query' => PMA_Util::formatSql( + $analyzed_sql_results['parsed_sql']['raw'] + ), + 'matched_rows' => $matched_rows, + 'matched_rows_url' => $matched_rows_url + ); +} + +/** + * Transforms a UPDATE query into SELECT statement. + * + * @param array $analyzed_sql_results Analyzed SQL results from parser. + * + * @return string SQL query + */ +function PMA_getSimulatedUpdateQuery($analyzed_sql_results) +{ + $where_clause = ''; + $extra_where_clause = ''; + $target_cols = array(); + + $prev_term = ''; + $i = 0; + foreach ($analyzed_sql_results['parsed_sql'] as $key => $term) { + if (! isset($get_set_expr) + && preg_match( + '/\bSET\b/i', + isset($term['data']) ? $term['data'] : '' + ) + ) { + $get_set_expr = true; + continue; + } + + if (isset($get_set_expr)) { + if (preg_match( + '/\bWHERE\b|\bORDER BY\b|\bLIMIT\b/i', + isset($term['data']) ? $term['data'] : '' + ) + ) { + break; + } + + if ($term['type'] == 'punct_listsep') { + $extra_where_clause .= ' OR '; + } else if ($term['type'] == 'punct') { + $extra_where_clause .= ' <> '; + } else { + $extra_where_clause .= $term['data']; + } + + // Get columns in SET expression. + if ($prev_term != 'punct') { + if ($term['type'] != 'punct_listsep' + && $term['type'] != 'punct' + && isset($term['data']) + ) { + if (isset($target_cols[$i])) { + $target_cols[$i] .= $term['data']; + } else { + $target_cols[$i] = $term['data']; + } + } + } else { + $i++; + } + + $prev_term = $term['type']; + continue; + } + } + + // Get table_references. + $table_references = PMA_getTableReferences($analyzed_sql_results); + $target_cols = implode(', ', $target_cols); + + // Get WHERE clause. + $where_clause .= $analyzed_sql_results['analyzed_sql'][0]['where_clause']; + if (empty($where_clause) && empty($extra_where_clause)) { + $where_clause = '1'; + } + + $matched_row_query = 'SELECT ' + . $target_cols + . ' FROM ' + . $table_references + . ' WHERE ' + . $where_clause; + + return $matched_row_query; +} + +/** + * Transforms a DELETE query into SELECT statement. + * + * @param array $analyzed_sql_results Analyzed SQL results from parser. + * + * @return string SQL query + */ +function PMA_getSimulatedDeleteQuery($analyzed_sql_results) +{ + $where_clause = ''; + + $where_clause .= $analyzed_sql_results['analyzed_sql'][0]['where_clause']; + if (empty($where_clause) && empty($extra_where_clause)) { + $where_clause = '1'; + } + + // Get the table_references. + $table_references = PMA_getTableReferences($analyzed_sql_results); + + $matched_row_query = 'SELECT * ' + . ' FROM ' + . $table_references + . ' WHERE ' + . $where_clause; + + return $matched_row_query; +} + +/** + * Finds table_references from a given query. + * Queries Supported: INSERT, UPDATE, DELETE, REPLACE, ALTER, DROP, TRUNCATE + * and RENAME. + * + * @param array $analyzed_sql_results Analyzed SQL results from parser + * + * @return string table_references + */ +function PMA_getTableReferences($analyzed_sql_results) +{ + $table_references = ''; + foreach ($analyzed_sql_results['parsed_sql'] as $key => $term) { + // Skip first KeyWord and other invalid keys. + if ($key == 0 || ! isset($term['data'])) { + continue; + } + + // Get the query type. + $query_type = (isset($analyzed_sql_results['analyzed_sql'][0]['querytype'])) + ? $analyzed_sql_results['analyzed_sql'][0]['querytype'] + : ''; + + // Terms to 'ignore' from query for table_references. + $ignore_re = '/'; + // Terminating condition for table_references. + $terminate_re = '/'; + + // Create relevant Regular Expressions. + switch ($query_type) { + case 'REPLACE': + case 'INSERT': + $ignore_re .= '\bINSERT\b|\bREPLACE\b|\bLOW_PRIORITY\b|\bDELAYED\b' + . '|\bHIGH_PRIORITY\b|\bIGNORE\b|\bINTO\b'; + $terminate_re .= '\bPARTITION\b|\(|\bVALUE\b|\bVALUES\b|\bSELECT\b'; + break; + case 'UPDATE': + $ignore_re .= '\bUPDATE\b|\bLOW_PRIORITY\b|\bIGNORE\b'; + $terminate_re .= '\bSET\b|\bUSING\b'; + break; + case 'DELETE': + $ignore_re .= '\bDELETE\b|\bLOW_PRIORITY\b|\bQUICK\b|\bIGNORE\b' + . '|\bFROM\b'; + $terminate_re .= '\bPARTITION\b|\bWHERE\b|\bORDER\b|\bLIMIT\b|\bUSING\b'; + break; + case 'ALTER': + $ignore_re .= '\bALTER\b|\bONLINE\b|\bOFFLINE\b|\bIGNORE\b|\bTABLE\b'; + $terminate_re .= '\bADD\b|\bALTER\b|\bCHANGE\b|\bMODIFY\b|\bDROP\b' + . '|\bDISABLE\b|\bENABLE\b|\bRENAME\b|\bORDER\b|\bCONVERT\b' + . '|\bDEFAULT\b|\bDISCARD\b|\bIMPORT\b|\bCOALESCE\b|\bREORGANIZE\b' + . '|\bANALYZE\b|\bCHECK\b|\bOPTIMIZE\b|\bREBUILD\b|\bREPAIR\b' + . '|\bPARTITION\b|\bREMOVE\b|\bCHARACTER\b'; + break; + case 'DROP': + $ignore_re .= '\bDROP\b|\bTEMPORARY\b|\bTABLE\b|\bIF\b|\bEXISTS\b'; + $terminate_re .= '\bRESTRICT\b|\bCASCADE\b'; + break; + case 'TRUNCATE': + $ignore_re .= '\bTRUNCATE\b|\bTABLE\b'; + $terminate_re .= ''; + break; + case 'RENAME': + $ignore_re .= '\bRENAME\b|\bTABLE\b'; + $terminate_re .= '\bTO\b'; + break; + default: + return false; + } + + // Ignore 'case' in RegEx. + $ignore_re .= '/i'; + $terminate_re .= '/i'; + + if ($query_type != 'TRUNCATE' + && preg_match($terminate_re, $term['data']) + ) { + break; + } + + if (preg_match($ignore_re, $term['data']) + || ! is_numeric($key) + || $key == 0 + ) { + continue; + } + + $table_references .= ' ' . $term['data']; + } + + return $table_references; +} + +/** + * Executes the matched_row_query and returns the resultant row count. + * + * @param string $matched_row_query SQL query + * + * @return integer Number of rows returned + */ +function PMA_executeMatchedRowQuery($matched_row_query) +{ + $GLOBALS['dbi']->selectDb($GLOBALS['db']); + // Execute the query. + $result = $GLOBALS['dbi']->tryQuery($matched_row_query); + // Count the number of rows in the result set. + $result = $GLOBALS['dbi']->numRows($result); + + return $result; +} + +/** + * Extracts unique table names from table_references. + * + * @param string $table_references table_references + * + * @return array $table_names + */ +function PMA_getTableNamesFromTableReferences($table_references) +{ + $table_names = array(); + $parsed_data = PMA_SQP_parse($table_references); + + $prev_term = array( + 'data' => '', + 'type' => '' + ); + $on_encountered = false; + $qualifier_encountered = false; + $i = 0; + foreach ($parsed_data as $key => $term) { + // To skip first 'raw' key and other invalid keys. + if (! is_numeric($key) + || ! isset($term['data']) + || ! isset($term['type']) + ) { + continue; + } + + $add_to_table_names = true; + + // Un-quote the data, if any. + if ($term['type'] == 'quote_backtick') { + $term['data'] = PMA_Util::unQuote($term['data']); + $term['type'] = 'alpha_identifier'; + } + + // New table name expected after 'JOIN' keyword. + if (preg_match('/\bJOIN\b/i', $term['data'])) { + $on_encountered = false; + } + + // If term is a qualifier, set flag. + if ($term['type'] == 'punct_qualifier') { + $qualifier_encountered = true; + } + + // Skip the JOIN conditions after 'ON' keyword. + if (preg_match('/\bON\b/i', $term['data'])) { + $on_encountered = true; + } + + // If the word is not an 'identifier', skip it. + if ($term['type'] != 'alpha_identifier') { + $add_to_table_names = false; + } + + // Skip table 'alias'. + if (preg_match('/\bAS\b/i', $prev_term['data']) + || $prev_term['type'] == 'alpha_identifier' + ) { + $add_to_table_names = false; + } + + // Everything fine up to now, add name to list if 'unique'. + if ($add_to_table_names + && ! $on_encountered + && ! in_array($term['data'], $table_names) + ) { + if (! $qualifier_encountered) { + $table_names[] = PMA_Util::backquote($term['data']); + $i++; + } else { + // If qualifier encountered, concatenate DB name and table name. + $table_names[$i-1] = $table_names[$i-1] + . '.' + . PMA_Util::backquote($term['data']); + $qualifier_encountered = false; + } + } + + // Update previous term. + $prev_term = $term; + } + + return $table_names; +} + +/** + * Handles request for ROLLBACK. + * + * @param string $sql_query SQL query(s) + * + * @return void + */ +function PMA_handleRollbackRequest($sql_query) +{ + $sql_delimiter = $_REQUEST['sql_delimiter']; + $queries = explode($sql_delimiter, $sql_query); + $error = false; + $error_msg = __( + 'Only INSERT, UPDATE, DELETE and REPLACE ' + . 'SQL queries containing transactional engine tables can be rolled back.' + ); + foreach ($queries as $sql_query) { + if (empty($sql_query)) { + continue; + } + + // Check each query for ROLLBACK support. + if (! PMA_checkIfRollbackPossible($sql_query)) { + $global_error = $GLOBALS['dbi']->getError(); + if ($global_error) { + $error = $global_error; + } else { + $error = $error_msg; + } + break; + } + } + + if ($error) { + unset($_REQUEST['rollback_query']); + $response = PMA_Response::getInstance(); + $message = PMA_Message::rawError($error); + $response->addJSON('message', $message); + exit; + } else { + // If everything fine, START a transaction. + $GLOBALS['dbi']->query('START TRANSACTION'); + } +} + +/** + * Checks if ROLLBACK is possible for a SQL query or not. + * + * @param string $sql_query SQL query + * + * @return bool + */ +function PMA_checkIfRollbackPossible($sql_query) +{ + // Supported queries. + $supported_queries = array( + 'INSERT', + 'UPDATE', + 'DELETE', + 'REPLACE' + ); + + // Parse and Analyze the query. + $parsed_sql = PMA_SQP_parse($sql_query); + $analyzed_sql = PMA_SQP_analyze($parsed_sql); + $analyzed_sql_results = array( + 'parsed_sql' => $parsed_sql, + 'analyzed_sql' => $analyzed_sql + ); + + // Get the query type. + $query_type = (isset($analyzed_sql_results['analyzed_sql'][0]['querytype'])) + ? $analyzed_sql_results['analyzed_sql'][0]['querytype'] + : ''; + + // Check if query is supported. + if (! in_array($query_type, $supported_queries)) { + return false; + } + + // Get table_references from the query. + $table_references = PMA_getTableReferences($analyzed_sql_results); + $table_references = $table_references ? $table_references : ''; + // Get table names from table_references. + $tables = PMA_getTableNamesFromTableReferences($table_references); + + // Check if each table is 'InnoDB'. + foreach ($tables as $table) { + if (! PMA_isTableTransactional($table)) { + return false; + } + } + + return true; +} + +/** + * Checks if a table is 'InnoDB' or not. + * + * @param string $table Table details + * + * @return bool + */ +function PMA_isTableTransactional($table) +{ + $table = explode('.', $table); + if (count($table) == 2) { + $db = PMA_Util::unQuote($table[0]); + $table = PMA_Util::unQuote($table[1]); + } else { + $db = $GLOBALS['db']; + $table = PMA_Util::unQuote($table[0]); + } + + // Query to check if table exists. + $check_table_query = 'SELECT * FROM ' . PMA_Util::backquote($db) + . '.' . PMA_Util::backquote($table) . ' ' + . 'LIMIT 1'; + + $result = $GLOBALS['dbi']->tryQuery($check_table_query); + + if (! $result) { + return false; + } + + // List of Transactional Engines. + $transactional_engines = array( + 'INNODB', + 'FALCON', + 'NDB', + 'INFINIDB', + 'TOKUDB', + 'XTRADB', + 'SEQUENCE', + 'BDB' + ); + + // Query to check if table is 'Transactional'. + $check_query = 'SELECT `ENGINE` FROM `information_schema`.`tables` ' + . 'WHERE `table_name` = "' . $table . '" ' + . 'AND `table_schema` = "' . $db . '" ' + . 'AND UPPER(`engine`) IN ("' + . implode('", "', $transactional_engines) + . '")'; + + $result = $GLOBALS['dbi']->tryQuery($check_query); + + if ($GLOBALS['dbi']->numRows($result) == 1) { + return true; + } else { + return false; + } +} ?> |