diff --git a/libraries/Theme.class.php b/libraries/Theme.class.php index 0783712e23..ef54a2f142 100644 --- a/libraries/Theme.class.php +++ b/libraries/Theme.class.php @@ -310,6 +310,61 @@ class PMA_Theme return $this->img_path; } + /** + * Builds a CSS rule used for html formatted SQL queries + * + * @param string $classname The class name + * @param string $property The property name + * @param string $value The property value + * + * @return string The CSS rule + * + * @access public + * + * @see PMA_SQP_buildCssData() + */ + public function SQP_buildCssRule($classname, $property, $value) + { + $str = '.' . $classname . ' {'; + if ($value != '') { + $str .= $property . ': ' . $value . ';'; + } + $str .= '}' . "\n"; + + return $str; + } // end of the "PMA_SQP_buildCssRule()" function + + + /** + * 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_SQP_buildCssRule() + */ + public function SQP_buildCssData() + { + global $cfg; + + $css_string = ''; + foreach ($cfg['SQP']['fmtColor'] AS $key => $col) { + $css_string .= $this->SQP_buildCssRule('syntax_' . $key, 'color', $col); + } + + for ($i = 0; $i < 8; $i++) { + $css_string .= $this->SQP_buildCssRule( + 'syntax_indent' . $i, 'margin-left', + ($i * $cfg['SQP']['fmtInd']) . $cfg['SQP']['fmtIndUnit'] + ); + } + + return $css_string; + } // end of the "PMA_SQP_buildCssData()" function + /** * load css (send to stdout, normally the browser) * @@ -320,7 +375,7 @@ class PMA_Theme { $success = true; - echo PMA_SQP_buildCssData(); + echo $this->SQP_buildCssData(); if ($GLOBALS['text_dir'] === 'ltr') { $right = 'right'; diff --git a/libraries/insert_edit.lib.php b/libraries/insert_edit.lib.php index 10d2e60e87..52faf44324 100644 --- a/libraries/insert_edit.lib.php +++ b/libraries/insert_edit.lib.php @@ -2,7 +2,7 @@ /* vim: set expandtab sw=4 ts=4 sts=4: */ /** * set of functions with the insert/edit features in pma - * + * * @package PhpMyAdmin */ @@ -12,23 +12,23 @@ if (! defined('PHPMYADMIN')) { /** * Retrieve form parameters for insert/edit form - * - * @param string $db name of the database - * @param string $table name of the table - * @param array $where_clauses where clauses - * @param array $where_clause_array array of where clauses - * @param string $err_url error url - * - * @return array $_form_params array of insert/edit form parameters + * + * @param string $db name of the database + * @param string $table name of the table + * @param array $where_clauses where clauses + * @param array $where_clause_array array of where clauses + * @param string $err_url error url + * + * @return array $_form_params array of insert/edit form parameters */ function PMA_getFormParametersForInsertForm($db, $table, $where_clauses, $where_clause_array, $err_url) { $_form_params = array( - 'db' => $db, - 'table' => $table, - 'goto' => $GLOBALS['goto'], - 'err_url' => $err_url, - 'sql_query' => $_REQUEST['sql_query'], + 'db' => $db, + 'table' => $table, + 'goto' => $GLOBALS['goto'], + 'err_url' => $err_url, + 'sql_query' => $_REQUEST['sql_query'], ); if (isset($where_clauses)) { foreach ($where_clause_array as $key_id => $where_clause) { @@ -43,13 +43,13 @@ function PMA_getFormParametersForInsertForm($db, $table, $where_clauses, $where_ /** * Retrieve the values for pma edit mode - * - * @param type $where_clause where clauses - * @param type $table name of the table - * @param type $db name of the database - * + * + * @param type $where_clause where clauses + * @param type $table name of the table + * @param type $db name of the database + * * @return type containing insert_mode,whereClauses, result array - * where_clauses_array and found_unique_key boolean value + * where_clauses_array and found_unique_key boolean value */ function PMA_getValuesForEditMode($where_clause, $table, $db) { @@ -67,11 +67,11 @@ function PMA_getValuesForEditMode($where_clause, $table, $db) /** * - * @return whereClauseArray array of where clauses + * @return whereClauseArray array of where clauses */ function PMA_getWhereClauseArray($where_clause) { - if(isset ($where_clause)) { + if (isset ($where_clause)) { if (is_array($where_clause)) { return $where_clause; } else { @@ -82,12 +82,12 @@ function PMA_getWhereClauseArray($where_clause) /** * Analysing where cluases array - * + * * @param array $where_clause_array array of where clauses * @param string $table name of the table * @param string $db name of the database * @param boolean $found_unique_key boolean variable for unique key - * + * * @return array $where_clauses, $result, $rows */ function PMA_analyzeWhereClauses($where_clause_array, $table, $db, $found_unique_key) @@ -108,15 +108,15 @@ function PMA_analyzeWhereClauses($where_clause_array, $table, $db, $found_unique } /** - * Show message for empty reult or set the unique_condition - * + * Show message for empty reult or set the unique_condition + * * @param array $rows * @param string $key_id * @param array $where_clause_array * @param string $local_query * @param array $result * @param boolean $found_unique_key - * + * * @return boolean $found_unique_key */ function PMA_showEmptyResultMessageOrSetUniqueCondition($rows, $key_id, @@ -128,7 +128,7 @@ function PMA_showEmptyResultMessageOrSetUniqueCondition($rows, $key_id, PMA_showMessage(__('MySQL returned an empty result set (i.e. zero rows).'), $local_query); echo "\n"; include 'libraries/footer.inc.php'; - } else {// end if (no row returned) + } else {// end if (no row returned) $meta = PMA_DBI_get_fields_meta($result[$key_id]); list($unique_condition, $tmp_clause_is_unique) = PMA_getUniqueCondition($result[$key_id], count($meta), $meta, $rows[$key_id], true); @@ -142,10 +142,10 @@ function PMA_showEmptyResultMessageOrSetUniqueCondition($rows, $key_id, /** * No primary key given, just load first row - * + * * @param string $table name of the table * @param string $db name of the database - * + * * @return array containing $result and $rows arrays */ function PMA_loadFirstRowInEditMode($table, $db) @@ -161,13 +161,13 @@ function PMA_loadFirstRowInEditMode($table, $db) /** * Add some url parameters - * + * * @param array $url_params containing $db and $table as url parameters - * + * * @return array Add some url parameters to $url_params array * and return it */ -function PMA_urlParamsInEditMode($url_params) +function PMA_urlParamsInEditMode($url_params) { if (isset($_REQUEST['where_clause'])) { $url_params['where_clause'] = trim($_REQUEST['where_clause']); @@ -180,15 +180,15 @@ function PMA_urlParamsInEditMode($url_params) /** * Show function fields in data edit view in pma - * + * * @param array $url_params containing url parameters - * + * * @return string an html snippet */ function PMA_showFunctionFieldsInEditMode($url_params, $showFuncFields) { $params = array(); - if(! $showFuncFields) { + if (! $showFuncFields) { $params['ShowFunctionFields'] = 1; } else { $params['ShowFunctionFields'] = 0; @@ -196,7 +196,7 @@ function PMA_showFunctionFieldsInEditMode($url_params, $showFuncFields) $params['ShowFieldTypesInDataEditView'] = $GLOBALS['cfg']['ShowFieldTypesInDataEditView']; $params['goto'] = 'sql.php'; $this_url_params = array_merge($url_params, $params); - if(! $showFuncFields) { + if (! $showFuncFields) { return ' : ' . __('Function') . '' . "\n"; } return '
'.__('There seems to be an error in your SQL query. The MySQL server error output below, if there is any, may also help you in diagnosing the problem') . '
' . "\n" + . '' . "\n" + . 'ERROR: ' . $message . "\n" + . 'SQL: ' . htmlspecialchars($sql) . "\n" + . '' . "\n"; + +} // end of the "PMA_SQP_throwError()" function + + +/** + * Do display the bug report + * + * @param string $message The error message + * @param string $sql The failing SQL query + * + * @return nothing + * + * @access public + */ +function PMA_SQP_bug($message, $sql) +{ + global $SQP_errorString; + $debugstr = 'ERROR: ' . $message . "\n"; + $debugstr .= 'MySQL: '.PMA_MYSQL_STR_VERSION . "\n"; + $debugstr .= 'USR OS, AGENT, VER: ' . PMA_USR_OS . ' '; + $debugstr .= 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($sql); + + $encodedstr = $debugstr; + if (@function_exists('gzcompress')) { + $encodedstr = gzcompress($debugstr, 9); } - if (! isset($mysql_charsets)) { - $mysql_charsets = array(); - $mysql_collations_flat = array(); + $encodedstr = preg_replace( + "/(\015\012)|(\015)|(\012)/", + '
' . "\n" + . $debugstr + . '' . "\n" + . '----' . __('END RAW') . '----
'.__('There seems to be an error in your SQL query. The MySQL server error output below, if there is any, may also help you in diagnosing the problem') . '
' . "\n" - . '' . "\n" - . 'ERROR: ' . $message . "\n" - . 'SQL: ' . htmlspecialchars($sql) . "\n" - . '' . "\n"; - - } // end of the "PMA_SQP_throwError()" function - - - /** - * Do display the bug report - * - * @param string The error message - * @param string The failing SQL query - * - * @access public - */ - function PMA_SQP_bug($message, $sql) - { - global $SQP_errorString; - $debugstr = 'ERROR: ' . $message . "\n"; - $debugstr .= 'MySQL: '.PMA_MYSQL_STR_VERSION . "\n"; - $debugstr .= 'USR OS, AGENT, VER: ' . PMA_USR_OS . ' '; - $debugstr .= 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($sql); - - $encodedstr = $debugstr; - if (@function_exists('gzcompress')) { - $encodedstr = gzcompress($debugstr, 9); - } - $encodedstr = preg_replace( - "/(\015\012)|(\015)|(\012)/", - '
' . "\n" - . $debugstr - . '' . "\n" - . '----' . __('END RAW') . '----
1'; - var_dump(PMA_STR_isSqlIdentifier($c, false)); - var_dump($c == '@'); - var_dump($c == '.'); - var_dump(PMA_STR_isDigit(PMA_substr($sql, $count2 + 1, 1))); - var_dump($previous_was_space); - var_dump($previous_was_bracket); - var_dump($previous_was_listsep); - echo ''; - */ - - // Checks for identifier (alpha or numeric) - if (PMA_STR_isSqlIdentifier($c, false) - || $c == '@' - || ($c == '.' - && PMA_STR_isDigit(PMA_substr($sql, $count2 + 1, 1)) - && ($previous_was_space || $previous_was_bracket || $previous_was_listsep)) - ) { - /* DEBUG - echo PMA_substr($sql, $count2); - echo '
2'; - var_dump($is_identifier); - var_dump($is_sql_variable); - var_dump($is_digit); - var_dump($is_float_digit); - echo ''; - */ - - // Fast skip is especially needed for huge BLOB data - if ($is_hex_digit) { - $count2++; - $pos = strspn($sql, '0123456789abcdefABCDEF', $count2); - if ($pos > $count2) { - $count2 = $pos; - } - unset($pos); - } elseif ($is_digit) { - $pos = strspn($sql, '0123456789', $count2); - if ($pos > $count2) { - $count2 = $pos; - } - unset($pos); - } - - while (($count2 < $len) && PMA_STR_isSqlIdentifier(PMA_substr($sql, $count2, 1), ($is_sql_variable || $is_digit))) { - $c2 = PMA_substr($sql, $count2, 1); - if ($is_sql_variable && ($c2 == '.')) { - $count2++; - continue; - } - if ($is_digit && (!$is_hex_digit) && ($c2 == '.')) { - $count2++; - if (!$is_float_digit) { - $is_float_digit = true; - continue; - } else { - $debugstr = __('Invalid Identifer') . ' @ ' . ($count1+1) . "\n" - . 'STR: ' . htmlspecialchars(PMA_substr($sql, $count1, $count2 - $count1)); - PMA_SQP_throwError($debugstr, $sql); - return $sql_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; - $count2++; - continue; - } else { - $is_digit = false; - $is_float_digit = false; - } - } - if (($is_hex_digit && PMA_STR_isHexDigit($c2)) || ($is_digit && PMA_STR_isDigit($c2))) { - $count2++; - continue; - } else { - $is_digit = false; - $is_hex_digit = false; - } - - $count2++; - } // end while - - $l = $count2 - $count1; - $str = PMA_substr($sql, $count1, $l); - - $type = ''; - if ($is_digit || $is_float_digit || $is_hex_digit) { - $type = 'digit'; - if ($is_float_digit) { - $type .= '_float'; - } elseif ($is_hex_digit) { - $type .= '_hex'; - } else { - $type .= '_integer'; - } - } elseif ($is_user) { - $type = 'punct_user'; - } elseif ($is_sql_variable != false) { - $type = 'alpha_variable'; - } else { - $type = 'alpha'; - } // end if... else.... - PMA_SQP_arrayAdd($sql_array, $type, $str, $arraysize, $count2); - - continue; - } - - // Checks for punct - if (PMA_strpos($allpunct_list, $c) !== false) { - while (($count2 < $len) && PMA_strpos($allpunct_list, PMA_substr($sql, $count2, 1)) !== false) { - $count2++; - } - $l = $count2 - $count1; - if ($l == 1) { - $punct_data = $c; - } else { - $punct_data = PMA_substr($sql, $count1, $l); - } - - // Special case, sometimes, althought two characters are - // adjectent directly, they ACTUALLY need to be seperate - /* DEBUG - echo '
'; - var_dump($l); - var_dump($punct_data); - echo ''; - */ - - if ($l == 1) { - $t_suffix = ''; - switch ($punct_data) { - case $punct_queryend: - $t_suffix = '_queryend'; - break; - case $punct_qualifier: - $t_suffix = '_qualifier'; - $this_was_punct = true; - break; - case $punct_listsep: - $this_was_listsep = true; - $t_suffix = '_listsep'; - break; - default: - break; - } - PMA_SQP_arrayAdd($sql_array, 'punct' . $t_suffix, $punct_data, $arraysize); - } elseif ($punct_data == $GLOBALS['sql_delimiter'] || isset($allpunct_list_pair[$punct_data])) { - // Ok, we have one of the valid combined punct expressions - PMA_SQP_arrayAdd($sql_array, 'punct', $punct_data, $arraysize); - } 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 == '*')) { - $count2 = $count1 + 1; - $punct_data = $first; - } elseif (($last2 == '/*') || (($last2 == '--') && ($count2 == $len || PMA_substr($sql, $count2, 1) <= ' '))) { - $count2 -= 2; - $punct_data = PMA_substr($sql, $count1, $count2 - $count1); - } elseif (($last == '-') || ($last == '+') || ($last == '!')) { - $count2--; - $punct_data = PMA_substr($sql, $count1, $count2 - $count1); - } elseif ($last != '~') { - /** - * @todo for negation operator, split in 2 tokens ? - * "select x&~1 from t" - * becomes "select x & ~ 1 from t" ? + // Checks for something inside quotation marks + if (PMA_strpos($quote_list, $c) !== false) { + $startquotepos = $count2; + $quotetype = $c; + $count2++; + $escaped = false; + $pos = $count2; + $oldpos = 0; + do { + $oldpos = $pos; + $pos = PMA_strpos(' ' . $sql, $quotetype, $oldpos + 1) - 1; + // ($pos === false) + if ($pos < 0) { + if ($c == '`') { + /* + * Behave same as MySQL and accept end of query as end of backtick. + * I know this is sick, but MySQL behaves like this: + * + * SELECT * FROM `table + * + * is treated like + * + * SELECT * FROM `table` */ - $debugstr = __('Unknown Punctuation String') . ' @ ' . ($count1+1) . "\n" - . 'STR: ' . htmlspecialchars($punct_data); + $pos_quote_separator = PMA_strpos(' ' . $sql, $GLOBALS['sql_delimiter'], $oldpos + 1) - 1; + if ($pos_quote_separator < 0) { + $len += 1; + $sql .= '`'; + $sql_array['raw'] .= '`'; + $pos = $len; + } else { + $len += 1; + $sql = PMA_substr($sql, 0, $pos_quote_separator) . '`' . PMA_substr($sql, $pos_quote_separator); + $sql_array['raw'] = $sql; + $pos = $pos_quote_separator; + } + if (class_exists('PMA_Message') && $GLOBALS['is_ajax_request'] != true) { + PMA_Message::notice(__('Automatically appended backtick to the end of query!'))->display(); + } + } else { + $debugstr = __('Unclosed quote') . ' @ ' . $startquotepos. "\n" + . 'STR: ' . htmlspecialchars($quotetype); PMA_SQP_throwError($debugstr, $sql); return $sql_array; } - PMA_SQP_arrayAdd($sql_array, 'punct', $punct_data, $arraysize); + } + + // 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 < $len) && PMA_STR_charIsEscaped($sql, $pos) && $c != '`') { + $pos ++; continue; - } // end if... elseif... else - continue; + } elseif (($pos + 1 < $len) && (PMA_substr($sql, $pos, 1) == $quotetype) && (PMA_substr($sql, $pos + 1, 1) == $quotetype)) { + $pos = $pos + 2; + continue; + } else { + break; + } + } while ($len > $pos); // end do + + $count2 = $pos; + $count2++; + $type = 'quote_'; + switch ($quotetype) { + case '\'': + $type .= 'single'; + $this_was_quote = true; + break; + case '"': + $type .= 'double'; + $this_was_quote = true; + break; + case '`': + $type .= 'backtick'; + $this_was_quote = true; + break; + default: + break; + } // end switch + $data = PMA_substr($sql, $count1, $count2 - $count1); + PMA_SQP_arrayAdd($sql_array, $type, $data, $arraysize); + continue; + } + + // Checks for brackets + if (PMA_strpos($bracket_list, $c) !== false) { + // All bracket tokens are only one item long + $this_was_bracket = true; + $count2++; + $type_type = ''; + if (PMA_strpos('([{', $c) !== false) { + $type_type = 'open'; + } else { + $type_type = 'close'; } - // DEBUG - $count2++; + $type_style = ''; + if (PMA_strpos('()', $c) !== false) { + $type_style = 'round'; + } elseif (PMA_strpos('[]', $c) !== false) { + $type_style = 'square'; + } else { + $type_style = 'curly'; + } - $debugstr = 'C1 C2 LEN: ' . $count1 . ' ' . $count2 . ' ' . $len . "\n" - . 'STR: ' . PMA_substr($sql, $count1, $count2 - $count1) . "\n"; - PMA_SQP_bug($debugstr, $sql); - return $sql_array; + $type = 'punct_bracket_' . $type_type . '_' . $type_style; + PMA_SQP_arrayAdd($sql_array, $type, $c, $arraysize); + continue; + } - } // end while ($count2 < $len) - - /* - echo '
'; - print_r($sql_array); + /* DEBUG + echo '1'; + var_dump(PMA_STR_isSqlIdentifier($c, false)); + var_dump($c == '@'); + var_dump($c == '.'); + var_dump(PMA_STR_isDigit(PMA_substr($sql, $count2 + 1, 1))); + var_dump($previous_was_space); + var_dump($previous_was_bracket); + var_dump($previous_was_listsep); echo ''; */ - if ($arraysize > 0) { - $t_next = $sql_array[0]['type']; - $t_prev = ''; - $t_bef_prev = ''; - $t_cur = ''; - $d_next = $sql_array[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 = ''; - } - - for ($i = 0; $i < $arraysize; $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 (($i + 1) < $arraysize) { - $t_next = $sql_array[$i + 1]['type']; - $d_next = $sql_array[$i + 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'; - // for example: `thebit` bit(8) NOT NULL DEFAULT b'0' - if ($t_prev == 'alpha' && $d_prev == 'DEFAULT' && $d_cur == 'b' && $t_next == 'quote_single') { - $t_suffix = '_bitfield_constant_introducer'; - } elseif (($t_next == 'punct_qualifier') || ($t_prev == 'punct_qualifier')) { - $t_suffix = '_identifier'; - } elseif (($t_next == 'punct_bracket_open_round') - && isset($PMA_SQPdata_function_name[$d_cur_upper])) { - /** - * @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 */ - } elseif (isset($PMA_SQPdata_column_type[$d_cur_upper])) { - $t_suffix = '_columnType'; - - /** - * Temporary fix for bugs #621357 and #2027720 - * - * @todo FIX PROPERLY NEEDS OVERHAUL OF SQL TOKENIZER - */ - if (($d_cur_upper == 'SET' || $d_cur_upper == 'BINARY') && $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 ($sql_array[$i-1]['type'] =='alpha_reservedWord') { - // $sql_array[$i-1]['type'] = 'alpha_identifier'; - //} - - } elseif (isset($PMA_SQPdata_reserved_word[$d_cur_upper])) { - $t_suffix = '_reservedWord'; - } elseif (isset($PMA_SQPdata_column_attrib[$d_cur_upper])) { - $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' - ) && in_array($d_cur, $mysql_charsets) - ) { - $t_suffix = '_charset'; - } - } elseif (in_array($d_cur, $mysql_charsets) - || in_array($d_cur, $mysql_collations_flat) - || ($d_cur{0} == '_' && in_array(substr($d_cur, 1), $mysql_charsets))) { - $t_suffix = '_charset'; - } else { - // Do nothing - } - // check if present in the list of forbidden words - if ($t_suffix == '_reservedWord' && isset($PMA_SQPdata_forbidden_word[$d_cur_upper])) { - $sql_array[$i]['forbidden'] = true; - } else { - $sql_array[$i]['forbidden'] = false; - } - $sql_array[$i]['type'] .= $t_suffix; - } - } // end for - - // Stores the size of the array inside the array, as count() is a slow - // operation. - $sql_array['len'] = $arraysize; - - // DEBUG echo 'After parsing'; print_r($sql_array); echo ''; - // Sends the data back - return $sql_array; - } // end of the "PMA_SQP_parse()" function - - /** - * Checks for token types being what we want... - * - * @param string String of type that we have - * @param string String of type that we want - * - * @return boolean result of check - * - * @access private - */ - function PMA_SQP_typeCheck($toCheck, $whatWeWant) - { - $typeSeparator = '_'; - if (strcmp($whatWeWant, $toCheck) == 0) { - return true; - } else { - if (strpos($whatWeWant, $typeSeparator) === false) { - return strncmp($whatWeWant, $toCheck, strpos($toCheck, $typeSeparator)) == 0; - } else { - return false; - } - } - } - - - /** - * Analyzes SQL queries - * - * @param array The SQL queries - * - * @return array The analyzed SQL queries - * - * @access public - */ - function PMA_SQP_analyze($arr) - { - if ($arr == array() || ! isset($arr['len'])) { - return array(); - } - $result = array(); - $size = $arr['len']; - $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' => '', - 'limit_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; - - $number_of_brackets = 0; - $in_subquery = false; - $seen_subquery = false; - $seen_from = false; - - // for SELECT EXTRACT(YEAR_MONTH FROM CURDATE()) - // we must not use CURDATE as a table_ref - // so we track whether we are in the EXTRACT() - $in_extract = false; - - // for GROUP_CONCAT(...) - $in_group_concat = false; - - /* Description of analyzer results - * - * 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 - * ['queryflags']['procedure'] = 1; for the presence of PROCEDURE - * - * query clauses - * ------------- - * - * The select is splitted in those clauses: - * ['select_expr_clause'] - * ['from_clause'] - * ['group_by_clause'] - * ['order_by_clause'] - * ['having_clause'] - * ['limit_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 - * ------------------- - * - * Used to detect the DEFAULT CURRENT_TIMESTAMP and - * ON UPDATE CURRENT_TIMESTAMP clauses of the CREATE TABLE query. - * Also used to store the default value of the field. - * 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 - * - * - * End of description of analyzer results - */ - - // 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' => 1, - 'GROUP' => 1, - 'HAVING' => 1, - 'LIMIT' => 1, - 'LOCK' => 1, - 'ORDER' => 1, - 'PROCEDURE' => 1, - 'UNION' => 1, - 'WHERE' => 1 - ); - - $words_ending_clauses = array( - 'FOR' => 1, - 'LIMIT' => 1, - 'LOCK' => 1, - 'PROCEDURE' => 1, - 'UNION' => 1 - ); - - $supported_query_types = array( - 'SELECT' => 1, - /* - // Support for these additional query types will come later on. - 'DELETE' => 1, - 'INSERT' => 1, - 'REPLACE' => 1, - 'TRUNCATE' => 1, - 'UPDATE' => 1, - 'EXPLAIN' => 1, - 'DESCRIBE' => 1, - 'SHOW' => 1, - 'CREATE' => 1, - 'SET' => 1, - 'ALTER' => 1 + // Checks for identifier (alpha or numeric) + if (PMA_STR_isSqlIdentifier($c, false) + || $c == '@' + || ($c == '.' + && PMA_STR_isDigit(PMA_substr($sql, $count2 + 1, 1)) + && ($previous_was_space || $previous_was_bracket || $previous_was_listsep)) + ) { + /* DEBUG + echo PMA_substr($sql, $count2); + echo '
'; */ - ); - // loop #1 for each token: select_expr, table_ref for SELECT - - for ($i = 0; $i < $size; $i++) { - //DEBUG echo "Loop1 " . $arr[$i]['data'] . " (" . $arr[$i]['type'] . ")
"; - - // High speed seek for locating the end of the current query - if ($seek_queryend == true) { - if ($arr[$i]['type'] == 'punct_queryend') { - $seek_queryend = false; - } else { - continue; - } // end if (type == punct_queryend) - } // end if ($seek_queryend) + $count2++; /** - * 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? + * @todo a @ can also be present in expressions like + * FROM 'user'@'%' or TO 'user'@'%' + * in this case, the @ is wrongly marked as alpha_variable */ - if ($arr[$i]['type'] == 'punct_queryend' && ($i + 1 != $size)) { - $result[] = $subresult; - $subresult = $subresult_empty; + $is_identifier = $previous_was_punct; + $is_sql_variable = $c == '@' && ! $previous_was_quote; + $is_user = $c == '@' && $previous_was_quote; + $is_digit = !$is_identifier && !$is_sql_variable && PMA_STR_isDigit($c); + $is_hex_digit = $is_digit && $c == '0' && $count2 < $len && PMA_substr($sql, $count2, 1) == 'x'; + $is_float_digit = $c == '.'; + $is_float_digit_exponent = false; + + /* DEBUG + echo '2'; + var_dump($is_identifier); + var_dump($is_sql_variable); + var_dump($is_digit); + var_dump($is_float_digit); + echo ''; + */ + + // Fast skip is especially needed for huge BLOB data + if ($is_hex_digit) { + $count2++; + $pos = strspn($sql, '0123456789abcdefABCDEF', $count2); + if ($pos > $count2) { + $count2 = $pos; + } + unset($pos); + } elseif ($is_digit) { + $pos = strspn($sql, '0123456789', $count2); + if ($pos > $count2) { + $count2 = $pos; + } + unset($pos); + } + + while (($count2 < $len) && PMA_STR_isSqlIdentifier(PMA_substr($sql, $count2, 1), ($is_sql_variable || $is_digit))) { + $c2 = PMA_substr($sql, $count2, 1); + if ($is_sql_variable && ($c2 == '.')) { + $count2++; + continue; + } + if ($is_digit && (!$is_hex_digit) && ($c2 == '.')) { + $count2++; + if (!$is_float_digit) { + $is_float_digit = true; + continue; + } else { + $debugstr = __('Invalid Identifer') . ' @ ' . ($count1+1) . "\n" + . 'STR: ' . htmlspecialchars(PMA_substr($sql, $count1, $count2 - $count1)); + PMA_SQP_throwError($debugstr, $sql); + return $sql_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; + $count2++; + continue; + } else { + $is_digit = false; + $is_float_digit = false; + } + } + if (($is_hex_digit && PMA_STR_isHexDigit($c2)) || ($is_digit && PMA_STR_isDigit($c2))) { + $count2++; + continue; + } else { + $is_digit = false; + $is_hex_digit = false; + } + + $count2++; + } // end while + + $l = $count2 - $count1; + $str = PMA_substr($sql, $count1, $l); + + $type = ''; + if ($is_digit || $is_float_digit || $is_hex_digit) { + $type = 'digit'; + if ($is_float_digit) { + $type .= '_float'; + } elseif ($is_hex_digit) { + $type .= '_hex'; + } else { + $type .= '_integer'; + } + } elseif ($is_user) { + $type = 'punct_user'; + } elseif ($is_sql_variable != false) { + $type = 'alpha_variable'; + } else { + $type = 'alpha'; + } // end if... else.... + PMA_SQP_arrayAdd($sql_array, $type, $str, $arraysize, $count2); + + continue; + } + + // Checks for punct + if (PMA_strpos($allpunct_list, $c) !== false) { + while (($count2 < $len) && PMA_strpos($allpunct_list, PMA_substr($sql, $count2, 1)) !== false) { + $count2++; + } + $l = $count2 - $count1; + if ($l == 1) { + $punct_data = $c; + } else { + $punct_data = PMA_substr($sql, $count1, $l); + } + + // Special case, sometimes, althought two characters are + // adjectent directly, they ACTUALLY need to be seperate + /* DEBUG + echo ''; + var_dump($l); + var_dump($punct_data); + echo ''; + */ + + if ($l == 1) { + $t_suffix = ''; + switch ($punct_data) { + case $punct_queryend: + $t_suffix = '_queryend'; + break; + case $punct_qualifier: + $t_suffix = '_qualifier'; + $this_was_punct = true; + break; + case $punct_listsep: + $this_was_listsep = true; + $t_suffix = '_listsep'; + break; + default: + break; + } + PMA_SQP_arrayAdd($sql_array, 'punct' . $t_suffix, $punct_data, $arraysize); + } elseif ($punct_data == $GLOBALS['sql_delimiter'] || isset($allpunct_list_pair[$punct_data])) { + // Ok, we have one of the valid combined punct expressions + PMA_SQP_arrayAdd($sql_array, 'punct', $punct_data, $arraysize); + } 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 == '*')) { + $count2 = $count1 + 1; + $punct_data = $first; + } elseif (($last2 == '/*') || (($last2 == '--') && ($count2 == $len || PMA_substr($sql, $count2, 1) <= ' '))) { + $count2 -= 2; + $punct_data = PMA_substr($sql, $count1, $count2 - $count1); + } elseif (($last == '-') || ($last == '+') || ($last == '!')) { + $count2--; + $punct_data = PMA_substr($sql, $count1, $count2 - $count1); + } elseif ($last != '~') { + /** + * @todo for negation operator, split in 2 tokens ? + * "select x&~1 from t" + * becomes "select x & ~ 1 from t" ? + */ + $debugstr = __('Unknown Punctuation String') . ' @ ' . ($count1+1) . "\n" + . 'STR: ' . htmlspecialchars($punct_data); + PMA_SQP_throwError($debugstr, $sql); + return $sql_array; + } + PMA_SQP_arrayAdd($sql_array, 'punct', $punct_data, $arraysize); + continue; + } // end if... elseif... else + continue; + } + + // DEBUG + $count2++; + + $debugstr = 'C1 C2 LEN: ' . $count1 . ' ' . $count2 . ' ' . $len . "\n" + . 'STR: ' . PMA_substr($sql, $count1, $count2 - $count1) . "\n"; + PMA_SQP_bug($debugstr, $sql); + return $sql_array; + + } // end while ($count2 < $len) + + /* + echo ''; + print_r($sql_array); + echo ''; + */ + + if ($arraysize > 0) { + $t_next = $sql_array[0]['type']; + $t_prev = ''; + $t_bef_prev = ''; + $t_cur = ''; + $d_next = $sql_array[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 = ''; + } + + for ($i = 0; $i < $arraysize; $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 (($i + 1) < $arraysize) { + $t_next = $sql_array[$i + 1]['type']; + $d_next = $sql_array[$i + 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'; + // for example: `thebit` bit(8) NOT NULL DEFAULT b'0' + if ($t_prev == 'alpha' && $d_prev == 'DEFAULT' && $d_cur == 'b' && $t_next == 'quote_single') { + $t_suffix = '_bitfield_constant_introducer'; + } elseif (($t_next == 'punct_qualifier') || ($t_prev == 'punct_qualifier')) { + $t_suffix = '_identifier'; + } elseif (($t_next == 'punct_bracket_open_round') + && isset($PMA_SQPdata_function_name[$d_cur_upper])) { + /** + * @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 */ + } elseif (isset($PMA_SQPdata_column_type[$d_cur_upper])) { + $t_suffix = '_columnType'; + + /** + * Temporary fix for bugs #621357 and #2027720 + * + * @todo FIX PROPERLY NEEDS OVERHAUL OF SQL TOKENIZER + */ + if (($d_cur_upper == 'SET' || $d_cur_upper == 'BINARY') && $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 ($sql_array[$i-1]['type'] =='alpha_reservedWord') { + // $sql_array[$i-1]['type'] = 'alpha_identifier'; + //} + + } elseif (isset($PMA_SQPdata_reserved_word[$d_cur_upper])) { + $t_suffix = '_reservedWord'; + } elseif (isset($PMA_SQPdata_column_attrib[$d_cur_upper])) { + $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' + ) && in_array($d_cur, $mysql_charsets) + ) { + $t_suffix = '_charset'; + } + } elseif (in_array($d_cur, $mysql_charsets) + || in_array($d_cur, $mysql_collations_flat) + || ($d_cur{0} == '_' && in_array(substr($d_cur, 1), $mysql_charsets))) { + $t_suffix = '_charset'; + } else { + // Do nothing + } + // check if present in the list of forbidden words + if ($t_suffix == '_reservedWord' && isset($PMA_SQPdata_forbidden_word[$d_cur_upper])) { + $sql_array[$i]['forbidden'] = true; + } else { + $sql_array[$i]['forbidden'] = false; + } + $sql_array[$i]['type'] .= $t_suffix; + } + } // end for + + // Stores the size of the array inside the array, as count() is a slow + // operation. + $sql_array['len'] = $arraysize; + + // DEBUG echo 'After parsing'; print_r($sql_array); echo ''; + // Sends the data back + return $sql_array; +} // end of the "PMA_SQP_parse()" function + +/** + * Checks for token types being what we want... + * + * @param string $toCheck String of type that we have + * @param string $whatWeWant String of type that we want + * + * @return boolean result of check + * + * @access private + */ +function PMA_SQP_typeCheck($toCheck, $whatWeWant) +{ + $typeSeparator = '_'; + if (strcmp($whatWeWant, $toCheck) == 0) { + return true; + } else { + if (strpos($whatWeWant, $typeSeparator) === false) { + return strncmp($whatWeWant, $toCheck, strpos($toCheck, $typeSeparator)) == 0; + } else { + return false; + } + } +} + + +/** + * Analyzes SQL queries + * + * @param array $arr The SQL queries + * + * @return array The analyzed SQL queries + * + * @access public + */ +function PMA_SQP_analyze($arr) +{ + if ($arr == array() || ! isset($arr['len'])) { + return array(); + } + $result = array(); + $size = $arr['len']; + $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' => '', + 'limit_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; + + $number_of_brackets = 0; + $in_subquery = false; + $seen_subquery = false; + $seen_from = false; + + // for SELECT EXTRACT(YEAR_MONTH FROM CURDATE()) + // we must not use CURDATE as a table_ref + // so we track whether we are in the EXTRACT() + $in_extract = false; + + // for GROUP_CONCAT(...) + $in_group_concat = false; + + /* Description of analyzer results + * + * 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 + * ['queryflags']['procedure'] = 1; for the presence of PROCEDURE + * + * query clauses + * ------------- + * + * The select is splitted in those clauses: + * ['select_expr_clause'] + * ['from_clause'] + * ['group_by_clause'] + * ['order_by_clause'] + * ['having_clause'] + * ['limit_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 + * ------------------- + * + * Used to detect the DEFAULT CURRENT_TIMESTAMP and + * ON UPDATE CURRENT_TIMESTAMP clauses of the CREATE TABLE query. + * Also used to store the default value of the field. + * 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 + * + * + * End of description of analyzer results + */ + + // 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' => 1, + 'GROUP' => 1, + 'HAVING' => 1, + 'LIMIT' => 1, + 'LOCK' => 1, + 'ORDER' => 1, + 'PROCEDURE' => 1, + 'UNION' => 1, + 'WHERE' => 1 + ); + + $words_ending_clauses = array( + 'FOR' => 1, + 'LIMIT' => 1, + 'LOCK' => 1, + 'PROCEDURE' => 1, + 'UNION' => 1 + ); + + $supported_query_types = array( + 'SELECT' => 1, + /* + // Support for these additional query types will come later on. + 'DELETE' => 1, + 'INSERT' => 1, + 'REPLACE' => 1, + 'TRUNCATE' => 1, + 'UPDATE' => 1, + 'EXPLAIN' => 1, + 'DESCRIBE' => 1, + 'SHOW' => 1, + 'CREATE' => 1, + 'SET' => 1, + 'ALTER' => 1 + */ + ); + + // loop #1 for each token: select_expr, table_ref for SELECT + + for ($i = 0; $i < $size; $i++) { + //DEBUG echo "Loop1 " . $arr[$i]['data'] . " (" . $arr[$i]['type'] . ")
"; + + // High speed seek for locating the end of the current query + if ($seek_queryend == true) { + if ($arr[$i]['type'] == 'punct_queryend') { + $seek_queryend = false; + } else { continue; } // end if (type == punct_queryend) + } // end if ($seek_queryend) - // ============================================================== - if ($arr[$i]['type'] == 'punct_bracket_open_round') { - $number_of_brackets++; - if ($in_extract) { - $number_of_brackets_in_extract++; - } - if ($in_group_concat) { - $number_of_brackets_in_group_concat++; + /** + * 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 ($arr[$i]['type'] == 'punct_queryend' && ($i + 1 != $size)) { + $result[] = $subresult; + $subresult = $subresult_empty; + continue; + } // end if (type == punct_queryend) + + // ============================================================== + if ($arr[$i]['type'] == 'punct_bracket_open_round') { + $number_of_brackets++; + if ($in_extract) { + $number_of_brackets_in_extract++; + } + if ($in_group_concat) { + $number_of_brackets_in_group_concat++; + } + } + // ============================================================== + if ($arr[$i]['type'] == 'punct_bracket_close_round') { + $number_of_brackets--; + if ($number_of_brackets == 0) { + $in_subquery = false; + } + if ($in_extract) { + $number_of_brackets_in_extract--; + if ($number_of_brackets_in_extract == 0) { + $in_extract = false; } } - // ============================================================== - if ($arr[$i]['type'] == 'punct_bracket_close_round') { - $number_of_brackets--; - if ($number_of_brackets == 0) { - $in_subquery = false; - } - 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 ($in_group_concat) { + $number_of_brackets_in_group_concat--; + if ($number_of_brackets_in_group_concat == 0) { + $in_group_concat = false; } } + } - if ($in_subquery) { - /** - * skip the subquery to avoid setting - * select_expr or table_ref with the contents - * of this subquery; this is to avoid a bug when - * trying to edit the results of - * select * from child where not exists (select id from - * parent where child.parent_id = parent.id); - */ - continue; + if ($in_subquery) { + /** + * skip the subquery to avoid setting + * select_expr or table_ref with the contents + * of this subquery; this is to avoid a bug when + * trying to edit the results of + * select * from child where not exists (select id from + * parent where child.parent_id = parent.id); + */ + continue; + } + // ============================================================== + if ($arr[$i]['type'] == 'alpha_functionName') { + $upper_data = strtoupper($arr[$i]['data']); + if ($upper_data =='EXTRACT') { + $in_extract = true; + $number_of_brackets_in_extract = 0; } - // ============================================================== - if ($arr[$i]['type'] == 'alpha_functionName') { - $upper_data = strtoupper($arr[$i]['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 ($upper_data =='GROUP_CONCAT') { + $in_group_concat = true; + $number_of_brackets_in_group_concat = 0; } + } - // ============================================================== - if ($arr[$i]['type'] == 'alpha_reservedWord' - //&& $arr[$i]['forbidden'] == false) { - ) { - // We don't know what type of query yet, so run this - if ($subresult['querytype'] == '') { - $subresult['querytype'] = strtoupper($arr[$i]['data']); - } // end if (querytype was empty) - - // Check if we support this type of query - if (!isset($supported_query_types[$subresult['querytype']])) { - // Skip ahead to the next one if we don't - $seek_queryend = true; - continue; - } // end if (query not supported) - - // upper once - $upper_data = strtoupper($arr[$i]['data']); - /** - * @todo reset for each query? - */ - - if ($upper_data == 'SELECT') { - if ($number_of_brackets > 0) { - $in_subquery = true; - $seen_subquery = true; - // this is a subquery so do not analyze inside it - continue; - } - $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 ($arr[$i]['type'] == 'quote_backtick' - || $arr[$i]['type'] == 'quote_double' - || $arr[$i]['type'] == 'quote_single' - || $arr[$i]['type'] == 'alpha_identifier' - || ($arr[$i]['type'] == 'alpha_reservedWord' - && $arr[$i]['forbidden'] == false) + // ============================================================== + if ($arr[$i]['type'] == 'alpha_reservedWord' + //&& $arr[$i]['forbidden'] == false) { ) { - switch ($arr[$i]['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 = $arr[$i]['data']; - break; + // We don't know what type of query yet, so run this + if ($subresult['querytype'] == '') { + $subresult['querytype'] = strtoupper($arr[$i]['data']); + } // end if (querytype was empty) - case 'quote_backtick': - case 'quote_double': - case 'quote_single': - $identifier = PMA_unQuote($arr[$i]['data']); - break; - } // end switch + // Check if we support this type of query + if (!isset($supported_query_types[$subresult['querytype']])) { + // Skip ahead to the next one if we don't + $seek_queryend = true; + continue; + } // end if (query not supported) - if ($subresult['querytype'] == 'SELECT' - && ! $in_group_concat - && ! ($seen_subquery && $arr[$i - 1]['type'] == 'punct_bracket_close_round') - ) { - 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; + // upper once + $upper_data = strtoupper($arr[$i]['data']); + /** + * @todo reset for each query? + */ + + if ($upper_data == 'SELECT') { + if ($number_of_brackets > 0) { + $in_subquery = true; + $seen_subquery = true; + // this is a subquery so do not analyze inside it + continue; + } + $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 ($arr[$i]['type'] == 'quote_backtick' + || $arr[$i]['type'] == 'quote_double' + || $arr[$i]['type'] == 'quote_single' + || $arr[$i]['type'] == 'alpha_identifier' + || ($arr[$i]['type'] == 'alpha_reservedWord' + && $arr[$i]['forbidden'] == false) + ) { + switch ($arr[$i]['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 = $arr[$i]['data']; + break; + + case 'quote_backtick': + case 'quote_double': + case 'quote_single': + $identifier = PMA_unQuote($arr[$i]['data']); + break; + } // end switch + + if ($subresult['querytype'] == 'SELECT' + && ! $in_group_concat + && ! ($seen_subquery && $arr[$i - 1]['type'] == 'punct_bracket_close_round') + ) { + 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 - } 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) - } // 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 ($arr[$i]['type'] == 'punct_qualifier') { + // to be able to detect an identifier following another + $previous_was_identifier = false; + continue; + } // end if (punct_qualifier) - // =================================== - if ($arr[$i]['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 + */ - /** - * @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 - // 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 - // 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 && $arr[$i]['type'] == 'punct_listsep') + || ($arr[$i]['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($chain) && !$seen_end_of_table_ref - && ((!$seen_from && $arr[$i]['type'] == 'punct_listsep') - || ($arr[$i]['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]; - 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: - */ - if (($arr[$i]['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 - && ($arr[$i]['type'] == 'punct_listsep' - || ($arr[$i]['type'] == 'alpha_reservedWord' && $upper_data!="AS") - || $seen_end_of_table_ref - || $i==$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]; + // 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['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) + $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) - // add the table alias into the whole expression - $subresult['table_ref'][$current_table_ref]['expr'] - .= ' ' . $subresult['table_ref'][$current_table_ref]['table_alias']; + // 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); - 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 || $i == $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 (($arr[$i]['type'] != 'alpha_identifier') - && ($arr[$i]['type'] != 'quote_double') - && ($arr[$i]['type'] != 'quote_single') - && ($arr[$i]['type'] != 'quote_backtick') - ) { - $previous_was_identifier = false; - } // end if - - // however, if we are on AS, we must keep the $previous_was_identifier + /** + * @todo explain this: + */ if (($arr[$i]['type'] == 'alpha_reservedWord') - && ($upper_data == 'AS') + && ($upper_data != 'FROM') ) { $previous_was_identifier = true; } - if (($arr[$i]['type'] == 'alpha_reservedWord') - && ($upper_data =='ON' || $upper_data =='USING') + } // 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 + && ($arr[$i]['type'] == 'punct_listsep' + || ($arr[$i]['type'] == 'alpha_reservedWord' && $upper_data!="AS") + || $seen_end_of_table_ref + || $i==$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 || $i == $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 (($arr[$i]['type'] != 'alpha_identifier') + && ($arr[$i]['type'] != 'quote_double') + && ($arr[$i]['type'] != 'quote_single') + && ($arr[$i]['type'] != 'quote_backtick') + ) { + $previous_was_identifier = false; + } // end if + + // however, if we are on AS, we must keep the $previous_was_identifier + if (($arr[$i]['type'] == 'alpha_reservedWord') + && ($upper_data == 'AS') + ) { + $previous_was_identifier = true; + } + + if (($arr[$i]['type'] == 'alpha_reservedWord') + && ($upper_data =='ON' || $upper_data =='USING') + ) { + $save_table_ref = false; + } // end if (data == ON) + + if (($arr[$i]['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 (($i == $size-1) + || ($arr[$i]['type'] == 'alpha_reservedWord' + && !$in_group_concat + && isset($words_ending_table_ref[$upper_data])) ) { - $save_table_ref = false; - } // end if (data == ON) + $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 - if (($arr[$i]['type'] == 'alpha_reservedWord') - && ($upper_data =='JOIN' || $upper_data =='FROM') - ) { - $save_table_ref = true; - } // end if (data == JOIN) + } // end if (check for end of table ref) + } //end if (!$seen_end_of_table_ref) - /** - * 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 (($i == $size-1) - || ($arr[$i]['type'] == 'alpha_reservedWord' - && !$in_group_concat - && isset($words_ending_table_ref[$upper_data])) - ) { - $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 + if ($seen_end_of_table_ref) { + $save_table_ref = false; + } // end if - } // end if (check for end of table ref) - } //end if (!$seen_end_of_table_ref) + } // end for $i (loop #1) - if ($seen_end_of_table_ref) { - $save_table_ref = false; - } // end if + //DEBUG + /* + 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"; + } + } - } // end for $i (loop #1) + if (isset($current_table_ref)) { + echo "current_table_ref = " . $current_table_ref . "
"; + for ($trace=0; $trace<=$current_table_ref; $trace++) { - //DEBUG - /* - 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"; - } - } - */ - // ------------------------------------------------------- + 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 + // 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; + } + + $section_before_limit = ''; + $section_after_limit = ''; // truly the section after the limit clause + $seen_reserved_word = false; + $seen_group = false; + $seen_order = false; + $seen_order_by = 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 + $seen_limit = false; // true if we have seen a LIMIT clause + $in_limit = false; // true when we are inside the LIMIT clause + $after_limit = false; // true when we are after the LIMIT clause + $in_from = false; // true when we are in the FROM clause + $in_group_concat = false; + $first_reserved_word = ''; + $current_identifier = ''; + $unsorted_query = $arr['raw']; // in case there is no ORDER BY + $number_of_brackets = 0; + $in_subquery = false; + + for ($i = 0; $i < $size; $i++) { + //DEBUG echo "Loop2 " . $arr[$i]['data'] . " (" . $arr[$i]['type'] . ")
"; + + // need_confirm // - // 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; + // 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 + + if ($arr[$i]['type'] == 'punct_bracket_open_round') { + $number_of_brackets++; } - $section_before_limit = ''; - $section_after_limit = ''; // truly the section after the limit clause - $seen_reserved_word = false; - $seen_group = false; - $seen_order = false; - $seen_order_by = 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 - $seen_limit = false; // true if we have seen a LIMIT clause - $in_limit = false; // true when we are inside the LIMIT clause - $after_limit = false; // true when we are after the LIMIT clause - $in_from = false; // true when we are in the FROM clause - $in_group_concat = false; - $first_reserved_word = ''; - $current_identifier = ''; - $unsorted_query = $arr['raw']; // in case there is no ORDER BY - $number_of_brackets = 0; - $in_subquery = false; + if ($arr[$i]['type'] == 'punct_bracket_close_round') { + $number_of_brackets--; + if ($number_of_brackets == 0) { + $in_subquery = false; + } + } - for ($i = 0; $i < $size; $i++) { - //DEBUG echo "Loop2 " . $arr[$i]['data'] . " (" . $arr[$i]['type'] . ")
"; + if ($arr[$i]['type'] == 'alpha_reservedWord') { + $upper_data = strtoupper($arr[$i]['data']); - // 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 - - if ($arr[$i]['type'] == 'punct_bracket_open_round') { - $number_of_brackets++; + if ($upper_data == 'SELECT' && $number_of_brackets > 0) { + $in_subquery = true; } - if ($arr[$i]['type'] == 'punct_bracket_close_round') { - $number_of_brackets--; - if ($number_of_brackets == 0) { - $in_subquery = false; - } - } + if (!$seen_reserved_word) { + $first_reserved_word = $upper_data; + $subresult['querytype'] = $upper_data; + $seen_reserved_word = true; - if ($arr[$i]['type'] == 'alpha_reservedWord') { - $upper_data = strtoupper($arr[$i]['data']); - - if ($upper_data == 'SELECT' && $number_of_brackets > 0) { - $in_subquery = true; - } - - 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 = $i; - } - - } else { - if ($upper_data == 'DROP' && $first_reserved_word == 'ALTER') { - $subresult['queryflags']['need_confirm'] = 1; - } - } - - if ($upper_data == 'LIMIT' && ! $in_subquery) { - $section_before_limit = substr($arr['raw'], 0, $arr[$i]['pos'] - 5); - $in_limit = true; - $seen_limit = true; - $limit_clause = ''; - $in_order_by = false; // @todo maybe others to set false - } - - if ($upper_data == 'PROCEDURE') { - $subresult['queryflags']['procedure'] = 1; - $in_limit = false; - $after_limit = true; - } - /** - * @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) { - $seen_order_by = true; - // Here we assume that the ORDER BY keywords took - // exactly 8 characters. - // We use PMA_substr() to be charset-safe; otherwise - // if the table name contains accents, the unsorted - // query would be missing some characters. - $unsorted_query = PMA_substr($arr['raw'], 0, $arr[$i]['pos'] - 8); - $in_order_by = true; - $order_by_clause = ''; - } - } - - // if we find one of the words that could end the clause - if (isset($words_ending_clauses[$upper_data])) { - - $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 ($arr[$i]['type'] == 'alpha_functionName') { - $sep=''; - $upper_data = strtoupper($arr[$i]['data']); - if ($upper_data =='GROUP_CONCAT') { - $in_group_concat = true; - $number_of_brackets_in_group_concat = 0; - } - } - - if ($arr[$i]['type'] == 'punct_bracket_open_round') { - if ($in_group_concat) { - $number_of_brackets_in_group_concat++; - } - } - if ($arr[$i]['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 ($arr[$i]['type'] == 'alpha_identifier' && $i < $size - 1 && $arr[$i + 1]['data'] == '.') { - $sep = ''; - } - - // do not add a space after a dot if followed by an identifier - if ($arr[$i]['data'] == '.' && $i < $size - 1 && $arr[$i + 1]['type'] == 'alpha_identifier') { - $sep = ''; - } - - if ($in_select_expr && $upper_data != 'SELECT' && $upper_data != 'DISTINCT') { - $select_expr_clause .= $arr[$i]['data'] . $sep; - } - if ($in_from && $upper_data != 'FROM') { - $from_clause .= $arr[$i]['data'] . $sep; - } - if ($in_group_by && $upper_data != 'GROUP' && $upper_data != 'BY') { - $group_by_clause .= $arr[$i]['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 ($arr[$i]['type'] == 'alpha_reservedWord') { - $order_by_clause .= $sep; - } - $order_by_clause .= $arr[$i]['data']; - } - if ($in_having && $upper_data != 'HAVING') { - $having_clause .= $arr[$i]['data'] . $sep; - } - if ($in_where && $upper_data != 'WHERE') { - $where_clause .= $arr[$i]['data'] . $sep; - - if (($arr[$i]['type'] == 'quote_backtick') - || ($arr[$i]['type'] == 'alpha_identifier') + // 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' ) { - $where_clause_identifiers[] = $arr[$i]['data']; - } - } - - // to grab the rest of the query after the ORDER BY clause - if (isset($subresult['queryflags']['select_from']) - && $subresult['queryflags']['select_from'] == 1 - && ! $in_order_by - && $seen_order_by - && $upper_data != 'BY' - ) { - $unsorted_query .= $arr[$i]['data']; - if ($arr[$i]['type'] != 'punct_bracket_open_round' - && $arr[$i]['type'] != 'punct_bracket_close_round' - && $arr[$i]['type'] != 'punct' - ) { - $unsorted_query .= $sep; - } - } - - if ($in_limit) { - if ($upper_data == 'OFFSET') { - $limit_clause .= $sep; - } - $limit_clause .= $arr[$i]['data']; - if ($upper_data == 'LIMIT' || $upper_data == 'OFFSET') { - $limit_clause .= $sep; - } - } - if ($after_limit && $seen_limit) { - $section_after_limit .= $arr[$i]['data'] . $sep; - } - - // clear $upper_data for next iteration - $upper_data=''; - } // end for $i (loop #2) - if (empty($section_before_limit)) { - $section_before_limit = $arr['raw']; - } - - // ----------------------------------------------------- - // 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; - $seen_alter = false; - $in_create_table_fields = false; - $brackets_level = 0; - $in_timestamp_options = false; - $seen_default = false; - - for ($i = 0; $i < $size; $i++) { - if ($arr[$i]['type'] == 'alpha_reservedWord') { - $upper_data = strtoupper($arr[$i]['data']); - - if ($upper_data == 'NOT' && $in_timestamp_options) { - $create_table_fields[$current_identifier]['timestamp_not_null'] = true; - + $subresult['queryflags']['need_confirm'] = 1; } - if ($upper_data == 'CREATE') { - $seen_create = true; + if ($first_reserved_word=='SELECT') { + $position_of_first_select = $i; } - if ($upper_data == 'ALTER') { - $seen_alter = 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 (isset($arr[$i+1]) && $arr[$i+1]['type'] == 'alpha_reservedWord') { - $second_upper_data = strtoupper($arr[$i+1]['data']); - if ($second_upper_data == 'DELETE') { - $clause = 'on_delete'; - } - if ($second_upper_data == 'UPDATE') { - $clause = 'on_update'; - } - if (isset($clause) - && ($arr[$i+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) - || ($arr[$i+2]['type'] == 'alpha_identifier' - && strtoupper($arr[$i+2]['data'])=='NO')) - ) { - $third_upper_data = strtoupper($arr[$i+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 ($arr[$i+3]['type'] == 'alpha_reservedWord') { - $value = $third_upper_data . '_' . strtoupper($arr[$i+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 ($arr[$i]['type'] == 'punct_bracket_open_round') { - $brackets_level++; - if ($seen_create_table && $brackets_level == 1) { - $in_create_table_fields = true; - } - } - - - if ($arr[$i]['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 (($arr[$i]['type'] == 'alpha_columnAttrib')) { - $upper_data = strtoupper($arr[$i]['data']); - if ($seen_create_table && $in_create_table_fields) { - if ($upper_data == 'DEFAULT') { - $seen_default = true; - $create_table_fields[$current_identifier]['default_value'] = $arr[$i + 1]['data']; - } - } - } - - /** - * @see @todo 2005-10-16 note: the "or" part here is a workaround for a bug - */ - if (($arr[$i]['type'] == 'alpha_columnType') || ($arr[$i]['type'] == 'alpha_functionName' && $seen_create_table)) { - $upper_data = strtoupper($arr[$i]['data']); - if ($seen_create_table && $in_create_table_fields && isset($current_identifier)) { - $create_table_fields[$current_identifier]['type'] = $upper_data; - if ($upper_data == 'TIMESTAMP') { - $arr[$i]['type'] = 'alpha_columnType'; - $in_timestamp_options = true; - } else { - $in_timestamp_options = false; - if ($upper_data == 'CHAR') { - $arr[$i]['type'] = 'alpha_columnType'; - } - } - } - } - - - if ($arr[$i]['type'] == 'quote_backtick' || $arr[$i]['type'] == 'alpha_identifier') { - - if ($arr[$i]['type'] == 'quote_backtick') { - // remove backquotes - $identifier = PMA_unQuote($arr[$i]['data']); - } else { - $identifier = $arr[$i]['data']; - } - - if ($seen_create_table && $in_create_table_fields) { - $current_identifier = $identifier; - // 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) { - if ($seen_alter && $brackets_level > 0) { - $foreign[$foreign_key_number]['ref_index_list'][] = $identifier; - // 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 - } elseif ($brackets_level > 1) { - $foreign[$foreign_key_number]['ref_index_list'][] = $identifier; - } elseif ($arr[$i+1]['type'] == 'punct_qualifier') { - // identifier is `db`.`table` - // the first pass will pick the db name - // the next pass will pick the table name - $foreign[$foreign_key_number]['ref_db_name'] = $identifier; - } else { - // identifier is `table` - $foreign[$foreign_key_number]['ref_table_name'] = $identifier; - } - } - } - } // 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($limit_clause)) { - $subresult['limit_clause'] = $limit_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'] != '') { - $result[] = $subresult; - } - return $result; - } // end of the "PMA_SQP_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 - * @param array The SQL queries html formatted - * - * @return array The colorized SQL queries - * - * @access public - */ - function PMA_SQP_formatHtml_colorize($arr) - { - $i = PMA_strpos($arr['type'], '_'); - $class = ''; - if ($i > 0) { - $class = 'syntax_' . PMA_substr($arr['type'], 0, $i) . ' '; - } - - $class .= 'syntax_' . $arr['type']; - - return '' . htmlspecialchars($arr['data']) . ''; - } // end of the "PMA_SQP_formatHtml_colorize()" function - - - /** - * Formats SQL queries to html - * - * @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 - * - * @access public - */ - function PMA_SQP_formatHtml( - $arr, $mode='color', $start_token=0, - $number_of_tokens=-1 - ) { - global $PMA_SQPdata_operators_docs, $PMA_SQPdata_functions_docs; - //DEBUG echo 'in Format'; print_r($arr); echo ''; - // then check for an array - if (! is_array($arr)) { - return htmlspecialchars($arr); - } - // first check for the SQL parser having hit an error - if (PMA_SQP_isError()) { - return htmlspecialchars($arr['raw']); - } - // else do it properly - switch ($mode) { - case 'color': - $str = ''; - $html_line_break = '
'; - $docu = true; - break; - case 'query_only': - $str = ''; - $html_line_break = "\n"; - $docu = false; - break; - case 'text': - $str = ''; - $html_line_break = '
'; - $docu = true; - break; - } // end switch - // inner_sql is a span that exists for all cases, except query_only - // of $cfg['SQP']['fmtType'] to make possible a replacement - // for inline editing - if ($mode!='query_only') { - $str .= ''; - } - $close_docu_link = false; - $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' => 1, - 'KEY' => 1, - 'ON' => 1, - 'USING' => 1 - ); - - $keywords_with_brackets_2before = array( - 'IGNORE' => 1, - 'INDEX' => 1, - 'INTO' => 1, - 'KEY' => 1, - 'PRIMARY' => 1, - 'PROCEDURE' => 1, - 'REFERENCES' => 1, - 'UNIQUE' => 1, - 'USE' => 1 - ); - - // These reserved words do NOT get a newline placed near them. - $keywords_no_newline = array( - 'AS' => 1, - 'ASC' => 1, - 'DESC' => 1, - 'DISTINCT' => 1, - 'DUPLICATE' => 1, - 'HOUR' => 1, - 'INTERVAL' => 1, - 'IS' => 1, - 'LIKE' => 1, - 'NOT' => 1, - 'NULL' => 1, - 'ON' => 1, - 'REGEXP' => 1 - ); - - // These reserved words introduce a privilege list - $keywords_priv_list = array( - 'GRANT' => 1, - 'REVOKE' => 1 - ); - - if ($number_of_tokens == -1) { - $number_of_tokens = $arr['len']; - } - $typearr = array(); - if ($number_of_tokens >= 0) { - $typearr[0] = ''; - $typearr[1] = ''; - $typearr[2] = ''; - $typearr[3] = $arr[$start_token]['type']; - } - - $in_priv_list = false; - for ($i = $start_token; $i < $number_of_tokens; $i++) { - // DEBUG echo "Loop format " . $arr[$i]['data'] . " " . $arr[$i]['type'] . "
"; - $before = ''; - $after = ''; - // array_shift($typearr); - /* - 0 prev2 - 1 prev - 2 current - 3 next - */ - if (($i + 1) < $number_of_tokens) { - $typearr[4] = $arr[$i + 1]['type']; } else { - $typearr[4] = ''; + if ($upper_data == 'DROP' && $first_reserved_word == 'ALTER') { + $subresult['queryflags']['need_confirm'] = 1; + } } - for ($j=0; $j<4; $j++) { - $typearr[$j] = $typearr[$j + 1]; + if ($upper_data == 'LIMIT' && ! $in_subquery) { + $section_before_limit = substr($arr['raw'], 0, $arr[$i]['pos'] - 5); + $in_limit = true; + $seen_limit = true; + $limit_clause = ''; + $in_order_by = false; // @todo maybe others to set false } - switch ($typearr[2]) { - case 'alpha_bitfield_constant_introducer': - $before = ' '; - $after = ''; - break; - 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') - && isset($keywords_with_brackets_2before[strtoupper($arr[$i - 2]['data'])])) - || (($typearr[1] == 'alpha_reservedWord') - && isset($keywords_with_brackets_1before[strtoupper($arr[$i - 1]['data'])])) - ) { - $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 = ''; - } - // for example SELECT 1 somealias - if ($typearr[1] == 'digit_integer') { - $before = ' '; - } - if (($typearr[3] == 'alpha_columnType') || ($typearr[3] == 'alpha_identifier')) { - $after .= ' '; - } - break; - case 'punct_user': - 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 .= ' '; - if ($docu && isset($PMA_SQPdata_operators_docs[$arr[$i]['data']]) - && ($arr[$i]['data'] != '*' || in_array($arr[$i]['type'], array('digit_integer','digit_float','digit_hex'))) - ) { - $before .= PMA_showMySQLDocu( - 'functions', - $PMA_SQPdata_operators_docs[$arr[$i]['data']]['link'], - false, - $PMA_SQPdata_operators_docs[$arr[$i]['data']]['anchor'], - true - ); - $after .= ''; - } + if ($upper_data == 'PROCEDURE') { + $subresult['queryflags']['procedure'] = 1; + $in_limit = false; + $after_limit = true; + } + /** + * @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; + } - // 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': - // only close bracket level when it was opened before - if ($bracketlevel > 0) { - $bracketlevel--; - if ($infunction == true) { - $functionlevel--; - $after .= ' '; - $before .= ' '; - } else { - $indent--; - $before .= ($mode != 'query_only' ? '' : ' '); - } - $infunction = ($functionlevel > 0) ? true : false; - } - break; - case 'alpha_columnType': - if ($docu) { - switch ($arr[$i]['data']) { - case 'tinyint': - case 'smallint': - case 'mediumint': - case 'int': - case 'bigint': - case 'decimal': - case 'float': - case 'double': - case 'real': - case 'bit': - case 'boolean': - case 'serial': - $before .= PMA_showMySQLDocu('data-types', 'numeric-types', false, '', true); - $after = '' . $after; - break; - case 'date': - case 'datetime': - case 'timestamp': - case 'time': - case 'year': - $before .= PMA_showMySQLDocu('data-types', 'date-and-time-types', false, '', true); - $after = '' . $after; - break; - case 'char': - case 'varchar': - case 'tinytext': - case 'text': - case 'mediumtext': - case 'longtext': - case 'binary': - case 'varbinary': - case 'tinyblob': - case 'mediumblob': - case 'blob': - case 'longblob': - case 'enum': - case 'set': - $before .= PMA_showMySQLDocu('data-types', 'string-types', false, '', true); - $after = '' . $after; - break; - } - } - if ($typearr[3] == 'alpha_columnAttrib') { - $after .= ' '; - } - if ($typearr[1] == 'alpha_columnType') { - $before .= ' '; - } - break; - case 'alpha_columnAttrib': + if ($upper_data == 'UNION') { + $subresult['queryflags']['union'] = 1; + } - // 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 ($upper_data == 'JOIN') { + $subresult['queryflags']['join'] = 1; + } - 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($arr[$i]['data']) == 'BINARY' - && $typearr[3] == 'alpha_identifier' - ) { - $after .= ' '; - } - break; - case 'alpha_functionName': - $funcname = strtoupper($arr[$i]['data']); - if ($docu && isset($PMA_SQPdata_functions_docs[$funcname])) { - $before .= PMA_showMySQLDocu( - 'functions', - $PMA_SQPdata_functions_docs[$funcname]['link'], - false, - $PMA_SQPdata_functions_docs[$funcname]['anchor'], - true - ); - $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 ($upper_data == 'OFFSET') { + $subresult['queryflags']['offset'] = 1; + } - if ($mode != 'query_only') { - $arr[$i]['data'] = strtoupper($arr[$i]['data']); + // 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 ((($typearr[1] != 'alpha_reservedWord') - || (($typearr[1] == 'alpha_reservedWord') - && isset($keywords_no_newline[strtoupper($arr[$i - 1]['data'])]))) - && ($typearr[1] != 'punct_level_plus') - && (!isset($keywords_no_newline[$arr[$i]['data']])) - ) { - // do not put a space before the first token, because - // we use a lot of pattern matching 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 (isset($keywords_priv_list[$arr[$i]['data']])) { - $in_priv_list = true; - } - } - } else { - $before .= ' '; + if ($seen_order) { + $seen_order_by = true; + // Here we assume that the ORDER BY keywords took + // exactly 8 characters. + // We use PMA_substr() to be charset-safe; otherwise + // if the table name contains accents, the unsorted + // query would be missing some characters. + $unsorted_query = PMA_substr($arr['raw'], 0, $arr[$i]['pos'] - 8); + $in_order_by = true; + $order_by_clause = ''; } + } - switch ($arr[$i]['data']) { - case 'CREATE': - case 'ALTER': - case 'DROP': - case 'RENAME'; - case 'TRUNCATE': - case 'ANALYZE': - case 'ANALYSE': - case 'OPTIMIZE': - if ($docu) { - switch ($arr[$i + 1]['data']) { - case 'EVENT': - case 'TABLE': - case 'TABLESPACE': - case 'FUNCTION': - case 'INDEX': - case 'PROCEDURE': - case 'TRIGGER': - case 'SERVER': - case 'DATABASE': - case 'VIEW': - $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'] . '_' . $arr[$i + 1]['data'], false, '', true); - $close_docu_link = true; - break; - } - if ($arr[$i + 1]['data'] == 'LOGFILE' && $arr[$i + 2]['data'] == 'GROUP') { - $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'] . '_LOGFILE_GROUP', false, '', true); - $close_docu_link = true; - } + // if we find one of the words that could end the clause + if (isset($words_ending_clauses[$upper_data])) { + + $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 ($arr[$i]['type'] == 'alpha_functionName') { + $sep=''; + $upper_data = strtoupper($arr[$i]['data']); + if ($upper_data =='GROUP_CONCAT') { + $in_group_concat = true; + $number_of_brackets_in_group_concat = 0; + } + } + + if ($arr[$i]['type'] == 'punct_bracket_open_round') { + if ($in_group_concat) { + $number_of_brackets_in_group_concat++; + } + } + if ($arr[$i]['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 ($arr[$i]['type'] == 'alpha_identifier' && $i < $size - 1 && $arr[$i + 1]['data'] == '.') { + $sep = ''; + } + + // do not add a space after a dot if followed by an identifier + if ($arr[$i]['data'] == '.' && $i < $size - 1 && $arr[$i + 1]['type'] == 'alpha_identifier') { + $sep = ''; + } + + if ($in_select_expr && $upper_data != 'SELECT' && $upper_data != 'DISTINCT') { + $select_expr_clause .= $arr[$i]['data'] . $sep; + } + if ($in_from && $upper_data != 'FROM') { + $from_clause .= $arr[$i]['data'] . $sep; + } + if ($in_group_by && $upper_data != 'GROUP' && $upper_data != 'BY') { + $group_by_clause .= $arr[$i]['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 ($arr[$i]['type'] == 'alpha_reservedWord') { + $order_by_clause .= $sep; + } + $order_by_clause .= $arr[$i]['data']; + } + if ($in_having && $upper_data != 'HAVING') { + $having_clause .= $arr[$i]['data'] . $sep; + } + if ($in_where && $upper_data != 'WHERE') { + $where_clause .= $arr[$i]['data'] . $sep; + + if (($arr[$i]['type'] == 'quote_backtick') + || ($arr[$i]['type'] == 'alpha_identifier') + ) { + $where_clause_identifiers[] = $arr[$i]['data']; + } + } + + // to grab the rest of the query after the ORDER BY clause + if (isset($subresult['queryflags']['select_from']) + && $subresult['queryflags']['select_from'] == 1 + && ! $in_order_by + && $seen_order_by + && $upper_data != 'BY' + ) { + $unsorted_query .= $arr[$i]['data']; + if ($arr[$i]['type'] != 'punct_bracket_open_round' + && $arr[$i]['type'] != 'punct_bracket_close_round' + && $arr[$i]['type'] != 'punct' + ) { + $unsorted_query .= $sep; + } + } + + if ($in_limit) { + if ($upper_data == 'OFFSET') { + $limit_clause .= $sep; + } + $limit_clause .= $arr[$i]['data']; + if ($upper_data == 'LIMIT' || $upper_data == 'OFFSET') { + $limit_clause .= $sep; + } + } + if ($after_limit && $seen_limit) { + $section_after_limit .= $arr[$i]['data'] . $sep; + } + + // clear $upper_data for next iteration + $upper_data=''; + } // end for $i (loop #2) + if (empty($section_before_limit)) { + $section_before_limit = $arr['raw']; + } + + // ----------------------------------------------------- + // 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; + $seen_alter = false; + $in_create_table_fields = false; + $brackets_level = 0; + $in_timestamp_options = false; + $seen_default = false; + + for ($i = 0; $i < $size; $i++) { + if ($arr[$i]['type'] == 'alpha_reservedWord') { + $upper_data = strtoupper($arr[$i]['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 == 'ALTER') { + $seen_alter = 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 (!$in_priv_list) { - $space_punct_listsep = $html_line_break; - $space_alpha_reserved_word = ' '; + } + } + + 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 (isset($arr[$i+1]) && $arr[$i+1]['type'] == 'alpha_reservedWord') { + $second_upper_data = strtoupper($arr[$i+1]['data']); + if ($second_upper_data == 'DELETE') { + $clause = 'on_delete'; } - break; - case 'EVENT': - case 'TABLESPACE': - case 'TABLE': - case 'FUNCTION': - case 'INDEX': - case 'PROCEDURE': - case 'SERVER': - case 'TRIGGER': - case 'DATABASE': - case 'VIEW': - case 'GROUP': - if ($close_docu_link) { - $after = '' . $after; - $close_docu_link = false; + if ($second_upper_data == 'UPDATE') { + $clause = 'on_update'; } - break; - case 'SET': - if ($docu && ($i == 0 || $arr[$i - 1]['data'] != 'CHARACTER')) { - $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'], false, '', true); - $after = '' . $after; - } - if (!$in_priv_list) { - $space_punct_listsep = $html_line_break; - $space_alpha_reserved_word = ' '; - } - break; - case 'EXPLAIN': - case 'DESCRIBE': - case 'DELETE': - case 'SHOW': - case 'UPDATE': - if ($docu) { - $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'], false, '', true); - $after = '' . $after; - } - if (!$in_priv_list) { - $space_punct_listsep = $html_line_break; - $space_alpha_reserved_word = ' '; - } - break; - case 'INSERT': - case 'REPLACE': - if ($docu) { - $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'], false, '', true); - $after = '' . $after; - } - 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': - if ($docu) { - $before .= PMA_showMySQLDocu('SQL-Syntax', 'SELECT', false, '', true); - $after = '' . $after; - } - $space_punct_listsep = ' '; - $space_alpha_reserved_word = $html_line_break; - break; - case 'CALL': - case 'DO': - case 'HANDLER': - if ($docu) { - $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'], false, '', true); - $after = '' . $after; - } - break; - default: - if ($close_docu_link && in_array($arr[$i]['data'], array('LIKE', 'NOT', 'IN', 'REGEXP', 'NULL'))) { - $after .= ''; - $close_docu_link = false; - } else if ($docu && isset($PMA_SQPdata_functions_docs[$arr[$i]['data']])) { - /* Handle multi word statements first */ - if (isset($typearr[4]) && $typearr[4] == 'alpha_reservedWord' && $typearr[3] == 'alpha_reservedWord' && isset($PMA_SQPdata_functions_docs[strtoupper($arr[$i]['data'] . '_' . $arr[$i + 1]['data'] . '_' . $arr[$i + 2]['data'])])) { - $tempname = strtoupper($arr[$i]['data'] . '_' . $arr[$i + 1]['data'] . '_' . $arr[$i + 2]['data']); - $before .= PMA_showMySQLDocu('functions', $PMA_SQPdata_functions_docs[$tempname]['link'], false, $PMA_SQPdata_functions_docs[$tempname]['anchor'], true); - $close_docu_link = true; - } else if (isset($typearr[3]) && $typearr[3] == 'alpha_reservedWord' && isset($PMA_SQPdata_functions_docs[strtoupper($arr[$i]['data'] . '_' . $arr[$i + 1]['data'])])) { - $tempname = strtoupper($arr[$i]['data'] . '_' . $arr[$i + 1]['data']); - $before .= PMA_showMySQLDocu('functions', $PMA_SQPdata_functions_docs[$tempname]['link'], false, $PMA_SQPdata_functions_docs[$tempname]['anchor'], true); - $close_docu_link = true; + if (isset($clause) + && ($arr[$i+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) + || ($arr[$i+2]['type'] == 'alpha_identifier' + && strtoupper($arr[$i+2]['data'])=='NO')) + ) { + $third_upper_data = strtoupper($arr[$i+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 ($arr[$i+3]['type'] == 'alpha_reservedWord') { + $value = $third_upper_data . '_' . strtoupper($arr[$i+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 { - $before .= PMA_showMySQLDocu('functions', $PMA_SQPdata_functions_docs[$arr[$i]['data']]['link'], false, $PMA_SQPdata_functions_docs[$arr[$i]['data']]['anchor'], true); - $after .= ''; + $value = ''; } - } - break; - } // end switch ($arr[$i]['data']) + if (!empty($value)) { + $foreign[$foreign_key_number][$clause] = $value; + } + unset($clause); + } // endif (isset($clause)) + } + } - $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 .= ' '; + } // end of reserved words analysis + + + if ($arr[$i]['type'] == 'punct_bracket_open_round') { + $brackets_level++; + if ($seen_create_table && $brackets_level == 1) { + $in_create_table_fields = true; + } + } + + + if ($arr[$i]['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 (($arr[$i]['type'] == 'alpha_columnAttrib')) { + $upper_data = strtoupper($arr[$i]['data']); + if ($seen_create_table && $in_create_table_fields) { + if ($upper_data == 'DEFAULT') { + $seen_default = true; + $create_table_fields[$current_identifier]['default_value'] = $arr[$i + 1]['data']; } - if ($infunction && $typearr[3] == 'punct_bracket_close_round') { + } + } + + /** + * @see @todo 2005-10-16 note: the "or" part here is a workaround for a bug + */ + if (($arr[$i]['type'] == 'alpha_columnType') || ($arr[$i]['type'] == 'alpha_functionName' && $seen_create_table)) { + $upper_data = strtoupper($arr[$i]['data']); + if ($seen_create_table && $in_create_table_fields && isset($current_identifier)) { + $create_table_fields[$current_identifier]['type'] = $upper_data; + if ($upper_data == 'TIMESTAMP') { + $arr[$i]['type'] = 'alpha_columnType'; + $in_timestamp_options = true; + } else { + $in_timestamp_options = false; + if ($upper_data == 'CHAR') { + $arr[$i]['type'] = 'alpha_columnType'; + } + } + } + } + + + if ($arr[$i]['type'] == 'quote_backtick' || $arr[$i]['type'] == 'alpha_identifier') { + + if ($arr[$i]['type'] == 'quote_backtick') { + // remove backquotes + $identifier = PMA_unQuote($arr[$i]['data']); + } else { + $identifier = $arr[$i]['data']; + } + + if ($seen_create_table && $in_create_table_fields) { + $current_identifier = $identifier; + // 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) { + if ($seen_alter && $brackets_level > 0) { + $foreign[$foreign_key_number]['ref_index_list'][] = $identifier; + // 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 + } elseif ($brackets_level > 1) { + $foreign[$foreign_key_number]['ref_index_list'][] = $identifier; + } elseif ($arr[$i+1]['type'] == 'punct_qualifier') { + // identifier is `db`.`table` + // the first pass will pick the db name + // the next pass will pick the table name + $foreign[$foreign_key_number]['ref_db_name'] = $identifier; + } else { + // identifier is `table` + $foreign[$foreign_key_number]['ref_table_name'] = $identifier; + } + } + } + } // 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($limit_clause)) { + $subresult['limit_clause'] = $limit_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'] != '') { + $result[] = $subresult; + } + return $result; +} // end of the "PMA_SQP_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 + * + * @param array $arr The SQL queries html formatted + * + * @return array The colorized SQL queries + * + * @access public + */ +function PMA_SQP_formatHtml_colorize($arr) +{ + $i = PMA_strpos($arr['type'], '_'); + $class = ''; + if ($i > 0) { + $class = 'syntax_' . PMA_substr($arr['type'], 0, $i) . ' '; + } + + $class .= 'syntax_' . $arr['type']; + + return '' . htmlspecialchars($arr['data']) . ''; +} // end of the "PMA_SQP_formatHtml_colorize()" function + + +/** + * Formats SQL queries to html + * + * @param array $arr The SQL queries + * @param string $mode mode of printing + * @param integer $start_token starting token + * @param integer $numbr_of_tokens number of tokens to format, -1 = all + * + * @return string The formatted SQL queries + * + * @access public + */ +function PMA_SQP_formatHtml( + $arr, $mode='color', $start_token=0, + $number_of_tokens=-1 +) { + global $PMA_SQPdata_operators_docs, $PMA_SQPdata_functions_docs; + //DEBUG echo 'in Format'; print_r($arr); echo ''; + // then check for an array + if (! is_array($arr)) { + return htmlspecialchars($arr); + } + // first check for the SQL parser having hit an error + if (PMA_SQP_isError()) { + return htmlspecialchars($arr['raw']); + } + // else do it properly + switch ($mode) { + case 'color': + $str = ''; + $html_line_break = '
'; + $docu = true; + break; + case 'query_only': + $str = ''; + $html_line_break = "\n"; + $docu = false; + break; + case 'text': + $str = ''; + $html_line_break = '
'; + $docu = true; + break; + } // end switch + // inner_sql is a span that exists for all cases, except query_only + // of $cfg['SQP']['fmtType'] to make possible a replacement + // for inline editing + if ($mode!='query_only') { + $str .= ''; + } + $close_docu_link = false; + $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' => 1, + 'KEY' => 1, + 'ON' => 1, + 'USING' => 1 + ); + + $keywords_with_brackets_2before = array( + 'IGNORE' => 1, + 'INDEX' => 1, + 'INTO' => 1, + 'KEY' => 1, + 'PRIMARY' => 1, + 'PROCEDURE' => 1, + 'REFERENCES' => 1, + 'UNIQUE' => 1, + 'USE' => 1 + ); + + // These reserved words do NOT get a newline placed near them. + $keywords_no_newline = array( + 'AS' => 1, + 'ASC' => 1, + 'DESC' => 1, + 'DISTINCT' => 1, + 'DUPLICATE' => 1, + 'HOUR' => 1, + 'INTERVAL' => 1, + 'IS' => 1, + 'LIKE' => 1, + 'NOT' => 1, + 'NULL' => 1, + 'ON' => 1, + 'REGEXP' => 1 + ); + + // These reserved words introduce a privilege list + $keywords_priv_list = array( + 'GRANT' => 1, + 'REVOKE' => 1 + ); + + if ($number_of_tokens == -1) { + $number_of_tokens = $arr['len']; + } + $typearr = array(); + if ($number_of_tokens >= 0) { + $typearr[0] = ''; + $typearr[1] = ''; + $typearr[2] = ''; + $typearr[3] = $arr[$start_token]['type']; + } + + $in_priv_list = false; + for ($i = $start_token; $i < $number_of_tokens; $i++) { + // DEBUG echo "Loop format " . $arr[$i]['data'] . " " . $arr[$i]['type'] . "
"; + $before = ''; + $after = ''; + // array_shift($typearr); + /* + 0 prev2 + 1 prev + 2 current + 3 next + */ + if (($i + 1) < $number_of_tokens) { + $typearr[4] = $arr[$i + 1]['type']; + } else { + $typearr[4] = ''; + } + + for ($j=0; $j<4; $j++) { + $typearr[$j] = $typearr[$j + 1]; + } + + switch ($typearr[2]) { + case 'alpha_bitfield_constant_introducer': + $before = ' '; + $after = ''; + break; + 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') + && isset($keywords_with_brackets_2before[strtoupper($arr[$i - 2]['data'])])) + || (($typearr[1] == 'alpha_reservedWord') + && isset($keywords_with_brackets_1before[strtoupper($arr[$i - 1]['data'])])) + ) { + $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 = ''; + } + // for example SELECT 1 somealias + if ($typearr[1] == 'digit_integer') { + $before = ' '; + } + if (($typearr[3] == 'alpha_columnType') || ($typearr[3] == 'alpha_identifier')) { + $after .= ' '; + } + break; + case 'punct_user': + 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 .= ' '; + if ($docu && isset($PMA_SQPdata_operators_docs[$arr[$i]['data']]) + && ($arr[$i]['data'] != '*' || in_array($arr[$i]['type'], array('digit_integer','digit_float','digit_hex'))) + ) { + $before .= PMA_showMySQLDocu( + 'functions', + $PMA_SQPdata_operators_docs[$arr[$i]['data']]['link'], + false, + $PMA_SQPdata_operators_docs[$arr[$i]['data']]['anchor'], + true + ); + $after .= ''; + } + + // 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': + // only close bracket level when it was opened before + if ($bracketlevel > 0) { + $bracketlevel--; + if ($infunction == true) { + $functionlevel--; $after .= ' '; - } - if ($typearr[1] == 'alpha_columnAttrib') { - $before .= ' '; - } - break; - case 'alpha_variable': - $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] != 'punct_user' && $typearr[1] != 'alpha_bitfield_constant_introducer') { - $before .= ' '; - } - if ($infunction && $typearr[3] == 'punct_bracket_close_round') { - $after .= ' '; - } - break; - case 'quote_backtick': - // here we check for punct_user to handle correctly - // DEFINER = `username`@`%` - // where @ is the punct_user and `%` is the quote_backtick - if ($typearr[3] != 'punct_qualifier' && $typearr[3] != 'alpha_variable' && $typearr[3] != 'punct_user') { - $after .= ' '; - } - if ($typearr[1] != 'punct_qualifier' && $typearr[1] != 'alpha_variable' && $typearr[1] != 'punct_user') { $before .= ' '; + } else { + $indent--; + $before .= ($mode != 'query_only' ? '' : ' '); + } + $infunction = ($functionlevel > 0) ? true : false; + } + break; + case 'alpha_columnType': + if ($docu) { + switch ($arr[$i]['data']) { + case 'tinyint': + case 'smallint': + case 'mediumint': + case 'int': + case 'bigint': + case 'decimal': + case 'float': + case 'double': + case 'real': + case 'bit': + case 'boolean': + case 'serial': + $before .= PMA_showMySQLDocu('data-types', 'numeric-types', false, '', true); + $after = '' . $after; + break; + case 'date': + case 'datetime': + case 'timestamp': + case 'time': + case 'year': + $before .= PMA_showMySQLDocu('data-types', 'date-and-time-types', false, '', true); + $after = '' . $after; + break; + case 'char': + case 'varchar': + case 'tinytext': + case 'text': + case 'mediumtext': + case 'longtext': + case 'binary': + case 'varbinary': + case 'tinyblob': + case 'mediumblob': + case 'blob': + case 'longblob': + case 'enum': + case 'set': + $before .= PMA_showMySQLDocu('data-types', 'string-types', false, '', true); + $after = '' . $after; + break; + } + } + 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($arr[$i]['data']) == 'BINARY' + && $typearr[3] == 'alpha_identifier' + ) { + $after .= ' '; + } + break; + case 'alpha_functionName': + $funcname = strtoupper($arr[$i]['data']); + if ($docu && isset($PMA_SQPdata_functions_docs[$funcname])) { + $before .= PMA_showMySQLDocu( + 'functions', + $PMA_SQPdata_functions_docs[$funcname]['link'], + false, + $PMA_SQPdata_functions_docs[$funcname]['anchor'], + true + ); + $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') { + $arr[$i]['data'] = strtoupper($arr[$i]['data']); + } + + if ((($typearr[1] != 'alpha_reservedWord') + || (($typearr[1] == 'alpha_reservedWord') + && isset($keywords_no_newline[strtoupper($arr[$i - 1]['data'])]))) + && ($typearr[1] != 'punct_level_plus') + && (!isset($keywords_no_newline[$arr[$i]['data']])) + ) { + // do not put a space before the first token, because + // we use a lot of pattern matching 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 (isset($keywords_priv_list[$arr[$i]['data']])) { + $in_priv_list = true; + } + } + } else { + $before .= ' '; + } + + switch ($arr[$i]['data']) { + case 'CREATE': + case 'ALTER': + case 'DROP': + case 'RENAME'; + case 'TRUNCATE': + case 'ANALYZE': + case 'ANALYSE': + case 'OPTIMIZE': + if ($docu) { + switch ($arr[$i + 1]['data']) { + case 'EVENT': + case 'TABLE': + case 'TABLESPACE': + case 'FUNCTION': + case 'INDEX': + case 'PROCEDURE': + case 'TRIGGER': + case 'SERVER': + case 'DATABASE': + case 'VIEW': + $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'] . '_' . $arr[$i + 1]['data'], false, '', true); + $close_docu_link = true; + break; + } + if ($arr[$i + 1]['data'] == 'LOGFILE' && $arr[$i + 2]['data'] == 'GROUP') { + $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'] . '_LOGFILE_GROUP', false, '', true); + $close_docu_link = true; + } + } + if (!$in_priv_list) { + $space_punct_listsep = $html_line_break; + $space_alpha_reserved_word = ' '; + } + break; + case 'EVENT': + case 'TABLESPACE': + case 'TABLE': + case 'FUNCTION': + case 'INDEX': + case 'PROCEDURE': + case 'SERVER': + case 'TRIGGER': + case 'DATABASE': + case 'VIEW': + case 'GROUP': + if ($close_docu_link) { + $after = '' . $after; + $close_docu_link = false; + } + break; + case 'SET': + if ($docu && ($i == 0 || $arr[$i - 1]['data'] != 'CHARACTER')) { + $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'], false, '', true); + $after = '' . $after; + } + if (!$in_priv_list) { + $space_punct_listsep = $html_line_break; + $space_alpha_reserved_word = ' '; + } + break; + case 'EXPLAIN': + case 'DESCRIBE': + case 'DELETE': + case 'SHOW': + case 'UPDATE': + if ($docu) { + $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'], false, '', true); + $after = '' . $after; + } + if (!$in_priv_list) { + $space_punct_listsep = $html_line_break; + $space_alpha_reserved_word = ' '; + } + break; + case 'INSERT': + case 'REPLACE': + if ($docu) { + $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'], false, '', true); + $after = '' . $after; + } + 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': + if ($docu) { + $before .= PMA_showMySQLDocu('SQL-Syntax', 'SELECT', false, '', true); + $after = '' . $after; + } + $space_punct_listsep = ' '; + $space_alpha_reserved_word = $html_line_break; + break; + case 'CALL': + case 'DO': + case 'HANDLER': + if ($docu) { + $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'], false, '', true); + $after = '' . $after; } break; default: + if ($close_docu_link && in_array($arr[$i]['data'], array('LIKE', 'NOT', 'IN', 'REGEXP', 'NULL'))) { + $after .= ''; + $close_docu_link = false; + } else if ($docu && isset($PMA_SQPdata_functions_docs[$arr[$i]['data']])) { + /* Handle multi word statements first */ + if (isset($typearr[4]) && $typearr[4] == 'alpha_reservedWord' && $typearr[3] == 'alpha_reservedWord' && isset($PMA_SQPdata_functions_docs[strtoupper($arr[$i]['data'] . '_' . $arr[$i + 1]['data'] . '_' . $arr[$i + 2]['data'])])) { + $tempname = strtoupper($arr[$i]['data'] . '_' . $arr[$i + 1]['data'] . '_' . $arr[$i + 2]['data']); + $before .= PMA_showMySQLDocu('functions', $PMA_SQPdata_functions_docs[$tempname]['link'], false, $PMA_SQPdata_functions_docs[$tempname]['anchor'], true); + $close_docu_link = true; + } else if (isset($typearr[3]) && $typearr[3] == 'alpha_reservedWord' && isset($PMA_SQPdata_functions_docs[strtoupper($arr[$i]['data'] . '_' . $arr[$i + 1]['data'])])) { + $tempname = strtoupper($arr[$i]['data'] . '_' . $arr[$i + 1]['data']); + $before .= PMA_showMySQLDocu('functions', $PMA_SQPdata_functions_docs[$tempname]['link'], false, $PMA_SQPdata_functions_docs[$tempname]['anchor'], true); + $close_docu_link = true; + } else { + $before .= PMA_showMySQLDocu('functions', $PMA_SQPdata_functions_docs[$arr[$i]['data']]['link'], false, $PMA_SQPdata_functions_docs[$arr[$i]['data']]['anchor'], true); + $after .= ''; + } + } break; - } // end switch ($typearr[2]) + } // end switch ($arr[$i]['data']) - /* - if ($typearr[3] != 'punct_qualifier') { - $after .= ' '; + $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 .= ' '; } - $after .= "\n"; - */ - $str .= $before; - if ($mode=='color') { - $str .= PMA_SQP_formatHTML_colorize($arr[$i]); - } elseif ($mode == 'text') { - $str .= htmlspecialchars($arr[$i]['data']); - } else { - $str .= $arr[$i]['data']; + if ($infunction && $typearr[3] == 'punct_bracket_close_round') { + $after .= ' '; } - $str .= $after; - } // end for - // close unclosed indent levels - while ($indent > 0) { - $indent--; - $str .= ($mode != 'query_only' ? '' : ' '); - } - /* End possibly unclosed documentation link */ - if ($close_docu_link) { - $str .= ''; - $close_docu_link = false; - } - if ($mode!='query_only') { - // close inner_sql span - $str .= ''; + if ($typearr[1] == 'alpha_columnAttrib') { + $before .= ' '; + } + break; + case 'alpha_variable': + $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] != 'punct_user' && $typearr[1] != 'alpha_bitfield_constant_introducer') { + $before .= ' '; + } + if ($infunction && $typearr[3] == 'punct_bracket_close_round') { + $after .= ' '; + } + break; + case 'quote_backtick': + // here we check for punct_user to handle correctly + // DEFINER = `username`@`%` + // where @ is the punct_user and `%` is the quote_backtick + if ($typearr[3] != 'punct_qualifier' && $typearr[3] != 'alpha_variable' && $typearr[3] != 'punct_user') { + $after .= ' '; + } + if ($typearr[1] != 'punct_qualifier' && $typearr[1] != 'alpha_variable' && $typearr[1] != 'punct_user') { + $before .= ' '; + } + break; + default: + break; + } // end switch ($typearr[2]) + + /* + if ($typearr[3] != 'punct_qualifier') { + $after .= ' '; } + $after .= "\n"; + */ + $str .= $before; if ($mode=='color') { - // close syntax span - $str .= ''; + $str .= PMA_SQP_formatHTML_colorize($arr[$i]); + } elseif ($mode == 'text') { + $str .= htmlspecialchars($arr[$i]['data']); + } else { + $str .= $arr[$i]['data']; } - - return $str; - } // end of the "PMA_SQP_formatHtml()" 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_SQP_buildCssData() - */ -function PMA_SQP_buildCssRule($classname, $property, $value) -{ - $str = '.' . $classname . ' {'; - if ($value != '') { - $str .= $property . ': ' . $value . ';'; + $str .= $after; + } // end for + // close unclosed indent levels + while ($indent > 0) { + $indent--; + $str .= ($mode != 'query_only' ? '' : ' '); + } + /* End possibly unclosed documentation link */ + if ($close_docu_link) { + $str .= ''; + $close_docu_link = false; + } + if ($mode!='query_only') { + // close inner_sql span + $str .= ''; + } + if ($mode=='color') { + // close syntax span + $str .= ''; } - $str .= '}' . "\n"; return $str; -} // end of the "PMA_SQP_buildCssRule()" function - +} // end of the "PMA_SQP_formatHtml()" function /** - * Builds CSS rules used for html formatted SQL queries + * Gets SQL queries with no format * - * @return string The CSS rules set + * @param array $arr The SQL queries list + * + * @return string The SQL queries with no format * * @access public - * - * @global array The current PMA configuration - * - * @see PMA_SQP_buildCssRule() */ -function PMA_SQP_buildCssData() +function PMA_SQP_formatNone($arr) { - global $cfg; + $formatted_sql = htmlspecialchars($arr['raw']); + $formatted_sql = preg_replace( + "@((\015\012)|(\015)|(\012)){3,}@", + "\n\n", + $formatted_sql + ); - $css_string = ''; - foreach ($cfg['SQP']['fmtColor'] AS $key => $col) { - $css_string .= PMA_SQP_buildCssRule('syntax_' . $key, 'color', $col); - } - - for ($i = 0; $i < 8; $i++) { - $css_string .= PMA_SQP_buildCssRule( - 'syntax_indent' . $i, 'margin-left', - ($i * $cfg['SQP']['fmtInd']) . $cfg['SQP']['fmtIndUnit'] - ); - } - - return $css_string; -} // end of the "PMA_SQP_buildCssData()" function - -if (! defined('PMA_MINIMUM_COMMON')) { - /** - * Gets SQL queries with no format - * - * @param array The SQL queries list - * - * @return string The SQL queries with no format - * - * @access public - */ - function PMA_SQP_formatNone($arr) - { - $formatted_sql = htmlspecialchars($arr['raw']); - $formatted_sql = preg_replace( - "@((\015\012)|(\015)|(\012)){3,}@", - "\n\n", - $formatted_sql - ); - - return $formatted_sql; - } // end of the "PMA_SQP_formatNone()" function - -} // end if: minimal common.lib needed? + return $formatted_sql; +} // end of the "PMA_SQP_formatNone()" function ?> diff --git a/phpmyadmin.css.php b/phpmyadmin.css.php index 8158ec184e..bfe4d47e03 100644 --- a/phpmyadmin.css.php +++ b/phpmyadmin.css.php @@ -11,7 +11,6 @@ define('PMA_MINIMUM_COMMON', true); require_once 'libraries/common.inc.php'; -require_once 'libraries/sqlparser.lib.php'; // MSIE 6 (at least some unpatched versions) has problems loading CSS // when zlib_compression is on