home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / phpMyAdmin / libraries / sqlparser.lib.php < prev    next >
Encoding:
PHP Script  |  2008-06-23  |  99.4 KB  |  2,526 lines

  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /** SQL Parser Functions for phpMyAdmin
  4.  *
  5.  * Copyright 2002 Robin Johnson <robbat2@users.sourceforge.net>
  6.  * http://www.orbis-terrarum.net/?l=people.robbat2
  7.  *
  8.  * These functions define an SQL parser system, capable of understanding and
  9.  * extracting data from a MySQL type SQL query.
  10.  *
  11.  * The basic procedure for using the new SQL parser:
  12.  * On any page that needs to extract data from a query or to pretty-print a
  13.  * query, you need code like this up at the top:
  14.  *
  15.  * ($sql contains the query)
  16.  * $parsed_sql = PMA_SQP_parse($sql);
  17.  *
  18.  * If you want to extract data from it then, you just need to run
  19.  * $sql_info = PMA_SQP_analyze($parsed_sql);
  20.  *
  21.  * lem9: See comments in PMA_SQP_analyze for the returned info
  22.  *       from the analyzer.
  23.  *
  24.  * If you want a pretty-printed version of the query, do:
  25.  * $string = PMA_SQP_formatHtml($parsed_sql);
  26.  * (note that that you need to have syntax.css.php included somehow in your
  27.  * page for it to work, I recommend '<link rel="stylesheet" type="text/css"
  28.  * href="syntax.css.php" />' at the moment.)
  29.  *
  30.  * @version $Id: sqlparser.lib.php 11326 2008-06-17 21:32:48Z lem9 $
  31.  */
  32. if (! defined('PHPMYADMIN')) {
  33.     exit;
  34. }
  35.  
  36.  
  37. /**
  38.  * Minimum inclusion? (i.e. for the stylesheet builder)
  39.  */
  40. if (! defined('PMA_MINIMUM_COMMON')) {
  41.     /**
  42.      * Include the string library as we use it heavily
  43.      */
  44.     require_once './libraries/string.lib.php';
  45.  
  46.     /**
  47.      * Include data for the SQL Parser
  48.      */
  49.     require_once './libraries/sqlparser.data.php';
  50.     require_once './libraries/mysql_charsets.lib.php';
  51.     if (!isset($mysql_charsets)) {
  52.         $mysql_charsets = array();
  53.         $mysql_charsets_count = 0;
  54.         $mysql_collations_flat = array();
  55.         $mysql_collations_count = 0;
  56.     }
  57.  
  58.     if (!defined('DEBUG_TIMING')) {
  59.         // currently we don't need the $pos (token position in query)
  60.         // for other purposes than LIMIT clause verification,
  61.         // so many calls to this function do not include the 4th parameter
  62.         function PMA_SQP_arrayAdd(&$arr, $type, $data, &$arrsize, $pos = 0)
  63.         {
  64.             $arr[] = array('type' => $type, 'data' => $data, 'pos' => $pos);
  65.             $arrsize++;
  66.         } // end of the "PMA_SQP_arrayAdd()" function
  67.     } else {
  68.         function PMA_SQP_arrayAdd(&$arr, $type, $data, &$arrsize, $pos = 0)
  69.         {
  70.             global $timer;
  71.  
  72.             $t     = $timer;
  73.             $arr[] = array('type' => $type, 'data' => $data, 'pos' => $pos, 'time' => $t);
  74.             $timer = microtime();
  75.             $arrsize++;
  76.         } // end of the "PMA_SQP_arrayAdd()" function
  77.     } // end if... else...
  78.  
  79.  
  80.     /**
  81.      * Reset the error variable for the SQL parser
  82.      *
  83.      * @access public
  84.      */
  85.     // Added, Robbat2 - 13 Janurary 2003, 2:59PM
  86.     function PMA_SQP_resetError()
  87.     {
  88.         global $SQP_errorString;
  89.         $SQP_errorString = '';
  90.         unset($SQP_errorString);
  91.     }
  92.  
  93.     /**
  94.      * Get the contents of the error variable for the SQL parser
  95.      *
  96.      * @return string Error string from SQL parser
  97.      *
  98.      * @access public
  99.      */
  100.     // Added, Robbat2 - 13 Janurary 2003, 2:59PM
  101.     function PMA_SQP_getErrorString()
  102.     {
  103.         global $SQP_errorString;
  104.         return isset($SQP_errorString) ? $SQP_errorString : '';
  105.     }
  106.  
  107.     /**
  108.      * Check if the SQL parser hit an error
  109.      *
  110.      * @return boolean error state
  111.      *
  112.      * @access public
  113.      */
  114.     // Added, Robbat2 - 13 Janurary 2003, 2:59PM
  115.     function PMA_SQP_isError()
  116.     {
  117.         global $SQP_errorString;
  118.         return isset($SQP_errorString) && !empty($SQP_errorString);
  119.     }
  120.  
  121.     /**
  122.      * Set an error message for the system
  123.      *
  124.      * @param  string  The error message
  125.      * @param  string  The failing SQL query
  126.      *
  127.      * @access private
  128.      * @scope SQL Parser internal
  129.      */
  130.     // Revised, Robbat2 - 13 Janurary 2003, 2:59PM
  131.     function PMA_SQP_throwError($message, $sql)
  132.     {
  133.  
  134.         global $SQP_errorString;
  135.         $SQP_errorString = '<p>'.$GLOBALS['strSQLParserUserError'] . '</p>' . "\n"
  136.             . '<pre>' . "\n"
  137.             . 'ERROR: ' . $message . "\n"
  138.             . 'SQL: ' . htmlspecialchars($sql) .  "\n"
  139.             . '</pre>' . "\n";
  140.  
  141.     } // end of the "PMA_SQP_throwError()" function
  142.  
  143.  
  144.     /**
  145.      * Do display the bug report
  146.      *
  147.      * @param  string  The error message
  148.      * @param  string  The failing SQL query
  149.      *
  150.      * @access public
  151.      */
  152.     function PMA_SQP_bug($message, $sql)
  153.     {
  154.         global $SQP_errorString;
  155.         $debugstr = 'ERROR: ' . $message . "\n";
  156.         $debugstr .= 'SVN: $Id: sqlparser.lib.php 11326 2008-06-17 21:32:48Z lem9 $' . "\n";
  157.         $debugstr .= 'MySQL: '.PMA_MYSQL_STR_VERSION . "\n";
  158.         $debugstr .= 'USR OS, AGENT, VER: ' . PMA_USR_OS . ' ' . PMA_USR_BROWSER_AGENT . ' ' . PMA_USR_BROWSER_VER . "\n";
  159.         $debugstr .= 'PMA: ' . PMA_VERSION . "\n";
  160.         $debugstr .= 'PHP VER,OS: ' . PMA_PHP_STR_VERSION . ' ' . PHP_OS . "\n";
  161.         $debugstr .= 'LANG: ' . $GLOBALS['lang'] . "\n";
  162.         $debugstr .= 'SQL: ' . htmlspecialchars($sql);
  163.  
  164.         $encodedstr     = $debugstr;
  165.         if (@function_exists('gzcompress')) {
  166.             $encodedstr = gzcompress($debugstr, 9);
  167.         }
  168.         $encodedstr     = preg_replace("/(\015\012)|(\015)|(\012)/", '<br />' . "\n", chunk_split(base64_encode($encodedstr)));
  169.  
  170.         $SQP_errorString .= $GLOBALS['strSQLParserBugMessage'] . '<br />' . "\n"
  171.              . '----' . $GLOBALS['strBeginCut'] . '----' . '<br />' . "\n"
  172.              . $encodedstr . "\n"
  173.              . '----' . $GLOBALS['strEndCut'] . '----' . '<br />' . "\n";
  174.  
  175.         $SQP_errorString .= '----' . $GLOBALS['strBeginRaw'] . '----<br />' . "\n"
  176.              . '<pre>' . "\n"
  177.              . $debugstr
  178.              . '</pre>' . "\n"
  179.              . '----' . $GLOBALS['strEndRaw'] . '----<br />' . "\n";
  180.  
  181.     } // end of the "PMA_SQP_bug()" function
  182.  
  183.  
  184.     /**
  185.      * Parses the SQL queries
  186.      *
  187.      * @param  string   The SQL query list
  188.      *
  189.      * @return mixed    Most of times, nothing...
  190.      *
  191.      * @global array    The current PMA configuration
  192.      * @global array    MySQL column attributes
  193.      * @global array    MySQL reserved words
  194.      * @global array    MySQL column types
  195.      * @global array    MySQL function names
  196.      * @global integer  MySQL column attributes count
  197.      * @global integer  MySQL reserved words count
  198.      * @global integer  MySQL column types count
  199.      * @global integer  MySQL function names count
  200.      * @global array    List of available character sets
  201.      * @global array    List of available collations
  202.      * @global integer  Character sets count
  203.      * @global integer  Collations count
  204.      *
  205.      * @access public
  206.      */
  207.     function PMA_SQP_parse($sql)
  208.     {
  209.         global $cfg;
  210.         global $PMA_SQPdata_column_attrib, $PMA_SQPdata_reserved_word, $PMA_SQPdata_column_type, $PMA_SQPdata_function_name,
  211.                $PMA_SQPdata_column_attrib_cnt, $PMA_SQPdata_reserved_word_cnt, $PMA_SQPdata_column_type_cnt, $PMA_SQPdata_function_name_cnt;
  212.         global $mysql_charsets, $mysql_collations_flat, $mysql_charsets_count, $mysql_collations_count;
  213.         global $PMA_SQPdata_forbidden_word, $PMA_SQPdata_forbidden_word_cnt;
  214.  
  215.         // rabus: Convert all line feeds to Unix style
  216.         $sql = str_replace("\r\n", "\n", $sql);
  217.         $sql = str_replace("\r", "\n", $sql);
  218.  
  219.         $len = PMA_strlen($sql);
  220.         if ($len == 0) {
  221.             return array();
  222.         }
  223.  
  224.         $sql_array               = array();
  225.         $sql_array['raw']        = $sql;
  226.         $count1                  = 0;
  227.         $count2                  = 0;
  228.         $punct_queryend          = ';';
  229.         $punct_qualifier         = '.';
  230.         $punct_listsep           = ',';
  231.         $punct_level_plus        = '(';
  232.         $punct_level_minus       = ')';
  233.         $punct_user              = '@';
  234.         $digit_floatdecimal      = '.';
  235.         $digit_hexset            = 'x';
  236.         $bracket_list            = '()[]{}';
  237.         $allpunct_list           =  '-,;:!?/.^~\*&%+<=>|';
  238.         $allpunct_list_pair      = array (
  239.             0 => '!=',
  240.             1 => '&&',
  241.             2 => ':=',
  242.             3 => '<<',
  243.             4 => '<=',
  244.             5 => '<=>',
  245.             6 => '<>',
  246.             7 => '>=',
  247.             8 => '>>',
  248.             9 => '||'
  249.         );
  250.         $allpunct_list_pair_size = 10; //count($allpunct_list_pair);
  251.         $quote_list              = '\'"`';
  252.         $arraysize               = 0;
  253.  
  254.         $previous_was_space   = false;
  255.         $this_was_space       = false;
  256.         $previous_was_bracket = false;
  257.         $this_was_bracket     = false;
  258.         $previous_was_punct   = false;
  259.         $this_was_punct       = false;
  260.         $previous_was_listsep = false;
  261.         $this_was_listsep     = false;
  262.         $previous_was_quote   = false;
  263.         $this_was_quote       = false;
  264.  
  265.         while ($count2 < $len) {
  266.             $c      = PMA_substr($sql, $count2, 1);
  267.             $count1 = $count2;
  268.  
  269.             $previous_was_space = $this_was_space;
  270.             $this_was_space = false;
  271.             $previous_was_bracket = $this_was_bracket;
  272.             $this_was_bracket = false;
  273.             $previous_was_punct = $this_was_punct;
  274.             $this_was_punct = false;
  275.             $previous_was_listsep = $this_was_listsep;
  276.             $this_was_listsep = false;
  277.             $previous_was_quote = $this_was_quote;
  278.             $this_was_quote = false;
  279.  
  280.             if (($c == "\n")) {
  281.                 $this_was_space = true;
  282.                 $count2++;
  283.                 PMA_SQP_arrayAdd($sql_array, 'white_newline', '', $arraysize);
  284.                 continue;
  285.             }
  286.  
  287.             // Checks for white space
  288.             if (PMA_STR_isSpace($c)) {
  289.                 $this_was_space = true;
  290.                 $count2++;
  291.                 continue;
  292.             }
  293.  
  294.             // Checks for comment lines.
  295.             // MySQL style #
  296.             // C style /* */
  297.             // ANSI style --
  298.             if (($c == '#')
  299.                 || (($count2 + 1 < $len) && ($c == '/') && (PMA_substr($sql, $count2 + 1, 1) == '*'))
  300.                 || (($count2 + 2 == $len) && ($c == '-') && (PMA_substr($sql, $count2 + 1, 1) == '-'))
  301.                 || (($count2 + 2 < $len) && ($c == '-') && (PMA_substr($sql, $count2 + 1, 1) == '-') && ((PMA_substr($sql, $count2 + 2, 1) <= ' ')))) {
  302.                 $count2++;
  303.                 $pos  = 0;
  304.                 $type = 'bad';
  305.                 switch ($c) {
  306.                     case '#':
  307.                         $type = 'mysql';
  308.                     case '-':
  309.                         $type = 'ansi';
  310.                         $pos  = $GLOBALS['PMA_strpos']($sql, "\n", $count2);
  311.                         break;
  312.                     case '/':
  313.                         $type = 'c';
  314.                         $pos  = $GLOBALS['PMA_strpos']($sql, '*/', $count2);
  315.                         $pos  += 2;
  316.                         break;
  317.                     default:
  318.                         break;
  319.                 } // end switch
  320.                 $count2 = ($pos < $count2) ? $len : $pos;
  321.                 $str    = PMA_substr($sql, $count1, $count2 - $count1);
  322.                 PMA_SQP_arrayAdd($sql_array, 'comment_' . $type, $str, $arraysize);
  323.                 continue;
  324.             } // end if
  325.  
  326.             // Checks for something inside quotation marks
  327.             if (PMA_STR_strInStr($c, $quote_list)) {
  328.                 $startquotepos   = $count2;
  329.                 $quotetype       = $c;
  330.                 $count2++;
  331.                 $escaped         = FALSE;
  332.                 $escaped_escaped = FALSE;
  333.                 $pos             = $count2;
  334.                 $oldpos          = 0;
  335.                 do {
  336.                     $oldpos = $pos;
  337.                     $pos    = $GLOBALS['PMA_strpos'](' ' . $sql, $quotetype, $oldpos + 1) - 1;
  338.                     // ($pos === FALSE)
  339.                     if ($pos < 0) {
  340.                         $debugstr = $GLOBALS['strSQPBugUnclosedQuote'] . ' @ ' . $startquotepos. "\n"
  341.                                   . 'STR: ' . htmlspecialchars($quotetype);
  342.                         PMA_SQP_throwError($debugstr, $sql);
  343.                         return $sql;
  344.                     }
  345.  
  346.                     // If the quote is the first character, it can't be
  347.                     // escaped, so don't do the rest of the code
  348.                     if ($pos == 0) {
  349.                         break;
  350.                     }
  351.  
  352.                     // Checks for MySQL escaping using a \
  353.                     // And checks for ANSI escaping using the $quotetype character
  354.                     if (($pos < $len) && PMA_STR_charIsEscaped($sql, $pos)) {
  355.                         $pos ++;
  356.                         continue;
  357.                     } elseif (($pos + 1 < $len) && (PMA_substr($sql, $pos, 1) == $quotetype) && (PMA_substr($sql, $pos + 1, 1) == $quotetype)) {
  358.                         $pos = $pos + 2;
  359.                         continue;
  360.                     } else {
  361.                         break;
  362.                     }
  363.                 } while ($len > $pos); // end do
  364.  
  365.                 $count2       = $pos;
  366.                 $count2++;
  367.                 $type         = 'quote_';
  368.                 switch ($quotetype) {
  369.                     case '\'':
  370.                         $type .= 'single';
  371.                         $this_was_quote = true;
  372.                         break;
  373.                     case '"':
  374.                         $type .= 'double';
  375.                         $this_was_quote = true;
  376.                         break;
  377.                     case '`':
  378.                         $type .= 'backtick';
  379.                         $this_was_quote = true;
  380.                         break;
  381.                     default:
  382.                         break;
  383.                 } // end switch
  384.                 $data = PMA_substr($sql, $count1, $count2 - $count1);
  385.                 PMA_SQP_arrayAdd($sql_array, $type, $data, $arraysize);
  386.                 continue;
  387.             }
  388.  
  389.             // Checks for brackets
  390.             if (PMA_STR_strInStr($c, $bracket_list)) {
  391.                 // All bracket tokens are only one item long
  392.                 $this_was_bracket = true;
  393.                 $count2++;
  394.                 $type_type     = '';
  395.                 if (PMA_STR_strInStr($c, '([{')) {
  396.                     $type_type = 'open';
  397.                 } else {
  398.                     $type_type = 'close';
  399.                 }
  400.  
  401.                 $type_style     = '';
  402.                 if (PMA_STR_strInStr($c, '()')) {
  403.                     $type_style = 'round';
  404.                 } elseif (PMA_STR_strInStr($c, '[]')) {
  405.                     $type_style = 'square';
  406.                 } else {
  407.                     $type_style = 'curly';
  408.                 }
  409.  
  410.                 $type = 'punct_bracket_' . $type_type . '_' . $type_style;
  411.                 PMA_SQP_arrayAdd($sql_array, $type, $c, $arraysize);
  412.                 continue;
  413.             }
  414.  
  415.             /* DEBUG
  416.             echo '<pre>1';
  417.             var_dump(PMA_STR_isSqlIdentifier($c, false));
  418.             var_dump($c == '@');
  419.             var_dump($c == '.');
  420.             var_dump(PMA_STR_isDigit(PMA_substr($sql, $count2 + 1, 1)));
  421.             var_dump($previous_was_space);
  422.             var_dump($previous_was_bracket);
  423.             var_dump($previous_was_listsep);
  424.             echo '</pre>';
  425.             */
  426.  
  427.             // Checks for identifier (alpha or numeric)
  428.             if (PMA_STR_isSqlIdentifier($c, false)
  429.              || $c == '@'
  430.              || ($c == '.'
  431.               && PMA_STR_isDigit(PMA_substr($sql, $count2 + 1, 1))
  432.               && ($previous_was_space || $previous_was_bracket || $previous_was_listsep))) {
  433.  
  434.                 /* DEBUG
  435.                 echo PMA_substr($sql, $count2);
  436.                 echo '<hr />';
  437.                 */
  438.  
  439.                 $count2++;
  440.  
  441.                 /**
  442.                  * @todo a @ can also be present in expressions like
  443.                  * FROM 'user'@'%' or  TO 'user'@'%'
  444.                  * in this case, the @ is wrongly marked as alpha_variable
  445.                  */
  446.                 $is_identifier           = $previous_was_punct;
  447.                 $is_sql_variable         = $c == '@' && ! $previous_was_quote;
  448.                 $is_user                 = $c == '@' && $previous_was_quote;
  449.                 $is_digit                = !$is_identifier && !$is_sql_variable && PMA_STR_isDigit($c);
  450.                 $is_hex_digit            = $is_digit && $c == '0' && $count2 < $len && PMA_substr($sql, $count2, 1) == 'x';
  451.                 $is_float_digit          = $c == '.';
  452.                 $is_float_digit_exponent = FALSE;
  453.  
  454.                 /* DEBUG
  455.                 echo '<pre>2';
  456.                 var_dump($is_identifier);
  457.                 var_dump($is_sql_variable);
  458.                 var_dump($is_digit);
  459.                 var_dump($is_float_digit);
  460.                 echo '</pre>';
  461.                  */
  462.  
  463.                 // Nijel: Fast skip is especially needed for huge BLOB data, requires PHP at least 4.3.0:
  464.                 if (PMA_PHP_INT_VERSION >= 40300) {
  465.                     if ($is_hex_digit) {
  466.                         $count2++;
  467.                         $pos = strspn($sql, '0123456789abcdefABCDEF', $count2);
  468.                         if ($pos > $count2) {
  469.                             $count2 = $pos;
  470.                         }
  471.                         unset($pos);
  472.                     } elseif ($is_digit) {
  473.                         $pos = strspn($sql, '0123456789', $count2);
  474.                         if ($pos > $count2) {
  475.                             $count2 = $pos;
  476.                         }
  477.                         unset($pos);
  478.                     }
  479.                 }
  480.  
  481.                 while (($count2 < $len) && PMA_STR_isSqlIdentifier(PMA_substr($sql, $count2, 1), ($is_sql_variable || $is_digit))) {
  482.                     $c2 = PMA_substr($sql, $count2, 1);
  483.                     if ($is_sql_variable && ($c2 == '.')) {
  484.                         $count2++;
  485.                         continue;
  486.                     }
  487.                     if ($is_digit && (!$is_hex_digit) && ($c2 == '.')) {
  488.                         $count2++;
  489.                         if (!$is_float_digit) {
  490.                             $is_float_digit = TRUE;
  491.                             continue;
  492.                         } else {
  493.                             $debugstr = $GLOBALS['strSQPBugInvalidIdentifer'] . ' @ ' . ($count1+1) . "\n"
  494.                                       . 'STR: ' . htmlspecialchars(PMA_substr($sql, $count1, $count2 - $count1));
  495.                             PMA_SQP_throwError($debugstr, $sql);
  496.                             return $sql;
  497.                         }
  498.                     }
  499.                     if ($is_digit && (!$is_hex_digit) && (($c2 == 'e') || ($c2 == 'E'))) {
  500.                         if (!$is_float_digit_exponent) {
  501.                             $is_float_digit_exponent = TRUE;
  502.                             $is_float_digit          = TRUE;
  503.                             $count2++;
  504.                             continue;
  505.                         } else {
  506.                             $is_digit                = FALSE;
  507.                             $is_float_digit          = FALSE;
  508.                         }
  509.                     }
  510.                     if (($is_hex_digit && PMA_STR_isHexDigit($c2)) || ($is_digit && PMA_STR_isDigit($c2))) {
  511.                         $count2++;
  512.                         continue;
  513.                     } else {
  514.                         $is_digit     = FALSE;
  515.                         $is_hex_digit = FALSE;
  516.                     }
  517.  
  518.                     $count2++;
  519.                 } // end while
  520.  
  521.                 $l    = $count2 - $count1;
  522.                 $str  = PMA_substr($sql, $count1, $l);
  523.  
  524.                 $type = '';
  525.                 if ($is_digit || $is_float_digit || $is_hex_digit) {
  526.                     $type     = 'digit';
  527.                     if ($is_float_digit) {
  528.                         $type .= '_float';
  529.                     } elseif ($is_hex_digit) {
  530.                         $type .= '_hex';
  531.                     } else {
  532.                         $type .= '_integer';
  533.                     }
  534.                 } elseif ($is_user) {
  535.                     $type = 'punct_user';
  536.                 } elseif ($is_sql_variable != FALSE) {
  537.                     $type = 'alpha_variable';
  538.                 } else {
  539.                     $type = 'alpha';
  540.                 } // end if... else....
  541.                 PMA_SQP_arrayAdd($sql_array, $type, $str, $arraysize, $count2);
  542.  
  543.                 continue;
  544.             }
  545.  
  546.             // Checks for punct
  547.             if (PMA_STR_strInStr($c, $allpunct_list)) {
  548.                 while (($count2 < $len) && PMA_STR_strInStr(PMA_substr($sql, $count2, 1), $allpunct_list)) {
  549.                     $count2++;
  550.                 }
  551.                 $l = $count2 - $count1;
  552.                 if ($l == 1) {
  553.                     $punct_data = $c;
  554.                 } else {
  555.                     $punct_data = PMA_substr($sql, $count1, $l);
  556.                 }
  557.  
  558.                 // Special case, sometimes, althought two characters are
  559.                 // adjectent directly, they ACTUALLY need to be seperate
  560.                 /* DEBUG
  561.                 echo '<pre>';
  562.                 var_dump($l);
  563.                 var_dump($punct_data);
  564.                 echo '</pre>';
  565.                 */
  566.  
  567.                 if ($l == 1) {
  568.                     $t_suffix         = '';
  569.                     switch ($punct_data) {
  570.                         case $punct_queryend:
  571.                             $t_suffix = '_queryend';
  572.                             break;
  573.                         case $punct_qualifier:
  574.                             $t_suffix = '_qualifier';
  575.                             $this_was_punct = true;
  576.                             break;
  577.                         case $punct_listsep:
  578.                             $this_was_listsep = true;
  579.                             $t_suffix = '_listsep';
  580.                             break;
  581.                         default:
  582.                             break;
  583.                     }
  584.                     PMA_SQP_arrayAdd($sql_array, 'punct' . $t_suffix, $punct_data, $arraysize);
  585.                 } elseif (PMA_STR_binarySearchInArr($punct_data, $allpunct_list_pair, $allpunct_list_pair_size)) {
  586.                     // Ok, we have one of the valid combined punct expressions
  587.                     PMA_SQP_arrayAdd($sql_array, 'punct', $punct_data, $arraysize);
  588.                 } else {
  589.                     // Bad luck, lets split it up more
  590.                     $first  = $punct_data[0];
  591.                     $first2 = $punct_data[0] . $punct_data[1];
  592.                     $last2  = $punct_data[$l - 2] . $punct_data[$l - 1];
  593.                     $last   = $punct_data[$l - 1];
  594.                     if (($first == ',') || ($first == ';') || ($first == '.') || ($first == '*')) {
  595.                         $count2     = $count1 + 1;
  596.                         $punct_data = $first;
  597.                     } elseif (($last2 == '/*') || (($last2 == '--') && ($count2 == $len || PMA_substr($sql, $count2, 1) <= ' '))) {
  598.                         $count2     -= 2;
  599.                         $punct_data = PMA_substr($sql, $count1, $count2 - $count1);
  600.                     } elseif (($last == '-') || ($last == '+') || ($last == '!')) {
  601.                         $count2--;
  602.                         $punct_data = PMA_substr($sql, $count1, $count2 - $count1);
  603.                     /**
  604.                      * @todo for negation operator, split in 2 tokens ?
  605.                      * "select x&~1 from t"
  606.                      * becomes "select x & ~ 1 from t" ?
  607.                      */
  608.  
  609.                     } elseif ($last != '~') {
  610.                         $debugstr =  $GLOBALS['strSQPBugUnknownPunctuation'] . ' @ ' . ($count1+1) . "\n"
  611.                                   . 'STR: ' . htmlspecialchars($punct_data);
  612.                         PMA_SQP_throwError($debugstr, $sql);
  613.                         return $sql;
  614.                     }
  615.                     PMA_SQP_arrayAdd($sql_array, 'punct', $punct_data, $arraysize);
  616.                     continue;
  617.                 } // end if... elseif... else
  618.                 continue;
  619.             }
  620.  
  621.             // DEBUG
  622.             $count2++;
  623.  
  624.             $debugstr = 'C1 C2 LEN: ' . $count1 . ' ' . $count2 . ' ' . $len .  "\n"
  625.                       . 'STR: ' . PMA_substr($sql, $count1, $count2 - $count1) . "\n";
  626.             PMA_SQP_bug($debugstr, $sql);
  627.             return $sql;
  628.  
  629.         } // end while ($count2 < $len)
  630.  
  631.         /*
  632.         echo '<pre>';
  633.         print_r($sql_array);
  634.         echo '</pre>';
  635.         */
  636.  
  637.         if ($arraysize > 0) {
  638.             $t_next           = $sql_array[0]['type'];
  639.             $t_prev           = '';
  640.             $t_bef_prev       = '';
  641.             $t_cur            = '';
  642.             $d_next           = $sql_array[0]['data'];
  643.             $d_prev           = '';
  644.             $d_bef_prev       = '';
  645.             $d_cur            = '';
  646.             $d_next_upper     = $t_next == 'alpha' ? strtoupper($d_next) : $d_next;
  647.             $d_prev_upper     = '';
  648.             $d_bef_prev_upper = '';
  649.             $d_cur_upper      = '';
  650.         }
  651.  
  652.         for ($i = 0; $i < $arraysize; $i++) {
  653.             $t_bef_prev       = $t_prev;
  654.             $t_prev           = $t_cur;
  655.             $t_cur            = $t_next;
  656.             $d_bef_prev       = $d_prev;
  657.             $d_prev           = $d_cur;
  658.             $d_cur            = $d_next;
  659.             $d_bef_prev_upper = $d_prev_upper;
  660.             $d_prev_upper     = $d_cur_upper;
  661.             $d_cur_upper      = $d_next_upper;
  662.             if (($i + 1) < $arraysize) {
  663.                 $t_next = $sql_array[$i + 1]['type'];
  664.                 $d_next = $sql_array[$i + 1]['data'];
  665.                 $d_next_upper = $t_next == 'alpha' ? strtoupper($d_next) : $d_next;
  666.             } else {
  667.                 $t_next       = '';
  668.                 $d_next       = '';
  669.                 $d_next_upper = '';
  670.             }
  671.  
  672.             //DEBUG echo "[prev: <b>".$d_prev."</b> ".$t_prev."][cur: <b>".$d_cur."</b> ".$t_cur."][next: <b>".$d_next."</b> ".$t_next."]<br />";
  673.  
  674.             if ($t_cur == 'alpha') {
  675.                 $t_suffix     = '_identifier';
  676.                 if (($t_next == 'punct_qualifier') || ($t_prev == 'punct_qualifier')) {
  677.                     $t_suffix = '_identifier';
  678.                 } elseif (($t_next == 'punct_bracket_open_round')
  679.                   && PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_function_name, $PMA_SQPdata_function_name_cnt)) {
  680.                     /**
  681.                      * @todo 2005-10-16: in the case of a CREATE TABLE containing
  682.                      * a TIMESTAMP, since TIMESTAMP() is also a function, it's
  683.                      * found here and the token is wrongly marked as alpha_functionName.
  684.                      * But we compensate for this when analysing for timestamp_not_null
  685.                      * later in this script.
  686.                      *
  687.                      * Same applies to CHAR vs. CHAR() function.
  688.                      */
  689.                     $t_suffix = '_functionName';
  690.                     /* There are functions which might be as well column types */
  691.                     if (PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_column_type, $PMA_SQPdata_column_type_cnt)) {
  692.                     }
  693.                 } elseif (PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_column_type, $PMA_SQPdata_column_type_cnt)) {
  694.                     $t_suffix = '_columnType';
  695.  
  696.                     /**
  697.                      * Temporary fix for BUG #621357
  698.                      *
  699.                      * @todo FIX PROPERLY NEEDS OVERHAUL OF SQL TOKENIZER
  700.                      */
  701.                     if ($d_cur_upper == 'SET' && $t_next != 'punct_bracket_open_round') {
  702.                         $t_suffix = '_reservedWord';
  703.                     }
  704.                     //END OF TEMPORARY FIX
  705.  
  706.                     // CHARACTER is a synonym for CHAR, but can also be meant as
  707.                     // CHARACTER SET. In this case, we have a reserved word.
  708.                     if ($d_cur_upper == 'CHARACTER' && $d_next_upper == 'SET') {
  709.                         $t_suffix = '_reservedWord';
  710.                     }
  711.  
  712.                     // experimental
  713.                     // current is a column type, so previous must not be
  714.                     // a reserved word but an identifier
  715.                     // CREATE TABLE SG_Persons (first varchar(64))
  716.  
  717.                     //if ($sql_array[$i-1]['type'] =='alpha_reservedWord') {
  718.                     //    $sql_array[$i-1]['type'] = 'alpha_identifier';
  719.                     //}
  720.  
  721.                 } elseif (PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_reserved_word, $PMA_SQPdata_reserved_word_cnt)) {
  722.                     $t_suffix = '_reservedWord';
  723.                 } elseif (PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_column_attrib, $PMA_SQPdata_column_attrib_cnt)) {
  724.                     $t_suffix = '_columnAttrib';
  725.                     // INNODB is a MySQL table type, but in "SHOW INNODB STATUS",
  726.                     // it should be regarded as a reserved word.
  727.                     if ($d_cur_upper == 'INNODB' && $d_prev_upper == 'SHOW' && $d_next_upper == 'STATUS') {
  728.                         $t_suffix = '_reservedWord';
  729.                     }
  730.  
  731.                     if ($d_cur_upper == 'DEFAULT' && $d_next_upper == 'CHARACTER') {
  732.                         $t_suffix = '_reservedWord';
  733.                     }
  734.                     // Binary as character set
  735.                     if ($d_cur_upper == 'BINARY' && (
  736.                       ($d_bef_prev_upper == 'CHARACTER' && $d_prev_upper == 'SET')
  737.                       || ($d_bef_prev_upper == 'SET' && $d_prev_upper == '=')
  738.                       || ($d_bef_prev_upper == 'CHARSET' && $d_prev_upper == '=')
  739.                       || $d_prev_upper == 'CHARSET'
  740.                       ) && PMA_STR_binarySearchInArr($d_cur, $mysql_charsets, count($mysql_charsets))) {
  741.                         $t_suffix = '_charset';
  742.                     }
  743.                 } elseif (PMA_STR_binarySearchInArr($d_cur, $mysql_charsets, $mysql_charsets_count)
  744.                   || PMA_STR_binarySearchInArr($d_cur, $mysql_collations_flat, $mysql_collations_count)
  745.                   || ($d_cur{0} == '_' && PMA_STR_binarySearchInArr(substr($d_cur, 1), $mysql_charsets, $mysql_charsets_count))) {
  746.                     $t_suffix = '_charset';
  747.                 } else {
  748.                     // Do nothing
  749.                 }
  750.                 // check if present in the list of forbidden words
  751.                 if ($t_suffix == '_reservedWord' && PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_forbidden_word, $PMA_SQPdata_forbidden_word_cnt)) {
  752.                     $sql_array[$i]['forbidden'] = TRUE;
  753.                 } else {
  754.                     $sql_array[$i]['forbidden'] = FALSE;
  755.                 }
  756.                 $sql_array[$i]['type'] .= $t_suffix;
  757.             }
  758.         } // end for
  759.  
  760.         // Stores the size of the array inside the array, as count() is a slow
  761.         // operation.
  762.         $sql_array['len'] = $arraysize;
  763.  
  764.         // DEBUG echo 'After parsing<pre>'; print_r($sql_array); echo '</pre>';
  765.         // Sends the data back
  766.         return $sql_array;
  767.     } // end of the "PMA_SQP_parse()" function
  768.  
  769.    /**
  770.     * Checks for token types being what we want...
  771.     *
  772.     * @param  string String of type that we have
  773.     * @param  string String of type that we want
  774.     *
  775.     * @return boolean result of check
  776.     *
  777.     * @access private
  778.     */
  779.     function PMA_SQP_typeCheck($toCheck, $whatWeWant)
  780.     {
  781.         $typeSeperator = '_';
  782.         if (strcmp($whatWeWant, $toCheck) == 0) {
  783.             return TRUE;
  784.         } else {
  785.             if (strpos($whatWeWant, $typeSeperator) === FALSE) {
  786.                 return strncmp($whatWeWant, $toCheck, strpos($toCheck, $typeSeperator)) == 0;
  787.             } else {
  788.                 return FALSE;
  789.             }
  790.         }
  791.     }
  792.  
  793.  
  794.     /**
  795.      * Analyzes SQL queries
  796.      *
  797.      * @param  array   The SQL queries
  798.      *
  799.      * @return array   The analyzed SQL queries
  800.      *
  801.      * @access public
  802.      */
  803.     function PMA_SQP_analyze($arr)
  804.     {
  805.         if ($arr == array()) {
  806.             return array();
  807.         }
  808.         $result          = array();
  809.         $size            = $arr['len'];
  810.         $subresult       = array(
  811.             'querytype'      => '',
  812.             'select_expr_clause'=> '', // the whole stuff between SELECT and FROM , except DISTINCT
  813.             'position_of_first_select' => '', // the array index
  814.             'from_clause'=> '',
  815.             'group_by_clause'=> '',
  816.             'order_by_clause'=> '',
  817.             'having_clause'  => '',
  818.             'limit_clause'   => '',
  819.             'where_clause'   => '',
  820.             'where_clause_identifiers'   => array(),
  821.             'unsorted_query' => '',
  822.             'queryflags'     => array(),
  823.             'select_expr'    => array(),
  824.             'table_ref'      => array(),
  825.             'foreign_keys'   => array(),
  826.             'create_table_fields' => array()
  827.         );
  828.         $subresult_empty = $subresult;
  829.         $seek_queryend         = FALSE;
  830.         $seen_end_of_table_ref = FALSE;
  831.         $number_of_brackets_in_extract = 0;
  832.         $number_of_brackets_in_group_concat = 0;
  833.  
  834.         $number_of_brackets = 0;
  835.         $in_subquery = false;
  836.         $seen_subquery = false;
  837.  
  838.         // for SELECT EXTRACT(YEAR_MONTH FROM CURDATE())
  839.         // we must not use CURDATE as a table_ref
  840.         // so we track wether we are in the EXTRACT()
  841.         $in_extract          = FALSE;
  842.  
  843.         // for GROUP_CONCAT(...)
  844.         $in_group_concat     = FALSE;
  845.  
  846. /* Description of analyzer results by lem9
  847.  *
  848.  * db, table, column, alias
  849.  * ------------------------
  850.  *
  851.  * Inside the $subresult array, we create ['select_expr'] and ['table_ref'] arrays.
  852.  *
  853.  * The SELECT syntax (simplified) is
  854.  *
  855.  * SELECT
  856.  *    select_expression,...
  857.  *    [FROM [table_references]
  858.  *
  859.  *
  860.  * ['select_expr'] is filled with each expression, the key represents the
  861.  * expression position in the list (0-based) (so we don't lose track of
  862.  * multiple occurences of the same column).
  863.  *
  864.  * ['table_ref'] is filled with each table ref, same thing for the key.
  865.  *
  866.  * I create all sub-values empty, even if they are
  867.  * not present (for example no select_expression alias).
  868.  *
  869.  * There is a debug section at the end of loop #1, if you want to
  870.  * see the exact contents of select_expr and table_ref
  871.  *
  872.  * queryflags
  873.  * ----------
  874.  *
  875.  * In $subresult, array 'queryflags' is filled, according to what we
  876.  * find in the query.
  877.  *
  878.  * Currently, those are generated:
  879.  *
  880.  * ['queryflags']['need_confirm'] = 1; if the query needs confirmation
  881.  * ['queryflags']['select_from'] = 1;  if this is a real SELECT...FROM
  882.  * ['queryflags']['distinct'] = 1;     for a DISTINCT
  883.  * ['queryflags']['union'] = 1;        for a UNION
  884.  * ['queryflags']['join'] = 1;         for a JOIN
  885.  * ['queryflags']['offset'] = 1;       for the presence of OFFSET
  886.  * ['queryflags']['procedure'] = 1;    for the presence of PROCEDURE
  887.  *
  888.  * query clauses
  889.  * -------------
  890.  *
  891.  * The select is splitted in those clauses:
  892.  * ['select_expr_clause']
  893.  * ['from_clause']
  894.  * ['group_by_clause']
  895.  * ['order_by_clause']
  896.  * ['having_clause']
  897.  * ['where_clause']
  898.  * ['limit_clause']
  899.  *
  900.  * The identifiers of the WHERE clause are put into the array
  901.  * ['where_clause_identifier']
  902.  *
  903.  * For a SELECT, the whole query without the ORDER BY clause is put into
  904.  * ['unsorted_query']
  905.  *
  906.  * foreign keys
  907.  * ------------
  908.  * The CREATE TABLE may contain FOREIGN KEY clauses, so they get
  909.  * analyzed and ['foreign_keys'] is an array filled with
  910.  * the constraint name, the index list,
  911.  * the REFERENCES table name and REFERENCES index list,
  912.  * and ON UPDATE | ON DELETE clauses
  913.  *
  914.  * position_of_first_select
  915.  * ------------------------
  916.  *
  917.  * The array index of the first SELECT we find. Will be used to
  918.  * insert a SQL_CALC_FOUND_ROWS.
  919.  *
  920.  * create_table_fields
  921.  * -------------------
  922.  *
  923.  * For now, mostly used to detect the DEFAULT CURRENT_TIMESTAMP and
  924.  * ON UPDATE CURRENT_TIMESTAMP clauses of the CREATE TABLE query.
  925.  * An array, each element is the identifier name.
  926.  * Note that for now, the timestamp_not_null element is created
  927.  * even for non-TIMESTAMP fields.
  928.  *
  929.  * Sub-elements: ['type'] which contains the column type
  930.  *               optional (currently they are never false but can be absent):
  931.  *               ['default_current_timestamp'] boolean
  932.  *               ['on_update_current_timestamp'] boolean
  933.  *               ['timestamp_not_null'] boolean
  934.  *
  935.  * section_before_limit, section_after_limit
  936.  * -----------------------------------------
  937.  *
  938.  * Marks the point of the query where we can insert a LIMIT clause;
  939.  * so the section_before_limit will contain the left part before
  940.  * a possible LIMIT clause
  941.  *
  942.  *
  943.  * End of description of analyzer results
  944.  */
  945.  
  946.         // must be sorted
  947.         // TODO: current logic checks for only one word, so I put only the
  948.         // first word of the reserved expressions that end a table ref;
  949.         // maybe this is not ok (the first word might mean something else)
  950. //        $words_ending_table_ref = array(
  951. //            'FOR UPDATE',
  952. //            'GROUP BY',
  953. //            'HAVING',
  954. //            'LIMIT',
  955. //            'LOCK IN SHARE MODE',
  956. //            'ORDER BY',
  957. //            'PROCEDURE',
  958. //            'UNION',
  959. //            'WHERE'
  960. //        );
  961.         $words_ending_table_ref = array(
  962.             'FOR',
  963.             'GROUP',
  964.             'HAVING',
  965.             'LIMIT',
  966.             'LOCK',
  967.             'ORDER',
  968.             'PROCEDURE',
  969.             'UNION',
  970.             'WHERE'
  971.         );
  972.         $words_ending_table_ref_cnt = 9; //count($words_ending_table_ref);
  973.  
  974.         $words_ending_clauses = array(
  975.             'FOR',
  976.             'LIMIT',
  977.             'LOCK',
  978.             'PROCEDURE',
  979.             'UNION'
  980.         );
  981.         $words_ending_clauses_cnt = 5; //count($words_ending_clauses);
  982.  
  983.  
  984.  
  985.  
  986.         // must be sorted
  987.         $supported_query_types = array(
  988.             'SELECT'
  989.             /*
  990.             // Support for these additional query types will come later on.
  991.             'DELETE',
  992.             'INSERT',
  993.             'REPLACE',
  994.             'TRUNCATE',
  995.             'UPDATE'
  996.             'EXPLAIN',
  997.             'DESCRIBE',
  998.             'SHOW',
  999.             'CREATE',
  1000.             'SET',
  1001.             'ALTER'
  1002.             */
  1003.         );
  1004.         $supported_query_types_cnt = count($supported_query_types);
  1005.  
  1006.         // loop #1 for each token: select_expr, table_ref for SELECT
  1007.  
  1008.         for ($i = 0; $i < $size; $i++) {
  1009. //DEBUG echo "Loop1 <b>"  . $arr[$i]['data'] . "</b> (" . $arr[$i]['type'] . ")<br />";
  1010.  
  1011.             // High speed seek for locating the end of the current query
  1012.             if ($seek_queryend == TRUE) {
  1013.                 if ($arr[$i]['type'] == 'punct_queryend') {
  1014.                     $seek_queryend = FALSE;
  1015.                 } else {
  1016.                     continue;
  1017.                 } // end if (type == punct_queryend)
  1018.             } // end if ($seek_queryend)
  1019.  
  1020.             /**
  1021.              * Note: do not split if this is a punct_queryend for the first and only query
  1022.              * @todo when we find a UNION, should we split in another subresult?
  1023.              */
  1024.             if ($arr[$i]['type'] == 'punct_queryend' && ($i + 1 != $size)) {
  1025.                 $result[]  = $subresult;
  1026.                 $subresult = $subresult_empty;
  1027.                 continue;
  1028.             } // end if (type == punct_queryend)
  1029.  
  1030. // ==============================================================
  1031.             if ($arr[$i]['type'] == 'punct_bracket_open_round') {
  1032.                 $number_of_brackets++;
  1033.                 if ($in_extract) {
  1034.                     $number_of_brackets_in_extract++;
  1035.                 }
  1036.                 if ($in_group_concat) {
  1037.                     $number_of_brackets_in_group_concat++;
  1038.                 }
  1039.             }
  1040. // ==============================================================
  1041.             if ($arr[$i]['type'] == 'punct_bracket_close_round') {
  1042.                 $number_of_brackets--;
  1043.                 if ($number_of_brackets == 0) {
  1044.                     $in_subquery = false;
  1045.                 }
  1046.                 if ($in_extract) {
  1047.                     $number_of_brackets_in_extract--;
  1048.                     if ($number_of_brackets_in_extract == 0) {
  1049.                        $in_extract = FALSE;
  1050.                     }
  1051.                 }
  1052.                 if ($in_group_concat) {
  1053.                     $number_of_brackets_in_group_concat--;
  1054.                     if ($number_of_brackets_in_group_concat == 0) {
  1055.                        $in_group_concat = FALSE;
  1056.                     }
  1057.                 }
  1058.             }
  1059.  
  1060.             if ($in_subquery) {
  1061.                 /**
  1062.                  * skip the subquery to avoid setting
  1063.                  * select_expr or table_ref with the contents
  1064.                  * of this subquery; this is to avoid a bug when
  1065.                  * trying to edit the results of
  1066.                  * select * from child where not exists (select id from
  1067.                  * parent where child.parent_id = parent.id);
  1068.                  */
  1069.                 continue;
  1070.             }
  1071. // ==============================================================
  1072.             if ($arr[$i]['type'] == 'alpha_functionName') {
  1073.                 $upper_data = strtoupper($arr[$i]['data']);
  1074.                 if ($upper_data =='EXTRACT') {
  1075.                     $in_extract = TRUE;
  1076.                     $number_of_brackets_in_extract = 0;
  1077.                 }
  1078.                 if ($upper_data =='GROUP_CONCAT') {
  1079.                     $in_group_concat = TRUE;
  1080.                     $number_of_brackets_in_group_concat = 0;
  1081.                 }
  1082.             }
  1083.  
  1084. // ==============================================================
  1085.             if ($arr[$i]['type'] == 'alpha_reservedWord'
  1086. //             && $arr[$i]['forbidden'] == FALSE) {
  1087.             ) {
  1088.                 // We don't know what type of query yet, so run this
  1089.                 if ($subresult['querytype'] == '') {
  1090.                     $subresult['querytype'] = strtoupper($arr[$i]['data']);
  1091.                 } // end if (querytype was empty)
  1092.  
  1093.                 // Check if we support this type of query
  1094.                 if (!PMA_STR_binarySearchInArr($subresult['querytype'], $supported_query_types, $supported_query_types_cnt)) {
  1095.                     // Skip ahead to the next one if we don't
  1096.                     $seek_queryend = TRUE;
  1097.                     continue;
  1098.                 } // end if (query not supported)
  1099.  
  1100.                 // upper once
  1101.                 $upper_data = strtoupper($arr[$i]['data']);
  1102.                 /**
  1103.                  * @todo reset for each query?
  1104.                  */
  1105.  
  1106.                 if ($upper_data == 'SELECT') {
  1107.                     if ($number_of_brackets > 0) {
  1108.                         $in_subquery = true;
  1109.                         $seen_subquery = true;
  1110.                         // this is a subquery so do not analyze inside it
  1111.                         continue;
  1112.                     }
  1113.                     $seen_from = FALSE;
  1114.                     $previous_was_identifier = FALSE;
  1115.                     $current_select_expr = -1;
  1116.                     $seen_end_of_table_ref = FALSE;
  1117.                 } // end if (data == SELECT)
  1118.  
  1119.                 if ($upper_data =='FROM' && !$in_extract) {
  1120.                     $current_table_ref = -1;
  1121.                     $seen_from = TRUE;
  1122.                     $previous_was_identifier = FALSE;
  1123.                     $save_table_ref = TRUE;
  1124.                 } // end if (data == FROM)
  1125.  
  1126.                 // here, do not 'continue' the loop, as we have more work for
  1127.                 // reserved words below
  1128.             } // end if (type == alpha_reservedWord)
  1129.  
  1130. // ==============================
  1131.             if ($arr[$i]['type'] == 'quote_backtick'
  1132.              || $arr[$i]['type'] == 'quote_double'
  1133.              || $arr[$i]['type'] == 'quote_single'
  1134.              || $arr[$i]['type'] == 'alpha_identifier'
  1135.              || ($arr[$i]['type'] == 'alpha_reservedWord'
  1136.                 && $arr[$i]['forbidden'] == FALSE)) {
  1137.  
  1138.                 switch ($arr[$i]['type']) {
  1139.                     case 'alpha_identifier':
  1140.                     case 'alpha_reservedWord':
  1141.                         /**
  1142.                          * this is not a real reservedWord, because it's not
  1143.                          * present in the list of forbidden words, for example
  1144.                          * "storage" which can be used as an identifier
  1145.                          *
  1146.                          * @todo avoid the pretty printing in color in this case
  1147.                          */
  1148.                         $identifier = $arr[$i]['data'];
  1149.                         break;
  1150.  
  1151.                     case 'quote_backtick':
  1152.                     case 'quote_double':
  1153.                     case 'quote_single':
  1154.                         $identifier = PMA_unQuote($arr[$i]['data']);
  1155.                         break;
  1156.                 } // end switch
  1157.  
  1158.                 if ($subresult['querytype'] == 'SELECT' 
  1159.                  && ! $in_group_concat
  1160.                  && ! ($seen_subquery && $arr[$i - 1]['type'] == 'punct_bracket_close_round')) {
  1161.                     if (!$seen_from) {
  1162.                         if ($previous_was_identifier && isset($chain)) {
  1163.                             // found alias for this select_expr, save it
  1164.                             // but only if we got something in $chain
  1165.                             // (for example, SELECT COUNT(*) AS cnt
  1166.                             // puts nothing in $chain, so we avoid
  1167.                             // setting the alias)
  1168.                             $alias_for_select_expr = $identifier;
  1169.                         } else {
  1170.                             $chain[] = $identifier;
  1171.                             $previous_was_identifier = TRUE;
  1172.  
  1173.                         } // end if !$previous_was_identifier
  1174.                     } else {
  1175.                         // ($seen_from)
  1176.                         if ($save_table_ref && !$seen_end_of_table_ref) {
  1177.                             if ($previous_was_identifier) {
  1178.                                 // found alias for table ref
  1179.                                 // save it for later
  1180.                                 $alias_for_table_ref = $identifier;
  1181.                             } else {
  1182.                                 $chain[] = $identifier;
  1183.                                 $previous_was_identifier = TRUE;
  1184.  
  1185.                             } // end if ($previous_was_identifier)
  1186.                         } // end if ($save_table_ref &&!$seen_end_of_table_ref)
  1187.                     } // end if (!$seen_from)
  1188.                 } // end if (querytype SELECT)
  1189.             } // end if (quote_backtick or double quote or alpha_identifier)
  1190.  
  1191. // ===================================
  1192.             if ($arr[$i]['type'] == 'punct_qualifier') {
  1193.                 // to be able to detect an identifier following another
  1194.                 $previous_was_identifier = FALSE;
  1195.                 continue;
  1196.             } // end if (punct_qualifier)
  1197.  
  1198.             /**
  1199.              * @todo check if 3 identifiers following one another -> error
  1200.              */
  1201.  
  1202.             //    s a v e    a    s e l e c t    e x p r
  1203.             // finding a list separator or FROM
  1204.             // means that we must save the current chain of identifiers
  1205.             // into a select expression
  1206.  
  1207.             // for now, we only save a select expression if it contains
  1208.             // at least one identifier, as we are interested in checking
  1209.             // the columns and table names, so in "select * from persons",
  1210.             // the "*" is not saved
  1211.  
  1212.             if (isset($chain) && !$seen_end_of_table_ref
  1213.              && ((!$seen_from && $arr[$i]['type'] == 'punct_listsep')
  1214.               || ($arr[$i]['type'] == 'alpha_reservedWord' && $upper_data == 'FROM'))) {
  1215.                 $size_chain = count($chain);
  1216.                 $current_select_expr++;
  1217.                 $subresult['select_expr'][$current_select_expr] = array(
  1218.                   'expr' => '',
  1219.                   'alias' => '',
  1220.                   'db'   => '',
  1221.                   'table_name' => '',
  1222.                   'table_true_name' => '',
  1223.                   'column' => ''
  1224.                  );
  1225.  
  1226.                 if (isset($alias_for_select_expr) && strlen($alias_for_select_expr)) {
  1227.                     // we had found an alias for this select expression
  1228.                     $subresult['select_expr'][$current_select_expr]['alias'] = $alias_for_select_expr;
  1229.                     unset($alias_for_select_expr);
  1230.                 }
  1231.                 // there is at least a column
  1232.                 $subresult['select_expr'][$current_select_expr]['column'] = $chain[$size_chain - 1];
  1233.                 $subresult['select_expr'][$current_select_expr]['expr'] = $chain[$size_chain - 1];
  1234.  
  1235.                 // maybe a table
  1236.                 if ($size_chain > 1) {
  1237.                     $subresult['select_expr'][$current_select_expr]['table_name'] = $chain[$size_chain - 2];
  1238.                     // we assume for now that this is also the true name
  1239.                     $subresult['select_expr'][$current_select_expr]['table_true_name'] = $chain[$size_chain - 2];
  1240.                     $subresult['select_expr'][$current_select_expr]['expr']
  1241.                      = $subresult['select_expr'][$current_select_expr]['table_name']
  1242.                       . '.' . $subresult['select_expr'][$current_select_expr]['expr'];
  1243.                 } // end if ($size_chain > 1)
  1244.  
  1245.                 // maybe a db
  1246.                 if ($size_chain > 2) {
  1247.                     $subresult['select_expr'][$current_select_expr]['db'] = $chain[$size_chain - 3];
  1248.                     $subresult['select_expr'][$current_select_expr]['expr']
  1249.                      = $subresult['select_expr'][$current_select_expr]['db']
  1250.                       . '.' . $subresult['select_expr'][$current_select_expr]['expr'];
  1251.                 } // end if ($size_chain > 2)
  1252.                 unset($chain);
  1253.  
  1254.                 /**
  1255.                  * @todo explain this:
  1256.                  */
  1257.                 if (($arr[$i]['type'] == 'alpha_reservedWord')
  1258.                  && ($upper_data != 'FROM')) {
  1259.                     $previous_was_identifier = TRUE;
  1260.                 }
  1261.  
  1262.             } // end if (save a select expr)
  1263.  
  1264.  
  1265.             //======================================
  1266.             //    s a v e    a    t a b l e    r e f
  1267.             //======================================
  1268.  
  1269.             // maybe we just saw the end of table refs
  1270.             // but the last table ref has to be saved
  1271.             // or we are at the last token
  1272.             // or we just got a reserved word
  1273.             /**
  1274.              * @todo there could be another query after this one
  1275.              */
  1276.  
  1277.             if (isset($chain) && $seen_from && $save_table_ref
  1278.              && ($arr[$i]['type'] == 'punct_listsep'
  1279.                || ($arr[$i]['type'] == 'alpha_reservedWord' && $upper_data!="AS")
  1280.                || $seen_end_of_table_ref
  1281.                || $i==$size-1)) {
  1282.  
  1283.                 $size_chain = count($chain);
  1284.                 $current_table_ref++;
  1285.                 $subresult['table_ref'][$current_table_ref] = array(
  1286.                   'expr'            => '',
  1287.                   'db'              => '',
  1288.                   'table_name'      => '',
  1289.                   'table_alias'     => '',
  1290.                   'table_true_name' => ''
  1291.                  );
  1292.                 if (isset($alias_for_table_ref) && strlen($alias_for_table_ref)) {
  1293.                     $subresult['table_ref'][$current_table_ref]['table_alias'] = $alias_for_table_ref;
  1294.                     unset($alias_for_table_ref);
  1295.                 }
  1296.                 $subresult['table_ref'][$current_table_ref]['table_name'] = $chain[$size_chain - 1];
  1297.                 // we assume for now that this is also the true name
  1298.                 $subresult['table_ref'][$current_table_ref]['table_true_name'] = $chain[$size_chain - 1];
  1299.                 $subresult['table_ref'][$current_table_ref]['expr']
  1300.                      = $subresult['table_ref'][$current_table_ref]['table_name'];
  1301.                 // maybe a db
  1302.                 if ($size_chain > 1) {
  1303.                     $subresult['table_ref'][$current_table_ref]['db'] = $chain[$size_chain - 2];
  1304.                     $subresult['table_ref'][$current_table_ref]['expr']
  1305.                      = $subresult['table_ref'][$current_table_ref]['db']
  1306.                       . '.' . $subresult['table_ref'][$current_table_ref]['expr'];
  1307.                 } // end if ($size_chain > 1)
  1308.  
  1309.                 // add the table alias into the whole expression
  1310.                 $subresult['table_ref'][$current_table_ref]['expr']
  1311.                  .= ' ' . $subresult['table_ref'][$current_table_ref]['table_alias'];
  1312.  
  1313.                 unset($chain);
  1314.                 $previous_was_identifier = TRUE;
  1315.                 //continue;
  1316.  
  1317.             } // end if (save a table ref)
  1318.  
  1319.  
  1320.             // when we have found all table refs,
  1321.             // for each table_ref alias, put the true name of the table
  1322.             // in the corresponding select expressions
  1323.  
  1324.             if (isset($current_table_ref) && ($seen_end_of_table_ref || $i == $size-1) && $subresult != $subresult_empty) {
  1325.                 for ($tr=0; $tr <= $current_table_ref; $tr++) {
  1326.                     $alias = $subresult['table_ref'][$tr]['table_alias'];
  1327.                     $truename = $subresult['table_ref'][$tr]['table_true_name'];
  1328.                     for ($se=0; $se <= $current_select_expr; $se++) {
  1329.                         if (isset($alias) && strlen($alias) && $subresult['select_expr'][$se]['table_true_name']
  1330.                            == $alias) {
  1331.                             $subresult['select_expr'][$se]['table_true_name']
  1332.                              = $truename;
  1333.                         } // end if (found the alias)
  1334.                     } // end for (select expressions)
  1335.  
  1336.                 } // end for (table refs)
  1337.             } // end if (set the true names)
  1338.  
  1339.  
  1340.             // e n d i n g    l o o p  #1
  1341.             // set the $previous_was_identifier to FALSE if the current
  1342.             // token is not an identifier
  1343.             if (($arr[$i]['type'] != 'alpha_identifier')
  1344.              && ($arr[$i]['type'] != 'quote_double')
  1345.              && ($arr[$i]['type'] != 'quote_single')
  1346.              && ($arr[$i]['type'] != 'quote_backtick')) {
  1347.                 $previous_was_identifier = FALSE;
  1348.             } // end if
  1349.  
  1350.             // however, if we are on AS, we must keep the $previous_was_identifier
  1351.             if (($arr[$i]['type'] == 'alpha_reservedWord')
  1352.              && ($upper_data == 'AS'))  {
  1353.                 $previous_was_identifier = TRUE;
  1354.             }
  1355.  
  1356.             if (($arr[$i]['type'] == 'alpha_reservedWord')
  1357.              && ($upper_data =='ON' || $upper_data =='USING')) {
  1358.                 $save_table_ref = FALSE;
  1359.             } // end if (data == ON)
  1360.  
  1361.             if (($arr[$i]['type'] == 'alpha_reservedWord')
  1362.              && ($upper_data =='JOIN' || $upper_data =='FROM')) {
  1363.                 $save_table_ref = TRUE;
  1364.             } // end if (data == JOIN)
  1365.  
  1366.             /**
  1367.              * no need to check the end of table ref if we already did
  1368.              *
  1369.              * @todo maybe add "&& $seen_from"
  1370.              */
  1371.             if (!$seen_end_of_table_ref) {
  1372.                 // if this is the last token, it implies that we have
  1373.                 // seen the end of table references
  1374.                 // Check for the end of table references
  1375.                 //
  1376.                 // Note: if we are analyzing a GROUP_CONCAT clause,
  1377.                 // we might find a word that seems to indicate that
  1378.                 // we have found the end of table refs (like ORDER)
  1379.                 // but it's a modifier of the GROUP_CONCAT so
  1380.                 // it's not the real end of table refs
  1381.                 if (($i == $size-1)
  1382.                  || ($arr[$i]['type'] == 'alpha_reservedWord'
  1383.                  && !$in_group_concat
  1384.                  && PMA_STR_binarySearchInArr($upper_data, $words_ending_table_ref, $words_ending_table_ref_cnt))) {
  1385.                     $seen_end_of_table_ref = TRUE;
  1386.                     // to be able to save the last table ref, but do not
  1387.                     // set it true if we found a word like "ON" that has
  1388.                     // already set it to false
  1389.                     if (isset($save_table_ref) && $save_table_ref != FALSE) {
  1390.                         $save_table_ref = TRUE;
  1391.                     } //end if
  1392.  
  1393.                 } // end if (check for end of table ref)
  1394.             } //end if (!$seen_end_of_table_ref)
  1395.  
  1396.             if ($seen_end_of_table_ref) {
  1397.                 $save_table_ref = FALSE;
  1398.             } // end if
  1399.  
  1400.         } // end for $i (loop #1)
  1401.  
  1402.         //DEBUG
  1403.         /*
  1404.           if (isset($current_select_expr)) {
  1405.            for ($trace=0; $trace<=$current_select_expr; $trace++) {
  1406.                echo "<br />";
  1407.                reset ($subresult['select_expr'][$trace]);
  1408.                while (list ($key, $val) = each ($subresult['select_expr'][$trace]))
  1409.                    echo "sel expr $trace $key => $val<br />\n";
  1410.                }
  1411.           }
  1412.  
  1413.           if (isset($current_table_ref)) {
  1414.            echo "current_table_ref = " . $current_table_ref . "<br>";
  1415.            for ($trace=0; $trace<=$current_table_ref; $trace++) {
  1416.  
  1417.                echo "<br />";
  1418.                reset ($subresult['table_ref'][$trace]);
  1419.                while (list ($key, $val) = each ($subresult['table_ref'][$trace]))
  1420.                echo "table ref $trace $key => $val<br />\n";
  1421.                }
  1422.           }
  1423.         */
  1424.         // -------------------------------------------------------
  1425.  
  1426.  
  1427.         // loop #2: - queryflags
  1428.         //          - querytype (for queries != 'SELECT')
  1429.         //          - section_before_limit, section_after_limit
  1430.         //
  1431.         // we will also need this queryflag in loop 2
  1432.         // so set it here
  1433.         if (isset($current_table_ref) && $current_table_ref > -1) {
  1434.             $subresult['queryflags']['select_from'] = 1;
  1435.         }
  1436.  
  1437.         $section_before_limit = '';
  1438.         $section_after_limit = ''; // truly the section after the limit clause
  1439.         $seen_reserved_word = FALSE;
  1440.         $seen_group = FALSE;
  1441.         $seen_order = FALSE;
  1442.         $seen_order_by = FALSE;
  1443.         $in_group_by = FALSE; // true when we are inside the GROUP BY clause
  1444.         $in_order_by = FALSE; // true when we are inside the ORDER BY clause
  1445.         $in_having = FALSE; // true when we are inside the HAVING clause
  1446.         $in_select_expr = FALSE; // true when we are inside the select expr clause
  1447.         $in_where = FALSE; // true when we are inside the WHERE clause
  1448.         $seen_limit = FALSE; // true if we have seen a LIMIT clause
  1449.         $in_limit = FALSE; // true when we are inside the LIMIT clause
  1450.         $after_limit = FALSE; // true when we are after the LIMIT clause
  1451.         $in_from = FALSE; // true when we are in the FROM clause
  1452.         $in_group_concat = FALSE;
  1453.         $first_reserved_word = '';
  1454.         $current_identifier = '';
  1455.         $unsorted_query = $arr['raw']; // in case there is no ORDER BY
  1456.  
  1457.         for ($i = 0; $i < $size; $i++) {
  1458. //DEBUG echo "Loop2 <b>"  . $arr[$i]['data'] . "</b> (" . $arr[$i]['type'] . ")<br />";
  1459.  
  1460.             // need_confirm
  1461.             //
  1462.             // check for reserved words that will have to generate
  1463.             // a confirmation request later in sql.php
  1464.             // the cases are:
  1465.             //   DROP TABLE
  1466.             //   DROP DATABASE
  1467.             //   ALTER TABLE... DROP
  1468.             //   DELETE FROM...
  1469.             //
  1470.             // this code is not used for confirmations coming from functions.js
  1471.  
  1472.             if ($arr[$i]['type'] == 'alpha_reservedWord') {
  1473.                 $upper_data = strtoupper($arr[$i]['data']);
  1474.                 if (!$seen_reserved_word) {
  1475.                     $first_reserved_word = $upper_data;
  1476.                     $subresult['querytype'] = $upper_data;
  1477.                     $seen_reserved_word = TRUE;
  1478.  
  1479.                     // if the first reserved word is DROP or DELETE,
  1480.                     // we know this is a query that needs to be confirmed
  1481.                     if ($first_reserved_word=='DROP'
  1482.                      || $first_reserved_word == 'DELETE'
  1483.                      || $first_reserved_word == 'TRUNCATE') {
  1484.                         $subresult['queryflags']['need_confirm'] = 1;
  1485.                     }
  1486.  
  1487.                     if ($first_reserved_word=='SELECT'){
  1488.                         $position_of_first_select = $i;
  1489.                     }
  1490.  
  1491.                 } else {
  1492.                     if ($upper_data == 'DROP' && $first_reserved_word == 'ALTER') {
  1493.                         $subresult['queryflags']['need_confirm'] = 1;
  1494.                     }
  1495.                 }
  1496.  
  1497.                 if ($upper_data == 'LIMIT') {
  1498.                     $section_before_limit = substr($arr['raw'], 0, $arr[$i]['pos'] - 5);
  1499.                     $in_limit = TRUE;
  1500.                     $seen_limit = TRUE;
  1501.                     $limit_clause = '';
  1502.                     $in_order_by = FALSE; // @todo maybe others to set FALSE
  1503.                 }
  1504.  
  1505.                 if ($upper_data == 'PROCEDURE') {
  1506.                     $subresult['queryflags']['procedure'] = 1;
  1507.                     $in_limit = FALSE;
  1508.                     $after_limit = TRUE;
  1509.                 }
  1510.                 /**
  1511.                  * @todo set also to FALSE if we find FOR UPDATE or LOCK IN SHARE MODE
  1512.                  */
  1513.                 if ($upper_data == 'SELECT') {
  1514.                     $in_select_expr = TRUE;
  1515.                     $select_expr_clause = '';
  1516.                 }
  1517.                 if ($upper_data == 'DISTINCT' && !$in_group_concat) {
  1518.                     $subresult['queryflags']['distinct'] = 1;
  1519.                 }
  1520.  
  1521.                 if ($upper_data == 'UNION') {
  1522.                     $subresult['queryflags']['union'] = 1;
  1523.                 }
  1524.  
  1525.                 if ($upper_data == 'JOIN') {
  1526.                     $subresult['queryflags']['join'] = 1;
  1527.                 }
  1528.  
  1529.                 if ($upper_data == 'OFFSET') {
  1530.                     $subresult['queryflags']['offset'] = 1;
  1531.                 }
  1532.  
  1533.                 // if this is a real SELECT...FROM
  1534.                 if ($upper_data == 'FROM' && isset($subresult['queryflags']['select_from']) && $subresult['queryflags']['select_from'] == 1) {
  1535.                     $in_from = TRUE;
  1536.                     $from_clause = '';
  1537.                     $in_select_expr = FALSE;
  1538.                 }
  1539.  
  1540.  
  1541.                 // (we could have less resetting of variables to FALSE
  1542.                 // if we trust that the query respects the standard
  1543.                 // MySQL order for clauses)
  1544.  
  1545.                 // we use $seen_group and $seen_order because we are looking
  1546.                 // for the BY
  1547.                 if ($upper_data == 'GROUP') {
  1548.                     $seen_group = TRUE;
  1549.                     $seen_order = FALSE;
  1550.                     $in_having = FALSE;
  1551.                     $in_order_by = FALSE;
  1552.                     $in_where = FALSE;
  1553.                     $in_select_expr = FALSE;
  1554.                     $in_from = FALSE;
  1555.                 }
  1556.                 if ($upper_data == 'ORDER' && !$in_group_concat) {
  1557.                     $seen_order = TRUE;
  1558.                     $seen_group = FALSE;
  1559.                     $in_having = FALSE;
  1560.                     $in_group_by = FALSE;
  1561.                     $in_where = FALSE;
  1562.                     $in_select_expr = FALSE;
  1563.                     $in_from = FALSE;
  1564.                 }
  1565.                 if ($upper_data == 'HAVING') {
  1566.                     $in_having = TRUE;
  1567.                     $having_clause = '';
  1568.                     $seen_group = FALSE;
  1569.                     $seen_order = FALSE;
  1570.                     $in_group_by = FALSE;
  1571.                     $in_order_by = FALSE;
  1572.                     $in_where = FALSE;
  1573.                     $in_select_expr = FALSE;
  1574.                     $in_from = FALSE;
  1575.                 }
  1576.  
  1577.                 if ($upper_data == 'WHERE') {
  1578.                     $in_where = TRUE;
  1579.                     $where_clause = '';
  1580.                     $where_clause_identifiers = array();
  1581.                     $seen_group = FALSE;
  1582.                     $seen_order = FALSE;
  1583.                     $in_group_by = FALSE;
  1584.                     $in_order_by = FALSE;
  1585.                     $in_having = FALSE;
  1586.                     $in_select_expr = FALSE;
  1587.                     $in_from = FALSE;
  1588.                 }
  1589.  
  1590.                 if ($upper_data == 'BY') {
  1591.                     if ($seen_group) {
  1592.                         $in_group_by = TRUE;
  1593.                         $group_by_clause = '';
  1594.                     }
  1595.                     if ($seen_order) {
  1596.                         $seen_order_by = TRUE;
  1597.                         // here we assume that the ORDER BY keywords took
  1598.                         // exactly 8 characters
  1599.                         $unsorted_query = substr($arr['raw'], 0, $arr[$i]['pos'] - 8);
  1600.                         $in_order_by = TRUE;
  1601.                         $order_by_clause = '';
  1602.                     }
  1603.                 }
  1604.  
  1605.                 // if we find one of the words that could end the clause
  1606.                 if (PMA_STR_binarySearchInArr($upper_data, $words_ending_clauses, $words_ending_clauses_cnt)) {
  1607.  
  1608.                     $in_group_by = FALSE;
  1609.                     $in_order_by = FALSE;
  1610.                     $in_having   = FALSE;
  1611.                     $in_where    = FALSE;
  1612.                     $in_select_expr = FALSE;
  1613.                     $in_from = FALSE;
  1614.                 }
  1615.  
  1616.             } // endif (reservedWord)
  1617.  
  1618.  
  1619.             // do not add a space after a function name
  1620.             /**
  1621.              * @todo can we combine loop 2 and loop 1? some code is repeated here...
  1622.              */
  1623.  
  1624.             $sep = ' ';
  1625.             if ($arr[$i]['type'] == 'alpha_functionName') {
  1626.                 $sep='';
  1627.                 $upper_data = strtoupper($arr[$i]['data']);
  1628.                 if ($upper_data =='GROUP_CONCAT') {
  1629.                     $in_group_concat = TRUE;
  1630.                     $number_of_brackets_in_group_concat = 0;
  1631.                 }
  1632.             }
  1633.  
  1634.             if ($arr[$i]['type'] == 'punct_bracket_open_round') {
  1635.                 if ($in_group_concat) {
  1636.                     $number_of_brackets_in_group_concat++;
  1637.                 }
  1638.             }
  1639.             if ($arr[$i]['type'] == 'punct_bracket_close_round') {
  1640.                 if ($in_group_concat) {
  1641.                     $number_of_brackets_in_group_concat--;
  1642.                     if ($number_of_brackets_in_group_concat == 0) {
  1643.                         $in_group_concat = FALSE;
  1644.                     }
  1645.                 }
  1646.             }
  1647.  
  1648.             // do not add a space after an identifier if followed by a dot
  1649.             if ($arr[$i]['type'] == 'alpha_identifier' && $i < $size - 1 && $arr[$i + 1]['data'] == '.') {
  1650.                 $sep = '';
  1651.             }
  1652.  
  1653.             // do not add a space after a dot if followed by an identifier
  1654.             if ($arr[$i]['data'] == '.' && $i < $size - 1 && $arr[$i + 1]['type'] == 'alpha_identifier') {
  1655.                 $sep = '';
  1656.             }
  1657.  
  1658.             if ($in_select_expr && $upper_data != 'SELECT' && $upper_data != 'DISTINCT') {
  1659.                 $select_expr_clause .= $arr[$i]['data'] . $sep;
  1660.             }
  1661.             if ($in_from && $upper_data != 'FROM') {
  1662.                 $from_clause .= $arr[$i]['data'] . $sep;
  1663.             }
  1664.             if ($in_group_by && $upper_data != 'GROUP' && $upper_data != 'BY') {
  1665.                 $group_by_clause .= $arr[$i]['data'] . $sep;
  1666.             }
  1667.             if ($in_order_by && $upper_data != 'ORDER' && $upper_data != 'BY') {
  1668.                 // add a space only before ASC or DESC
  1669.                 // not around the dot between dbname and tablename
  1670.                 if ($arr[$i]['type'] == 'alpha_reservedWord') {
  1671.                     $order_by_clause .= $sep;
  1672.                 }
  1673.                 $order_by_clause .= $arr[$i]['data'];
  1674.             }
  1675.             if ($in_having && $upper_data != 'HAVING') {
  1676.                 $having_clause .= $arr[$i]['data'] . $sep;
  1677.             }
  1678.             if ($in_where && $upper_data != 'WHERE') {
  1679.                 $where_clause .= $arr[$i]['data'] . $sep;
  1680.  
  1681.                 if (($arr[$i]['type'] == 'quote_backtick')
  1682.                  || ($arr[$i]['type'] == 'alpha_identifier')) {
  1683.                     $where_clause_identifiers[] = $arr[$i]['data'];
  1684.                 }
  1685.             }
  1686.  
  1687.             // to grab the rest of the query after the ORDER BY clause
  1688.             if (isset($subresult['queryflags']['select_from'])
  1689.              && $subresult['queryflags']['select_from'] == 1
  1690.              && ! $in_order_by
  1691.              && $seen_order_by
  1692.              && $upper_data != 'BY') {
  1693.                 $unsorted_query .= $arr[$i]['data'];
  1694.                 if ($arr[$i]['type'] != 'punct_bracket_open_round'
  1695.                  && $arr[$i]['type'] != 'punct_bracket_close_round'
  1696.                  && $arr[$i]['type'] != 'punct') {
  1697.                     $unsorted_query .= $sep;
  1698.                 }
  1699.             }
  1700.             
  1701.             if ($in_limit) {
  1702.                 if ($upper_data == 'OFFSET') {
  1703.                     $limit_clause .= $sep;
  1704.                 }
  1705.                 $limit_clause .= $arr[$i]['data'];
  1706.                 if ($upper_data == 'LIMIT' || $upper_data == 'OFFSET') {
  1707.                     $limit_clause .= $sep;
  1708.                 }
  1709.             }
  1710.             if ($after_limit && $seen_limit) { 
  1711.                 $section_after_limit .= $arr[$i]['data'] . $sep;
  1712.             }
  1713.  
  1714.             // clear $upper_data for next iteration
  1715.             $upper_data='';
  1716.  
  1717.         } // end for $i (loop #2)
  1718.         if (empty($section_before_limit)) {
  1719.             $section_before_limit = $arr['raw'];
  1720.         }
  1721.  
  1722.         // -----------------------------------------------------
  1723.         // loop #3: foreign keys and MySQL 4.1.2+ TIMESTAMP options
  1724.         // (for now, check only the first query)
  1725.         // (for now, identifiers are assumed to be backquoted)
  1726.  
  1727.         // If we find that we are dealing with a CREATE TABLE query,
  1728.         // we look for the next punct_bracket_open_round, which
  1729.         // introduces the fields list. Then, when we find a
  1730.         // quote_backtick, it must be a field, so we put it into
  1731.         // the create_table_fields array. Even if this field is
  1732.         // not a timestamp, it will be useful when logic has been
  1733.         // added for complete field attributes analysis.
  1734.  
  1735.         $seen_foreign = FALSE;
  1736.         $seen_references = FALSE;
  1737.         $seen_constraint = FALSE;
  1738.         $foreign_key_number = -1;
  1739.         $seen_create_table = FALSE;
  1740.         $seen_create = FALSE;
  1741.         $in_create_table_fields = FALSE;
  1742.         $brackets_level = 0;
  1743.         $in_timestamp_options = FALSE;
  1744.         $seen_default = FALSE;
  1745.  
  1746.         for ($i = 0; $i < $size; $i++) {
  1747.         // DEBUG echo "Loop 3 <b>" . $arr[$i]['data'] . "</b> " . $arr[$i]['type'] . "<br />";
  1748.  
  1749.             if ($arr[$i]['type'] == 'alpha_reservedWord') {
  1750.                 $upper_data = strtoupper($arr[$i]['data']);
  1751.  
  1752.                 if ($upper_data == 'NOT' && $in_timestamp_options) {
  1753.                     $create_table_fields[$current_identifier]['timestamp_not_null'] = TRUE;
  1754.  
  1755.                 }
  1756.  
  1757.                 if ($upper_data == 'CREATE') {
  1758.                     $seen_create = TRUE;
  1759.                 }
  1760.  
  1761.                 if ($upper_data == 'TABLE' && $seen_create) {
  1762.                     $seen_create_table = TRUE;
  1763.                     $create_table_fields = array();
  1764.                 }
  1765.  
  1766.                 if ($upper_data == 'CURRENT_TIMESTAMP') {
  1767.                     if ($in_timestamp_options) {
  1768.                         if ($seen_default) {
  1769.                             $create_table_fields[$current_identifier]['default_current_timestamp'] = TRUE;
  1770.                         }
  1771.                     }
  1772.                 }
  1773.  
  1774.                 if ($upper_data == 'CONSTRAINT') {
  1775.                     $foreign_key_number++;
  1776.                     $seen_foreign = FALSE;
  1777.                     $seen_references = FALSE;
  1778.                     $seen_constraint = TRUE;
  1779.                 }
  1780.                 if ($upper_data == 'FOREIGN') {
  1781.                     $seen_foreign = TRUE;
  1782.                     $seen_references = FALSE;
  1783.                     $seen_constraint = FALSE;
  1784.                 }
  1785.                 if ($upper_data == 'REFERENCES') {
  1786.                     $seen_foreign = FALSE;
  1787.                     $seen_references = TRUE;
  1788.                     $seen_constraint = FALSE;
  1789.                 }
  1790.  
  1791.  
  1792.                 // Cases covered:
  1793.  
  1794.                 // [ON DELETE {CASCADE | SET NULL | NO ACTION | RESTRICT}]
  1795.                 // [ON UPDATE {CASCADE | SET NULL | NO ACTION | RESTRICT}]
  1796.  
  1797.                 // but we set ['on_delete'] or ['on_cascade'] to
  1798.                 // CASCADE | SET_NULL | NO_ACTION | RESTRICT
  1799.  
  1800.                 // ON UPDATE CURRENT_TIMESTAMP
  1801.  
  1802.                 if ($upper_data == 'ON') {
  1803.                     if (isset($arr[$i+1]) && $arr[$i+1]['type'] == 'alpha_reservedWord') {
  1804.                         $second_upper_data = strtoupper($arr[$i+1]['data']);
  1805.                         if ($second_upper_data == 'DELETE') {
  1806.                             $clause = 'on_delete';
  1807.                         }
  1808.                         if ($second_upper_data == 'UPDATE') {
  1809.                             $clause = 'on_update';
  1810.                         }
  1811.                         if (isset($clause)
  1812.                         && ($arr[$i+2]['type'] == 'alpha_reservedWord'
  1813.  
  1814.                 // ugly workaround because currently, NO is not
  1815.                 // in the list of reserved words in sqlparser.data
  1816.                 // (we got a bug report about not being able to use
  1817.                 // 'no' as an identifier)
  1818.                            || ($arr[$i+2]['type'] == 'alpha_identifier'
  1819.                               && strtoupper($arr[$i+2]['data'])=='NO'))
  1820.                           ) {
  1821.                             $third_upper_data = strtoupper($arr[$i+2]['data']);
  1822.                             if ($third_upper_data == 'CASCADE'
  1823.                             || $third_upper_data == 'RESTRICT') {
  1824.                                 $value = $third_upper_data;
  1825.                             } elseif ($third_upper_data == 'SET'
  1826.                               || $third_upper_data == 'NO') {
  1827.                                 if ($arr[$i+3]['type'] == 'alpha_reservedWord') {
  1828.                                     $value = $third_upper_data . '_' . strtoupper($arr[$i+3]['data']);
  1829.                                 }
  1830.                             } elseif ($third_upper_data == 'CURRENT_TIMESTAMP') {
  1831.                                 if ($clause == 'on_update'
  1832.                                 && $in_timestamp_options) {
  1833.                                     $create_table_fields[$current_identifier]['on_update_current_timestamp'] = TRUE;
  1834.                                     $seen_default = FALSE;
  1835.                                 }
  1836.  
  1837.                             } else {
  1838.                                 $value = '';
  1839.                             }
  1840.                             if (!empty($value)) {
  1841.                                 $foreign[$foreign_key_number][$clause] = $value;
  1842.                             }
  1843.                             unset($clause);
  1844.                         } // endif (isset($clause))
  1845.                     }
  1846.                 }
  1847.  
  1848.             } // end of reserved words analysis
  1849.  
  1850.  
  1851.             if ($arr[$i]['type'] == 'punct_bracket_open_round') {
  1852.                 $brackets_level++;
  1853.                 if ($seen_create_table && $brackets_level == 1) {
  1854.                     $in_create_table_fields = TRUE;
  1855.                 }
  1856.             }
  1857.  
  1858.  
  1859.             if ($arr[$i]['type'] == 'punct_bracket_close_round') {
  1860.                 $brackets_level--;
  1861.                 if ($seen_references) {
  1862.                     $seen_references = FALSE;
  1863.                 }
  1864.                 if ($seen_create_table && $brackets_level == 0) {
  1865.                     $in_create_table_fields = FALSE;
  1866.                 }
  1867.             }
  1868.  
  1869.             if (($arr[$i]['type'] == 'alpha_columnAttrib')) {
  1870.                 $upper_data = strtoupper($arr[$i]['data']);
  1871.                 if ($seen_create_table && $in_create_table_fields) {
  1872.                     if ($upper_data == 'DEFAULT') {
  1873.                         $seen_default = TRUE;
  1874.                     }
  1875.                 }
  1876.             }
  1877.  
  1878.             /**
  1879.              * @see @todo 2005-10-16 note: the "or" part here is a workaround for a bug
  1880.              */
  1881.             if (($arr[$i]['type'] == 'alpha_columnType') || ($arr[$i]['type'] == 'alpha_functionName' && $seen_create_table)) {
  1882.                 $upper_data = strtoupper($arr[$i]['data']);
  1883.                 if ($seen_create_table && $in_create_table_fields && isset($current_identifier)) {
  1884.                     $create_table_fields[$current_identifier]['type'] = $upper_data;
  1885.                     if ($upper_data == 'TIMESTAMP') {
  1886.                         $arr[$i]['type'] = 'alpha_columnType';
  1887.                         $in_timestamp_options = TRUE;
  1888.                     } else {
  1889.                         $in_timestamp_options = FALSE;
  1890.                         if ($upper_data == 'CHAR') {
  1891.                             $arr[$i]['type'] = 'alpha_columnType';
  1892.                         }
  1893.                     }
  1894.                 }
  1895.             }
  1896.  
  1897.  
  1898.             if ($arr[$i]['type'] == 'quote_backtick' || $arr[$i]['type'] == 'alpha_identifier') {
  1899.  
  1900.                 if ($arr[$i]['type'] == 'quote_backtick') {
  1901.                     // remove backquotes
  1902.                     $identifier = PMA_unQuote($arr[$i]['data']);
  1903.                 } else {
  1904.                     $identifier = $arr[$i]['data'];
  1905.                 }
  1906.  
  1907.                 if ($seen_create_table && $in_create_table_fields) {
  1908.                     $current_identifier = $identifier;
  1909.                     // warning: we set this one even for non TIMESTAMP type
  1910.                     $create_table_fields[$current_identifier]['timestamp_not_null'] = FALSE;
  1911.                 }
  1912.  
  1913.                 if ($seen_constraint) {
  1914.                     $foreign[$foreign_key_number]['constraint'] = $identifier;
  1915.                 }
  1916.  
  1917.                 if ($seen_foreign && $brackets_level > 0) {
  1918.                     $foreign[$foreign_key_number]['index_list'][] = $identifier;
  1919.                 }
  1920.  
  1921.                 if ($seen_references) {
  1922.                     // here, the first bracket level corresponds to the
  1923.                     // bracket of CREATE TABLE
  1924.                     // so if we are on level 2, it must be the index list
  1925.                     // of the foreign key REFERENCES
  1926.                     if ($brackets_level > 1) {
  1927.                         $foreign[$foreign_key_number]['ref_index_list'][] = $identifier;
  1928.                     } else {
  1929.                         // for MySQL 4.0.18, identifier is
  1930.                         // `table` or `db`.`table`
  1931.                         // the first pass will pick the db name
  1932.                         // the next pass will execute the else and pick the
  1933.                         // db name in $db_table[0]
  1934.                         if ($arr[$i+1]['type'] == 'punct_qualifier') {
  1935.                                 $foreign[$foreign_key_number]['ref_db_name'] = $identifier;
  1936.                         } else {
  1937.                         // for MySQL 4.0.16, identifier is
  1938.                         // `table` or `db.table`
  1939.                             $db_table = explode('.', $identifier);
  1940.                             if (isset($db_table[1])) {
  1941.                                 $foreign[$foreign_key_number]['ref_db_name'] = $db_table[0];
  1942.                                 $foreign[$foreign_key_number]['ref_table_name'] = $db_table[1];
  1943.                             } else {
  1944.                                 $foreign[$foreign_key_number]['ref_table_name'] = $db_table[0];
  1945.                             }
  1946.                         }
  1947.                     }
  1948.                 }
  1949.             }
  1950.         } // end for $i (loop #3)
  1951.  
  1952.  
  1953.         // Fill the $subresult array
  1954.  
  1955.         if (isset($create_table_fields)) {
  1956.             $subresult['create_table_fields'] = $create_table_fields;
  1957.         }
  1958.  
  1959.         if (isset($foreign)) {
  1960.             $subresult['foreign_keys'] = $foreign;
  1961.         }
  1962.  
  1963.         if (isset($select_expr_clause)) {
  1964.             $subresult['select_expr_clause'] = $select_expr_clause;
  1965.         }
  1966.         if (isset($from_clause)) {
  1967.             $subresult['from_clause'] = $from_clause;
  1968.         }
  1969.         if (isset($group_by_clause)) {
  1970.             $subresult['group_by_clause'] = $group_by_clause;
  1971.         }
  1972.         if (isset($order_by_clause)) {
  1973.             $subresult['order_by_clause'] = $order_by_clause;
  1974.         }
  1975.         if (isset($having_clause)) {
  1976.             $subresult['having_clause'] = $having_clause;
  1977.         }
  1978.         if (isset($limit_clause)) {
  1979.             $subresult['limit_clause'] = $limit_clause;
  1980.         }
  1981.         if (isset($where_clause)) {
  1982.             $subresult['where_clause'] = $where_clause;
  1983.         }
  1984.         if (isset($unsorted_query) && !empty($unsorted_query)) {
  1985.             $subresult['unsorted_query'] = $unsorted_query;
  1986.         }
  1987.         if (isset($where_clause_identifiers)) {
  1988.             $subresult['where_clause_identifiers'] = $where_clause_identifiers;
  1989.         }
  1990.  
  1991.         if (isset($position_of_first_select)) {
  1992.             $subresult['position_of_first_select'] = $position_of_first_select;
  1993.             $subresult['section_before_limit'] = $section_before_limit;
  1994.             $subresult['section_after_limit'] = $section_after_limit;
  1995.         }
  1996.  
  1997.         // They are naughty and didn't have a trailing semi-colon,
  1998.         // then still handle it properly
  1999.         if ($subresult['querytype'] != '') {
  2000.             $result[] = $subresult;
  2001.         }
  2002.         return $result;
  2003.     } // end of the "PMA_SQP_analyze()" function
  2004.  
  2005.  
  2006.     /**
  2007.      * Colorizes SQL queries html formatted
  2008.      *
  2009.      * @todo check why adding a "\n" after the </span> would cause extra blanks
  2010.      * to be displayed: SELECT p . person_name
  2011.      * @param  array   The SQL queries html formatted
  2012.      *
  2013.      * @return array   The colorized SQL queries
  2014.      *
  2015.      * @access public
  2016.      */
  2017.     function PMA_SQP_formatHtml_colorize($arr)
  2018.     {
  2019.         $i         = $GLOBALS['PMA_strpos']($arr['type'], '_');
  2020.         $class     = '';
  2021.         if ($i > 0) {
  2022.             $class = 'syntax_' . PMA_substr($arr['type'], 0, $i) . ' ';
  2023.         }
  2024.  
  2025.         $class     .= 'syntax_' . $arr['type'];
  2026.  
  2027.         return '<span class="' . $class . '">' . htmlspecialchars($arr['data']) . '</span>';
  2028.     } // end of the "PMA_SQP_formatHtml_colorize()" function
  2029.  
  2030.  
  2031.     /**
  2032.      * Formats SQL queries to html
  2033.      *
  2034.      * @param  array   The SQL queries
  2035.      * @param  string  mode
  2036.      * @param  integer starting token
  2037.      * @param  integer number of tokens to format, -1 = all
  2038.      *
  2039.      * @return string  The formatted SQL queries
  2040.      *
  2041.      * @access public
  2042.      */
  2043.     function PMA_SQP_formatHtml($arr, $mode='color', $start_token=0,
  2044.         $number_of_tokens=-1)
  2045.     {
  2046.         //DEBUG echo 'in Format<pre>'; print_r($arr); echo '</pre>';
  2047.         // then check for an array
  2048.         if (!is_array($arr)) {
  2049.             return htmlspecialchars($arr);
  2050.         }
  2051.         // first check for the SQL parser having hit an error
  2052.         if (PMA_SQP_isError()) {
  2053.             return htmlspecialchars($arr['raw']);
  2054.         }
  2055.         // else do it properly
  2056.         switch ($mode) {
  2057.             case 'color':
  2058.                 $str                                = '<span class="syntax">';
  2059.                 $html_line_break                    = '<br />';
  2060.                 break;
  2061.             case 'query_only':
  2062.                 $str                                = '';
  2063.                 $html_line_break                    = "\n";
  2064.                 break;
  2065.             case 'text':
  2066.                 $str                                = '';
  2067.                 $html_line_break                    = '<br />';
  2068.                 break;
  2069.         } // end switch
  2070.         $indent                                     = 0;
  2071.         $bracketlevel                               = 0;
  2072.         $functionlevel                              = 0;
  2073.         $infunction                                 = FALSE;
  2074.         $space_punct_listsep                        = ' ';
  2075.         $space_punct_listsep_function_name          = ' ';
  2076.         // $space_alpha_reserved_word = '<br />'."\n";
  2077.         $space_alpha_reserved_word                  = ' ';
  2078.  
  2079.         $keywords_with_brackets_1before            = array(
  2080.             'INDEX',
  2081.             'KEY',
  2082.             'ON',
  2083.             'USING'
  2084.         );
  2085.         $keywords_with_brackets_1before_cnt        = 4;
  2086.  
  2087.         $keywords_with_brackets_2before            = array(
  2088.             'IGNORE',
  2089.             'INDEX',
  2090.             'INTO',
  2091.             'KEY',
  2092.             'PRIMARY',
  2093.             'PROCEDURE',
  2094.             'REFERENCES',
  2095.             'UNIQUE',
  2096.             'USE'
  2097.         );
  2098.         // $keywords_with_brackets_2before_cnt = count($keywords_with_brackets_2before);
  2099.         $keywords_with_brackets_2before_cnt        = 9;
  2100.  
  2101.         // These reserved words do NOT get a newline placed near them.
  2102.         $keywords_no_newline               = array(
  2103.             'AS',
  2104.             'ASC',
  2105.             'DESC',
  2106.             'DISTINCT',
  2107.             'DUPLICATE',
  2108.             'HOUR',
  2109.             'INTERVAL',
  2110.             'IS',
  2111.             'LIKE',
  2112.             'NOT',
  2113.             'NULL',
  2114.             'ON',
  2115.             'REGEXP'
  2116.         );
  2117.         $keywords_no_newline_cnt           = 12;
  2118.  
  2119.         // These reserved words introduce a privilege list
  2120.         $keywords_priv_list                = array(
  2121.             'GRANT',
  2122.             'REVOKE'
  2123.         );
  2124.         $keywords_priv_list_cnt            = 2;
  2125.  
  2126.         if ($number_of_tokens == -1) {
  2127.             $arraysize = $arr['len'];
  2128.         } else {
  2129.             $arraysize = $number_of_tokens;
  2130.         }
  2131.         $typearr   = array();
  2132.         if ($arraysize >= 0) {
  2133.             $typearr[0] = '';
  2134.             $typearr[1] = '';
  2135.             $typearr[2] = '';
  2136.             //$typearr[3] = $arr[0]['type'];
  2137.             $typearr[3] = $arr[$start_token]['type'];
  2138.         }
  2139.  
  2140.         $in_priv_list = FALSE;
  2141.         for ($i = $start_token; $i < $arraysize; $i++) {
  2142. // DEBUG echo "Loop format <b>" . $arr[$i]['data'] . "</b> " . $arr[$i]['type'] . "<br />";
  2143.             $before = '';
  2144.             $after  = '';
  2145.             $indent = 0;
  2146.             // array_shift($typearr);
  2147.             /*
  2148.             0 prev2
  2149.             1 prev
  2150.             2 current
  2151.             3 next
  2152.             */
  2153.             if (($i + 1) < $arraysize) {
  2154.                 // array_push($typearr, $arr[$i + 1]['type']);
  2155.                 $typearr[4] = $arr[$i + 1]['type'];
  2156.             } else {
  2157.                 //array_push($typearr, null);
  2158.                 $typearr[4] = '';
  2159.             }
  2160.  
  2161.             for ($j=0; $j<4; $j++) {
  2162.                 $typearr[$j] = $typearr[$j + 1];
  2163.             }
  2164.  
  2165.             switch ($typearr[2]) {
  2166.                 case 'white_newline':
  2167.                     $before     = '';
  2168.                     break;
  2169.                 case 'punct_bracket_open_round':
  2170.                     $bracketlevel++;
  2171.                     $infunction = FALSE;
  2172.                     // Make sure this array is sorted!
  2173.                     if (($typearr[1] == 'alpha_functionName') || ($typearr[1] == 'alpha_columnType') || ($typearr[1] == 'punct')
  2174.                         || ($typearr[3] == 'digit_integer') || ($typearr[3] == 'digit_hex') || ($typearr[3] == 'digit_float')
  2175.                         || (($typearr[0] == 'alpha_reservedWord')
  2176.                             && PMA_STR_binarySearchInArr(strtoupper($arr[$i - 2]['data']), $keywords_with_brackets_2before, $keywords_with_brackets_2before_cnt))
  2177.                         || (($typearr[1] == 'alpha_reservedWord')
  2178.                             && PMA_STR_binarySearchInArr(strtoupper($arr[$i - 1]['data']), $keywords_with_brackets_1before, $keywords_with_brackets_1before_cnt))
  2179.                         ) {
  2180.                         $functionlevel++;
  2181.                         $infunction = TRUE;
  2182.                         $after      .= ' ';
  2183.                     } else {
  2184.                         $indent++;
  2185.                         $after      .= ($mode != 'query_only' ? '<div class="syntax_indent' . $indent . '">' : ' ');
  2186.                     }
  2187.                     break;
  2188.                 case 'alpha_identifier':
  2189.                     if (($typearr[1] == 'punct_qualifier') || ($typearr[3] == 'punct_qualifier')) {
  2190.                         $after      = '';
  2191.                         $before     = '';
  2192.                     }
  2193.                     if (($typearr[3] == 'alpha_columnType') || ($typearr[3] == 'alpha_identifier')) {
  2194.                         $after      .= ' ';
  2195.                     }
  2196.                     break;
  2197.                 case 'punct_user':
  2198.                 case 'punct_qualifier':
  2199.                     $before         = '';
  2200.                     $after          = '';
  2201.                     break;
  2202.                 case 'punct_listsep':
  2203.                     if ($infunction == TRUE) {
  2204.                         $after      .= $space_punct_listsep_function_name;
  2205.                     } else {
  2206.                         $after      .= $space_punct_listsep;
  2207.                     }
  2208.                     break;
  2209.                 case 'punct_queryend':
  2210.                     if (($typearr[3] != 'comment_mysql') && ($typearr[3] != 'comment_ansi') && $typearr[3] != 'comment_c') {
  2211.                         $after     .= $html_line_break;
  2212.                         $after     .= $html_line_break;
  2213.                     }
  2214.                     $space_punct_listsep               = ' ';
  2215.                     $space_punct_listsep_function_name = ' ';
  2216.                     $space_alpha_reserved_word         = ' ';
  2217.                     $in_priv_list                      = FALSE;
  2218.                     break;
  2219.                 case 'comment_mysql':
  2220.                 case 'comment_ansi':
  2221.                     $after         .= $html_line_break;
  2222.                     break;
  2223.                 case 'punct':
  2224.                     $before         .= ' ';
  2225.                     // workaround for
  2226.                     // select * from mytable limit 0,-1
  2227.                     // (a side effect of this workaround is that
  2228.                     // select 20 - 9
  2229.                     // becomes
  2230.                     // select 20 -9
  2231.                     // )
  2232.                     if ($typearr[3] != 'digit_integer') {
  2233.                        $after        .= ' ';
  2234.                     }
  2235.                     break;
  2236.                 case 'punct_bracket_close_round':
  2237.                     $bracketlevel--;
  2238.                     if ($infunction == TRUE) {
  2239.                         $functionlevel--;
  2240.                         $after     .= ' ';
  2241.                         $before    .= ' ';
  2242.                     } else {
  2243.                         $indent--;
  2244.                         $before    .= ($mode != 'query_only' ? '</div>' : ' ');
  2245.                     }
  2246.                     $infunction    = ($functionlevel > 0) ? TRUE : FALSE;
  2247.                     break;
  2248.                 case 'alpha_columnType':
  2249.                     if ($typearr[3] == 'alpha_columnAttrib') {
  2250.                         $after     .= ' ';
  2251.                     }
  2252.                     if ($typearr[1] == 'alpha_columnType') {
  2253.                         $before    .= ' ';
  2254.                     }
  2255.                     break;
  2256.                 case 'alpha_columnAttrib':
  2257.  
  2258.                     // ALTER TABLE tbl_name AUTO_INCREMENT = 1
  2259.                     // COLLATE LATIN1_GENERAL_CI DEFAULT
  2260.                     if ($typearr[1] == 'alpha_identifier' || $typearr[1] == 'alpha_charset') {
  2261.                         $before .= ' ';
  2262.                     }
  2263.                     if (($typearr[3] == 'alpha_columnAttrib') || ($typearr[3] == 'quote_single') || ($typearr[3] == 'digit_integer')) {
  2264.                         $after     .= ' ';
  2265.                     }
  2266.                     // workaround for
  2267.                     // AUTO_INCREMENT = 31DEFAULT_CHARSET = utf-8
  2268.  
  2269.                     if ($typearr[2] == 'alpha_columnAttrib' && $typearr[3] == 'alpha_reservedWord') {
  2270.                         $before .= ' ';
  2271.                     }
  2272.                     // workaround for
  2273.                     // select * from mysql.user where binary user="root"
  2274.                     // binary is marked as alpha_columnAttrib
  2275.                     // but should be marked as a reserved word
  2276.                     if (strtoupper($arr[$i]['data']) == 'BINARY'
  2277.                       && $typearr[3] == 'alpha_identifier') {
  2278.                         $after     .= ' ';
  2279.                     }
  2280.                     break;
  2281.                 case 'alpha_reservedWord':
  2282.                     // do not uppercase the reserved word if we are calling
  2283.                     // this function in query_only mode, because we need
  2284.                     // the original query (otherwise we get problems with
  2285.                     // semi-reserved words like "storage" which is legal
  2286.                     // as an identifier name)
  2287.  
  2288.                     if ($mode != 'query_only') {
  2289.                         $arr[$i]['data'] = strtoupper($arr[$i]['data']);
  2290.                     }
  2291.  
  2292.                     if ((($typearr[1] != 'alpha_reservedWord')
  2293.                         || (($typearr[1] == 'alpha_reservedWord')
  2294.                             && PMA_STR_binarySearchInArr(strtoupper($arr[$i - 1]['data']), $keywords_no_newline, $keywords_no_newline_cnt)))
  2295.                         && ($typearr[1] != 'punct_level_plus')
  2296.                         && (!PMA_STR_binarySearchInArr($arr[$i]['data'], $keywords_no_newline, $keywords_no_newline_cnt))) {
  2297.                         // do not put a space before the first token, because
  2298.                         // we use a lot of eregi() checking for the first
  2299.                         // reserved word at beginning of query
  2300.                         // so do not put a newline before
  2301.                         //
  2302.                         // also we must not be inside a privilege list
  2303.                         if ($i > 0) {
  2304.                             // the alpha_identifier exception is there to
  2305.                             // catch cases like
  2306.                             // GRANT SELECT ON mydb.mytable TO myuser@localhost
  2307.                             // (else, we get mydb.mytableTO)
  2308.                             //
  2309.                             // the quote_single exception is there to
  2310.                             // catch cases like
  2311.                             // GRANT ... TO 'marc'@'domain.com' IDENTIFIED...
  2312.                             /**
  2313.                              * @todo fix all cases and find why this happens
  2314.                              */
  2315.  
  2316.                             if (!$in_priv_list || $typearr[1] == 'alpha_identifier' || $typearr[1] == 'quote_single' || $typearr[1] == 'white_newline') {
  2317.                                 $before    .= $space_alpha_reserved_word;
  2318.                             }
  2319.                         } else {
  2320.                         // on first keyword, check if it introduces a
  2321.                         // privilege list
  2322.                             if (PMA_STR_binarySearchInArr($arr[$i]['data'], $keywords_priv_list, $keywords_priv_list_cnt)) {
  2323.                                 $in_priv_list = TRUE;
  2324.                             }
  2325.                         }
  2326.                     } else {
  2327.                         $before    .= ' ';
  2328.                     }
  2329.  
  2330.                     switch ($arr[$i]['data']) {
  2331.                         case 'CREATE':
  2332.                             if (!$in_priv_list) {
  2333.                                 $space_punct_listsep       = $html_line_break;
  2334.                                 $space_alpha_reserved_word = ' ';
  2335.                             }
  2336.                             break;
  2337.                         case 'EXPLAIN':
  2338.                         case 'DESCRIBE':
  2339.                         case 'SET':
  2340.                         case 'ALTER':
  2341.                         case 'DELETE':
  2342.                         case 'SHOW':
  2343.                         case 'DROP':
  2344.                         case 'UPDATE':
  2345.                         case 'TRUNCATE':
  2346.                         case 'ANALYZE':
  2347.                         case 'ANALYSE':
  2348.                             if (!$in_priv_list) {
  2349.                                 $space_punct_listsep       = $html_line_break;
  2350.                                 $space_alpha_reserved_word = ' ';
  2351.                             }
  2352.                             break;
  2353.                         case 'INSERT':
  2354.                         case 'REPLACE':
  2355.                             if (!$in_priv_list) {
  2356.                                 $space_punct_listsep       = $html_line_break;
  2357.                                 $space_alpha_reserved_word = $html_line_break;
  2358.                             }
  2359.                             break;
  2360.                         case 'VALUES':
  2361.                             $space_punct_listsep       = ' ';
  2362.                             $space_alpha_reserved_word = $html_line_break;
  2363.                             break;
  2364.                         case 'SELECT':
  2365.                             $space_punct_listsep       = ' ';
  2366.                             $space_alpha_reserved_word = $html_line_break;
  2367.                             break;
  2368.                         default:
  2369.                             break;
  2370.                     } // end switch ($arr[$i]['data'])
  2371.  
  2372.                     $after         .= ' ';
  2373.                     break;
  2374.                 case 'digit_integer':
  2375.                 case 'digit_float':
  2376.                 case 'digit_hex':
  2377.                     /**
  2378.                      * @todo could there be other types preceding a digit?
  2379.                      */
  2380.                     if ($typearr[1] == 'alpha_reservedWord') {
  2381.                         $after .= ' ';
  2382.                     }
  2383.                     if ($infunction && $typearr[3] == 'punct_bracket_close_round') {
  2384.                         $after     .= ' ';
  2385.                     }
  2386.                     if ($typearr[1] == 'alpha_columnAttrib') {
  2387.                         $before .= ' ';
  2388.                     }
  2389.                     break;
  2390.                 case 'alpha_variable':
  2391.                     $after      = ' ';
  2392.                     break;
  2393.                 case 'quote_double':
  2394.                 case 'quote_single':
  2395.                     // workaround: for the query
  2396.                     // REVOKE SELECT ON `base2\_db`.* FROM 'user'@'%'
  2397.                     // the @ is incorrectly marked as alpha_variable
  2398.                     // in the parser, and here, the '%' gets a blank before,
  2399.                     // which is a syntax error
  2400.                     if ($typearr[1] != 'punct_user') {
  2401.                         $before        .= ' ';
  2402.                     }
  2403.                     if ($infunction && $typearr[3] == 'punct_bracket_close_round') {
  2404.                         $after     .= ' ';
  2405.                     }
  2406.                     break;
  2407.                 case 'quote_backtick':
  2408.                     // here we check for punct_user to handle correctly
  2409.                     // DEFINER = `username`@`%`
  2410.                     // where @ is the punct_user and `%` is the quote_backtick 
  2411.                     if ($typearr[3] != 'punct_qualifier' && $typearr[3] != 'alpha_variable' && $typearr[3] != 'punct_user') {
  2412.                         $after     .= ' ';
  2413.                     }
  2414.                     if ($typearr[1] != 'punct_qualifier' && $typearr[1] != 'alpha_variable' && $typearr[1] != 'punct_user') {
  2415.                         $before    .= ' ';
  2416.                     }
  2417.                     break;
  2418.                 default:
  2419.                     break;
  2420.             } // end switch ($typearr[2])
  2421.  
  2422. /*
  2423.             if ($typearr[3] != 'punct_qualifier') {
  2424.                 $after             .= ' ';
  2425.             }
  2426.             $after                 .= "\n";
  2427. */
  2428.             $str .= $before . ($mode=='color' ? PMA_SQP_formatHTML_colorize($arr[$i]) : $arr[$i]['data']). $after;
  2429.         } // end for
  2430.         if ($mode=='color') {
  2431.             $str .= '</span>';
  2432.         }
  2433.  
  2434.         return $str;
  2435.     } // end of the "PMA_SQP_formatHtml()" function
  2436. }
  2437.  
  2438. /**
  2439.  * Builds a CSS rule used for html formatted SQL queries
  2440.  *
  2441.  * @param  string  The class name
  2442.  * @param  string  The property name
  2443.  * @param  string  The property value
  2444.  *
  2445.  * @return string  The CSS rule
  2446.  *
  2447.  * @access public
  2448.  *
  2449.  * @see    PMA_SQP_buildCssData()
  2450.  */
  2451. function PMA_SQP_buildCssRule($classname, $property, $value)
  2452. {
  2453.     $str     = '.' . $classname . ' {';
  2454.     if ($value != '') {
  2455.         $str .= $property . ': ' . $value . ';';
  2456.     }
  2457.     $str     .= '}' . "\n";
  2458.  
  2459.     return $str;
  2460. } // end of the "PMA_SQP_buildCssRule()" function
  2461.  
  2462.  
  2463. /**
  2464.  * Builds CSS rules used for html formatted SQL queries
  2465.  *
  2466.  * @return string  The CSS rules set
  2467.  *
  2468.  * @access public
  2469.  *
  2470.  * @global array   The current PMA configuration
  2471.  *
  2472.  * @see    PMA_SQP_buildCssRule()
  2473.  */
  2474. function PMA_SQP_buildCssData()
  2475. {
  2476.     global $cfg;
  2477.  
  2478.     $css_string     = '';
  2479.     foreach ($cfg['SQP']['fmtColor'] AS $key => $col) {
  2480.         $css_string .= PMA_SQP_buildCssRule('syntax_' . $key, 'color', $col);
  2481.     }
  2482.  
  2483.     for ($i = 0; $i < 8; $i++) {
  2484.         $css_string .= PMA_SQP_buildCssRule('syntax_indent' . $i, 'margin-left', ($i * $cfg['SQP']['fmtInd']) . $cfg['SQP']['fmtIndUnit']);
  2485.     }
  2486.  
  2487.     return $css_string;
  2488. } // end of the "PMA_SQP_buildCssData()" function
  2489.  
  2490. if (! defined('PMA_MINIMUM_COMMON')) {
  2491.     /**
  2492.      * Gets SQL queries with no format
  2493.      *
  2494.      * @param  array   The SQL queries list
  2495.      *
  2496.      * @return string  The SQL queries with no format
  2497.      *
  2498.      * @access public
  2499.      */
  2500.     function PMA_SQP_formatNone($arr)
  2501.     {
  2502.         $formatted_sql = htmlspecialchars($arr['raw']);
  2503.         $formatted_sql = preg_replace("@((\015\012)|(\015)|(\012)){3,}@", "\n\n", $formatted_sql);
  2504.  
  2505.         return $formatted_sql;
  2506.     } // end of the "PMA_SQP_formatNone()" function
  2507.  
  2508.  
  2509.     /**
  2510.      * Gets SQL queries in text format
  2511.      *
  2512.      * @todo WRITE THIS!
  2513.      * @param  array   The SQL queries list
  2514.      *
  2515.      * @return string  The SQL queries in text format
  2516.      *
  2517.      * @access public
  2518.      */
  2519.     function PMA_SQP_formatText($arr)
  2520.     {
  2521.          return PMA_SQP_formatNone($arr);
  2522.     } // end of the "PMA_SQP_formatText()" function
  2523. } // end if: minimal common.lib needed?
  2524.  
  2525. ?>
  2526.