Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/phpmyadmin/phpmyadmin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Foellmann <foellmann@foe-services.de>2014-12-23 14:48:13 +0300
committerChristian Foellmann <foellmann@foe-services.de>2014-12-23 14:48:13 +0300
commit2bfb20e57418ebf396149782be9f98e868fe8608 (patch)
treea5ec4011410970603d3f462a535a5a359057fa77 /libraries/import.lib.php
parent20f1bf77c8281efc675a14e0f6bf52f657dabd9a (diff)
UPDATE 4.3.34.3.3
Diffstat (limited to 'libraries/import.lib.php')
-rw-r--r--libraries/import.lib.php695
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;
+ }
+}
?>