home *** CD-ROM | disk | FTP | other *** search
/ Enter 2004 June / ENTER.ISO / files / xampp-win32-1.4.5-installer.exe / xampp / sqlparser.lib.php < prev    next >
Encoding:
PHP Script  |  2004-03-05  |  78.2 KB  |  2,050 lines

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