[ Index ]
 

Code source de phpMyAdmin 2.10.3

Accédez au Source d'autres logiciels libres

Classes | Fonctions | Variables | Constantes | Tables

title

Body

[fermer]

/libraries/ -> sqlparser.lib.php (source)

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


Généré le : Mon Nov 26 15:18:20 2007 par Balluche grâce à PHPXref 0.7
  Clicky Web Analytics