diff --git a/libraries/Sql.class.php b/libraries/Sql.class.php
deleted file mode 100644
index 5b037c184..000000000
--- a/libraries/Sql.class.php
+++ /dev/null
@@ -1,2978 +0,0 @@
-__construct($sql);
- }
-
- /**
- * Constructor
- *
- * @access public
- * @uses PMA_Sql::$_raw to set it
- * @uses PMA_Sql::$_tokens to set it
- * @param string SQL query
- */
- function __construct($sql)
- {
- $this->_raw = $sql;
-
- $this->_tokens['puncts']['queryend'] = ';';
- $this->_tokens['puncts']['qualifier'] = '.';
- $this->_tokens['puncts']['listsep'] = ',';
- //$this->_tokens['puncts']['level_plus'] = '(';
- //$this->_tokens['puncts']['level_minus'] = ')';
- $this->_tokens['puncts']['minus'] = '-';
- $this->_tokens['puncts']['colon'] = ':';
- $this->_tokens['puncts']['negator'] = '!';
- $this->_tokens['puncts']['questionmark'] = '?';
- $this->_tokens['puncts']['divisor'] = '/';
- $this->_tokens['puncts']['potenz'] = '^';
- $this->_tokens['puncts']['level_minus'] = '~';
- $this->_tokens['puncts']['escape'] = '\\';
- $this->_tokens['puncts']['m'] = '*';
- $this->_tokens['puncts']['and'] = '&';
- $this->_tokens['puncts']['percent'] = '%';
- $this->_tokens['puncts']['plus'] = '+';
- $this->_tokens['puncts']['less'] = '<';
- $this->_tokens['puncts']['equal'] = '=';
- $this->_tokens['puncts']['greater'] = '>';
- $this->_tokens['puncts']['or'] = '|';
-
- $this->_tokens['punctpairs']['NEQ'] = '!=';
- $this->_tokens['punctpairs']['AND'] = '&&';
- $this->_tokens['punctpairs']['COL'] = ':=';
- $this->_tokens['punctpairs']['SHIFTL'] = '<<';
- $this->_tokens['punctpairs']['LTE'] = '<=';
- $this->_tokens['punctpairs']['NEQ2'] = '<=>';
- $this->_tokens['punctpairs']['NEQ3'] = '<>';
- $this->_tokens['punctpairs']['GTE'] = '>=';
- $this->_tokens['punctpairs']['SHIFTR'] = '>>';
- $this->_tokens['punctpairs']['OR'] = '||';
-
- //$this->_tokens['digit']['floatdecimal'] = '.';
- //$this->_tokens['digit']['hexset'] = 'x';
-
- $this->_tokens['brackets']['round_open'] = '(';
- $this->_tokens['brackets']['round_close'] = ')';
- $this->_tokens['brackets']['square_open'] = '[';
- $this->_tokens['brackets']['square_close'] = ']';
- $this->_tokens['brackets']['brace_open'] = '{';
- $this->_tokens['brackets']['brace_close'] = '}';
-
- $this->_tokens['quotes']['single'] = "'";
- $this->_tokens['quotes']['double'] = '"';
- $this->_tokens['quotes']['backtick'] = '`';
- }
-
- /**
- * everytime called it adds an array element to the array which holds the parse info
- * usally called for every token found in a sql query by PMA_Sql::parse()
- *
- * @access protected
- * @uses PMA_Sql::$_parsed
- * @uses PMA_Sql::$_parsed_size
- * @uses DEBUG_TIMING
- * @uses defined()
- * @uses microtime()
- * @uses $GLOBALS['timer']
- * @param string $type type of the token
- * @param string $data content of this token
- */
- function _addParseInfo($type, $data)
- {
- $this->_parsed[$this->_parsed_size] = array('type' => $type, 'data' => $data);
-
- if (defined('DEBUG_TIMING')) {
- $this->_parsed[$this->_parsed_size]['time'] = $GLOBALS['timer'];
- $GLOBALS['timer'] = microtime();
- }
-
- $this->_parsed_size++;
- } // end of the "_addParseInfo()" function
-
- /**
- * Reset the error variable for the SQL parser
- * Added, Robbat2 - 13 Janurary 2003, 2:59PM
- *
- * @access public
- * @uses PMA_Sql::$_error_message to set it
- */
- function resetError()
- {
- $this->_error_message = '';
- }
-
- /**
- * Get the contents of the error variable for the SQL parser
- * Added, Robbat2 - 13 Janurary 2003, 2:59PM
- *
- * @return string Error string from SQL parser
- * @access public
- * @uses PMA_Sql::$_error_message to read it
- */
- function getErrorMessage()
- {
- return $this->_error_message;
- }
-
- /**
- * Check if the SQL parser hit an error
- * Added, Robbat2 - 13 Janurary 2003, 2:59PM
- *
- * @access public
- * @uses PMA_Sql::$_error_message to check it
- * @return boolean error state
- */
- function isError()
- {
- return ! empty($this->_error_message);
- }
-
- /**
- * Set an error message for the system
- * Added, Robbat2 - 13 Janurary 2003, 2:59PM
- *
- * @uses PMA_Sql::$_error_message to set it
- * @uses PMA_Sql::$_raw to read it
- * @uses $GLOBALS['strSQLParserUserError']
- * @uses htmlspecialchars()
- * @access protected
- * @scope SQL Parser internal
- * @param string $message The error message
- */
- function _throwError($message)
- {
- $this->_error_message = '
'.$GLOBALS['strSQLParserUserError'] . '
' . "\n"
- . '' . "\n"
- . 'ERROR: ' . $message . "\n"
- . 'SQL: ' . htmlspecialchars($this->_raw) . "\n"
- . '
' . "\n";
- } // end of the "_throwError()" function
-
- /**
- * generate the bug report and ncludes compressed debug info
- *
- * @access public
- * @uses PMA_Sql::$_raw to read it
- * @uses PMA_Sql::$_error_message to set it
- * @uses PMA_MYSQL_STR_VERSION
- * @uses PMA_USR_OS
- * @uses PMA_USR_BROWSER_AGENT
- * @uses PMA_VERSION
- * @uses PMA_PHP_STR_VERSION
- * @uses PHP_OS
- * @uses $GLOBALS['strSQLParserBugMessage']
- * @uses $GLOBALS['strBeginCut']
- * @uses $GLOBALS['strEndCut']
- * @uses $GLOBALS['strBeginRaw']
- * @uses $GLOBALS['strEndRaw']
- * @uses $GLOBALS['lang']
- * @uses htmlspecialchars()
- * @uses function_exists()
- * @uses gzcompress()
- * @uses preg_replace()
- * @uses chunk_split()
- * @uses base64_encode()
- * @param string $message The error message
- */
- function generateBugReport($message)
- {
- $debugstr = 'ERROR: ' . $message . "\n";
- $debugstr .= 'SVN: $Id$' . "\n";
- $debugstr .= 'MySQL: ' . PMA_MYSQL_STR_VERSION . "\n";
- $debugstr .= 'USR OS, AGENT, VER: ' . PMA_USR_OS . ' ' . PMA_USR_BROWSER_AGENT . ' ' . PMA_USR_BROWSER_VER . "\n";
- $debugstr .= 'PMA: ' . PMA_VERSION . "\n";
- $debugstr .= 'PHP VER,OS: ' . PMA_PHP_STR_VERSION . ' ' . PHP_OS . "\n";
- $debugstr .= 'LANG: ' . $GLOBALS['lang'] . "\n";
- $debugstr .= 'SQL: ' . htmlspecialchars($this->_raw);
-
- $encodedstr = $debugstr;
- if (@function_exists('gzcompress')) {
- $encodedstr = gzcompress($debugstr, 9);
- }
- $encodedstr = preg_replace("/(\015\012)|(\015)|(\012)/", '
' . "\n",
- chunk_split(base64_encode($encodedstr)));
-
- $this->_error_message .= $GLOBALS['strSQLParserBugMessage'] . '
' . "\n"
- . '----' . $GLOBALS['strBeginCut'] . '----' . '
' . "\n"
- . $encodedstr . "\n"
- . '----' . $GLOBALS['strEndCut'] . '----' . '
' . "\n";
-
- $this->_error_message .= '----' . $GLOBALS['strBeginRaw'] . '----
' . "\n"
- . '' . "\n"
- . $debugstr
- . '
' . "\n"
- . '----' . $GLOBALS['strEndRaw'] . '----
' . "\n";
- } // end of the "generateBugReport()" function
-
- /**
- * Parses the SQL queries
- *
- * @todo split into smaller methods
- * @access public
- * @global array The current PMA configuration
- * @global array MySQL column attributes
- * @global array MySQL reserved words
- * @global array MySQL column types
- * @global array MySQL function names
- * @global integer MySQL column attributes count
- * @global integer MySQL reserved words count
- * @global integer MySQL column types count
- * @global integer MySQL function names count
- * @global array List of available character sets
- * @global array List of available collations
- * @global integer Character sets count
- * @global integer Collations count
- * @return mixed Most of times, nothing...
- */
- function parse()
- {
- global
- $cfg,
- $PMA_SQPdata_column_attrib, $PMA_SQPdata_reserved_word,
- $PMA_SQPdata_column_type, $PMA_SQPdata_function_name,
- $PMA_SQPdata_column_attrib_cnt, $PMA_SQPdata_reserved_word_cnt,
- $PMA_SQPdata_column_type_cnt, $PMA_SQPdata_function_name_cnt,
- $PMA_SQPdata_forbidden_word, $PMA_SQPdata_forbidden_word_cnt,
- $mysql_charsets, $mysql_collations_flat, $mysql_charsets_count,
- $mysql_collations_count;
-
- $sql = $this->_raw;
-
- // rabus: Convert all line feeds to Unix style
- $sql = str_replace("\r\n", "\n", $sql);
- $sql = str_replace("\r", "\n", $sql);
-
- $this->_parsed = array();
- $this->_parsed_size = 0;
-
- // there is nothing to do if empty or only spaces
- if (PMA_strlen(trim($sql)) === 0) {
- return $this->_parsed;
- }
- // but we will not cut off spaces if there are not only spaces
- $sql_len = PMA_strlen($sql);
-
- $bracket_list = implode($this->_tokens['brackets']);
- $allpunct_list = implode($this->_tokens['puncts']);
- $quote_list = implode($this->_tokens['quotes']);
-
- $punctpairs_count = count($this->_tokens['punctpairs']);
-
- $sql_pos = 0;
- while ($sql_pos < $sql_len) {
- $c = PMA_substr($sql, $sql_pos, 1);
-
- // Checks for white space
- if (PMA_STR_isSpace($c)) {
- $sql_pos++;
- continue;
- }
-
- if (($c == "\n")) {
- $sql_pos++;
- $this->_addParseInfo('white_newline', '');
- continue;
- }
-
- $token_start = $sql_pos;
-
- // Checks for comment lines.
- // MySQL style #
- // C style /* */
- // ANSI style --
- if (($c == '#')
- || (($sql_pos + 1 < $sql_len) && (PMA_substr($sql, $sql_pos, 2) == '/*'))
- || (($sql_pos + 2 == $sql_len) && ($c == '-') && (PMA_substr($sql, $sql_pos + 1, 1) == '-'))
- || (($sql_pos + 2 < $sql_len) && ($c == '-') && (PMA_substr($sql, $sql_pos + 1, 1) == '-') && ((PMA_substr($sql, $sql_pos + 2, 1) <= ' ')))) {
- $sql_pos++;
- $pos = 0;
- $type = 'bad';
- switch ($c) {
- case '#':
- $type = 'mysql';
- case '-':
- $type = 'ansi';
- $pos = $GLOBALS['PMA_strpos']($sql, "\n", $sql_pos);
- break;
- case '/':
- $type = 'c';
- $pos = $GLOBALS['PMA_strpos']($sql, '*/', $sql_pos);
- $pos += 2;
- break;
- default:
- break;
- } // end switch
- $sql_pos = ($pos < $sql_pos) ? $sql_len : $pos;
- $this->_addParseInfo('comment_' . $type,
- PMA_substr($sql, $token_start, $sql_pos - $token_start));
- continue;
- } // end if
-
- // Checks for something inside quotation marks
- if (PMA_STR_strInStr($c, $quote_list)) {
- // some examples of valid quotes:
- // "'" -> '
- // "\"" -> "
- // 'blah''blaj' -> blah'blaj
- // """" -> "
-
- $startquotepos = $sql_pos;
- $quotetype = $c;
- $sql_pos++;
- $escaped = FALSE;
- $escaped_escaped = FALSE;
- $pos = $sql_pos;
- $oldpos = 0;
- do {
- $oldpos = $pos;
- $pos = $GLOBALS['PMA_strpos'](' ' . $sql, $quotetype, $oldpos + 1) - 1;
- // ($pos === FALSE)
- if ($pos < 0) {
- $debugstr = $GLOBALS['strSQPBugUnclosedQuote'] . ' @ ' . $startquotepos. "\n"
- . 'STR: ' . htmlspecialchars($quotetype);
- $this->_throwError($debugstr);
- return array();
- }
-
- // If the quote is the first character, it can't be
- // escaped, so don't do the rest of the code
- if ($pos == 0) {
- break;
- }
-
- // Checks for MySQL escaping using a \
- // And checks for ANSI escaping using the $quotetype character
- if (($pos < $sql_len) && PMA_STR_charIsEscaped($sql, $pos)) {
- $pos ++;
- continue;
- } elseif (($pos + 1 < $sql_len) && (PMA_substr($sql, $pos, 1) == $quotetype) && (PMA_substr($sql, $pos + 1, 1) == $quotetype)) {
- $pos = $pos + 2;
- continue;
- } else {
- break;
- }
- } while ($sql_len > $pos); // end do
-
- $sql_pos = $pos;
- $sql_pos++;
- $type = 'quote_';
- switch ($quotetype) {
- case '\'':
- $type .= 'single';
- break;
- case '"':
- $type .= 'double';
- break;
- case '`':
- $type .= 'backtick';
- break;
- default:
- break;
- } // end switch
- $this->_addParseInfo($type,
- PMA_substr($sql, $token_start, $sql_pos - $token_start));
- continue;
- }
-
- // Checks for brackets
- if (PMA_STR_strInStr($c, $bracket_list)) {
- // All bracket tokens are only one item long
- $sql_pos++;
- $type = 'punct_bracket_';
- if (PMA_STR_strInStr($c, '([{')) {
- $type .= 'open';
- } else {
- $type .= 'close';
- }
-
- if (PMA_STR_strInStr($c, '()')) {
- $type .= '_round';
- } elseif (PMA_STR_strInStr($c, '[]')) {
- $type .= '_square';
- } else {
- $type .= '_curly';
- }
-
- $this->_addParseInfo($type, $c);
- continue;
- }
-
- // Checks for identifier (alpha or numeric)
- if (PMA_STR_isSqlIdentifier($c, FALSE) || $c == '@' || $c == '.' && PMA_STR_isDigit(PMA_substr($sql, $sql_pos + 1, 1))) {
- $sql_pos ++;
-
- /**
- * @todo a @ can also be present in expressions like
- * FROM 'user'@'%' or TO 'user'@'%'
- * in this case, the @ is wrongly marked as alpha_variable
- */
-
- $is_sql_variable = $c == '@';
- $is_digit = !$is_sql_variable && PMA_STR_isDigit($c);
- $is_hex_digit = $is_digit && $c == '.' && $c == '0' && $sql_pos < $sql_len && PMA_substr($sql, $sql_pos, 1) == 'x';
- $is_float_digit = $c == '.';
- $is_float_digit_exponent = FALSE;
-
- // Nijel: Fast skip is especially needed for huge BLOB data, requires PHP at least 4.3.0:
- if (PMA_PHP_INT_VERSION >= 40300) {
- if ($is_hex_digit) {
- $sql_pos++;
- $pos = strspn($sql, '0123456789abcdefABCDEF', $sql_pos);
- if ($pos > $sql_pos) {
- $sql_pos = $pos;
- }
- unset($pos);
- } elseif ($is_digit) {
- $pos = strspn($sql, '0123456789', $sql_pos);
- if ($pos > $sql_pos) {
- $sql_pos = $pos;
- }
- unset($pos);
- }
- }
-
- while (($sql_pos < $sql_len)
- && PMA_STR_isSqlIdentifier(PMA_substr($sql, $sql_pos, 1), ($is_sql_variable || $is_digit))) {
- $c2 = PMA_substr($sql, $sql_pos, 1);
- if ($is_sql_variable && ($c2 == '.')) {
- $sql_pos++;
- continue;
- }
- if ($is_digit && (!$is_hex_digit) && ($c2 == '.')) {
- $sql_pos++;
- if (!$is_float_digit) {
- $is_float_digit = TRUE;
- continue;
- } else {
- $debugstr = $GLOBALS['strSQPBugInvalidIdentifer'] . ' @ ' . ($token_start + 1) . "\n"
- . 'STR: ' . htmlspecialchars(PMA_substr($sql, $token_start, $sql_pos - $token_start));
- $this->_throwError($debugstr);
- return array();
- }
- }
- if ($is_digit && (!$is_hex_digit) && (($c2 == 'e') || ($c2 == 'E'))) {
- if (!$is_float_digit_exponent) {
- $is_float_digit_exponent = TRUE;
- $is_float_digit = TRUE;
- $sql_pos++;
- continue;
- } else {
- $is_digit = FALSE;
- $is_float_digit = FALSE;
- }
- }
- if (($is_hex_digit && PMA_STR_isHexDigit($c2)) || ($is_digit && PMA_STR_isDigit($c2))) {
- $sql_pos++;
- continue;
- } else {
- $is_digit = FALSE;
- $is_hex_digit = FALSE;
- }
-
- $sql_pos++;
- } // end while
-
- if ($is_digit) {
- if ($is_float_digit) {
- $type = 'digit_float';
- } elseif ($is_hex_digit) {
- $type = 'digit_hex';
- } else {
- $type = 'digit_integer';
- }
- } else {
- $type = 'alpha';
- if ($is_sql_variable != FALSE) {
- $type .= '_variable';
- }
- } // end if... else....
- $this->_addParseInfo($type,
- PMA_substr($sql, $token_start, $sql_pos - $token_start));
-
- continue;
- }
-
- // Checks for punct
- if (PMA_STR_strInStr($c, $allpunct_list)) {
- while (($sql_pos < $sql_len) && PMA_STR_strInStr(PMA_substr($sql, $sql_pos, 1), $allpunct_list)) {
- $sql_pos++;
- }
- $l = $sql_pos - $token_start;
- if ($l == 1) {
- $punct_data = $c;
- } else {
- $punct_data = PMA_substr($sql, $token_start, $l);
- }
-
- // Special case, sometimes, althought two characters are
- // adjectent directly, they ACTUALLY need to be seperate
- $t_suffix = '';
- if ($l == 1) {
- switch ($punct_data) {
- case $this->_tokens['puncts']['queryend']:
- $t_suffix = '_queryend';
- break;
- case $this->_tokens['puncts']['qualifier']:
- $t_suffix = '_qualifier';
- break;
- case $this->_tokens['puncts']['listsep']:
- $t_suffix = '_listsep';
- break;
- default:
- break;
- }
- } elseif (PMA_STR_binarySearchInArr($punct_data, $this->_tokens['punctpairs'], $punctpairs_count)) {
- // Ok, we have one of the valid combined punct expressions
- } else {
- // Bad luck, lets split it up more
- $first = $punct_data[0];
- $first2 = $punct_data[0] . $punct_data[1];
- $last2 = $punct_data[$l - 2] . $punct_data[$l - 1];
- $last = $punct_data[$l - 1];
- if (($first == ',') || ($first == ';') || ($first == '.') || ($first == '*')) {
- $sql_pos = $token_start + 1;
- $punct_data = $first;
- } elseif ($last2 == '/*' || ($last2 == '--' && ($sql_pos == $sql_len || PMA_substr($sql, $sql_pos, 1) <= ' '))) {
- $sql_pos -= 2;
- $punct_data = PMA_substr($sql, $token_start, $sql_pos - $token_start);
- } elseif (($last == '-') || ($last == '+') || ($last == '!')) {
- $sql_pos--;
- $punct_data = PMA_substr($sql, $token_start, $sql_pos - $token_start);
- /**
- * @todo for negation operator, split in 2 tokens ?
- * "select x&~1 from t"
- * becomes "select x & ~ 1 from t" ?
- */
-
- } elseif ($last != '~') {
- $debugstr = $GLOBALS['strSQPBugUnknownPunctuation'] . ' @ ' . ($token_start + 1) . "\n"
- . 'STR: ' . htmlspecialchars($punct_data);
- $this->_throwError($debugstr);
- return array();
- }
- } // end if... elseif... else
- $this->_addParseInfo('punct' . $t_suffix, $punct_data);
- continue;
- }
-
- // we reach this point only in case of error
- $sql_pos++;
-
- // DEBUG
- $debugstr = 'C1 C2 LEN: ' . $token_start . ' ' . $sql_pos . ' ' . $sql_len . "\n"
- . 'STR: ' . PMA_substr($sql, $token_start, $sql_pos - $token_start) . "\n";
- $this->generateBugReport($debugstr);
- return array();
- } // end while ($sql_pos < $sql_len)
-
-
- if ($this->_parsed_size > 0) {
- $t_next = $this->_parsed[0]['type'];
- $t_prev = '';
- $t_bef_prev = '';
- $t_cur = '';
- $d_next = $this->_parsed[0]['data'];
- $d_prev = '';
- $d_bef_prev = '';
- $d_cur = '';
- $d_next_upper = $t_next == 'alpha' ? strtoupper($d_next) : $d_next;
- $d_prev_upper = '';
- $d_bef_prev_upper = '';
- $d_cur_upper = '';
- }
-
- foreach ($this->_parsed as $pos => $each_parsed) {
-// for ($i = 0; $i < $this->_parsed_size; $i++) {
- $t_bef_prev = $t_prev;
- $t_prev = $t_cur;
- $t_cur = $t_next;
- $d_bef_prev = $d_prev;
- $d_prev = $d_cur;
- $d_cur = $d_next;
- $d_bef_prev_upper = $d_prev_upper;
- $d_prev_upper = $d_cur_upper;
- $d_cur_upper = $d_next_upper;
- if (($pos + 1) < $this->_parsed_size) {
- $t_next = $this->_parsed[$pos + 1]['type'];
- $d_next = $this->_parsed[$pos + 1]['data'];
- $d_next_upper = $t_next == 'alpha' ? strtoupper($d_next) : $d_next;
- } else {
- $t_next = '';
- $d_next = '';
- $d_next_upper = '';
- }
-
- //DEBUG echo "[prev: ".$d_prev." ".$t_prev."][cur: ".$d_cur." ".$t_cur."][next: ".$d_next." ".$t_next."]
";
-
- if ($t_cur == 'alpha') {
- $t_suffix = '_identifier';
- if (($t_next == 'punct_qualifier') || ($t_prev == 'punct_qualifier')) {
- $t_suffix = '_identifier';
- } elseif (($t_next == 'punct_bracket_open_round')
- && PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_function_name, $PMA_SQPdata_function_name_cnt)) {
- /**
- * @todo 2005-10-16: in the case of a CREATE TABLE containing
- * a TIMESTAMP, since TIMESTAMP() is also a function, it's
- * found here and the token is wrongly marked as alpha_functionName.
- * But we compensate for this when analysing for timestamp_not_null
- * later in this script.
- *
- * Same applies to CHAR vs. CHAR() function.
- */
- $t_suffix = '_functionName';
- /* There are functions which might be as well column types */
- if (PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_column_type, $PMA_SQPdata_column_type_cnt)) {
- }
- } elseif (PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_column_type, $PMA_SQPdata_column_type_cnt)) {
- $t_suffix = '_columnType';
-
- /**
- * Temporary fix for BUG #621357
- *
- * @todo FIX PROPERLY NEEDS OVERHAUL OF SQL TOKENIZER
- */
- if ($d_cur_upper == 'SET' && $t_next != 'punct_bracket_open_round') {
- $t_suffix = '_reservedWord';
- }
- //END OF TEMPORARY FIX
-
- // CHARACTER is a synonym for CHAR, but can also be meant as
- // CHARACTER SET. In this case, we have a reserved word.
- if ($d_cur_upper == 'CHARACTER' && $d_next_upper == 'SET') {
- $t_suffix = '_reservedWord';
- }
-
- // experimental
- // current is a column type, so previous must not be
- // a reserved word but an identifier
- // CREATE TABLE SG_Persons (first varchar(64))
-
- //if ($this->_parsed[$pos-1]['type'] =='alpha_reservedWord') {
- // $this->_parsed[$pos-1]['type'] = 'alpha_identifier';
- //}
- } elseif (PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_reserved_word, $PMA_SQPdata_reserved_word_cnt)) {
- $t_suffix = '_reservedWord';
- } elseif (PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_column_attrib, $PMA_SQPdata_column_attrib_cnt)) {
- $t_suffix = '_columnAttrib';
- // INNODB is a MySQL table type, but in "SHOW INNODB STATUS",
- // it should be regarded as a reserved word.
- if ($d_cur_upper == 'INNODB' && $d_prev_upper == 'SHOW' && $d_next_upper == 'STATUS') {
- $t_suffix = '_reservedWord';
- }
-
- if ($d_cur_upper == 'DEFAULT' && $d_next_upper == 'CHARACTER') {
- $t_suffix = '_reservedWord';
- }
- // Binary as character set
- if ($d_cur_upper == 'BINARY' && (
- ($d_bef_prev_upper == 'CHARACTER' && $d_prev_upper == 'SET')
- || ($d_bef_prev_upper == 'SET' && $d_prev_upper == '=')
- || ($d_bef_prev_upper == 'CHARSET' && $d_prev_upper == '=')
- || $d_prev_upper == 'CHARSET'
- ) && PMA_STR_binarySearchInArr($d_cur, $mysql_charsets, count($mysql_charsets))) {
- $t_suffix = '_charset';
- }
- } elseif (PMA_STR_binarySearchInArr($d_cur, $mysql_charsets, $mysql_charsets_count)
- || PMA_STR_binarySearchInArr($d_cur, $mysql_collations_flat, $mysql_collations_count)
- || ($d_cur{0} == '_' && PMA_STR_binarySearchInArr(substr($d_cur, 1), $mysql_charsets, $mysql_charsets_count))) {
- $t_suffix = '_charset';
- } else {
- // Do nothing
- }
- // check if present in the list of forbidden words
- if ($t_suffix == '_reservedWord'
- && PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_forbidden_word, $PMA_SQPdata_forbidden_word_cnt)) {
- $this->_parsed['forbidden'] = TRUE;
- } else {
- $this->_parsed['forbidden'] = FALSE;
- }
- $this->_parsed['type'] .= $t_suffix;
- }
- } // end for
-
- // Stores the size of the array inside the array, as count() is a slow
- // operation.
- //$this->_parsed['len'] = $this->_parsed_size;
-
- // DEBUG echo 'After parsing'; print_r($this->_parsed); echo '
';
- // Sends the data back
- return $this->_parsed;
- } // end of the "parse()" function
-
- /**
- * returns SQL query analyzed
- *
- * @access public
- * @uses PMA_Sql::$_analyzed
- * @uses PMA_Sql::getAnalyzed()
- * @uses PMA_Sql::analyzeIfRequired()
- * @uses PMA_Sql
- * @param string $sql the SQL query
- * @return array PMA_Sql::$_analyzed
- */
- function getAnalyzed($sql = null)
- {
- if (null !== $sql) {
- $pma_sql = new PMA_Sql($sql);
- return $pma_sql->getAnalyzed();
- }
-
- $this->analyzeIfRequired();
-
- return $this->_analyzed;
- }
-
- /**
- * returns SQL query parsed
- *
- * @access public
- * @uses PMA_Sql::$_parsed
- * @uses PMA_Sql::getParsed()
- * @uses PMA_Sql::parseIfRequired()
- * @uses PMA_Sql
- * @param string $sql the SQL query
- * @return array PMA_Sql::$_parsed
- */
- function getParsed($sql = null)
- {
- if (null !== $sql) {
- $pma_sql = new PMA_Sql($sql);
- return $pma_sql->getParsed();
- }
-
- $this->parseIfRequired();
-
- return $this->_parsed;
- }
-
- /**
- * checks if the given field/column has attribute ON UPDATE CURRENT_TIMESTAMP
- *
- * @uses PMA_Sql::analyzeIfRequired()
- * @uses PMA_Sql::$_analyzed to read
- * @param string $field_name name of field/column to check
- * @param integer $subquery which subquery to check, default 0
- * @return boolean whether the field has ON UPDATE CURRENT TIMESTAMP
- * @todo create a child class espacially for SHOW CREATE TABLE result?
- * @todo this function should only be used by class PMA_Table or PMA_Field/Column
- */
- function fieldIsOnUpdateCurrentTimestamp($field_name, $subquery = 0)
- {
- $this->analyzeIfRequired();
-
- if (isset($this->_analyzed[$subquery]['create_table_fields'][$field_name]['on_update_current_timestamp'])) {
- return true;
- }
- return false;
- }
-
- /**
- * checks if the given field/column is a TIMESTAMP that cannot be NULL
- *
- * @uses PMA_Sql::analyzeIfRequired()
- * @uses PMA_Sql::$_analyzed to read
- * @param string $field_name name of field/column to check
- * @param integer $subquery which subquery to check, default 0
- * @return boolean whether the field is of type TIMESTAMP and NOT NULL
- * @todo create a child class espacially for SHOW CREATE TABLE result?
- * @todo this function should only be used by class PMA_Table or PMA_Field/Column
- */
- function fieldIsTimestampNotNull($field_name, $subquery = 0)
- {
- $this->analyzeIfRequired();
-
- if (! empty($this->_analyzed[$subquery]['create_table_fields'][$field_name]['type'])
- && $this->_analyzed[$subquery]['create_table_fields'][$field_name]['type'] == 'TIMESTAMP'
- && $this->_analyzed[$subquery]['create_table_fields'][$field_name]['timestamp_not_null']) {
- return true;
- }
- return false;
- }
-
- /**
- * checks if the given field/column has CURRENT TIMESTAMP as DEFAULT
- *
- * @uses PMA_Sql::analyzeIfRequired()
- * @uses PMA_Sql::$_analyzed to read
- * @param string $field_name name of field/column to check
- * @param integer $subquery which subquery to check, default 0
- * @return boolean whether field defaults to CURRENT TIMESTAMP
- * @todo create a child class espacially for SHOW CREATE TABLE result?
- * @todo this function should only be used by class PMA_Table or PMA_Field/Column
- */
- function fieldIsDefaultCurrentTimestamp($field_name, $subquery = 0)
- {
- $this->analyzeIfRequired();
-
- if (isset($this->_analyzed[$subquery]['create_table_fields'][$field_name]['default_current_timestamp'])) {
- return true;
- }
- return false;
- }
-
- /**
- * returns the name for the given field/column
- *
- * @uses PMA_Sql::analyzeIfRequired()
- * @uses PMA_Sql::$_analyzed to read
- * @param integer $field_number number of field/column for which to return name
- * @param integer $subquery which subquery to check, default 0
- * @return string name for the column
- */
- function fieldGetName($field_number, $subquery = 0)
- {
- $this->analyzeIfRequired();
-
- if (isset($this->_analyzed[$subquery]['select_expr'][$field_number]['column'])) {
- return $this->_analyzed[$subquery]['select_expr'][$field_number]['column'];
- }
-
- return false;
- }
-
- /**
- * check if SQL is SELECT
- *
- * @uses PMA_Sql::$_analyzed to check
- * @param integer $subquery which subquery to check, default 0
- * @return boolean whether the SQL is a SELECT or not
- */
- function isSelect($subquery = 0)
- {
- return isset($this->_analyzed[$subquery]['queryflags']['select_from']);
- }
-
- function tableGetTrueName($reference = 0, $subquery = 0)
- {
- if (isset($this->_analyzed[$subquery]['table_ref'][$reference]['table_true_name'])) {
- return $this->_analyzed[$subquery]['table_ref'][$reference]['table_true_name'];
- }
-
- return false;
- }
-
- /**
- * analyze only if not already
- *
- * @uses PMA_Sql::$_analyzed to check if empty
- * @uses PMA_Sql::analyze() if PMA_Sql::$_analyzed is empty
- */
- function analyzeIfRequired()
- {
- if (empty($this->_analyzed)) {
- $this->analyze();
- }
- }
-
- /**
- * parse only if not already
- *
- * @uses PMA_Sql::$_parsed to check if empty
- * @uses PMA_Sql::parse() if PMA_Sql::$_parsed is empty
- */
- function parseIfRequired()
- {
- if (empty($this->_parsed)) {
- $this->parse();
- }
- }
-
- /**
- * Analyzes SQL queries
- *
- * db, table, column, alias
- * ------------------------
- *
- * Inside the $subresult array, we create ['select_expr'] and ['table_ref'] arrays.
- *
- * The SELECT syntax (simplified) is
- *
- * SELECT
- * select_expression,...
- * [FROM [table_references]
- *
- *
- * ['select_expr'] is filled with each expression, the key represents the
- * expression position in the list (0-based) (so we don't lose track of
- * multiple occurences of the same column).
- *
- * ['table_ref'] is filled with each table ref, same thing for the key.
- *
- * I create all sub-values empty, even if they are
- * not present (for example no select_expression alias).
- *
- * There is a debug section at the end of loop #1, if you want to
- * see the exact contents of select_expr and table_ref
- *
- * queryflags
- * ----------
- *
- * In $subresult, array 'queryflags' is filled, according to what we
- * find in the query.
- *
- * Currently, those are generated:
- *
- * ['queryflags']['need_confirm'] = 1; if the query needs confirmation
- * ['queryflags']['select_from'] = 1; if this is a real SELECT...FROM
- * ['queryflags']['distinct'] = 1; for a DISTINCT
- * ['queryflags']['union'] = 1; for a UNION
- * ['queryflags']['join'] = 1; for a JOIN
- * ['queryflags']['offset'] = 1; for the presence of OFFSET
- *
- * query clauses
- * -------------
- *
- * The select is splitted in those clauses:
- * ['select_expr_clause']
- * ['from_clause']
- * ['group_by_clause']
- * ['order_by_clause']
- * ['having_clause']
- * ['where_clause']
- *
- * The identifiers of the WHERE clause are put into the array
- * ['where_clause_identifier']
- *
- * For a SELECT, the whole query without the ORDER BY clause is put into
- * ['unsorted_query']
- *
- * foreign keys
- * ------------
- * The CREATE TABLE may contain FOREIGN KEY clauses, so they get
- * analyzed and ['foreign_keys'] is an array filled with
- * the constraint name, the index list,
- * the REFERENCES table name and REFERENCES index list,
- * and ON UPDATE | ON DELETE clauses
- *
- * position_of_first_select
- * ------------------------
- *
- * The array index of the first SELECT we find. Will be used to
- * insert a SQL_CALC_FOUND_ROWS.
- *
- * create_table_fields
- * -------------------
- *
- * For now, mostly used to detect the DEFAULT CURRENT_TIMESTAMP and
- * ON UPDATE CURRENT_TIMESTAMP clauses of the CREATE TABLE query.
- * An array, each element is the identifier name.
- * Note that for now, the timestamp_not_null element is created
- * even for non-TIMESTAMP fields.
- *
- * Sub-elements: ['type'] which contains the column type
- * optional (currently they are never false but can be absent):
- * ['default_current_timestamp'] boolean
- * ['on_update_current_timestamp'] boolean
- * ['timestamp_not_null'] boolean
- *
- * section_before_limit, section_after_limit
- * -----------------------------------------
- *
- * Marks the point of the query where we can insert a LIMIT clause;
- * so the section_before_limit will contain the left part before
- * a possible LIMIT clause
- *
- * @access public
- * @param array The SQL queries
- * @return array The analyzed SQL queries
- */
- function analyze()
- {
- $this->_analyzed = array();
- $this->parseIfRequired();
-
- if (empty($this->_parsed)) {
- return $this->_analyzed;
- }
-
- $subresult = array(
- 'querytype' => '',
- 'select_expr_clause'=> '', // the whole stuff between SELECT and FROM , except DISTINCT
- 'position_of_first_select' => '', // the array index
- 'from_clause'=> '',
- 'group_by_clause'=> '',
- 'order_by_clause'=> '',
- 'having_clause' => '',
- 'where_clause' => '',
- 'where_clause_identifiers' => array(),
- 'unsorted_query' => '',
- 'queryflags' => array(),
- 'select_expr' => array(),
- 'table_ref' => array(),
- 'foreign_keys' => array(),
- 'create_table_fields' => array()
- );
- $subresult_empty = $subresult;
- $seek_queryend = FALSE;
- $seen_end_of_table_ref = FALSE;
- $number_of_brackets_in_extract = 0;
- $number_of_brackets_in_group_concat = 0;
-
- // for SELECT EXTRACT(YEAR_MONTH FROM CURDATE())
- // we must not use CURDATE as a table_ref
- // so we track wether we are in the EXTRACT()
- $in_extract = FALSE;
-
- // for GROUP_CONCAT(...)
- $in_group_concat = FALSE;
-
- // must be sorted
- // TODO: current logic checks for only one word, so I put only the
- // first word of the reserved expressions that end a table ref;
- // maybe this is not ok (the first word might mean something else)
-// $words_ending_table_ref = array(
-// 'FOR UPDATE',
-// 'GROUP BY',
-// 'HAVING',
-// 'LIMIT',
-// 'LOCK IN SHARE MODE',
-// 'ORDER BY',
-// 'PROCEDURE',
-// 'UNION',
-// 'WHERE'
-// );
- $words_ending_table_ref = array(
- 'FOR',
- 'GROUP',
- 'HAVING',
- 'LIMIT',
- 'LOCK',
- 'ORDER',
- 'PROCEDURE',
- 'UNION',
- 'WHERE'
- );
- $words_ending_table_ref_cnt = 9; //count($words_ending_table_ref);
-
- $words_ending_clauses = array(
- 'FOR',
- 'LIMIT',
- 'LOCK',
- 'PROCEDURE',
- 'UNION'
- );
- $words_ending_clauses_cnt = 5; //count($words_ending_clauses);
-
- // must be sorted
- $supported_query_types = array(
- 'SELECT'
- /*
- // Support for these additional query types will come later on.
- 'DELETE',
- 'INSERT',
- 'REPLACE',
- 'TRUNCATE',
- 'UPDATE'
- 'EXPLAIN',
- 'DESCRIBE',
- 'SHOW',
- 'CREATE',
- 'SET',
- 'ALTER'
- */
- );
- $supported_query_types_cnt = count($supported_query_types);
-
- // loop #1 for each token: select_expr, table_ref for SELECT
-
- foreach ($this->_parsed as $pos => $each_parsed) {
-//DEBUG echo "Loop1 " . $each_parsed['data'] . " (" . $each_parsed['type'] . ")
";
-
- // High speed seek for locating the end of the current query
- if ($seek_queryend == TRUE) {
- if ($each_parsed['type'] === 'punct_queryend') {
- $seek_queryend = FALSE;
- } else {
- continue;
- } // end if (type == punct_queryend)
- } // end if ($seek_queryend)
-
- /**
- * Note: do not split if this is a punct_queryend for the first and only query
- * @todo when we find a UNION, should we split in another subresult?
- */
- if ($each_parsed['type'] == 'punct_queryend'
- && ($pos + 1 != $this->_parsed_size)) {
- $this->_analyzed[] = $subresult;
- $subresult = $subresult_empty;
- continue;
- } // end if (type == punct_queryend)
-
-// ==============================================================
- if ($each_parsed['type'] == 'punct_bracket_open_round') {
- if ($in_extract) {
- $number_of_brackets_in_extract++;
- }
- if ($in_group_concat) {
- $number_of_brackets_in_group_concat++;
- }
- }
-// ==============================================================
- if ($each_parsed['type'] == 'punct_bracket_close_round') {
- if ($in_extract) {
- $number_of_brackets_in_extract--;
- if ($number_of_brackets_in_extract == 0) {
- $in_extract = FALSE;
- }
- }
- if ($in_group_concat) {
- $number_of_brackets_in_group_concat--;
- if ($number_of_brackets_in_group_concat == 0) {
- $in_group_concat = FALSE;
- }
- }
- }
-// ==============================================================
- if ($each_parsed['type'] == 'alpha_functionName') {
- $upper_data = strtoupper($each_parsed['data']);
- if ($upper_data === 'EXTRACT') {
- $in_extract = TRUE;
- $number_of_brackets_in_extract = 0;
- }
- if ($upper_data === 'GROUP_CONCAT') {
- $in_group_concat = TRUE;
- $number_of_brackets_in_group_concat = 0;
- }
- }
-
-// ==============================================================
- if ($each_parsed['type'] == 'alpha_reservedWord'
-// && $each_parsed['forbidden'] == FALSE) {
- ) {
- // upper once
- $upper_data = strtoupper($each_parsed['data']);
-
- // We don't know what type of query yet, so run this
- if ($subresult['querytype'] === '') {
- $subresult['querytype'] = $upper_data;
-
- // Check if we support this type of query
- if (! PMA_STR_binarySearchInArr($subresult['querytype'],
- $supported_query_types, $supported_query_types_cnt)) {
- // Skip ahead to the next one if we don't
- $seek_queryend = TRUE;
- continue;
- } // end if (query not supported)
- } // end if (querytype was empty)
-
- /**
- * @todo reset for each query?
- */
-
- if ($upper_data == 'SELECT') {
- $seen_from = FALSE;
- $previous_was_identifier = FALSE;
- $current_select_expr = -1;
- $seen_end_of_table_ref = FALSE;
- } // end if (data == SELECT)
-
- if ($upper_data =='FROM' && ! $in_extract) {
- $current_table_ref = -1;
- $seen_from = TRUE;
- $previous_was_identifier = FALSE;
- $save_table_ref = TRUE;
- } // end if (data == FROM)
-
- // here, do not 'continue' the loop, as we have more work for
- // reserved words below
- } // end if (type == alpha_reservedWord)
-
-// ==============================
- if ($each_parsed['type'] == 'quote_backtick'
- || $each_parsed['type'] == 'quote_double'
- || $each_parsed['type'] == 'quote_single'
- || $each_parsed['type'] == 'alpha_identifier'
- || ($each_parsed['type'] == 'alpha_reservedWord'
- && $each_parsed['forbidden'] == FALSE)) {
-
- switch ($each_parsed['type']) {
- case 'alpha_identifier':
- case 'alpha_reservedWord':
- /**
- * this is not a real reservedWord, because it's not
- * present in the list of forbidden words, for example
- * "storage" which can be used as an identifier
- *
- * @todo avoid the pretty printing in color in this case
- */
- $identifier = $each_parsed['data'];
- break;
-
- case 'quote_backtick':
- case 'quote_double':
- case 'quote_single':
- $identifier = PMA_unQuote($each_parsed['data']);
- break;
- } // end switch
-
- if ($subresult['querytype'] == 'SELECT' && ! $in_group_concat) {
- if (! $seen_from) {
- if ($previous_was_identifier && isset($chain)) {
- // found alias for this select_expr, save it
- // but only if we got something in $chain
- // (for example, SELECT COUNT(*) AS cnt
- // puts nothing in $chain, so we avoid
- // setting the alias)
- $alias_for_select_expr = $identifier;
- } else {
- $chain[] = $identifier;
- $previous_was_identifier = TRUE;
-
- } // end if !$previous_was_identifier
- } else {
- // ($seen_from)
- if ($save_table_ref && !$seen_end_of_table_ref) {
- if ($previous_was_identifier) {
- // found alias for table ref
- // save it for later
- $alias_for_table_ref = $identifier;
- } else {
- $chain[] = $identifier;
- $previous_was_identifier = TRUE;
-
- } // end if ($previous_was_identifier)
- } // end if ($save_table_ref &&!$seen_end_of_table_ref)
- } // end if (!$seen_from)
- } // end if (querytype SELECT)
- } // end if (quote_backtick or double quote or alpha_identifier)
-
-// ===================================
- if ($each_parsed['type'] == 'punct_qualifier') {
- // to be able to detect an identifier following another
- $previous_was_identifier = FALSE;
- continue;
- } // end if (punct_qualifier)
-
- /**
- * @todo check if 3 identifiers following one another -> error
- */
-
- // s a v e a s e l e c t e x p r
- // finding a list separator or FROM
- // means that we must save the current chain of identifiers
- // into a select expression
-
- // for now, we only save a select expression if it contains
- // at least one identifier, as we are interested in checking
- // the columns and table names, so in "select * from persons",
- // the "*" is not saved
-
- if (isset($chain)
- && !$seen_end_of_table_ref
- && ((! $seen_from
- && $each_parsed['type'] == 'punct_listsep')
- || ($each_parsed['type'] == 'alpha_reservedWord'
- && $upper_data == 'FROM'))) {
- $size_chain = count($chain);
- $current_select_expr++;
- $subresult['select_expr'][$current_select_expr] = array(
- 'expr' => '',
- 'alias' => '',
- 'db' => '',
- 'table_name' => '',
- 'table_true_name' => '',
- 'column' => ''
- );
-
- if (isset($alias_for_select_expr) && strlen($alias_for_select_expr)) {
- // we had found an alias for this select expression
- $subresult['select_expr'][$current_select_expr]['alias'] = $alias_for_select_expr;
- unset($alias_for_select_expr);
- }
- // there is at least a column
- $subresult['select_expr'][$current_select_expr]['column'] = $chain[$size_chain - 1];
- $subresult['select_expr'][$current_select_expr]['expr'] = $chain[$size_chain - 1];
-
- // maybe a table
- if ($size_chain > 1) {
- $subresult['select_expr'][$current_select_expr]['table_name'] = $chain[$size_chain - 2];
- // we assume for now that this is also the true name
- $subresult['select_expr'][$current_select_expr]['table_true_name'] = $chain[$size_chain - 2];
- $subresult['select_expr'][$current_select_expr]['expr']
- = $subresult['select_expr'][$current_select_expr]['table_name']
- . '.' . $subresult['select_expr'][$current_select_expr]['expr'];
- } // end if ($size_chain > 1)
-
- // maybe a db
- if ($size_chain > 2) {
- $subresult['select_expr'][$current_select_expr]['db'] = $chain[$size_chain - 3];
- $subresult['select_expr'][$current_select_expr]['expr']
- = $subresult['select_expr'][$current_select_expr]['db']
- . '.' . $subresult['select_expr'][$current_select_expr]['expr'];
- } // end if ($size_chain > 2)
- unset($chain);
-
- /**
- * @todo explain this:
- * even if this was a reservedWord it is possible this is just an alias
- * wihtout using 'AS'
- */
- if (($each_parsed['type'] == 'alpha_reservedWord')
- && ($upper_data != 'FROM')) {
- $previous_was_identifier = TRUE;
- }
-
- } // end if (save a select expr)
-
-
- //======================================
- // s a v e a t a b l e r e f
- //======================================
-
- // maybe we just saw the end of table refs
- // but the last table ref has to be saved
- // or we are at the last token
- // or we just got a reserved word
- /**
- * @todo there could be another query after this one
- */
-
- if (isset($chain) && $seen_from && $save_table_ref
- && ($each_parsed['type'] == 'punct_listsep'
- || ($each_parsed['type'] == 'alpha_reservedWord' && $upper_data != "AS")
- || $seen_end_of_table_ref
- || $pos == $this->_parsed_size - 1)) {
-
- $size_chain = count($chain);
- $current_table_ref++;
- $subresult['table_ref'][$current_table_ref] = array(
- 'expr' => '',
- 'db' => '',
- 'table_name' => '',
- 'table_alias' => '',
- 'table_true_name' => ''
- );
- if (isset($alias_for_table_ref) && strlen($alias_for_table_ref)) {
- $subresult['table_ref'][$current_table_ref]['table_alias'] = $alias_for_table_ref;
- unset($alias_for_table_ref);
- }
- $subresult['table_ref'][$current_table_ref]['table_name'] = $chain[$size_chain - 1];
- // we assume for now that this is also the true name
- $subresult['table_ref'][$current_table_ref]['table_true_name'] = $chain[$size_chain - 1];
- $subresult['table_ref'][$current_table_ref]['expr']
- = $subresult['table_ref'][$current_table_ref]['table_name'];
- // maybe a db
- if ($size_chain > 1) {
- $subresult['table_ref'][$current_table_ref]['db'] = $chain[$size_chain - 2];
- $subresult['table_ref'][$current_table_ref]['expr']
- = $subresult['table_ref'][$current_table_ref]['db']
- . '.' . $subresult['table_ref'][$current_table_ref]['expr'];
- } // end if ($size_chain > 1)
-
- // add the table alias into the whole expression
- $subresult['table_ref'][$current_table_ref]['expr']
- .= ' ' . $subresult['table_ref'][$current_table_ref]['table_alias'];
-
- unset($chain);
- $previous_was_identifier = TRUE;
- //continue;
-
- } // end if (save a table ref)
-
-
- // when we have found all table refs,
- // for each table_ref alias, put the true name of the table
- // in the corresponding select expressions
-
- if (isset($current_table_ref)
- && ($seen_end_of_table_ref || $pos == $this->_parsed_size - 1)
- && $subresult != $subresult_empty) {
- for ($tr=0; $tr <= $current_table_ref; $tr++) {
- $alias = $subresult['table_ref'][$tr]['table_alias'];
- $truename = $subresult['table_ref'][$tr]['table_true_name'];
- for ($se=0; $se <= $current_select_expr; $se++) {
- if (isset($alias) && strlen($alias)
- && $subresult['select_expr'][$se]['table_true_name'] == $alias) {
- $subresult['select_expr'][$se]['table_true_name'] = $truename;
- } // end if (found the alias)
- } // end for (select expressions)
-
- } // end for (table refs)
- } // end if (set the true names)
-
-
- // e n d i n g l o o p #1
- // set the $previous_was_identifier to FALSE if the current
- // token is not an identifier
- if (($each_parsed['type'] != 'alpha_identifier')
- && ($each_parsed['type'] != 'quote_double')
- && ($each_parsed['type'] != 'quote_single')
- && ($each_parsed['type'] != 'quote_backtick')) {
- $previous_was_identifier = FALSE;
- } // end if
-
- // however, if we are on AS, we must keep the $previous_was_identifier
- if (($each_parsed['type'] == 'alpha_reservedWord')
- && ($upper_data == 'AS')) {
- $previous_was_identifier = TRUE;
- }
-
- if (($each_parsed['type'] == 'alpha_reservedWord')
- && ($upper_data =='ON' || $upper_data === 'USING')) {
- $save_table_ref = FALSE;
- } // end if (data == ON)
-
- if (($each_parsed['type'] == 'alpha_reservedWord')
- && ($upper_data =='JOIN' || $upper_data === 'FROM')) {
- $save_table_ref = TRUE;
- } // end if (data == JOIN)
-
- /**
- * no need to check the end of table ref if we already did
- *
- * @todo maybe add "&& $seen_from"
- */
- if (!$seen_end_of_table_ref) {
- // if this is the last token, it implies that we have
- // seen the end of table references
- // Check for the end of table references
- //
- // Note: if we are analyzing a GROUP_CONCAT clause,
- // we might find a word that seems to indicate that
- // we have found the end of table refs (like ORDER)
- // but it's a modifier of the GROUP_CONCAT so
- // it's not the real end of table refs
- if (($pos == $this->_parsed_size - 1)
- || ($each_parsed['type'] == 'alpha_reservedWord'
- && !$in_group_concat
- && PMA_STR_binarySearchInArr($upper_data, $words_ending_table_ref, $words_ending_table_ref_cnt))) {
- $seen_end_of_table_ref = TRUE;
- // to be able to save the last table ref, but do not
- // set it true if we found a word like "ON" that has
- // already set it to false
- if (isset($save_table_ref) && $save_table_ref != FALSE) {
- $save_table_ref = TRUE;
- } //end if
-
- } // end if (check for end of table ref)
- } //end if (!$seen_end_of_table_ref)
-
- if ($seen_end_of_table_ref) {
- $save_table_ref = FALSE;
- } // end if
- } // end for $i (loop #1)
-
- // -------------------------------------------------------
- // This is a big hunk of debugging code by Marc for this.
- // -------------------------------------------------------
- /*
- if (isset($current_select_expr)) {
- for ($trace=0; $trace<=$current_select_expr; $trace++) {
- echo "
";
- reset ($subresult['select_expr'][$trace]);
- while (list ($key, $val) = each ($subresult['select_expr'][$trace]))
- echo "sel expr $trace $key => $val
\n";
- }
- }
-
- if (isset($current_table_ref)) {
- echo "current_table_ref = " . $current_table_ref . "
";
- for ($trace=0; $trace<=$current_table_ref; $trace++) {
-
- echo "
";
- reset ($subresult['table_ref'][$trace]);
- while (list ($key, $val) = each ($subresult['table_ref'][$trace]))
- echo "table ref $trace $key => $val
\n";
- }
- }
- */
- // -------------------------------------------------------
-
-
- // loop #2: - queryflags
- // - querytype (for queries != 'SELECT')
- // - section_before_limit, section_after_limit
- //
- // we will also need this queryflag in loop 2
- // so set it here
- if (isset($current_table_ref) && $current_table_ref > -1) {
- $subresult['queryflags']['select_from'] = 1;
- }
-
- $collect_section_before_limit = TRUE;
- $section_before_limit = '';
- $section_after_limit = '';
- $seen_reserved_word = FALSE;
- $seen_group = FALSE;
- $seen_order = FALSE;
- $in_group_by = FALSE; // true when we are inside the GROUP BY clause
- $in_order_by = FALSE; // true when we are inside the ORDER BY clause
- $in_having = FALSE; // true when we are inside the HAVING clause
- $in_select_expr = FALSE; // true when we are inside the select expr clause
- $in_where = FALSE; // true when we are inside the WHERE clause
- $in_from = FALSE;
- $in_group_concat = FALSE;
- $unsorted_query = '';
- $first_reserved_word = '';
- $current_identifier = '';
-
- foreach ($this->_parsed as $pos => $each_parsed) {
-//DEBUG echo "Loop2 " . $each_parsed['data'] . " (" . $each_parsed['type'] . ")
";
-
- // need_confirm
- //
- // check for reserved words that will have to generate
- // a confirmation request later in sql.php
- // the cases are:
- // DROP TABLE
- // DROP DATABASE
- // ALTER TABLE... DROP
- // DELETE FROM...
- //
- // this code is not used for confirmations coming from functions.js
-
- /**
- * @todo check for punct_queryend
- * @todo verify C-style comments?
- */
- if ($each_parsed['type'] == 'comment_ansi') {
- $collect_section_before_limit = FALSE;
- }
-
- if ($each_parsed['type'] == 'alpha_reservedWord') {
- $upper_data = strtoupper($each_parsed['data']);
- if (!$seen_reserved_word) {
- $first_reserved_word = $upper_data;
- $subresult['querytype'] = $upper_data;
- $seen_reserved_word = TRUE;
-
- // if the first reserved word is DROP or DELETE,
- // we know this is a query that needs to be confirmed
- if ($first_reserved_word=='DROP'
- || $first_reserved_word == 'DELETE'
- || $first_reserved_word == 'TRUNCATE') {
- $subresult['queryflags']['need_confirm'] = 1;
- }
-
- if ($first_reserved_word=='SELECT'){
- $position_of_first_select = $pos;
- }
-
- } else {
- if ($upper_data=='DROP' && $first_reserved_word=='ALTER') {
- $subresult['queryflags']['need_confirm'] = 1;
- }
- }
-
- if ($upper_data == 'PROCEDURE') {
- $collect_section_before_limit = FALSE;
- }
- /**
- * @todo set also to FALSE if we find FOR UPDATE or LOCK IN SHARE MODE
- */
- if ($upper_data == 'SELECT') {
- $in_select_expr = TRUE;
- $select_expr_clause = '';
- }
- if ($upper_data == 'DISTINCT' && !$in_group_concat) {
- $subresult['queryflags']['distinct'] = 1;
- }
-
- if ($upper_data == 'UNION') {
- $subresult['queryflags']['union'] = 1;
- }
-
- if ($upper_data == 'JOIN') {
- $subresult['queryflags']['join'] = 1;
- }
-
- if ($upper_data == 'OFFSET') {
- $subresult['queryflags']['offset'] = 1;
- }
-
- // if this is a real SELECT...FROM
- if ($upper_data == 'FROM'
- && isset($subresult['queryflags']['select_from'])
- && $subresult['queryflags']['select_from'] == 1) {
- $in_from = TRUE;
- $from_clause = '';
- $in_select_expr = FALSE;
- }
-
-
- // (we could have less resetting of variables to FALSE
- // if we trust that the query respects the standard
- // MySQL order for clauses)
-
- // we use $seen_group and $seen_order because we are looking
- // for the BY
- if ($upper_data == 'GROUP') {
- $seen_group = TRUE;
- $seen_order = FALSE;
- $in_having = FALSE;
- $in_order_by = FALSE;
- $in_where = FALSE;
- $in_select_expr = FALSE;
- $in_from = FALSE;
- }
- if ($upper_data == 'ORDER' && !$in_group_concat) {
- $seen_order = TRUE;
- $seen_group = FALSE;
- $in_having = FALSE;
- $in_group_by = FALSE;
- $in_where = FALSE;
- $in_select_expr = FALSE;
- $in_from = FALSE;
- }
- if ($upper_data == 'HAVING') {
- $in_having = TRUE;
- $having_clause = '';
- $seen_group = FALSE;
- $seen_order = FALSE;
- $in_group_by = FALSE;
- $in_order_by = FALSE;
- $in_where = FALSE;
- $in_select_expr = FALSE;
- $in_from = FALSE;
- }
-
- if ($upper_data == 'WHERE') {
- $in_where = TRUE;
- $where_clause = '';
- $where_clause_identifiers = array();
- $seen_group = FALSE;
- $seen_order = FALSE;
- $in_group_by = FALSE;
- $in_order_by = FALSE;
- $in_having = FALSE;
- $in_select_expr = FALSE;
- $in_from = FALSE;
- }
-
- if ($upper_data == 'BY') {
- if ($seen_group) {
- $in_group_by = TRUE;
- $group_by_clause = '';
- }
- if ($seen_order) {
- $in_order_by = TRUE;
- $order_by_clause = '';
- }
- }
-
- // if we find one of the words that could end the clause
- if (PMA_STR_binarySearchInArr($upper_data, $words_ending_clauses, $words_ending_clauses_cnt)) {
- $in_group_by = FALSE;
- $in_order_by = FALSE;
- $in_having = FALSE;
- $in_where = FALSE;
- $in_select_expr = FALSE;
- $in_from = FALSE;
- }
-
- } // endif (reservedWord)
-
-
- // do not add a space after a function name
- /**
- * @todo can we combine loop 2 and loop 1? some code is repeated here...
- */
-
- $sep = ' ';
- if ($each_parsed['type'] == 'alpha_functionName') {
- $sep='';
- $upper_data = strtoupper($each_parsed['data']);
- if ($upper_data =='GROUP_CONCAT') {
- $in_group_concat = TRUE;
- $number_of_brackets_in_group_concat = 0;
- }
- }
-
- if ($each_parsed['type'] == 'punct_bracket_open_round') {
- if ($in_group_concat) {
- $number_of_brackets_in_group_concat++;
- }
- }
- if ($each_parsed['type'] == 'punct_bracket_close_round') {
- if ($in_group_concat) {
- $number_of_brackets_in_group_concat--;
- if ($number_of_brackets_in_group_concat == 0) {
- $in_group_concat = FALSE;
- }
- }
- }
-
- // do not add a space after an identifier if followed by a dot
- if ($each_parsed['type'] == 'alpha_identifier'
- && $pos < $this->_parsed_size - 1
- && $this->_parsed[$pos + 1]['data'] == '.') {
- $sep = '';
- }
-
- // do not add a space after a dot if followed by an identifier
- if ($each_parsed['data'] == '.'
- && $pos < $this->_parsed_size - 1
- && $this->_parsed[$pos + 1]['type'] == 'alpha_identifier') {
- $sep = '';
- }
-
- if ($in_select_expr && $upper_data != 'SELECT' && $upper_data != 'DISTINCT') {
- $select_expr_clause .= $each_parsed['data'] . $sep;
- }
- if ($in_from && $upper_data != 'FROM') {
- $from_clause .= $each_parsed['data'] . $sep;
- }
- if ($in_group_by && $upper_data != 'GROUP' && $upper_data != 'BY') {
- $group_by_clause .= $each_parsed['data'] . $sep;
- }
- if ($in_order_by && $upper_data != 'ORDER' && $upper_data != 'BY') {
- // add a space only before ASC or DESC
- // not around the dot between dbname and tablename
- if ($each_parsed['type'] == 'alpha_reservedWord') {
- $order_by_clause .= $sep;
- }
- $order_by_clause .= $each_parsed['data'];
- }
- if ($in_having && $upper_data != 'HAVING') {
- $having_clause .= $each_parsed['data'] . $sep;
- }
- if ($in_where && $upper_data != 'WHERE') {
- $where_clause .= $each_parsed['data'] . $sep;
-
- if (($each_parsed['type'] == 'quote_backtick')
- || ($each_parsed['type'] == 'alpha_identifier')) {
- $where_clause_identifiers[] = $each_parsed['data'];
- }
- }
-
- if (isset($subresult['queryflags']['select_from'])
- && $subresult['queryflags']['select_from'] == 1
- && !$seen_order) {
- $unsorted_query .= $each_parsed['data'];
-
- if ($each_parsed['type'] != 'punct_bracket_open_round'
- && $each_parsed['type'] != 'punct_bracket_close_round'
- && $each_parsed['type'] != 'punct') {
- $unsorted_query .= $sep;
- }
- }
-
- // clear $upper_data for next iteration
- $upper_data='';
-
- if ($collect_section_before_limit && $each_parsed['type'] != 'punct_queryend') {
- $section_before_limit .= $each_parsed['data'] . $sep;
- } else {
- $section_after_limit .= $each_parsed['data'] . $sep;
- }
-
-
- } // end for $i (loop #2)
-
-
- // -----------------------------------------------------
- // loop #3: foreign keys and MySQL 4.1.2+ TIMESTAMP options
- // (for now, check only the first query)
- // (for now, identifiers are assumed to be backquoted)
-
- // If we find that we are dealing with a CREATE TABLE query,
- // we look for the next punct_bracket_open_round, which
- // introduces the fields list. Then, when we find a
- // quote_backtick, it must be a field, so we put it into
- // the create_table_fields array. Even if this field is
- // not a timestamp, it will be useful when logic has been
- // added for complete field attributes analysis.
-
- $seen_foreign = FALSE;
- $seen_references = FALSE;
- $seen_constraint = FALSE;
- $foreign_key_number = -1;
- $seen_create_table = FALSE;
- $seen_create = FALSE;
- $in_create_table_fields = FALSE;
- $brackets_level = 0;
- $in_timestamp_options = FALSE;
- $seen_default = FALSE;
-
- foreach ($this->_parsed as $pos => $each_parsed) {
- // DEBUG echo "Loop 3 " . $each_parsed['data'] . " " . $each_parsed['type'] . "
";
-
- if ($each_parsed['type'] == 'alpha_reservedWord') {
- $upper_data = strtoupper($each_parsed['data']);
-
- if ($upper_data == 'NOT' && $in_timestamp_options) {
- $create_table_fields[$current_identifier]['timestamp_not_null'] = TRUE;
-
- }
-
- if ($upper_data == 'CREATE') {
- $seen_create = TRUE;
- }
-
- if ($upper_data == 'TABLE' && $seen_create) {
- $seen_create_table = TRUE;
- $create_table_fields = array();
- }
-
- if ($upper_data == 'CURRENT_TIMESTAMP') {
- if ($in_timestamp_options) {
- if ($seen_default) {
- $create_table_fields[$current_identifier]['default_current_timestamp'] = TRUE;
- }
- }
- }
-
- if ($upper_data == 'CONSTRAINT') {
- $foreign_key_number++;
- $seen_foreign = FALSE;
- $seen_references = FALSE;
- $seen_constraint = TRUE;
- }
- if ($upper_data == 'FOREIGN') {
- $seen_foreign = TRUE;
- $seen_references = FALSE;
- $seen_constraint = FALSE;
- }
- if ($upper_data == 'REFERENCES') {
- $seen_foreign = FALSE;
- $seen_references = TRUE;
- $seen_constraint = FALSE;
- }
-
-
- // Cases covered:
-
- // [ON DELETE {CASCADE | SET NULL | NO ACTION | RESTRICT}]
- // [ON UPDATE {CASCADE | SET NULL | NO ACTION | RESTRICT}]
-
- // but we set ['on_delete'] or ['on_cascade'] to
- // CASCADE | SET_NULL | NO_ACTION | RESTRICT
-
- // ON UPDATE CURRENT_TIMESTAMP
-
- if ($upper_data == 'ON') {
- if ($this->_parsed[$pos + 1]['type'] == 'alpha_reservedWord') {
- $second_upper_data = strtoupper($this->_parsed[$pos + 1]['data']);
- if ($second_upper_data == 'DELETE') {
- $clause = 'on_delete';
- }
- if ($second_upper_data == 'UPDATE') {
- $clause = 'on_update';
- }
- if (isset($clause)
- && ($this->_parsed[$pos + 2]['type'] == 'alpha_reservedWord'
-
- // ugly workaround because currently, NO is not
- // in the list of reserved words in sqlparser.data
- // (we got a bug report about not being able to use
- // 'no' as an identifier)
- || ($this->_parsed[$pos + 2]['type'] == 'alpha_identifier'
- && strtoupper($this->_parsed[$pos + 2]['data'])=='NO'))
- ) {
- $third_upper_data = strtoupper($this->_parsed[$pos + 2]['data']);
- if ($third_upper_data == 'CASCADE'
- || $third_upper_data == 'RESTRICT') {
- $value = $third_upper_data;
- } elseif ($third_upper_data == 'SET'
- || $third_upper_data == 'NO') {
- if ($this->_parsed[$pos + 3]['type'] == 'alpha_reservedWord') {
- $value = $third_upper_data . '_'
- . strtoupper($this->_parsed[$pos + 3]['data']);
- }
- } elseif ($third_upper_data == 'CURRENT_TIMESTAMP') {
- if ($clause == 'on_update'
- && $in_timestamp_options) {
- $create_table_fields[$current_identifier]['on_update_current_timestamp'] = TRUE;
- $seen_default = FALSE;
- }
-
- } else {
- $value = '';
- }
- if (!empty($value)) {
- $foreign[$foreign_key_number][$clause] = $value;
- }
- unset($clause);
- } // endif (isset($clause))
- }
- }
-
- } // end of reserved words analysis
-
-
- if ($each_parsed['type'] == 'punct_bracket_open_round') {
- $brackets_level++;
- if ($seen_create_table && $brackets_level == 1) {
- $in_create_table_fields = TRUE;
- }
- }
-
-
- if ($each_parsed['type'] == 'punct_bracket_close_round') {
- $brackets_level--;
- if ($seen_references) {
- $seen_references = FALSE;
- }
- if ($seen_create_table && $brackets_level == 0) {
- $in_create_table_fields = FALSE;
- }
- }
-
- if (($each_parsed['type'] == 'alpha_columnAttrib')) {
- $upper_data = strtoupper($each_parsed['data']);
- if ($seen_create_table && $in_create_table_fields) {
- if ($upper_data == 'DEFAULT') {
- $seen_default = TRUE;
- }
- }
- }
-
- /**
- * @see @todo 2005-10-16 note: the "or" part here is a workaround for a bug
- */
- if ($each_parsed['type'] == 'alpha_columnType'
- || ($each_parsed['type'] == 'alpha_functionName'
- && $seen_create_table)) {
- $upper_data = strtoupper($each_parsed['data']);
- if ($seen_create_table && $in_create_table_fields && isset($current_identifier)) {
- $create_table_fields[$current_identifier]['type'] = $upper_data;
- if ($upper_data == 'TIMESTAMP') {
- $each_parsed['type'] = 'alpha_columnType';
- $in_timestamp_options = TRUE;
- } else {
- $in_timestamp_options = FALSE;
- if ($upper_data == 'CHAR') {
- $each_parsed['type'] = 'alpha_columnType';
- }
- }
- }
- }
-
-
- if ($each_parsed['type'] == 'quote_backtick'
- || $each_parsed['type'] == 'alpha_identifier') {
-
- if ($each_parsed['type'] == 'quote_backtick') {
- // remove backquotes
- $identifier = PMA_unQuote($each_parsed['data']);
- } else {
- $identifier = $each_parsed['data'];
- }
-
- if ($seen_create_table && $in_create_table_fields) {
- $current_identifier = $identifier;
- // warning: we set this one even for non TIMESTAMP type
- $create_table_fields[$current_identifier]['timestamp_not_null'] = FALSE;
- }
-
- if ($seen_constraint) {
- $foreign[$foreign_key_number]['constraint'] = $identifier;
- }
-
- if ($seen_foreign && $brackets_level > 0) {
- $foreign[$foreign_key_number]['index_list'][] = $identifier;
- }
-
- if ($seen_references) {
- // here, the first bracket level corresponds to the
- // bracket of CREATE TABLE
- // so if we are on level 2, it must be the index list
- // of the foreign key REFERENCES
- if ($brackets_level > 1) {
- $foreign[$foreign_key_number]['ref_index_list'][] = $identifier;
- } else {
- // for MySQL 4.0.18, identifier is
- // `table` or `db`.`table`
- // the first pass will pick the db name
- // the next pass will execute the else and pick the
- // db name in $db_table[0]
- if ($this->_parsed[$pos + 1]['type'] == 'punct_qualifier') {
- $foreign[$foreign_key_number]['ref_db_name'] = $identifier;
- } else {
- // for MySQL 4.0.16, identifier is
- // `table` or `db.table`
- $db_table = explode('.', $identifier);
- if (isset($db_table[1])) {
- $foreign[$foreign_key_number]['ref_db_name'] = $db_table[0];
- $foreign[$foreign_key_number]['ref_table_name'] = $db_table[1];
- } else {
- $foreign[$foreign_key_number]['ref_table_name'] = $db_table[0];
- }
- }
- }
- }
- }
- } // end for $i (loop #3)
-
-
- // Fill the $subresult array
-
- if (isset($create_table_fields)) {
- $subresult['create_table_fields'] = $create_table_fields;
- }
-
- if (isset($foreign)) {
- $subresult['foreign_keys'] = $foreign;
- }
-
- if (isset($select_expr_clause)) {
- $subresult['select_expr_clause'] = $select_expr_clause;
- }
- if (isset($from_clause)) {
- $subresult['from_clause'] = $from_clause;
- }
- if (isset($group_by_clause)) {
- $subresult['group_by_clause'] = $group_by_clause;
- }
- if (isset($order_by_clause)) {
- $subresult['order_by_clause'] = $order_by_clause;
- }
- if (isset($having_clause)) {
- $subresult['having_clause'] = $having_clause;
- }
- if (isset($where_clause)) {
- $subresult['where_clause'] = $where_clause;
- }
- if (isset($unsorted_query) && !empty($unsorted_query)) {
- $subresult['unsorted_query'] = $unsorted_query;
- }
- if (isset($where_clause_identifiers)) {
- $subresult['where_clause_identifiers'] = $where_clause_identifiers;
- }
-
- if (isset($position_of_first_select)) {
- $subresult['position_of_first_select'] = $position_of_first_select;
- $subresult['section_before_limit'] = $section_before_limit;
- $subresult['section_after_limit'] = $section_after_limit;
- }
-
- // They are naughty and didn't have a trailing semi-colon,
- // then still handle it properly
- if ($subresult['querytype'] != '') {
- $this->_analyzed[] = $subresult;
- }
- return $this->_analyzed;
- } // end of the "analyze()" function
-
- /**
- * Colorizes SQL queries html formatted
- *
- * @todo check why adding a "\n" after the would cause extra blanks
- * to be displayed: SELECT p . person_name
- *
- * @access public
- * @param array The SQL queries html formatted
- * @return array The colorized SQL queries
- */
- function getFormatedHtmlColored($parsed)
- {
- $i = $GLOBALS['PMA_strpos']($parsed['type'], '_');
- $class = '';
- if ($i > 0) {
- $class = 'syntax_' . PMA_substr($parsed['type'], 0, $i) . ' ';
- }
-
- $class .= 'syntax_' . $parsed['type'];
-
- return '' . htmlspecialchars($parsed['data']) . '';
- } // end of the "getFormatedHtmlColored()" function
-
- /**
- * @uses PMA_Sql::analyzeIfRequired()
- * @uses PMA_Sql::$_analyzed to check
- * @param integer $subquery which subquery to check, default 0
- * @return boolean
- */
- function isDistinct($subquery = 0)
- {
- $this->analyzeIfRequired();
-
- if (isset($this->_analyzed[$subquery]['queryflags']['distinct'])) {
- return true;
- }
-
- return false;
- }
-
- /**
- * @uses PMA_Sql::analyzeIfRequired()
- * @uses PMA_Sql::$_analyzed to check
- * @param integer $subquery which subquery to check, default 0
- * @return boolean
- */
- function isUnion($subquery = 0)
- {
- $this->analyzeIfRequired();
-
- if (isset($this->_analyzed[$subquery]['queryflags']['union'])) {
- return true;
- }
-
- return false;
- }
-
- function isOnlyOneTable($subquery = 0)
- {
- if (! isset($this->_analyzed[$subquery]['table_ref'][1]['table_name'])) {
- return true;
- }
-
- return false;
- }
-
- function isWhereAlwaysTrue($subquery = 0)
- {
- if (! $this->hasWhere($subquery) || trim($this->getWhereClause($subquery)) == '1') {
- return true;
- }
-
- return false;
- }
-
- function getCountQuery()
- {
- if (PMA_MYSQL_INT_VERSION < 40000) {
-
- // detect this case:
- // SELECT DISTINCT x AS foo, y AS bar FROM sometable
- if ($this->isDistinct()) {
- $count_what = 'DISTINCT ';
- foreach ($this->_analyzed[0]['select_expr'] as $part) {
- $count_what .= $part['expr'] . ', ';
- }
- $count_what = substr($count_what, 0, -2);
- } else {
- $count_what = '*';
- }
- // this one does not apply to VIEWs
- $count_query = 'SELECT COUNT(' . $count_what . ') AS count';
-
- // add the remaining of select expression if there is
- // a GROUP BY or HAVING clause
- if ($count_what === '*'
- && ($this->hasGroupBy() || $this->hasHaving())) {
- $count_query .= ' ,' . $this->_analyzed[0]['select_expr_clause'];
- }
-
- if ($this->hasFrom()) {
- $count_query .= ' FROM ' . $this->getFromClause();
- }
- if ($this->hasWhere()) {
- $count_query .= ' WHERE ' . $this->getWhereClause();
- }
- if ($this->hasGroupBy()) {
- $count_query .= ' GROUP BY ' . $this->getGroupByClause();
- }
- if ($this->hasHaving()) {
- $count_query .= ' HAVING ' . $this->getHavingClause();
- }
- } else { // if (PMA_MYSQL_INT_VERSION >= 40000)
- // 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 = $this->getFormatedHtml('query_only', 0, $this->getPositionOfFirstSelect() + 1);
- $count_query .= ' SQL_CALC_FOUND_ROWS ';
- // add everything that was after the first SELECT
- $count_query = $this->getFormatedHtml('query_only', $this->getPositionOfFirstSelect() + 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 (! $this->isUnion()) {
- $count_query .= ' LIMIT 1';
- }
- }
-
- return $count_query;
- }
-
- function isGroup()
- {
- return preg_match('@(GROUP[[:space:]]+BY|HAVING|SELECT[[:space:]]+DISTINCT)[[:space:]]+@i', $this->_raw);
- }
-
- function getFoundRows()
- {
- $count_query = $this->getCountQuery();
-
- if (PMA_MYSQL_INT_VERSION < 40000) {
- if ($cnt_all_result = PMA_DBI_try_query($count_query)) {
- if ($this->isGroup() && ! $this->isDistinct()) {
- $unlim_num_rows = @PMA_DBI_num_rows($cnt_all_result);
- } else {
- $unlim_num_rows = PMA_DBI_fetch_assoc($cnt_all_result);
- $unlim_num_rows = $unlim_num_rows['count'];
- }
- PMA_DBI_free_result($cnt_all_result);
- } else {
- if (PMA_DBI_getError()) {
-
- // there are some cases where the generated
- // count_query (for MySQL 3) is wrong,
- // so we get here.
- /**
- * @todo use a big unlimited query to get the correct
- * number of rows (depending on a config variable?)
- */
- $unlim_num_rows = 0;
- }
- }
- } else {
- PMA_DBI_try_query($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 = PMA_DBI_fetch_value('SELECT FOUND_ROWS();');
- }
-
- return $unlim_num_rows;
- }
-
- /**
- * @uses PMA_Sql::analyzeIfRequired()
- * @uses PMA_Sql::$_analyzed to check
- * @param integer $subquery which subquery to check, default 0
- * @return boolean
- */
- function hasGroupBy($subquery = 0)
- {
- $this->analyzeIfRequired();
-
- if (! empty($this->_analyzed[$subquery]['group_by_clause'])) {
- return true;
- }
-
- return false;
- }
-
- /**
- * @uses PMA_Sql::analyzeIfRequired()
- * @uses PMA_Sql::$_analyzed to check
- * @param integer $subquery which subquery to check, default 0
- * @return boolean
- */
- function hasFrom($subquery = 0)
- {
- $this->analyzeIfRequired();
-
- if (! empty($this->_analyzed[$subquery]['from_clause'])) {
- return true;
- }
-
- return false;
- }
-
- /**
- * @uses PMA_Sql::analyzeIfRequired()
- * @uses PMA_Sql::$_analyzed to check
- * @param integer $subquery which subquery to check, default 0
- * @return boolean
- */
- function hasHaving($subquery = 0)
- {
- $this->analyzeIfRequired();
-
- if (! empty($this->_analyzed[$subquery]['having_clause'])) {
- return true;
- }
-
- return false;
- }
-
- /**
- * @uses PMA_Sql::analyzeIfRequired()
- * @uses PMA_Sql::$_analyzed to check
- * @param integer $subquery which subquery to check, default 0
- * @return boolean
- */
- function hasWhere($subquery = 0)
- {
- $this->analyzeIfRequired();
-
- if (! empty($this->_analyzed[$subquery]['where_clause'])) {
- return true;
- }
-
- return false;
- }
-
- /**
- * @uses PMA_Sql::hasGroupBy() to check
- * @param integer $subquery which subquery to check, default 0
- * @return string or false
- */
- function getGroupByClause($subquery = 0)
- {
- if ($this->hasGroupBy()) {
- return $this->_analyzed[$subquery]['group_by_clause'];
- }
-
- return false;
- }
-
- /**
- * @uses PMA_Sql::hasHaving() to check
- * @param integer $subquery which subquery to check, default 0
- * @return string or false
- */
- function getHavingClause($subquery = 0)
- {
- if ($this->hasHaving()) {
- return $this->_analyzed[$subquery]['having_clause'];
- }
-
- return false;
- }
-
- /**
- * @uses PMA_Sql::hasWhere() to check
- * @param integer $subquery which subquery to check, default 0
- * @return string or false
- */
- function getWhereClause($subquery = 0)
- {
- if ($this->hasWhere()) {
- return $this->_analyzed[$subquery]['where_clause'];
- }
-
- return false;
- }
-
- /**
- * @uses PMA_Sql::hasFrom() to check
- * @param integer $subquery which subquery to check, default 0
- * @return string or false
- */
- function getFromClause($subquery = 0)
- {
- if ($this->hasFrom()) {
- return $this->_analyzed[$subquery]['from_clause'];
- }
-
- return false;
- }
-
- /**
- *
- * @uses PMA_Sql::analyzeIfRequired()
- * @uses PMA_Sql::hasFrom() to check
- * @param integer $subquery which subquery to check, default 0
- * @return integer or false
- */
- function getPositionOfFirstSelect($subquery = 0)
- {
- $this->analyzeIfRequired();
-
- if (isset($this->_analyzed[$subquery]['position_of_first_select'])) {
- return $this->_analyzed[$subquery]['position_of_first_select'];
- }
-
- return false;
- }
-
- /**
- * Formats SQL queries to html
- *
- * @access public
- * @param array The SQL queries
- * @param string mode
- * @param integer starting token
- * @param integer number of tokens to format, -1 = all
- * @return string The formatted SQL queries
- */
- function getFormatedHtml($mode = 'color', $start_token = 0, $number_of_tokens = -1)
- {
- $this->parseIfRequired();
-
- //DEBUG echo 'in Format'; print_r($this->_parsed); echo '
';
- // check for parsed data
- // check for the SQL parser having hit an error
- if (empty($this->_parsed) || $this->isError()) {
- return htmlspecialchars($this->_raw);
- }
-
- // else do it properly
- switch ($mode) {
- case 'color':
- $str = '';
- $html_line_break = '
';
- break;
- case 'query_only':
- $str = '';
- $html_line_break = "\n";
- break;
- case 'text':
- $str = '';
- $html_line_break = '
';
- break;
- } // end switch
- $indent = 0;
- $bracketlevel = 0;
- $functionlevel = 0;
- $infunction = FALSE;
- $space_punct_listsep = ' ';
- $space_punct_listsep_function_name = ' ';
- // $space_alpha_reserved_word = '
'."\n";
- $space_alpha_reserved_word = ' ';
-
- $keywords_with_brackets_1before = array(
- 'INDEX',
- 'KEY',
- 'ON',
- 'USING'
- );
- $keywords_with_brackets_1before_cnt = 4;
-
- $keywords_with_brackets_2before = array(
- 'IGNORE',
- 'INDEX',
- 'INTO',
- 'KEY',
- 'PRIMARY',
- 'PROCEDURE',
- 'REFERENCES',
- 'UNIQUE',
- 'USE'
- );
- // $keywords_with_brackets_2before_cnt = count($keywords_with_brackets_2before);
- $keywords_with_brackets_2before_cnt = 9;
-
- // These reserved words do NOT get a newline placed near them.
- $keywords_no_newline = array(
- 'AS',
- 'ASC',
- 'DESC',
- 'DISTINCT',
- 'DUPLICATE',
- 'HOUR',
- 'INTERVAL',
- 'IS',
- 'LIKE',
- 'NOT',
- 'NULL',
- 'ON',
- 'REGEXP'
- );
- $keywords_no_newline_cnt = 12;
-
- // These reserved words introduce a privilege list
- $keywords_priv_list = array(
- 'GRANT',
- 'REVOKE'
- );
- $keywords_priv_list_cnt = 2;
-
- if ($number_of_tokens == -1) {
- $number_of_tokens = $this->_parsed_size;
- }
- $typearr = array();
- if ($number_of_tokens >= 0) {
- $typearr[0] = '';
- $typearr[1] = '';
- $typearr[2] = '';
- //$typearr[3] = $this->_parsed[0]['type'];
- $typearr[3] = $this->_parsed[$start_token]['type'];
- }
-
- $in_priv_list = FALSE;
- for ($i = $start_token; $i < $number_of_tokens; $i++) {
- $each_parsed = $this->_parsed[$i];
-// DEBUG echo "Loop format " . $each_parsed['data'] . " " . $each_parsed['type'] . "
";
- $before = '';
- $after = '';
- $indent = 0;
- // array_shift($typearr);
- /*
- 0 prev2
- 1 prev
- 2 current
- 3 next
- */
- if (($i + 1) < $number_of_tokens) {
- // array_push($typearr, $this->_parsed[$i + 1]['type']);
- $typearr[4] = $this->_parsed[$i + 1]['type'];
- } else {
- //array_push($typearr, null);
- $typearr[4] = '';
- }
-
- for ($j = 0; $j < 4; $j++) {
- $typearr[$j] = $typearr[$j + 1];
- }
-
- switch ($typearr[2]) {
- case 'white_newline':
- $before = '';
- break;
- case 'punct_bracket_open_round':
- $bracketlevel++;
- $infunction = FALSE;
- // Make sure this array is sorted!
- if ($typearr[1] == 'alpha_functionName'
- || $typearr[1] == 'alpha_columnType'
- || $typearr[1] == 'punct'
- || $typearr[3] == 'digit_integer'
- || $typearr[3] == 'digit_hex'
- || $typearr[3] == 'digit_float'
- || ($typearr[0] == 'alpha_reservedWord'
- && PMA_STR_binarySearchInArr(strtoupper($this->_parsed[$i - 2]['data']), $keywords_with_brackets_2before, $keywords_with_brackets_2before_cnt))
- || ($typearr[1] == 'alpha_reservedWord'
- && PMA_STR_binarySearchInArr(strtoupper($this->_parsed[$i - 1]['data']), $keywords_with_brackets_1before, $keywords_with_brackets_1before_cnt))
- ) {
- $functionlevel++;
- $infunction = TRUE;
- $after .= ' ';
- } else {
- $indent++;
- $after .= ($mode != 'query_only' ? '' : ' ');
- }
- break;
- case 'alpha_identifier':
- if ($typearr[1] == 'punct_qualifier'
- || $typearr[3] == 'punct_qualifier') {
- $after = '';
- $before = '';
- }
- if ($typearr[3] == 'alpha_columnType'
- || $typearr[3] == 'alpha_identifier') {
- $after .= ' ';
- }
- break;
- case 'punct_qualifier':
- $before = '';
- $after = '';
- break;
- case 'punct_listsep':
- if ($infunction == TRUE) {
- $after .= $space_punct_listsep_function_name;
- } else {
- $after .= $space_punct_listsep;
- }
- break;
- case 'punct_queryend':
- if ($typearr[3] != 'comment_mysql'
- && $typearr[3] != 'comment_ansi'
- && $typearr[3] != 'comment_c') {
- $after .= $html_line_break;
- $after .= $html_line_break;
- }
- $space_punct_listsep = ' ';
- $space_punct_listsep_function_name = ' ';
- $space_alpha_reserved_word = ' ';
- $in_priv_list = FALSE;
- break;
- case 'comment_mysql':
- case 'comment_ansi':
- $after .= $html_line_break;
- break;
- case 'punct':
- $before .= ' ';
- // workaround for
- // select * from mytable limit 0,-1
- // (a side effect of this workaround is that
- // select 20 - 9
- // becomes
- // select 20 -9
- // )
- if ($typearr[3] != 'digit_integer') {
- $after .= ' ';
- }
- break;
- case 'punct_bracket_close_round':
- $bracketlevel--;
- if ($infunction == TRUE) {
- $functionlevel--;
- $after .= ' ';
- $before .= ' ';
- } else {
- $indent--;
- $before .= ($mode != 'query_only' ? '
' : ' ');
- }
- $infunction = ($functionlevel > 0) ? TRUE : FALSE;
- break;
- case 'alpha_columnType':
- if ($typearr[3] == 'alpha_columnAttrib') {
- $after .= ' ';
- }
- if ($typearr[1] == 'alpha_columnType') {
- $before .= ' ';
- }
- break;
- case 'alpha_columnAttrib':
-
- // ALTER TABLE tbl_name AUTO_INCREMENT = 1
- // COLLATE LATIN1_GENERAL_CI DEFAULT
- if ($typearr[1] == 'alpha_identifier'
- || $typearr[1] == 'alpha_charset') {
- $before .= ' ';
- }
- if ($typearr[3] == 'alpha_columnAttrib'
- || $typearr[3] == 'quote_single'
- || $typearr[3] == 'digit_integer') {
- $after .= ' ';
- }
- // workaround for
- // AUTO_INCREMENT = 31DEFAULT_CHARSET = utf-8
-
- if ($typearr[2] == 'alpha_columnAttrib'
- && $typearr[3] == 'alpha_reservedWord') {
- $before .= ' ';
- }
- // workaround for
- // select * from mysql.user where binary user="root"
- // binary is marked as alpha_columnAttrib
- // but should be marked as a reserved word
- if (strtoupper($each_parsed['data']) == 'BINARY'
- && $typearr[3] == 'alpha_identifier') {
- $after .= ' ';
- }
- break;
- case 'alpha_reservedWord':
- // do not uppercase the reserved word if we are calling
- // this function in query_only mode, because we need
- // the original query (otherwise we get problems with
- // semi-reserved words like "storage" which is legal
- // as an identifier name)
-
- if ($mode != 'query_only') {
- $each_parsed['data'] = strtoupper($each_parsed['data']);
- }
-
- if (($typearr[1] != 'alpha_reservedWord'
- || ($typearr[1] == 'alpha_reservedWord'
- && PMA_STR_binarySearchInArr(strtoupper($this->_parsed[$i - 1]['data']), $keywords_no_newline, $keywords_no_newline_cnt)))
- && $typearr[1] != 'punct_level_plus'
- && !PMA_STR_binarySearchInArr($each_parsed['data'], $keywords_no_newline, $keywords_no_newline_cnt)) {
- // do not put a space before the first token, because
- // we use a lot of eregi() checking for the first
- // reserved word at beginning of query
- // so do not put a newline before
- //
- // also we must not be inside a privilege list
- if ($i > 0) {
- // the alpha_identifier exception is there to
- // catch cases like
- // GRANT SELECT ON mydb.mytable TO myuser@localhost
- // (else, we get mydb.mytableTO )
- //
- // the quote_single exception is there to
- // catch cases like
- // GRANT ... TO 'marc'@'domain.com' IDENTIFIED...
- /**
- * @todo fix all cases and find why this happens
- */
-
- if (!$in_priv_list
- || $typearr[1] == 'alpha_identifier'
- || $typearr[1] == 'quote_single'
- || $typearr[1] == 'white_newline') {
- $before .= $space_alpha_reserved_word;
- }
- } else {
- // on first keyword, check if it introduces a
- // privilege list
- if (PMA_STR_binarySearchInArr($each_parsed['data'], $keywords_priv_list, $keywords_priv_list_cnt)) {
- $in_priv_list = TRUE;
- }
- }
- } else {
- $before .= ' ';
- }
-
- switch ($each_parsed['data']) {
- case 'CREATE':
- if (!$in_priv_list) {
- $space_punct_listsep = $html_line_break;
- $space_alpha_reserved_word = ' ';
- }
- break;
- case 'EXPLAIN':
- case 'DESCRIBE':
- case 'SET':
- case 'ALTER':
- case 'DELETE':
- case 'SHOW':
- case 'DROP':
- case 'UPDATE':
- case 'TRUNCATE':
- case 'ANALYZE':
- case 'ANALYSE':
- if (!$in_priv_list) {
- $space_punct_listsep = $html_line_break;
- $space_alpha_reserved_word = ' ';
- }
- break;
- case 'INSERT':
- case 'REPLACE':
- if (!$in_priv_list) {
- $space_punct_listsep = $html_line_break;
- $space_alpha_reserved_word = $html_line_break;
- }
- break;
- case 'VALUES':
- $space_punct_listsep = ' ';
- $space_alpha_reserved_word = $html_line_break;
- break;
- case 'SELECT':
- $space_punct_listsep = ' ';
- $space_alpha_reserved_word = $html_line_break;
- break;
- default:
- break;
- } // end switch ($each_parsed['data'])
-
- $after .= ' ';
- break;
- case 'digit_integer':
- case 'digit_float':
- case 'digit_hex':
- /**
- * @todo could there be other types preceding a digit?
- */
- if ($typearr[1] == 'alpha_reservedWord') {
- $after .= ' ';
- }
- if ($infunction && $typearr[3] == 'punct_bracket_close_round') {
- $after .= ' ';
- }
- if ($typearr[1] == 'alpha_columnAttrib') {
- $before .= ' ';
- }
- break;
- case 'alpha_variable':
- // other workaround for a problem similar to the one
- // explained below for quote_single
- if (!$in_priv_list && $typearr[3] != 'quote_backtick') {
- $after = ' ';
- }
- break;
- case 'quote_double':
- case 'quote_single':
- // workaround: for the query
- // REVOKE SELECT ON `base2\_db`.* FROM 'user'@'%'
- // the @ is incorrectly marked as alpha_variable
- // in the parser, and here, the '%' gets a blank before,
- // which is a syntax error
- if ($typearr[1] !='alpha_variable') {
- $before .= ' ';
- }
- if ($infunction && $typearr[3] == 'punct_bracket_close_round') {
- $after .= ' ';
- }
- break;
- case 'quote_backtick':
- if ($typearr[3] != 'punct_qualifier'
- && $typearr[3] != 'alpha_variable') {
- $after .= ' ';
- }
- if ($typearr[1] != 'punct_qualifier'
- && $typearr[1] != 'alpha_variable') {
- $before .= ' ';
- }
- break;
- default:
- break;
- } // end switch ($typearr[2])
-
- /*
- if ($typearr[3] != 'punct_qualifier') {
- $after .= ' ';
- }
- $after .= "\n";
- */
- $str .= $before
- . ($mode == 'color' ? $this->getFormatedHtmlColored($each_parsed) : $each_parsed['data'])
- . $after;
- } // end for
- if ($mode == 'color') {
- $str .= '';
- }
-
- return $str;
- } // end of the "getFormatedHtml()" function
-
- /**
- * Builds a CSS rule used for html formatted SQL queries
- *
- * @param string The class name
- * @param string The property name
- * @param string The property value
- * @return string The CSS rule
- * @access public
- * @see PMA_Sql::getCssData()
- */
- function getCssRule($classname, $property, $value)
- {
- $str = '.' . $classname . ' {';
- if ($value != '') {
- $str .= $property . ': ' . $value . ';';
- }
- $str .= '}' . "\n";
-
- return $str;
- } // end of the "getCssRule()" function
-
- /**
- * return raw SQL query
- * @uses PMA_Sql::$_raw as return value
- * @return string SQL query
- */
- function getRaw()
- {
- return $this->_raw;
- }
-
- /**
- * Builds CSS rules used for html formatted SQL queries
- *
- * @return string The CSS rules set
- * @access public
- * @global array The current PMA configuration
- * @see PMA_Sql::getCssRule()
- */
- function getCssData()
- {
- global $cfg;
-
- $css_string = '';
- foreach ($cfg['SQP']['fmtColor'] as $key => $col) {
- $css_string .= $this->getCssRule('syntax_' . $key, 'color', $col);
- }
-
- for ($i = 0; $i < 8; $i++) {
- $css_string .= $this->getCssRule('syntax_indent' . $i, 'margin-left',
- ($i * $cfg['SQP']['fmtInd']) . $cfg['SQP']['fmtIndUnit']);
- }
-
- return $css_string;
- } // end of the "getCssData()" function
-
- /**
- * Gets SQL queries with no format
- *
- * @param array The SQL queries list
- * @return string The SQL queries with no format
- * @access public
- */
- function getFormatedNone()
- {
- $formatted_sql = htmlspecialchars($this->_raw);
- $formatted_sql = preg_replace("@((\015\012)|(\015)|(\012)){3,}@", "\n\n",
- $formatted_sql);
-
- return $formatted_sql;
- } // end of the "getFormatedNone()" function
-
- /**
- * Gets SQL queries in text format
- *
- * @todo WRITE THIS!
- * @param array The SQL queries list
- * @return string The SQL queries in text format
- * @access public
- */
- function getFormatedText()
- {
- return $this->getFormatedNone();
- } // end of the "getFormatedText()" function
-}
-?>