Documentation TYPO3 par Ameos |
00001 <?php 00002 /* $Id: sqlparser.lib.php,v 2.36 2005/08/08 20:22:11 lem9 Exp $ */ 00003 // vim: expandtab sw=4 ts=4 sts=4: 00004 00038 if (!isset($is_minimum_common)) { 00039 $is_minimum_common = FALSE; 00040 } 00041 00042 if ($is_minimum_common == FALSE) { 00046 require_once('./libraries/string.lib.php'); 00047 00051 require_once('./libraries/sqlparser.data.php'); 00052 require_once('./libraries/mysql_charsets.lib.php'); 00053 if (!isset($mysql_charsets)) { 00054 $mysql_charsets = array(); 00055 $mysql_charsets_count = 0; 00056 $mysql_collations_flat = array(); 00057 $mysql_collations_count = 0; 00058 } 00059 00060 if (!defined('DEBUG_TIMING')) { 00061 function PMA_SQP_arrayAdd(&$arr, $type, $data, &$arrsize) 00062 { 00063 $arr[] = array('type' => $type, 'data' => $data); 00064 $arrsize++; 00065 } // end of the "PMA_SQP_arrayAdd()" function 00066 } else { 00067 function PMA_SQP_arrayAdd(&$arr, $type, $data, &$arrsize) 00068 { 00069 global $timer; 00070 00071 $t = $timer; 00072 $arr[] = array('type' => $type, 'data' => $data , 'time' => $t); 00073 $timer = microtime(); 00074 $arrsize++; 00075 } // end of the "PMA_SQP_arrayAdd()" function 00076 } // end if... else... 00077 00078 00084 // Added, Robbat2 - 13 Janurary 2003, 2:59PM 00085 function PMA_SQP_resetError() { 00086 global $SQP_errorString; 00087 $SQP_errorString = ''; 00088 unset($SQP_errorString); 00089 } 00090 00098 // Added, Robbat2 - 13 Janurary 2003, 2:59PM 00099 function PMA_SQP_getErrorString() { 00100 global $SQP_errorString; 00101 return isset($SQP_errorString) ? $SQP_errorString : ''; 00102 } 00103 00111 // Added, Robbat2 - 13 Janurary 2003, 2:59PM 00112 function PMA_SQP_isError() { 00113 global $SQP_errorString; 00114 return isset($SQP_errorString) && !empty($SQP_errorString); 00115 } 00116 00126 // Revised, Robbat2 - 13 Janurary 2003, 2:59PM 00127 function PMA_SQP_throwError($message, $sql) 00128 { 00129 00130 global $SQP_errorString; 00131 $SQP_errorString = '<p>'.$GLOBALS['strSQLParserUserError'] . '</p>' . "\n" 00132 . '<pre>' . "\n" 00133 . 'ERROR: ' . $message . "\n" 00134 . 'SQL: ' . htmlspecialchars($sql) . "\n" 00135 . '</pre>' . "\n"; 00136 00137 } // end of the "PMA_SQP_throwError()" function 00138 00139 00148 function PMA_SQP_bug($message, $sql) 00149 { 00150 global $SQP_errorString; 00151 $debugstr = 'ERROR: ' . $message . "\n"; 00152 $debugstr .= 'CVS: $Id: sqlparser.lib.php,v 2.36 2005/08/08 20:22:11 lem9 Exp $' . "\n"; 00153 $debugstr .= 'MySQL: '.PMA_MYSQL_STR_VERSION . "\n"; 00154 $debugstr .= 'USR OS, AGENT, VER: ' . PMA_USR_OS . ' ' . PMA_USR_BROWSER_AGENT . ' ' . PMA_USR_BROWSER_VER . "\n"; 00155 $debugstr .= 'PMA: ' . PMA_VERSION . "\n"; 00156 $debugstr .= 'PHP VER,OS: ' . PMA_PHP_STR_VERSION . ' ' . PHP_OS . "\n"; 00157 $debugstr .= 'LANG: ' . $GLOBALS['lang'] . "\n"; 00158 $debugstr .= 'SQL: ' . htmlspecialchars($sql); 00159 00160 $encodedstr = $debugstr; 00161 if (@function_exists('gzcompress')) { 00162 $encodedstr = gzcompress($debugstr, 9); 00163 } 00164 $encodedstr = preg_replace("/(\015\012)|(\015)|(\012)/", '<br />' . "\n", chunk_split(base64_encode($encodedstr))); 00165 00166 $SQP_errorString .= $GLOBALS['strSQLParserBugMessage'] . '<br />' . "\n" 00167 . '----' . $GLOBALS['strBeginCut'] . '----' . '<br />' . "\n" 00168 . $encodedstr . "\n" 00169 . '----' . $GLOBALS['strEndCut'] . '----' . '<br />' . "\n"; 00170 00171 $SQP_errorString .= '----' . $GLOBALS['strBeginRaw'] . '----<br />' . "\n" 00172 . '<pre>' . "\n" 00173 . $debugstr 00174 . '</pre>' . "\n" 00175 . '----' . $GLOBALS['strEndRaw'] . '----<br />' . "\n"; 00176 00177 } // end of the "PMA_SQP_bug()" function 00178 00179 00203 function PMA_SQP_parse($sql) 00204 { 00205 global $cfg; 00206 global $PMA_SQPdata_column_attrib, $PMA_SQPdata_reserved_word, $PMA_SQPdata_column_type, $PMA_SQPdata_function_name, 00207 $PMA_SQPdata_column_attrib_cnt, $PMA_SQPdata_reserved_word_cnt, $PMA_SQPdata_column_type_cnt, $PMA_SQPdata_function_name_cnt; 00208 global $mysql_charsets, $mysql_collations_flat, $mysql_charsets_count, $mysql_collations_count; 00209 global $PMA_SQPdata_forbidden_word, $PMA_SQPdata_forbidden_word_cnt; 00210 00211 // rabus: Convert all line feeds to Unix style 00212 $sql = str_replace("\r\n", "\n", $sql); 00213 $sql = str_replace("\r", "\n", $sql); 00214 00215 $len = PMA_strlen($sql); 00216 if ($len == 0) { 00217 return array(); 00218 } 00219 00220 $sql_array = array(); 00221 $sql_array['raw'] = $sql; 00222 $count1 = 0; 00223 $count2 = 0; 00224 $punct_queryend = ';'; 00225 $punct_qualifier = '.'; 00226 $punct_listsep = ','; 00227 $punct_level_plus = '('; 00228 $punct_level_minus = ')'; 00229 $digit_floatdecimal = '.'; 00230 $digit_hexset = 'x'; 00231 $bracket_list = '()[]{}'; 00232 $allpunct_list = '-,;:!?/.^~\*&%+<=>|'; 00233 $allpunct_list_pair = array ( 00234 0 => '!=', 00235 1 => '&&', 00236 2 => ':=', 00237 3 => '<<', 00238 4 => '<=', 00239 5 => '<=>', 00240 6 => '<>', 00241 7 => '>=', 00242 8 => '>>', 00243 9 => '||' 00244 ); 00245 $allpunct_list_pair_size = 10; //count($allpunct_list_pair); 00246 $quote_list = '\'"`'; 00247 $arraysize = 0; 00248 00249 while ($count2 < $len) { 00250 $c = PMA_substr($sql, $count2, 1); 00251 $count1 = $count2; 00252 00253 if (($c == "\n")) { 00254 $count2++; 00255 PMA_SQP_arrayAdd($sql_array, 'white_newline', '', $arraysize); 00256 continue; 00257 } 00258 00259 // Checks for white space 00260 if (PMA_STR_isSpace($c)) { 00261 $count2++; 00262 continue; 00263 } 00264 00265 // Checks for comment lines. 00266 // MySQL style # 00267 // C style /* */ 00268 // ANSI style -- 00269 if (($c == '#') 00270 || (($count2 + 1 < $len) && ($c == '/') && (PMA_substr($sql, $count2 + 1, 1) == '*')) 00271 || (($count2 + 2 == $len) && ($c == '-') && (PMA_substr($sql, $count2 + 1, 1) == '-')) 00272 || (($count2 + 2 < $len) && ($c == '-') && (PMA_substr($sql, $count2 + 1, 1) == '-') && ((PMA_substr($sql, $count2 + 2, 1) <= ' ')))) { 00273 $count2++; 00274 $pos = 0; 00275 $type = 'bad'; 00276 switch ($c) { 00277 case '#': 00278 $type = 'mysql'; 00279 case '-': 00280 $type = 'ansi'; 00281 $pos = $GLOBALS['PMA_strpos']($sql, "\n", $count2); 00282 break; 00283 case '/': 00284 $type = 'c'; 00285 $pos = $GLOBALS['PMA_strpos']($sql, '*/', $count2); 00286 $pos += 2; 00287 break; 00288 default: 00289 break; 00290 } // end switch 00291 $count2 = ($pos < $count2) ? $len : $pos; 00292 $str = PMA_substr($sql, $count1, $count2 - $count1); 00293 PMA_SQP_arrayAdd($sql_array, 'comment_' . $type, $str, $arraysize); 00294 continue; 00295 } // end if 00296 00297 // Checks for something inside quotation marks 00298 if (PMA_STR_strInStr($c, $quote_list)) { 00299 $startquotepos = $count2; 00300 $quotetype = $c; 00301 $count2++; 00302 $escaped = FALSE; 00303 $escaped_escaped = FALSE; 00304 $pos = $count2; 00305 $oldpos = 0; 00306 do { 00307 $oldpos = $pos; 00308 $pos = $GLOBALS['PMA_strpos'](' ' . $sql, $quotetype, $oldpos + 1) - 1; 00309 // ($pos === FALSE) 00310 if ($pos < 0) { 00311 $debugstr = $GLOBALS['strSQPBugUnclosedQuote'] . ' @ ' . $startquotepos. "\n" 00312 . 'STR: ' . htmlspecialchars($quotetype); 00313 PMA_SQP_throwError($debugstr, $sql); 00314 return $sql; 00315 } 00316 00317 // If the quote is the first character, it can't be 00318 // escaped, so don't do the rest of the code 00319 if ($pos == 0) { 00320 break; 00321 } 00322 00323 // Checks for MySQL escaping using a \ 00324 // And checks for ANSI escaping using the $quotetype character 00325 if (($pos < $len) && PMA_STR_charIsEscaped($sql, $pos)) { 00326 $pos ++; 00327 continue; 00328 } else if (($pos + 1 < $len) && (PMA_substr($sql, $pos, 1) == $quotetype) && (PMA_substr($sql, $pos + 1, 1) == $quotetype)) { 00329 $pos = $pos + 2; 00330 continue; 00331 } else { 00332 break; 00333 } 00334 } while ($len > $pos); // end do 00335 00336 $count2 = $pos; 00337 $count2++; 00338 $type = 'quote_'; 00339 switch ($quotetype) { 00340 case '\'': 00341 $type .= 'single'; 00342 break; 00343 case '"': 00344 $type .= 'double'; 00345 break; 00346 case '`': 00347 $type .= 'backtick'; 00348 break; 00349 default: 00350 break; 00351 } // end switch 00352 $data = PMA_substr($sql, $count1, $count2 - $count1); 00353 PMA_SQP_arrayAdd($sql_array, $type, $data, $arraysize); 00354 continue; 00355 } 00356 00357 // Checks for brackets 00358 if (PMA_STR_strInStr($c, $bracket_list)) { 00359 // All bracket tokens are only one item long 00360 $count2++; 00361 $type_type = ''; 00362 if (PMA_STR_strInStr($c, '([{')) { 00363 $type_type = 'open'; 00364 } else { 00365 $type_type = 'close'; 00366 } 00367 00368 $type_style = ''; 00369 if (PMA_STR_strInStr($c, '()')) { 00370 $type_style = 'round'; 00371 } elseif (PMA_STR_strInStr($c, '[]')) { 00372 $type_style = 'square'; 00373 } else { 00374 $type_style = 'curly'; 00375 } 00376 00377 $type = 'punct_bracket_' . $type_type . '_' . $type_style; 00378 PMA_SQP_arrayAdd($sql_array, $type, $c, $arraysize); 00379 continue; 00380 } 00381 00382 // Checks for punct 00383 if (PMA_STR_strInStr($c, $allpunct_list)) { 00384 while (($count2 < $len) && PMA_STR_strInStr(PMA_substr($sql, $count2, 1), $allpunct_list)) { 00385 $count2++; 00386 } 00387 $l = $count2 - $count1; 00388 if ($l == 1) { 00389 $punct_data = $c; 00390 } else { 00391 $punct_data = PMA_substr($sql, $count1, $l); 00392 } 00393 00394 // Special case, sometimes, althought two characters are 00395 // adjectent directly, they ACTUALLY need to be seperate 00396 if ($l == 1) { 00397 $t_suffix = ''; 00398 switch ($punct_data) { 00399 case $punct_queryend: 00400 $t_suffix = '_queryend'; 00401 break; 00402 case $punct_qualifier: 00403 $t_suffix = '_qualifier'; 00404 break; 00405 case $punct_listsep: 00406 $t_suffix = '_listsep'; 00407 break; 00408 default: 00409 break; 00410 } 00411 PMA_SQP_arrayAdd($sql_array, 'punct' . $t_suffix, $punct_data, $arraysize); 00412 } 00413 else if (PMA_STR_binarySearchInArr($punct_data, $allpunct_list_pair, $allpunct_list_pair_size)) { 00414 // Ok, we have one of the valid combined punct expressions 00415 PMA_SQP_arrayAdd($sql_array, 'punct', $punct_data, $arraysize); 00416 } 00417 else { 00418 // Bad luck, lets split it up more 00419 $first = $punct_data[0]; 00420 $first2 = $punct_data[0] . $punct_data[1]; 00421 $last2 = $punct_data[$l - 2] . $punct_data[$l - 1]; 00422 $last = $punct_data[$l - 1]; 00423 if (($first == ',') || ($first == ';') || ($first == '.') || ($first == '*')) { 00424 $count2 = $count1 + 1; 00425 $punct_data = $first; 00426 } else if (($last2 == '/*') || (($last2 == '--') && ($count2 == $len || PMA_substr($sql, $count2, 1) <= ' ') )) { 00427 $count2 -= 2; 00428 $punct_data = PMA_substr($sql, $count1, $count2 - $count1); 00429 } else if (($last == '-') || ($last == '+') || ($last == '!')) { 00430 $count2--; 00431 $punct_data = PMA_substr($sql, $count1, $count2 - $count1); 00432 // TODO: for negation operator, split in 2 tokens ? 00433 // "select x&~1 from t" 00434 // becomes "select x & ~ 1 from t" ? 00435 00436 } else if ($last != '~') { 00437 $debugstr = $GLOBALS['strSQPBugUnknownPunctuation'] . ' @ ' . ($count1+1) . "\n" 00438 . 'STR: ' . htmlspecialchars($punct_data); 00439 PMA_SQP_throwError($debugstr, $sql); 00440 return $sql; 00441 } 00442 PMA_SQP_arrayAdd($sql_array, 'punct', $punct_data, $arraysize); 00443 continue; 00444 } // end if... else if... else 00445 continue; 00446 } 00447 00448 // Checks for alpha 00449 if (PMA_STR_isSqlIdentifier($c, FALSE) || ($c == '@')) { 00450 $count2 ++; 00451 00452 //TODO: a @ can also be present in expressions like 00453 // FROM 'user'@'%' 00454 // or TO 'user'@'%' 00455 // in this case, the @ is wrongly marked as alpha_variable 00456 00457 $is_sql_variable = ($c == '@'); 00458 $is_digit = (!$is_sql_variable) && PMA_STR_isDigit($c); 00459 $is_hex_digit = ($is_digit) && ($c == '0') && ($count2 < $len) && (PMA_substr($sql, $count2, 1) == 'x'); 00460 $is_float_digit = FALSE; 00461 $is_float_digit_exponent = FALSE; 00462 00463 if ($is_hex_digit) { 00464 $count2++; 00465 } 00466 00467 while (($count2 < $len) && PMA_STR_isSqlIdentifier(PMA_substr($sql, $count2, 1), ($is_sql_variable || $is_digit))) { 00468 $c2 = PMA_substr($sql, $count2, 1); 00469 if ($is_sql_variable && ($c2 == '.')) { 00470 $count2++; 00471 continue; 00472 } 00473 if ($is_digit && (!$is_hex_digit) && ($c2 == '.')) { 00474 $count2++; 00475 if (!$is_float_digit) { 00476 $is_float_digit = TRUE; 00477 continue; 00478 } else { 00479 $debugstr = $GLOBALS['strSQPBugInvalidIdentifer'] . ' @ ' . ($count1+1) . "\n" 00480 . 'STR: ' . htmlspecialchars(PMA_substr($sql, $count1, $count2 - $count1)); 00481 PMA_SQP_throwError($debugstr, $sql); 00482 return $sql; 00483 } 00484 } 00485 if ($is_digit && (!$is_hex_digit) && (($c2 == 'e') || ($c2 == 'E'))) { 00486 if (!$is_float_digit_exponent) { 00487 $is_float_digit_exponent = TRUE; 00488 $is_float_digit = TRUE; 00489 $count2++; 00490 continue; 00491 } else { 00492 $is_digit = FALSE; 00493 $is_float_digit = FALSE; 00494 } 00495 } 00496 if (($is_hex_digit && PMA_STR_isHexDigit($c2)) || ($is_digit && PMA_STR_isDigit($c2))) { 00497 $count2++; 00498 continue; 00499 } else { 00500 $is_digit = FALSE; 00501 $is_hex_digit = FALSE; 00502 } 00503 00504 $count2++; 00505 } // end while 00506 00507 $l = $count2 - $count1; 00508 $str = PMA_substr($sql, $count1, $l); 00509 00510 $type = ''; 00511 if ($is_digit) { 00512 $type = 'digit'; 00513 if ($is_float_digit) { 00514 $type .= '_float'; 00515 } else if ($is_hex_digit) { 00516 $type .= '_hex'; 00517 } else { 00518 $type .= '_integer'; 00519 } 00520 } 00521 else { 00522 if ($is_sql_variable != FALSE) { 00523 $type = 'alpha_variable'; 00524 } else { 00525 $type = 'alpha'; 00526 } 00527 } // end if... else.... 00528 PMA_SQP_arrayAdd($sql_array, $type, $str, $arraysize); 00529 00530 continue; 00531 } 00532 00533 // DEBUG 00534 $count2++; 00535 00536 $debugstr = 'C1 C2 LEN: ' . $count1 . ' ' . $count2 . ' ' . $len . "\n" 00537 . 'STR: ' . PMA_substr($sql, $count1, $count2 - $count1) . "\n"; 00538 PMA_SQP_bug($debugstr, $sql); 00539 return $sql; 00540 00541 } // end while ($count2 < $len) 00542 00543 00544 if ($arraysize > 0) { 00545 $t_next = $sql_array[0]['type']; 00546 $t_prev = ''; 00547 $t_bef_prev = ''; 00548 $t_cur = ''; 00549 $d_next = $sql_array[0]['data']; 00550 $d_prev = ''; 00551 $d_bef_prev = ''; 00552 $d_cur = ''; 00553 $d_next_upper = $t_next == 'alpha' ? strtoupper($d_next) : $d_next; 00554 $d_prev_upper = ''; 00555 $d_bef_prev_upper = ''; 00556 $d_cur_upper = ''; 00557 } 00558 00559 for ($i = 0; $i < $arraysize; $i++) { 00560 $t_bef_prev = $t_prev; 00561 $t_prev = $t_cur; 00562 $t_cur = $t_next; 00563 $d_bef_prev = $d_prev; 00564 $d_prev = $d_cur; 00565 $d_cur = $d_next; 00566 $d_bef_prev_upper = $d_prev_upper; 00567 $d_prev_upper = $d_cur_upper; 00568 $d_cur_upper = $d_next_upper; 00569 if (($i + 1) < $arraysize) { 00570 $t_next = $sql_array[$i + 1]['type']; 00571 $d_next = $sql_array[$i + 1]['data']; 00572 $d_next_upper = $t_next == 'alpha' ? strtoupper($d_next) : $d_next; 00573 } else { 00574 $t_next = ''; 00575 $d_next = ''; 00576 $d_next_upper = ''; 00577 } 00578 00579 //DEBUG echo "[prev: <b>".$d_prev."</b> ".$t_prev."][cur: <b>".$d_cur."</b> ".$t_cur."][next: <b>".$d_next."</b> ".$t_next."]<br />"; 00580 00581 if ($t_cur == 'alpha') { 00582 $t_suffix = '_identifier'; 00583 if (($t_next == 'punct_qualifier') || ($t_prev == 'punct_qualifier')) { 00584 $t_suffix = '_identifier'; 00585 } else if (($t_next == 'punct_bracket_open_round') 00586 && PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_function_name, $PMA_SQPdata_function_name_cnt)) { 00587 $t_suffix = '_functionName'; 00588 } else if (PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_column_type, $PMA_SQPdata_column_type_cnt)) { 00589 $t_suffix = '_columnType'; 00590 00591 // Temporary fix for BUG #621357 00592 //TODO FIX PROPERLY NEEDS OVERHAUL OF SQL TOKENIZER 00593 if ($d_cur_upper == 'SET' && $t_next != 'punct_bracket_open_round') { 00594 $t_suffix = '_reservedWord'; 00595 } 00596 //END OF TEMPORARY FIX 00597 00598 // CHARACTER is a synonym for CHAR, but can also be meant as 00599 // CHARACTER SET. In this case, we have a reserved word. 00600 if ($d_cur_upper == 'CHARACTER' && $d_next_upper == 'SET') { 00601 $t_suffix = '_reservedWord'; 00602 } 00603 00604 // experimental 00605 // current is a column type, so previous must not be 00606 // a reserved word but an identifier 00607 // CREATE TABLE SG_Persons (first varchar(64)) 00608 00609 //if ($sql_array[$i-1]['type'] =='alpha_reservedWord') { 00610 // $sql_array[$i-1]['type'] = 'alpha_identifier'; 00611 //} 00612 00613 } else if (PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_reserved_word, $PMA_SQPdata_reserved_word_cnt)) { 00614 $t_suffix = '_reservedWord'; 00615 } else if (PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_column_attrib, $PMA_SQPdata_column_attrib_cnt)) { 00616 $t_suffix = '_columnAttrib'; 00617 // INNODB is a MySQL table type, but in "SHOW INNODB STATUS", 00618 // it should be regarded as a reserved word. 00619 if ($d_cur_upper == 'INNODB' && $d_prev_upper == 'SHOW' && $d_next_upper == 'STATUS') { 00620 $t_suffix = '_reservedWord'; 00621 } 00622 00623 if ($d_cur_upper == 'DEFAULT' && $d_next_upper == 'CHARACTER') { 00624 $t_suffix = '_reservedWord'; 00625 } 00626 // Binary as character set 00627 if ($d_cur_upper == 'BINARY' && ( 00628 ($d_bef_prev_upper == 'CHARACTER' && $d_prev_upper == 'SET') 00629 || ($d_bef_prev_upper == 'SET' && $d_prev_upper == '=') 00630 || ($d_bef_prev_upper == 'CHARSET' && $d_prev_upper == '=') 00631 || $d_prev_upper == 'CHARSET' 00632 ) && PMA_STR_binarySearchInArr($d_cur, $mysql_charsets, count($mysql_charsets))) { 00633 $t_suffix = '_charset'; 00634 } 00635 } elseif (PMA_STR_binarySearchInArr($d_cur, $mysql_charsets, $mysql_charsets_count) 00636 || PMA_STR_binarySearchInArr($d_cur, $mysql_collations_flat, $mysql_collations_count) 00637 || ($d_cur{0} == '_' && PMA_STR_binarySearchInArr(substr($d_cur, 1), $mysql_charsets, $mysql_charsets_count))) { 00638 $t_suffix = '_charset'; 00639 } else { 00640 // Do nothing 00641 } 00642 // check if present in the list of forbidden words 00643 if ($t_suffix == '_reservedWord' && PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_forbidden_word, $PMA_SQPdata_forbidden_word_cnt)) { 00644 $sql_array[$i]['forbidden'] = TRUE; 00645 } else { 00646 $sql_array[$i]['forbidden'] = FALSE; 00647 } 00648 $sql_array[$i]['type'] .= $t_suffix; 00649 } 00650 } // end for 00651 00652 // Stores the size of the array inside the array, as count() is a slow 00653 // operation. 00654 $sql_array['len'] = $arraysize; 00655 00656 // Sends the data back 00657 return $sql_array; 00658 } // end of the "PMA_SQP_parse()" function 00659 00670 function PMA_SQP_typeCheck($toCheck, $whatWeWant) 00671 { 00672 $typeSeperator = '_'; 00673 if (strcmp($whatWeWant, $toCheck) == 0) { 00674 return TRUE; 00675 } else { 00676 if (strpos($whatWeWant, $typeSeperator) === FALSE) { 00677 return strncmp($whatWeWant, $toCheck , strpos($toCheck, $typeSeperator)) == 0; 00678 } else { 00679 return FALSE; 00680 } 00681 } 00682 } 00683 00684 00694 function PMA_SQP_analyze($arr) 00695 { 00696 $result = array(); 00697 $size = $arr['len']; 00698 $subresult = array( 00699 'querytype' => '', 00700 'select_expr_clause'=> '', // the whole stuff between SELECT and FROM , except DISTINCT 00701 'position_of_first_select' => '', // the array index 00702 'from_clause'=> '', 00703 'group_by_clause'=> '', 00704 'order_by_clause'=> '', 00705 'having_clause' => '', 00706 'where_clause' => '', 00707 'where_clause_identifiers' => array(), 00708 'unsorted_query' => '', 00709 'queryflags' => array(), 00710 'select_expr' => array(), 00711 'table_ref' => array(), 00712 'foreign_keys' => array(), 00713 'create_table_fields' => array() 00714 ); 00715 $subresult_empty = $subresult; 00716 $seek_queryend = FALSE; 00717 $seen_end_of_table_ref = FALSE; 00718 $number_of_brackets_in_extract = 0; 00719 $number_of_brackets_in_group_concat = 0; 00720 00721 // for SELECT EXTRACT(YEAR_MONTH FROM CURDATE()) 00722 // we must not use CURDATE as a table_ref 00723 // so we track wether we are in the EXTRACT() 00724 $in_extract = FALSE; 00725 00726 // for GROUP_CONCAT( ... ) 00727 $in_group_concat = FALSE; 00728 00729 /* Description of analyzer results by lem9 00730 * 00731 * db, table, column, alias 00732 * ------------------------ 00733 * 00734 * Inside the $subresult array, we create ['select_expr'] and ['table_ref'] arrays. 00735 * 00736 * The SELECT syntax (simplified) is 00737 * 00738 * SELECT 00739 * select_expression,... 00740 * [FROM [table_references] 00741 * 00742 * 00743 * ['select_expr'] is filled with each expression, the key represents the 00744 * expression position in the list (0-based) (so we don't lose track of 00745 * multiple occurences of the same column). 00746 * 00747 * ['table_ref'] is filled with each table ref, same thing for the key. 00748 * 00749 * I create all sub-values empty, even if they are 00750 * not present (for example no select_expression alias). 00751 * 00752 * There is a debug section at the end of loop #1, if you want to 00753 * see the exact contents of select_expr and table_ref 00754 * 00755 * queryflags 00756 * ---------- 00757 * 00758 * In $subresult, array 'queryflags' is filled, according to what we 00759 * find in the query. 00760 * 00761 * Currently, those are generated: 00762 * 00763 * ['queryflags']['need_confirm'] = 1; if the query needs confirmation 00764 * ['queryflags']['select_from'] = 1; if this is a real SELECT...FROM 00765 * ['queryflags']['distinct'] = 1; for a DISTINCT 00766 * ['queryflags']['union'] = 1; for a UNION 00767 * ['queryflags']['join'] = 1; for a JOIN 00768 * ['queryflags']['offset'] = 1; for the presence of OFFSET 00769 * 00770 * query clauses 00771 * ------------- 00772 * 00773 * The select is splitted in those clauses: 00774 * ['select_expr_clause'] 00775 * ['from_clause'] 00776 * ['group_by_clause'] 00777 * ['order_by_clause'] 00778 * ['having_clause'] 00779 * ['where_clause'] 00780 * 00781 * The identifiers of the WHERE clause are put into the array 00782 * ['where_clause_identifier'] 00783 * 00784 * For a SELECT, the whole query without the ORDER BY clause is put into 00785 * ['unsorted_query'] 00786 * 00787 * foreign keys 00788 * ------------ 00789 * The CREATE TABLE may contain FOREIGN KEY clauses, so they get 00790 * analyzed and ['foreign_keys'] is an array filled with 00791 * the constraint name, the index list, 00792 * the REFERENCES table name and REFERENCES index list, 00793 * and ON UPDATE | ON DELETE clauses 00794 * 00795 * position_of_first_select 00796 * ------------------------ 00797 * 00798 * The array index of the first SELECT we find. Will be used to 00799 * insert a SQL_CALC_FOUND_ROWS. 00800 * 00801 * create_table_fields 00802 * ------------------- 00803 * 00804 * For now, mostly used to detect the DEFAULT CURRENT_TIMESTAMP and 00805 * ON UPDATE CURRENT_TIMESTAMP clauses of the CREATE TABLE query. 00806 * An array, each element is the identifier name. 00807 * Note that for now, the timestamp_not_null element is created 00808 * even for non-TIMESTAMP fields. 00809 * 00810 * Sub-elements: ['type'] which contains the column type 00811 * optional (currently they are never false but can be absent): 00812 * ['default_current_timestamp'] boolean 00813 * ['on_update_current_timestamp'] boolean 00814 * ['timestamp_not_null'] boolean 00815 * 00816 * section_before_limit, section_after_limit 00817 * ----------------------------------------- 00818 * 00819 * Marks the point of the query where we can insert a LIMIT clause; 00820 * so the section_before_limit will contain the left part before 00821 * a possible LIMIT clause 00822 * 00823 * 00824 * End of description of analyzer results 00825 */ 00826 00827 // must be sorted 00828 // TODO: current logic checks for only one word, so I put only the 00829 // first word of the reserved expressions that end a table ref; 00830 // maybe this is not ok (the first word might mean something else) 00831 // $words_ending_table_ref = array( 00832 // 'FOR UPDATE', 00833 // 'GROUP BY', 00834 // 'HAVING', 00835 // 'LIMIT', 00836 // 'LOCK IN SHARE MODE', 00837 // 'ORDER BY', 00838 // 'PROCEDURE', 00839 // 'UNION', 00840 // 'WHERE' 00841 // ); 00842 $words_ending_table_ref = array( 00843 'FOR', 00844 'GROUP', 00845 'HAVING', 00846 'LIMIT', 00847 'LOCK', 00848 'ORDER', 00849 'PROCEDURE', 00850 'UNION', 00851 'WHERE' 00852 ); 00853 $words_ending_table_ref_cnt = 9; //count($words_ending_table_ref); 00854 00855 $words_ending_clauses = array( 00856 'FOR', 00857 'LIMIT', 00858 'LOCK', 00859 'PROCEDURE', 00860 'UNION' 00861 ); 00862 $words_ending_clauses_cnt = 5; //count($words_ending_clauses); 00863 00864 00865 00866 00867 // must be sorted 00868 $supported_query_types = array( 00869 'SELECT' 00870 /* 00871 // Support for these additional query types will come later on. 00872 'DELETE', 00873 'INSERT', 00874 'REPLACE', 00875 'TRUNCATE', 00876 'UPDATE' 00877 'EXPLAIN', 00878 'DESCRIBE', 00879 'SHOW', 00880 'CREATE', 00881 'SET', 00882 'ALTER' 00883 */ 00884 ); 00885 $supported_query_types_cnt = count($supported_query_types); 00886 00887 // loop #1 for each token: select_expr, table_ref for SELECT 00888 00889 for ($i = 0; $i < $size; $i++) { 00890 //DEBUG echo "trace loop1 <b>" . $arr[$i]['data'] . "</b> (" . $arr[$i]['type'] . ")<br />"; 00891 00892 // High speed seek for locating the end of the current query 00893 if ($seek_queryend == TRUE) { 00894 if ($arr[$i]['type'] == 'punct_queryend') { 00895 $seek_queryend = FALSE; 00896 } else { 00897 continue; 00898 } // end if (type == punct_queryend) 00899 } // end if ($seek_queryend) 00900 00901 // TODO: when we find a UNION, should we split 00902 // in another subresult? 00903 if ($arr[$i]['type'] == 'punct_queryend') { 00904 $result[] = $subresult; 00905 $subresult = $subresult_empty; 00906 continue; 00907 } // end if (type == punct_queryend) 00908 00909 // ============================================================== 00910 if ($arr[$i]['type'] == 'punct_bracket_open_round') { 00911 if ($in_extract) { 00912 $number_of_brackets_in_extract++; 00913 } 00914 if ($in_group_concat) { 00915 $number_of_brackets_in_group_concat++; 00916 } 00917 } 00918 // ============================================================== 00919 if ($arr[$i]['type'] == 'punct_bracket_close_round') { 00920 if ($in_extract) { 00921 $number_of_brackets_in_extract--; 00922 if ($number_of_brackets_in_extract == 0) { 00923 $in_extract = FALSE; 00924 } 00925 } 00926 if ($in_group_concat) { 00927 $number_of_brackets_in_group_concat--; 00928 if ($number_of_brackets_in_group_concat == 0) { 00929 $in_group_concat = FALSE; 00930 } 00931 } 00932 } 00933 // ============================================================== 00934 if ($arr[$i]['type'] == 'alpha_functionName') { 00935 $upper_data = strtoupper($arr[$i]['data']); 00936 if ($upper_data =='EXTRACT') { 00937 $in_extract = TRUE; 00938 $number_of_brackets_in_extract = 0; 00939 } 00940 if ($upper_data =='GROUP_CONCAT') { 00941 $in_group_concat = TRUE; 00942 $number_of_brackets_in_group_concat = 0; 00943 } 00944 } 00945 00946 // ============================================================== 00947 if ($arr[$i]['type'] == 'alpha_reservedWord' 00948 // && $arr[$i]['forbidden'] == FALSE) { 00949 ){ 00950 // We don't know what type of query yet, so run this 00951 if ($subresult['querytype'] == '') { 00952 $subresult['querytype'] = strtoupper($arr[$i]['data']); 00953 } // end if (querytype was empty) 00954 00955 // Check if we support this type of query 00956 if (!PMA_STR_binarySearchInArr($subresult['querytype'], $supported_query_types, $supported_query_types_cnt)) { 00957 // Skip ahead to the next one if we don't 00958 $seek_queryend = TRUE; 00959 continue; 00960 } // end if (query not supported) 00961 00962 // upper once 00963 $upper_data = strtoupper($arr[$i]['data']); 00964 //TODO: reset for each query? 00965 00966 if ($upper_data == 'SELECT') { 00967 $seen_from = FALSE; 00968 $previous_was_identifier = FALSE; 00969 $current_select_expr = -1; 00970 $seen_end_of_table_ref = FALSE; 00971 } // end if ( data == SELECT) 00972 00973 if ($upper_data =='FROM' && !$in_extract) { 00974 $current_table_ref = -1; 00975 $seen_from = TRUE; 00976 $previous_was_identifier = FALSE; 00977 $save_table_ref = TRUE; 00978 } // end if (data == FROM) 00979 00980 // here, do not 'continue' the loop, as we have more work for 00981 // reserved words below 00982 } // end if (type == alpha_reservedWord) 00983 00984 // ============================== 00985 if ($arr[$i]['type'] == 'quote_backtick' 00986 || $arr[$i]['type'] == 'quote_double' 00987 || $arr[$i]['type'] == 'quote_single' 00988 || $arr[$i]['type'] == 'alpha_identifier' 00989 || ($arr[$i]['type'] == 'alpha_reservedWord' 00990 && $arr[$i]['forbidden'] == FALSE)) { 00991 00992 switch ($arr[$i]['type']) { 00993 case 'alpha_identifier': 00994 case 'alpha_reservedWord': 00995 // this is not a real reservedWord, because 00996 // it's not present in the list of forbidden words, 00997 // for example "storage" which can be used as 00998 // an identifier 00999 // 01000 // TODO: avoid the pretty printing in color 01001 // in this case 01002 01003 $identifier = $arr[$i]['data']; 01004 break; 01005 01006 //TODO: check embedded double quotes or backticks? 01007 // and/or remove just the first and last character? 01008 case 'quote_backtick': 01009 $identifier = str_replace('`','',$arr[$i]['data']); 01010 break; 01011 case 'quote_double': 01012 $identifier = str_replace('"','',$arr[$i]['data']); 01013 break; 01014 case 'quote_single': 01015 $identifier = str_replace("'","",$arr[$i]['data']); 01016 break; 01017 } // end switch 01018 01019 if ($subresult['querytype'] == 'SELECT' && !$in_group_concat) { 01020 if (!$seen_from) { 01021 if ($previous_was_identifier && isset($chain)) { 01022 // found alias for this select_expr, save it 01023 // but only if we got something in $chain 01024 // (for example, SELECT COUNT(*) AS cnt 01025 // puts nothing in $chain, so we avoid 01026 // setting the alias) 01027 $alias_for_select_expr = $identifier; 01028 } else { 01029 $chain[] = $identifier; 01030 $previous_was_identifier = TRUE; 01031 01032 } // end if !$previous_was_identifier 01033 } else { 01034 // ($seen_from) 01035 if ($save_table_ref && !$seen_end_of_table_ref) { 01036 if ($previous_was_identifier) { 01037 // found alias for table ref 01038 // save it for later 01039 $alias_for_table_ref = $identifier; 01040 } else { 01041 $chain[] = $identifier; 01042 $previous_was_identifier = TRUE; 01043 01044 } // end if ($previous_was_identifier) 01045 } // end if ($save_table_ref &&!$seen_end_of_table_ref) 01046 } // end if (!$seen_from) 01047 } // end if (querytype SELECT) 01048 } // end if ( quote_backtick or double quote or alpha_identifier) 01049 01050 // =================================== 01051 if ($arr[$i]['type'] == 'punct_qualifier') { 01052 // to be able to detect an identifier following another 01053 $previous_was_identifier = FALSE; 01054 continue; 01055 } // end if (punct_qualifier) 01056 01057 // TODO: check if 3 identifiers following one another -> error 01058 01059 // s a v e a s e l e c t e x p r 01060 // finding a list separator or FROM 01061 // means that we must save the current chain of identifiers 01062 // into a select expression 01063 01064 // for now, we only save a select expression if it contains 01065 // at least one identifier, as we are interested in checking 01066 // the columns and table names, so in "select * from persons", 01067 // the "*" is not saved 01068 01069 if (isset($chain) && !$seen_end_of_table_ref 01070 && ( (!$seen_from 01071 && $arr[$i]['type'] == 'punct_listsep') 01072 || ($arr[$i]['type'] == 'alpha_reservedWord' && $upper_data == 'FROM')) ) { 01073 $size_chain = count($chain); 01074 $current_select_expr++; 01075 $subresult['select_expr'][$current_select_expr] = array( 01076 'expr' => '', 01077 'alias' => '', 01078 'db' => '', 01079 'table_name' => '', 01080 'table_true_name' => '', 01081 'column' => '' 01082 ); 01083 01084 if (!empty($alias_for_select_expr)) { 01085 // we had found an alias for this select expression 01086 $subresult['select_expr'][$current_select_expr]['alias'] = $alias_for_select_expr; 01087 unset($alias_for_select_expr); 01088 } 01089 // there is at least a column 01090 $subresult['select_expr'][$current_select_expr]['column'] = $chain[$size_chain - 1]; 01091 $subresult['select_expr'][$current_select_expr]['expr'] = $chain[$size_chain - 1]; 01092 01093 // maybe a table 01094 if ($size_chain > 1) { 01095 $subresult['select_expr'][$current_select_expr]['table_name'] = $chain[$size_chain - 2]; 01096 // we assume for now that this is also the true name 01097 $subresult['select_expr'][$current_select_expr]['table_true_name'] = $chain[$size_chain - 2]; 01098 $subresult['select_expr'][$current_select_expr]['expr'] 01099 = $subresult['select_expr'][$current_select_expr]['table_name'] 01100 . '.' . $subresult['select_expr'][$current_select_expr]['expr']; 01101 } // end if ($size_chain > 1) 01102 01103 // maybe a db 01104 if ($size_chain > 2) { 01105 $subresult['select_expr'][$current_select_expr]['db'] = $chain[$size_chain - 3]; 01106 $subresult['select_expr'][$current_select_expr]['expr'] 01107 = $subresult['select_expr'][$current_select_expr]['db'] 01108 . '.' . $subresult['select_expr'][$current_select_expr]['expr']; 01109 } // end if ($size_chain > 2) 01110 unset($chain); 01111 01112 // TODO: explain this: 01113 if (($arr[$i]['type'] == 'alpha_reservedWord') 01114 && ($upper_data != 'FROM')) { 01115 $previous_was_identifier = TRUE; 01116 } 01117 01118 } // end if (save a select expr) 01119 01120 01121 //====================================== 01122 // s a v e a t a b l e r e f 01123 //====================================== 01124 01125 // maybe we just saw the end of table refs 01126 // but the last table ref has to be saved 01127 // or we are at the last token (TODO: there could be another 01128 // query after this one) 01129 // or we just got a reserved word 01130 01131 if (isset($chain) && $seen_from && $save_table_ref 01132 && ($arr[$i]['type'] == 'punct_listsep' 01133 || ($arr[$i]['type'] == 'alpha_reservedWord' && $upper_data!="AS") 01134 || $seen_end_of_table_ref 01135 || $i==$size-1 )) { 01136 01137 $size_chain = count($chain); 01138 $current_table_ref++; 01139 $subresult['table_ref'][$current_table_ref] = array( 01140 'expr' => '', 01141 'db' => '', 01142 'table_name' => '', 01143 'table_alias' => '', 01144 'table_true_name' => '' 01145 ); 01146 if (!empty($alias_for_table_ref)) { 01147 $subresult['table_ref'][$current_table_ref]['table_alias'] = $alias_for_table_ref; 01148 unset($alias_for_table_ref); 01149 } 01150 $subresult['table_ref'][$current_table_ref]['table_name'] = $chain[$size_chain - 1]; 01151 // we assume for now that this is also the true name 01152 $subresult['table_ref'][$current_table_ref]['table_true_name'] = $chain[$size_chain - 1]; 01153 $subresult['table_ref'][$current_table_ref]['expr'] 01154 = $subresult['table_ref'][$current_table_ref]['table_name']; 01155 // maybe a db 01156 if ($size_chain > 1) { 01157 $subresult['table_ref'][$current_table_ref]['db'] = $chain[$size_chain - 2]; 01158 $subresult['table_ref'][$current_table_ref]['expr'] 01159 = $subresult['table_ref'][$current_table_ref]['db'] 01160 . '.' . $subresult['table_ref'][$current_table_ref]['expr']; 01161 } // end if ($size_chain > 1) 01162 01163 // add the table alias into the whole expression 01164 $subresult['table_ref'][$current_table_ref]['expr'] 01165 .= ' ' . $subresult['table_ref'][$current_table_ref]['table_alias']; 01166 01167 unset($chain); 01168 $previous_was_identifier = TRUE; 01169 //continue; 01170 01171 } // end if (save a table ref) 01172 01173 01174 // when we have found all table refs, 01175 // for each table_ref alias, put the true name of the table 01176 // in the corresponding select expressions 01177 01178 if (isset($current_table_ref) && ($seen_end_of_table_ref || $i == $size-1)) { 01179 for ($tr=0; $tr <= $current_table_ref; $tr++) { 01180 $alias = $subresult['table_ref'][$tr]['table_alias']; 01181 $truename = $subresult['table_ref'][$tr]['table_true_name']; 01182 for ($se=0; $se <= $current_select_expr; $se++) { 01183 if (!empty($alias) && $subresult['select_expr'][$se]['table_true_name'] 01184 == $alias) { 01185 $subresult['select_expr'][$se]['table_true_name'] 01186 = $truename; 01187 } // end if (found the alias) 01188 } // end for (select expressions) 01189 01190 } // end for (table refs) 01191 } // end if (set the true names) 01192 01193 01194 // e n d i n g l o o p #1 01195 // set the $previous_was_identifier to FALSE if the current 01196 // token is not an identifier 01197 if (($arr[$i]['type'] != 'alpha_identifier') 01198 && ($arr[$i]['type'] != 'quote_double') 01199 && ($arr[$i]['type'] != 'quote_single') 01200 && ($arr[$i]['type'] != 'quote_backtick')) { 01201 $previous_was_identifier = FALSE; 01202 } // end if 01203 01204 // however, if we are on AS, we must keep the $previous_was_identifier 01205 if (($arr[$i]['type'] == 'alpha_reservedWord') 01206 && ($upper_data == 'AS')) { 01207 $previous_was_identifier = TRUE; 01208 } 01209 01210 if (($arr[$i]['type'] == 'alpha_reservedWord') 01211 && ($upper_data =='ON' || $upper_data =='USING')) { 01212 $save_table_ref = FALSE; 01213 } // end if (data == ON) 01214 01215 if (($arr[$i]['type'] == 'alpha_reservedWord') 01216 && ($upper_data =='JOIN' || $upper_data =='FROM')) { 01217 $save_table_ref = TRUE; 01218 } // end if (data == JOIN) 01219 01220 // no need to check the end of table ref if we already did 01221 // TODO: maybe add "&& $seen_from" 01222 if (!$seen_end_of_table_ref) { 01223 // if this is the last token, it implies that we have 01224 // seen the end of table references 01225 // Check for the end of table references 01226 // 01227 // Note: if we are analyzing a GROUP_CONCAT clause, 01228 // we might find a word that seems to indicate that 01229 // we have found the end of table refs (like ORDER) 01230 // but it's a modifier of the GROUP_CONCAT so 01231 // it's not the real end of table refs 01232 if (($i == $size-1) 01233 || ($arr[$i]['type'] == 'alpha_reservedWord' 01234 && !$in_group_concat 01235 && PMA_STR_binarySearchInArr($upper_data, $words_ending_table_ref, $words_ending_table_ref_cnt))) { 01236 $seen_end_of_table_ref = TRUE; 01237 // to be able to save the last table ref, but do not 01238 // set it true if we found a word like "ON" that has 01239 // already set it to false 01240 if (isset($save_table_ref) && $save_table_ref != FALSE) { 01241 $save_table_ref = TRUE; 01242 } //end if 01243 01244 } // end if (check for end of table ref) 01245 } //end if (!$seen_end_of_table_ref) 01246 01247 if ($seen_end_of_table_ref) { 01248 $save_table_ref = FALSE; 01249 } // end if 01250 01251 } // end for $i (loop #1) 01252 01253 // ------------------------------------------------------- 01254 // This is a big hunk of debugging code by Marc for this. 01255 // ------------------------------------------------------- 01256 /* 01257 if (isset($current_select_expr)) { 01258 for ($trace=0; $trace<=$current_select_expr; $trace++) { 01259 echo "<br />"; 01260 reset ($subresult['select_expr'][$trace]); 01261 while (list ($key, $val) = each ($subresult['select_expr'][$trace])) 01262 echo "sel expr $trace $key => $val<br />\n"; 01263 } 01264 } 01265 01266 if (isset($current_table_ref)) { 01267 echo "current_table_ref = " . $current_table_ref . "<br>"; 01268 for ($trace=0; $trace<=$current_table_ref; $trace++) { 01269 01270 echo "<br />"; 01271 reset ($subresult['table_ref'][$trace]); 01272 while (list ($key, $val) = each ($subresult['table_ref'][$trace])) 01273 echo "table ref $trace $key => $val<br />\n"; 01274 } 01275 } 01276 */ 01277 // ------------------------------------------------------- 01278 01279 01280 // loop #2: - queryflags 01281 // - querytype (for queries != 'SELECT') 01282 // - section_before_limit, section_after_limit 01283 // 01284 // we will also need this queryflag in loop 2 01285 // so set it here 01286 if (isset($current_table_ref) && $current_table_ref > -1) { 01287 $subresult['queryflags']['select_from'] = 1; 01288 } 01289 01290 $collect_section_before_limit = TRUE; 01291 $section_before_limit = ''; 01292 $section_after_limit = ''; 01293 $seen_reserved_word = FALSE; 01294 $seen_group = FALSE; 01295 $seen_order = FALSE; 01296 $in_group_by = FALSE; // true when we are inside the GROUP BY clause 01297 $in_order_by = FALSE; // true when we are inside the ORDER BY clause 01298 $in_having = FALSE; // true when we are inside the HAVING clause 01299 $in_select_expr = FALSE; // true when we are inside the select expr clause 01300 $in_where = FALSE; // true when we are inside the WHERE clause 01301 $in_from = FALSE; 01302 $in_group_concat = FALSE; 01303 $unsorted_query = ''; 01304 $first_reserved_word = ''; 01305 $current_identifier = ''; 01306 01307 for ($i = 0; $i < $size; $i++) { 01308 //DEBUG echo "trace loop2 <b>" . $arr[$i]['data'] . "</b> (" . $arr[$i]['type'] . ")<br />"; 01309 01310 // need_confirm 01311 // 01312 // check for reserved words that will have to generate 01313 // a confirmation request later in sql.php 01314 // the cases are: 01315 // DROP TABLE 01316 // DROP DATABASE 01317 // ALTER TABLE... DROP 01318 // DELETE FROM... 01319 // 01320 // this code is not used for confirmations coming from functions.js 01321 01322 // TODO: check for punct_queryend 01323 01324 01325 // TODO: verify C-style comments? 01326 if ($arr[$i]['type'] == 'comment_ansi') { 01327 $collect_section_before_limit = FALSE; 01328 } 01329 01330 if ($arr[$i]['type'] == 'alpha_reservedWord') { 01331 $upper_data = strtoupper($arr[$i]['data']); 01332 if (!$seen_reserved_word) { 01333 $first_reserved_word = $upper_data; 01334 $subresult['querytype'] = $upper_data; 01335 $seen_reserved_word = TRUE; 01336 01337 // if the first reserved word is DROP or DELETE, 01338 // we know this is a query that needs to be confirmed 01339 if ($first_reserved_word=='DROP' 01340 || $first_reserved_word == 'DELETE' 01341 || $first_reserved_word == 'TRUNCATE') { 01342 $subresult['queryflags']['need_confirm'] = 1; 01343 } 01344 01345 if ($first_reserved_word=='SELECT'){ 01346 $position_of_first_select = $i; 01347 } 01348 01349 } else { 01350 if ($upper_data=='DROP' && $first_reserved_word=='ALTER') { 01351 $subresult['queryflags']['need_confirm'] = 1; 01352 } 01353 } 01354 01355 if ($upper_data == 'PROCEDURE') { 01356 $collect_section_before_limit = FALSE; 01357 } 01358 // TODO: set also to FALSE if we find 01359 // FOR UPDATE 01360 // LOCK IN SHARE MODE 01361 01362 if ($upper_data == 'SELECT') { 01363 $in_select_expr = TRUE; 01364 $select_expr_clause = ''; 01365 } 01366 if ($upper_data == 'DISTINCT' && !$in_group_concat) { 01367 $subresult['queryflags']['distinct'] = 1; 01368 } 01369 01370 if ($upper_data == 'UNION') { 01371 $subresult['queryflags']['union'] = 1; 01372 } 01373 01374 if ($upper_data == 'JOIN') { 01375 $subresult['queryflags']['join'] = 1; 01376 } 01377 01378 if ($upper_data == 'OFFSET') { 01379 $subresult['queryflags']['offset'] = 1; 01380 } 01381 01382 // if this is a real SELECT...FROM 01383 if ($upper_data == 'FROM' && isset($subresult['queryflags']['select_from']) && $subresult['queryflags']['select_from'] == 1) { 01384 $in_from = TRUE; 01385 $from_clause = ''; 01386 $in_select_expr = FALSE; 01387 } 01388 01389 01390 // (we could have less resetting of variables to FALSE 01391 // if we trust that the query respects the standard 01392 // MySQL order for clauses) 01393 01394 // we use $seen_group and $seen_order because we are looking 01395 // for the BY 01396 if ($upper_data == 'GROUP') { 01397 $seen_group = TRUE; 01398 $seen_order = FALSE; 01399 $in_having = FALSE; 01400 $in_order_by = FALSE; 01401 $in_where = FALSE; 01402 $in_select_expr = FALSE; 01403 $in_from = FALSE; 01404 } 01405 if ($upper_data == 'ORDER' && !$in_group_concat) { 01406 $seen_order = TRUE; 01407 $seen_group = FALSE; 01408 $in_having = FALSE; 01409 $in_group_by = FALSE; 01410 $in_where = FALSE; 01411 $in_select_expr = FALSE; 01412 $in_from = FALSE; 01413 } 01414 if ($upper_data == 'HAVING') { 01415 $in_having = TRUE; 01416 $having_clause = ''; 01417 $seen_group = FALSE; 01418 $seen_order = FALSE; 01419 $in_group_by = FALSE; 01420 $in_order_by = FALSE; 01421 $in_where = FALSE; 01422 $in_select_expr = FALSE; 01423 $in_from = FALSE; 01424 } 01425 01426 if ($upper_data == 'WHERE') { 01427 $in_where = TRUE; 01428 $where_clause = ''; 01429 $where_clause_identifiers = array(); 01430 $seen_group = FALSE; 01431 $seen_order = FALSE; 01432 $in_group_by = FALSE; 01433 $in_order_by = FALSE; 01434 $in_having = FALSE; 01435 $in_select_expr = FALSE; 01436 $in_from = FALSE; 01437 } 01438 01439 if ($upper_data == 'BY') { 01440 if ($seen_group) { 01441 $in_group_by = TRUE; 01442 $group_by_clause = ''; 01443 } 01444 if ($seen_order) { 01445 $in_order_by = TRUE; 01446 $order_by_clause = ''; 01447 } 01448 } 01449 01450 // if we find one of the words that could end the clause 01451 if (PMA_STR_binarySearchInArr($upper_data, $words_ending_clauses, $words_ending_clauses_cnt)) { 01452 01453 $in_group_by = FALSE; 01454 $in_order_by = FALSE; 01455 $in_having = FALSE; 01456 $in_where = FALSE; 01457 $in_select_expr = FALSE; 01458 $in_from = FALSE; 01459 } 01460 01461 } // endif (reservedWord) 01462 01463 01464 // do not add a blank after a function name 01465 // TODO: can we combine loop 2 and loop 1? 01466 // some code is repeated here... 01467 01468 $sep=' '; 01469 if ($arr[$i]['type'] == 'alpha_functionName') { 01470 $sep=''; 01471 $upper_data = strtoupper($arr[$i]['data']); 01472 if ($upper_data =='GROUP_CONCAT') { 01473 $in_group_concat = TRUE; 01474 $number_of_brackets_in_group_concat = 0; 01475 } 01476 } 01477 01478 if ($arr[$i]['type'] == 'punct_bracket_open_round') { 01479 if ($in_group_concat) { 01480 $number_of_brackets_in_group_concat++; 01481 } 01482 } 01483 if ($arr[$i]['type'] == 'punct_bracket_close_round') { 01484 if ($in_group_concat) { 01485 $number_of_brackets_in_group_concat--; 01486 if ($number_of_brackets_in_group_concat == 0) { 01487 $in_group_concat = FALSE; 01488 } 01489 } 01490 } 01491 01492 if ($in_select_expr && $upper_data != 'SELECT' && $upper_data != 'DISTINCT') { 01493 $select_expr_clause .= $arr[$i]['data'] . $sep; 01494 } 01495 if ($in_from && $upper_data != 'FROM') { 01496 $from_clause .= $arr[$i]['data'] . $sep; 01497 } 01498 if ($in_group_by && $upper_data != 'GROUP' && $upper_data != 'BY') { 01499 $group_by_clause .= $arr[$i]['data'] . $sep; 01500 } 01501 if ($in_order_by && $upper_data != 'ORDER' && $upper_data != 'BY') { 01502 $order_by_clause .= $arr[$i]['data'] . $sep; 01503 } 01504 if ($in_having && $upper_data != 'HAVING') { 01505 $having_clause .= $arr[$i]['data'] . $sep; 01506 } 01507 if ($in_where && $upper_data != 'WHERE') { 01508 $where_clause .= $arr[$i]['data'] . $sep; 01509 01510 if (($arr[$i]['type'] == 'quote_backtick') 01511 || ($arr[$i]['type'] == 'alpha_identifier')) { 01512 $where_clause_identifiers[] = $arr[$i]['data']; 01513 } 01514 } 01515 01516 if (isset($subresult['queryflags']['select_from']) 01517 && $subresult['queryflags']['select_from'] == 1 01518 && !$seen_order) { 01519 $unsorted_query .= $arr[$i]['data']; 01520 01521 if ($arr[$i]['type'] != 'punct_bracket_open_round' 01522 && $arr[$i]['type'] != 'punct_bracket_close_round' 01523 && $arr[$i]['type'] != 'punct') { 01524 $unsorted_query .= $sep; 01525 } 01526 } 01527 01528 // clear $upper_data for next iteration 01529 $upper_data=''; 01530 01531 if ($collect_section_before_limit) { 01532 $section_before_limit .= $arr[$i]['data'] . $sep; 01533 } else { 01534 $section_after_limit .= $arr[$i]['data'] . $sep; 01535 } 01536 01537 01538 } // end for $i (loop #2) 01539 01540 01541 // ----------------------------------------------------- 01542 // loop #3: foreign keys and MySQL 4.1.2+ TIMESTAMP options 01543 // (for now, check only the first query) 01544 // (for now, identifiers are assumed to be backquoted) 01545 01546 // If we find that we are dealing with a CREATE TABLE query, 01547 // we look for the next punct_bracket_open_round, which 01548 // introduces the fields list. Then, when we find a 01549 // quote_backtick, it must be a field, so we put it into 01550 // the create_table_fields array. Even if this field is 01551 // not a timestamp, it will be useful when logic has been 01552 // added for complete field attributes analysis. 01553 01554 $seen_foreign = FALSE; 01555 $seen_references = FALSE; 01556 $seen_constraint = FALSE; 01557 $foreign_key_number = -1; 01558 $seen_create_table = FALSE; 01559 $seen_create = FALSE; 01560 $in_create_table_fields = FALSE; 01561 $brackets_level = 0; 01562 $in_timestamp_options = FALSE; 01563 $seen_default = FALSE; 01564 01565 for ($i = 0; $i < $size; $i++) { 01566 // DEBUG echo "<b>" . $arr[$i]['data'] . "</b> " . $arr[$i]['type'] . "<br />"; 01567 01568 if ($arr[$i]['type'] == 'alpha_reservedWord') { 01569 $upper_data = strtoupper($arr[$i]['data']); 01570 01571 if ($upper_data == 'NOT' && $in_timestamp_options) { 01572 $create_table_fields[$current_identifier]['timestamp_not_null'] = TRUE; 01573 01574 } 01575 01576 if ($upper_data == 'CREATE') { 01577 $seen_create = TRUE; 01578 } 01579 01580 if ($upper_data == 'TABLE' && $seen_create) { 01581 $seen_create_table = TRUE; 01582 $create_table_fields = array(); 01583 } 01584 01585 if ($upper_data == 'CURRENT_TIMESTAMP') { 01586 if ($in_timestamp_options) { 01587 if ($seen_default) { 01588 $create_table_fields[$current_identifier]['default_current_timestamp'] = TRUE; 01589 } 01590 } 01591 } 01592 01593 if ($upper_data == 'CONSTRAINT') { 01594 $foreign_key_number++; 01595 $seen_foreign = FALSE; 01596 $seen_references = FALSE; 01597 $seen_constraint = TRUE; 01598 } 01599 if ($upper_data == 'FOREIGN') { 01600 $seen_foreign = TRUE; 01601 $seen_references = FALSE; 01602 $seen_constraint = FALSE; 01603 } 01604 if ($upper_data == 'REFERENCES') { 01605 $seen_foreign = FALSE; 01606 $seen_references = TRUE; 01607 $seen_constraint = FALSE; 01608 } 01609 01610 01611 // Cases covered: 01612 01613 // [ON DELETE {CASCADE | SET NULL | NO ACTION | RESTRICT}] 01614 // [ON UPDATE {CASCADE | SET NULL | NO ACTION | RESTRICT}] 01615 01616 // but we set ['on_delete'] or ['on_cascade'] to 01617 // CASCADE | SET_NULL | NO_ACTION | RESTRICT 01618 01619 // ON UPDATE CURRENT_TIMESTAMP 01620 01621 if ($upper_data == 'ON') { 01622 if ($arr[$i+1]['type'] == 'alpha_reservedWord') { 01623 $second_upper_data = strtoupper($arr[$i+1]['data']); 01624 if ($second_upper_data == 'DELETE') { 01625 $clause = 'on_delete'; 01626 } 01627 if ($second_upper_data == 'UPDATE') { 01628 $clause = 'on_update'; 01629 } 01630 if (isset($clause) 01631 && ($arr[$i+2]['type'] == 'alpha_reservedWord' 01632 01633 // ugly workaround because currently, NO is not 01634 // in the list of reserved words in sqlparser.data 01635 // (we got a bug report about not being able to use 01636 // 'no' as an identifier) 01637 || ($arr[$i+2]['type'] == 'alpha_identifier' 01638 && strtoupper($arr[$i+2]['data'])=='NO') ) 01639 ) { 01640 $third_upper_data = strtoupper($arr[$i+2]['data']); 01641 if ($third_upper_data == 'CASCADE' 01642 || $third_upper_data == 'RESTRICT') { 01643 $value = $third_upper_data; 01644 } elseif ($third_upper_data == 'SET' 01645 || $third_upper_data == 'NO') { 01646 if ($arr[$i+3]['type'] == 'alpha_reservedWord') { 01647 $value = $third_upper_data . '_' . strtoupper($arr[$i+3]['data']); 01648 } 01649 } elseif ($third_upper_data == 'CURRENT_TIMESTAMP') { 01650 if ($clause == 'on_update' 01651 && $in_timestamp_options) { 01652 $create_table_fields[$current_identifier]['on_update_current_timestamp'] = TRUE; 01653 $seen_default = FALSE; 01654 } 01655 01656 } else { 01657 $value = ''; 01658 } 01659 if (!empty($value)) { 01660 $foreign[$foreign_key_number][$clause] = $value; 01661 } 01662 unset($clause); 01663 } // endif (isset($clause)) 01664 } 01665 } 01666 01667 } // end of reserved words analysis 01668 01669 01670 if ($arr[$i]['type'] == 'punct_bracket_open_round') { 01671 $brackets_level++; 01672 if ($seen_create_table && $brackets_level == 1) { 01673 $in_create_table_fields = TRUE; 01674 } 01675 } 01676 01677 01678 if ($arr[$i]['type'] == 'punct_bracket_close_round') { 01679 $brackets_level--; 01680 if ($seen_references) { 01681 $seen_references = FALSE; 01682 } 01683 if ($seen_create_table && $brackets_level == 0) { 01684 $in_create_table_fields = FALSE; 01685 } 01686 } 01687 01688 if (($arr[$i]['type'] == 'alpha_columnAttrib')) { 01689 $upper_data = strtoupper($arr[$i]['data']); 01690 if ($seen_create_table && $in_create_table_fields) { 01691 if ($upper_data == 'DEFAULT') { 01692 $seen_default = TRUE; 01693 } 01694 } 01695 } 01696 01697 if (($arr[$i]['type'] == 'alpha_columnType')) { 01698 $upper_data = strtoupper($arr[$i]['data']); 01699 if ($seen_create_table && $in_create_table_fields && isset($current_identifier)) { 01700 $create_table_fields[$current_identifier]['type'] = $upper_data; 01701 if ($upper_data == 'TIMESTAMP') { 01702 $in_timestamp_options = TRUE; 01703 } else { 01704 $in_timestamp_options = FALSE; 01705 } 01706 } 01707 } 01708 01709 01710 if ($arr[$i]['type'] == 'quote_backtick' || $arr[$i]['type'] == 'alpha_identifier') { 01711 01712 if ($arr[$i]['type'] == 'quote_backtick') { 01713 // remove backquotes 01714 $identifier = str_replace('`','',$arr[$i]['data']); 01715 } else { 01716 $identifier = $arr[$i]['data']; 01717 } 01718 01719 if ($seen_create_table && $in_create_table_fields) { 01720 $current_identifier = $identifier; 01721 // warning: we set this one even for non TIMESTAMP type 01722 $create_table_fields[$current_identifier]['timestamp_not_null'] = FALSE; 01723 } 01724 01725 if ($seen_constraint) { 01726 $foreign[$foreign_key_number]['constraint'] = $identifier; 01727 } 01728 01729 if ($seen_foreign && $brackets_level > 0) { 01730 $foreign[$foreign_key_number]['index_list'][] = $identifier; 01731 } 01732 01733 if ($seen_references) { 01734 // here, the first bracket level corresponds to the 01735 // bracket of CREATE TABLE 01736 // so if we are on level 2, it must be the index list 01737 // of the foreign key REFERENCES 01738 if ($brackets_level > 1) { 01739 $foreign[$foreign_key_number]['ref_index_list'][] = $identifier; 01740 } else { 01741 // for MySQL 4.0.18, identifier is 01742 // `table` or `db`.`table` 01743 // the first pass will pick the db name 01744 // the next pass will execute the else and pick the 01745 // db name in $db_table[0] 01746 if ($arr[$i+1]['type'] == 'punct_qualifier') { 01747 $foreign[$foreign_key_number]['ref_db_name'] = $identifier; 01748 } else { 01749 // for MySQL 4.0.16, identifier is 01750 // `table` or `db.table` 01751 $db_table = explode('.',$identifier); 01752 if (isset($db_table[1])) { 01753 $foreign[$foreign_key_number]['ref_db_name'] = $db_table[0]; 01754 $foreign[$foreign_key_number]['ref_table_name'] = $db_table[1]; 01755 } else { 01756 $foreign[$foreign_key_number]['ref_table_name'] = $db_table[0]; 01757 } 01758 } 01759 } 01760 } 01761 } 01762 } // end for $i (loop #3) 01763 01764 01765 // Fill the $subresult array 01766 01767 if (isset($create_table_fields)) { 01768 $subresult['create_table_fields'] = $create_table_fields; 01769 } 01770 01771 if (isset($foreign)) { 01772 $subresult['foreign_keys'] = $foreign; 01773 } 01774 01775 if (isset($select_expr_clause)) { 01776 $subresult['select_expr_clause'] = $select_expr_clause; 01777 } 01778 if (isset($from_clause)) { 01779 $subresult['from_clause'] = $from_clause; 01780 } 01781 if (isset($group_by_clause)) { 01782 $subresult['group_by_clause'] = $group_by_clause; 01783 } 01784 if (isset($order_by_clause)) { 01785 $subresult['order_by_clause'] = $order_by_clause; 01786 } 01787 if (isset($having_clause)) { 01788 $subresult['having_clause'] = $having_clause; 01789 } 01790 if (isset($where_clause)) { 01791 $subresult['where_clause'] = $where_clause; 01792 } 01793 if (isset($unsorted_query) && !empty($unsorted_query)) { 01794 $subresult['unsorted_query'] = $unsorted_query; 01795 } 01796 if (isset($where_clause_identifiers)) { 01797 $subresult['where_clause_identifiers'] = $where_clause_identifiers; 01798 } 01799 01800 if (isset($position_of_first_select)) { 01801 $subresult['position_of_first_select'] = $position_of_first_select; 01802 $subresult['section_before_limit'] = $section_before_limit; 01803 $subresult['section_after_limit'] = $section_after_limit; 01804 } 01805 01806 // They are naughty and didn't have a trailing semi-colon, 01807 // then still handle it properly 01808 if ($subresult['querytype'] != '') { 01809 $result[] = $subresult; 01810 } 01811 return $result; 01812 } // end of the "PMA_SQP_analyze()" function 01813 01814 01824 function PMA_SQP_formatHtml_colorize($arr) 01825 { 01826 $i = $GLOBALS['PMA_strpos']($arr['type'], '_'); 01827 $class = ''; 01828 if ($i > 0) { 01829 $class = 'syntax_' . PMA_substr($arr['type'], 0, $i) . ' '; 01830 } 01831 01832 $class .= 'syntax_' . $arr['type']; 01833 01834 //TODO: check why adding a "\n" after the </span> would cause extra 01835 // blanks to be displayed: 01836 // SELECT p . person_name 01837 01838 return '<span class="' . $class . '">' . htmlspecialchars($arr['data']) . '</span>'; 01839 } // end of the "PMA_SQP_formatHtml_colorize()" function 01840 01841 01854 function PMA_SQP_formatHtml($arr, $mode='color', $start_token=0, 01855 $number_of_tokens=-1) 01856 { 01857 // then check for an array 01858 if (!is_array($arr)) { 01859 return htmlspecialchars($arr); 01860 } 01861 // first check for the SQL parser having hit an error 01862 if (PMA_SQP_isError()) { 01863 return htmlspecialchars($arr['raw']); 01864 } 01865 // else do it properly 01866 switch ($mode) { 01867 case 'color': 01868 $str = '<span class="syntax">'; 01869 $html_line_break = '<br />'; 01870 break; 01871 case 'query_only': 01872 $str = ''; 01873 $html_line_break = "\n"; 01874 break; 01875 case 'text': 01876 $str = ''; 01877 $html_line_break = '<br />'; 01878 break; 01879 } // end switch 01880 $indent = 0; 01881 $bracketlevel = 0; 01882 $functionlevel = 0; 01883 $infunction = FALSE; 01884 $space_punct_listsep = ' '; 01885 $space_punct_listsep_function_name = ' '; 01886 // $space_alpha_reserved_word = '<br />'."\n"; 01887 $space_alpha_reserved_word = ' '; 01888 01889 $keywords_with_brackets_1before = array( 01890 'INDEX', 01891 'KEY', 01892 'ON', 01893 'USING' 01894 ); 01895 $keywords_with_brackets_1before_cnt = 4; 01896 01897 $keywords_with_brackets_2before = array( 01898 'IGNORE', 01899 'INDEX', 01900 'INTO', 01901 'KEY', 01902 'PRIMARY', 01903 'PROCEDURE', 01904 'REFERENCES', 01905 'UNIQUE', 01906 'USE' 01907 ); 01908 // $keywords_with_brackets_2before_cnt = count($keywords_with_brackets_2before); 01909 $keywords_with_brackets_2before_cnt = 9; 01910 01911 // These reserved words do NOT get a newline placed near them. 01912 $keywords_no_newline = array( 01913 'AS', 01914 'ASC', 01915 'DESC', 01916 'DISTINCT', 01917 'HOUR', 01918 'INTERVAL', 01919 'IS', 01920 'LIKE', 01921 'NOT', 01922 'NULL', 01923 'ON', 01924 'REGEXP' 01925 ); 01926 $keywords_no_newline_cnt = 12; 01927 01928 // These reserved words introduce a privilege list 01929 $keywords_priv_list = array( 01930 'GRANT', 01931 'REVOKE' 01932 ); 01933 $keywords_priv_list_cnt = 2; 01934 01935 if ($number_of_tokens == -1) { 01936 $arraysize = $arr['len']; 01937 } else { 01938 $arraysize = $number_of_tokens; 01939 } 01940 $typearr = array(); 01941 if ($arraysize >= 0) { 01942 $typearr[0] = ''; 01943 $typearr[1] = ''; 01944 $typearr[2] = ''; 01945 //$typearr[3] = $arr[0]['type']; 01946 $typearr[3] = $arr[$start_token]['type']; 01947 } 01948 01949 $in_priv_list = FALSE; 01950 for ($i = $start_token; $i < $arraysize; $i++) { 01951 // DEBUG echo "<b>" . $arr[$i]['data'] . "</b> " . $arr[$i]['type'] . "<br />"; 01952 $before = ''; 01953 $after = ''; 01954 $indent = 0; 01955 // array_shift($typearr); 01956 /* 01957 0 prev2 01958 1 prev 01959 2 current 01960 3 next 01961 */ 01962 if (($i + 1) < $arraysize) { 01963 // array_push($typearr, $arr[$i + 1]['type']); 01964 $typearr[4] = $arr[$i + 1]['type']; 01965 } else { 01966 //array_push($typearr, NULL); 01967 $typearr[4] = ''; 01968 } 01969 01970 for ($j=0; $j<4; $j++) { 01971 $typearr[$j] = $typearr[$j + 1]; 01972 } 01973 01974 switch ($typearr[2]) { 01975 case 'white_newline': 01976 $before = ''; 01977 break; 01978 case 'punct_bracket_open_round': 01979 $bracketlevel++; 01980 $infunction = FALSE; 01981 // Make sure this array is sorted! 01982 if (($typearr[1] == 'alpha_functionName') || ($typearr[1] == 'alpha_columnType') || ($typearr[1] == 'punct') 01983 || ($typearr[3] == 'digit_integer') || ($typearr[3] == 'digit_hex') || ($typearr[3] == 'digit_float') 01984 || (($typearr[0] == 'alpha_reservedWord') 01985 && PMA_STR_binarySearchInArr(strtoupper($arr[$i - 2]['data']), $keywords_with_brackets_2before, $keywords_with_brackets_2before_cnt)) 01986 || (($typearr[1] == 'alpha_reservedWord') 01987 && PMA_STR_binarySearchInArr(strtoupper($arr[$i - 1]['data']), $keywords_with_brackets_1before, $keywords_with_brackets_1before_cnt)) 01988 ) { 01989 $functionlevel++; 01990 $infunction = TRUE; 01991 $after .= ' '; 01992 } else { 01993 $indent++; 01994 $after .= ($mode != 'query_only' ? '<div class="syntax_indent' . $indent . '">' : ' '); 01995 } 01996 break; 01997 case 'alpha_identifier': 01998 if (($typearr[1] == 'punct_qualifier') || ($typearr[3] == 'punct_qualifier')) { 01999 $after = ''; 02000 $before = ''; 02001 } 02002 if (($typearr[3] == 'alpha_columnType') || ($typearr[3] == 'alpha_identifier')) { 02003 $after .= ' '; 02004 } 02005 break; 02006 case 'punct_qualifier': 02007 $before = ''; 02008 $after = ''; 02009 break; 02010 case 'punct_listsep': 02011 if ($infunction == TRUE) { 02012 $after .= $space_punct_listsep_function_name; 02013 } else { 02014 $after .= $space_punct_listsep; 02015 } 02016 break; 02017 case 'punct_queryend': 02018 if (($typearr[3] != 'comment_mysql') && ($typearr[3] != 'comment_ansi') && $typearr[3] != 'comment_c') { 02019 $after .= $html_line_break; 02020 $after .= $html_line_break; 02021 } 02022 $space_punct_listsep = ' '; 02023 $space_punct_listsep_function_name = ' '; 02024 $space_alpha_reserved_word = ' '; 02025 $in_priv_list = FALSE; 02026 break; 02027 case 'comment_mysql': 02028 case 'comment_ansi': 02029 $after .= $html_line_break; 02030 break; 02031 case 'punct': 02032 $before .= ' '; 02033 // workaround for 02034 // select * from mytable limit 0,-1 02035 // (a side effect of this workaround is that 02036 // select 20 - 9 02037 // becomes 02038 // select 20 -9 02039 // ) 02040 if ($typearr[3] != 'digit_integer') { 02041 $after .= ' '; 02042 } 02043 break; 02044 case 'punct_bracket_close_round': 02045 $bracketlevel--; 02046 if ($infunction == TRUE) { 02047 $functionlevel--; 02048 $after .= ' '; 02049 $before .= ' '; 02050 } else { 02051 $indent--; 02052 $before .= ($mode != 'query_only' ? '</div>' : ' '); 02053 } 02054 $infunction = ($functionlevel > 0) ? TRUE : FALSE; 02055 break; 02056 case 'alpha_columnType': 02057 if ($typearr[3] == 'alpha_columnAttrib') { 02058 $after .= ' '; 02059 } 02060 if ($typearr[1] == 'alpha_columnType') { 02061 $before .= ' '; 02062 } 02063 break; 02064 case 'alpha_columnAttrib': 02065 02066 // ALTER TABLE tbl_name AUTO_INCREMENT = 1 02067 // COLLATE LATIN1_GENERAL_CI DEFAULT 02068 if ($typearr[1] == 'alpha_identifier' || $typearr[1] == 'alpha_charset') { 02069 $before .= ' '; 02070 } 02071 if (($typearr[3] == 'alpha_columnAttrib') || ($typearr[3] == 'quote_single') || ($typearr[3] == 'digit_integer')) { 02072 $after .= ' '; 02073 } 02074 // workaround for 02075 // select * from mysql.user where binary user="root" 02076 // binary is marked as alpha_columnAttrib 02077 // but should be marked as a reserved word 02078 if (strtoupper($arr[$i]['data']) == 'BINARY' 02079 && $typearr[3] == 'alpha_identifier') { 02080 $after .= ' '; 02081 } 02082 break; 02083 case 'alpha_reservedWord': 02084 // do not uppercase the reserved word if we are calling 02085 // this function in query_only mode, because we need 02086 // the original query (otherwise we get problems with 02087 // semi-reserved words like "storage" which is legal 02088 // as an identifier name) 02089 02090 if ($mode != 'query_only') { 02091 $arr[$i]['data'] = strtoupper($arr[$i]['data']); 02092 } 02093 02094 if ((($typearr[1] != 'alpha_reservedWord') 02095 || (($typearr[1] == 'alpha_reservedWord') 02096 && PMA_STR_binarySearchInArr(strtoupper($arr[$i - 1]['data']), $keywords_no_newline, $keywords_no_newline_cnt))) 02097 && ($typearr[1] != 'punct_level_plus') 02098 && (!PMA_STR_binarySearchInArr($arr[$i]['data'], $keywords_no_newline, $keywords_no_newline_cnt))) { 02099 // do not put a space before the first token, because 02100 // we use a lot of eregi() checking for the first 02101 // reserved word at beginning of query 02102 // so do not put a newline before 02103 // 02104 // also we must not be inside a privilege list 02105 if ($i > 0) { 02106 // the alpha_identifier exception is there to 02107 // catch cases like 02108 // GRANT SELECT ON mydb.mytable TO myuser@localhost 02109 // (else, we get mydb.mytableTO ) 02110 // 02111 // the quote_single exception is there to 02112 // catch cases like 02113 // GRANT ... TO 'marc'@'domain.com' IDENTIFIED... 02114 // 02115 // TODO: fix all cases and find why this happens 02116 02117 if (!$in_priv_list || $typearr[1] == 'alpha_identifier' || $typearr[1] == 'quote_single' || $typearr[1] == 'white_newline') { 02118 $before .= $space_alpha_reserved_word; 02119 } 02120 } else { 02121 // on first keyword, check if it introduces a 02122 // privilege list 02123 if (PMA_STR_binarySearchInArr($arr[$i]['data'], $keywords_priv_list, $keywords_priv_list_cnt)) { 02124 $in_priv_list = TRUE; 02125 } 02126 } 02127 } else { 02128 $before .= ' '; 02129 } 02130 02131 switch ($arr[$i]['data']) { 02132 case 'CREATE': 02133 if (!$in_priv_list) { 02134 $space_punct_listsep = $html_line_break; 02135 $space_alpha_reserved_word = ' '; 02136 } 02137 break; 02138 case 'EXPLAIN': 02139 case 'DESCRIBE': 02140 case 'SET': 02141 case 'ALTER': 02142 case 'DELETE': 02143 case 'SHOW': 02144 case 'DROP': 02145 case 'UPDATE': 02146 case 'TRUNCATE': 02147 case 'ANALYZE': 02148 case 'ANALYSE': 02149 if (!$in_priv_list) { 02150 $space_punct_listsep = $html_line_break; 02151 $space_alpha_reserved_word = ' '; 02152 } 02153 break; 02154 case 'INSERT': 02155 case 'REPLACE': 02156 if (!$in_priv_list) { 02157 $space_punct_listsep = $html_line_break; 02158 $space_alpha_reserved_word = $html_line_break; 02159 } 02160 break; 02161 case 'VALUES': 02162 $space_punct_listsep = ' '; 02163 $space_alpha_reserved_word = $html_line_break; 02164 break; 02165 case 'SELECT': 02166 $space_punct_listsep = ' '; 02167 $space_alpha_reserved_word = $html_line_break; 02168 break; 02169 default: 02170 break; 02171 } // end switch ($arr[$i]['data']) 02172 02173 $after .= ' '; 02174 break; 02175 case 'digit_integer': 02176 case 'digit_float': 02177 case 'digit_hex': 02178 //TODO: could there be other types preceding a digit? 02179 if ($typearr[1] == 'alpha_reservedWord') { 02180 $after .= ' '; 02181 } 02182 if ($infunction && $typearr[3] == 'punct_bracket_close_round') { 02183 $after .= ' '; 02184 } 02185 if ($typearr[1] == 'alpha_columnAttrib') { 02186 $before .= ' '; 02187 } 02188 break; 02189 case 'alpha_variable': 02190 // other workaround for a problem similar to the one 02191 // explained below for quote_single 02192 if (!$in_priv_list) { 02193 $after = ' '; 02194 } 02195 break; 02196 case 'quote_double': 02197 case 'quote_single': 02198 // workaround: for the query 02199 // REVOKE SELECT ON `base2\_db`.* FROM 'user'@'%' 02200 // the @ is incorrectly marked as alpha_variable 02201 // in the parser, and here, the '%' gets a blank before, 02202 // which is a syntax error 02203 if ($typearr[1] !='alpha_variable') { 02204 $before .= ' '; 02205 } 02206 if ($infunction && $typearr[3] == 'punct_bracket_close_round') { 02207 $after .= ' '; 02208 } 02209 break; 02210 case 'quote_backtick': 02211 if ($typearr[3] != 'punct_qualifier') { 02212 $after .= ' '; 02213 } 02214 if ($typearr[1] != 'punct_qualifier') { 02215 $before .= ' '; 02216 } 02217 break; 02218 default: 02219 break; 02220 } // end switch ($typearr[2]) 02221 02222 /* 02223 if ($typearr[3] != 'punct_qualifier') { 02224 $after .= ' '; 02225 } 02226 $after .= "\n"; 02227 */ 02228 $str .= $before . ($mode=='color' ? PMA_SQP_formatHTML_colorize($arr[$i]) : $arr[$i]['data']). $after; 02229 } // end for 02230 if ($mode=='color') { 02231 $str .= '</span>'; 02232 } 02233 02234 return $str; 02235 } // end of the "PMA_SQP_formatHtml()" function 02236 } 02237 02251 function PMA_SQP_buildCssRule($classname, $property, $value) 02252 { 02253 $str = '.' . $classname . ' {'; 02254 if ($value != '') { 02255 $str .= $property . ': ' . $value . ';'; 02256 } 02257 $str .= '}' . "\n"; 02258 02259 return $str; 02260 } // end of the "PMA_SQP_buildCssRule()" function 02261 02262 02274 function PMA_SQP_buildCssData() 02275 { 02276 global $cfg; 02277 02278 $css_string = ''; 02279 foreach ($cfg['SQP']['fmtColor'] AS $key => $col) { 02280 $css_string .= PMA_SQP_buildCssRule('syntax_' . $key, 'color', $col); 02281 } 02282 02283 for ($i = 0; $i < 8; $i++) { 02284 $css_string .= PMA_SQP_buildCssRule('syntax_indent' . $i, 'margin-left', ($i * $cfg['SQP']['fmtInd']) . $cfg['SQP']['fmtIndUnit']); 02285 } 02286 02287 return $css_string; 02288 } // end of the "PMA_SQP_buildCssData()" function 02289 02290 if ($is_minimum_common == FALSE) { 02300 function PMA_SQP_formatNone($arr) 02301 { 02302 $formatted_sql = htmlspecialchars($arr['raw']); 02303 $formatted_sql = preg_replace("@((\015\012)|(\015)|(\012)){3,}@", "\n\n", $formatted_sql); 02304 02305 return $formatted_sql; 02306 } // end of the "PMA_SQP_formatNone()" function 02307 02308 02318 function PMA_SQP_formatText($arr) 02319 { 02323 return PMA_SQP_formatNone($arr); 02324 } // end of the "PMA_SQP_formatText()" function 02325 } // end if: minimal common.lib needed? 02326 02327 ?>