Documentation TYPO3 par Ameos |
00001 <?php 00002 /* $Id: sqlparser.lib.php,v 2.6 2004/01/09 23:17:21 rabus 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.6 2004/01/09 23:17:21 rabus 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 00210 // rabus: Convert all line feeds to Unix style 00211 $sql = str_replace("\r\n", "\n", $sql); 00212 $sql = str_replace("\r", "\n", $sql); 00213 00214 $len = $GLOBALS['PMA_strlen']($sql); 00215 if ($len == 0) { 00216 return array(); 00217 } 00218 00219 $sql_array = array(); 00220 $sql_array['raw'] = $sql; 00221 $count1 = 0; 00222 $count2 = 0; 00223 $punct_queryend = ';'; 00224 $punct_qualifier = '.'; 00225 $punct_listsep = ','; 00226 $punct_level_plus = '('; 00227 $punct_level_minus = ')'; 00228 $digit_floatdecimal = '.'; 00229 $digit_hexset = 'x'; 00230 $bracket_list = '()[]{}'; 00231 $allpunct_list = '-,;:!?/.^~\*&%+<=>|'; 00232 $allpunct_list_pair = array ( 00233 0 => '!=', 00234 1 => '&&', 00235 2 => ':=', 00236 3 => '<<', 00237 4 => '<=', 00238 5 => '<=>', 00239 6 => '<>', 00240 7 => '>=', 00241 8 => '>>', 00242 9 => '||' 00243 ); 00244 $allpunct_list_pair_size = 10; //count($allpunct_list_pair); 00245 $quote_list = '\'"`'; 00246 $arraysize = 0; 00247 00248 while ($count2 < $len) { 00249 $c = $sql[$count2]; 00250 $count1 = $count2; 00251 00252 if (($c == "\n")) { 00253 $count2++; 00254 PMA_SQP_arrayAdd($sql_array, 'white_newline', '', $arraysize); 00255 continue; 00256 } 00257 00258 // Checks for white space 00259 if (PMA_STR_isSpace($c)) { 00260 $count2++; 00261 continue; 00262 } 00263 00264 // Checks for comment lines. 00265 // MySQL style # 00266 // C style /* */ 00267 // ANSI style -- 00268 if (($c == '#') 00269 || (($count2 + 1 < $len) && ($c == '/') && ($sql[$count2 + 1] == '*')) 00270 || (($count2 + 2 < $len) && ($c == '-') && ($sql[$count2 + 1] == '-') && (($sql[$count2 + 2] == ' ') || ($sql[$count2 + 2] == "\n")))) { 00271 $count2++; 00272 $pos = 0; 00273 $type = 'bad'; 00274 switch ($c) { 00275 case '#': 00276 $type = 'mysql'; 00277 case '-': 00278 $type = 'ansi'; 00279 $pos = $GLOBALS['PMA_strpos']($sql, "\n", $count2); 00280 break; 00281 case '/': 00282 $type = 'c'; 00283 $pos = $GLOBALS['PMA_strpos']($sql, '*/', $count2); 00284 $pos += 2; 00285 break; 00286 default: 00287 break; 00288 } // end switch 00289 $count2 = ($pos < $count2) ? $len : $pos; 00290 $str = $GLOBALS['PMA_substr']($sql, $count1, $count2 - $count1); 00291 PMA_SQP_arrayAdd($sql_array, 'comment_' . $type, $str, $arraysize); 00292 continue; 00293 } // end if 00294 00295 // Checks for something inside quotation marks 00296 if (PMA_STR_strInStr($c, $quote_list)) { 00297 $startquotepos = $count2; 00298 $quotetype = $c; 00299 $count2++; 00300 $escaped = FALSE; 00301 $escaped_escaped = FALSE; 00302 $pos = $count2; 00303 $oldpos = 0; 00304 do { 00305 $oldpos = $pos; 00306 $pos = $GLOBALS['PMA_strpos'](' ' . $sql, $quotetype, $oldpos + 1) - 1; 00307 // ($pos === FALSE) 00308 if ($pos < 0) { 00309 $debugstr = $GLOBALS['strSQPBugUnclosedQuote'] . ' @ ' . $startquotepos. "\n" 00310 . 'STR: ' . $quotetype; 00311 PMA_SQP_throwError($debugstr, $sql); 00312 return $sql; 00313 } 00314 00315 // If the quote is the first character, it can't be 00316 // escaped, so don't do the rest of the code 00317 if ($pos == 0) { 00318 break; 00319 } 00320 00321 // Checks for MySQL escaping using a \ 00322 // And checks for ANSI escaping using the $quotetype character 00323 if (($pos < $len) && PMA_STR_charIsEscaped($sql, $pos)) { 00324 $pos ++; 00325 continue; 00326 } else if (($pos + 1 < $len) && ($sql[$pos] == $quotetype) && ($sql[$pos + 1] == $quotetype)) { 00327 $pos = $pos + 2; 00328 continue; 00329 } else { 00330 break; 00331 } 00332 } while ($len > $pos); // end do 00333 00334 $count2 = $pos; 00335 $count2++; 00336 $type = 'quote_'; 00337 switch ($quotetype) { 00338 case '\'': 00339 $type .= 'single'; 00340 break; 00341 case '"': 00342 $type .= 'double'; 00343 break; 00344 case '`': 00345 $type .= 'backtick'; 00346 break; 00347 default: 00348 break; 00349 } // end switch 00350 $data = $GLOBALS['PMA_substr']($sql, $count1, $count2 - $count1); 00351 PMA_SQP_arrayAdd($sql_array, $type, $data, $arraysize); 00352 continue; 00353 } 00354 00355 // Checks for brackets 00356 if (PMA_STR_strInStr($c, $bracket_list)) { 00357 // All bracket tokens are only one item long 00358 $count2++; 00359 $type_type = ''; 00360 if (PMA_STR_strInStr($c, '([{')) { 00361 $type_type = 'open'; 00362 } else { 00363 $type_type = 'close'; 00364 } 00365 00366 $type_style = ''; 00367 if (PMA_STR_strInStr($c, '()')) { 00368 $type_style = 'round'; 00369 } elseif (PMA_STR_strInStr($c, '[]')) { 00370 $type_style = 'square'; 00371 } else { 00372 $type_style = 'curly'; 00373 } 00374 00375 $type = 'punct_bracket_' . $type_type . '_' . $type_style; 00376 PMA_SQP_arrayAdd($sql_array, $type, $c, $arraysize); 00377 continue; 00378 } 00379 00380 // Checks for punct 00381 if (PMA_STR_strInStr($c, $allpunct_list)) { 00382 while (($count2 < $len) && PMA_STR_strInStr($sql[$count2], $allpunct_list)) { 00383 $count2++; 00384 } 00385 $l = $count2 - $count1; 00386 if ($l == 1) { 00387 $punct_data = $c; 00388 } else { 00389 $punct_data = $GLOBALS['PMA_substr']($sql, $count1, $l); 00390 } 00391 00392 // Special case, sometimes, althought two characters are 00393 // adjectent directly, they ACTUALLY need to be seperate 00394 if ($l == 1) { 00395 $t_suffix = ''; 00396 switch ($punct_data) { 00397 case $punct_queryend: 00398 $t_suffix = '_queryend'; 00399 break; 00400 case $punct_qualifier: 00401 $t_suffix = '_qualifier'; 00402 break; 00403 case $punct_listsep: 00404 $t_suffix = '_listsep'; 00405 break; 00406 default: 00407 break; 00408 } 00409 PMA_SQP_arrayAdd($sql_array, 'punct' . $t_suffix, $punct_data, $arraysize); 00410 } 00411 else if (PMA_STR_binarySearchInArr($punct_data, $allpunct_list_pair, $allpunct_list_pair_size)) { 00412 // Ok, we have one of the valid combined punct expressions 00413 PMA_SQP_arrayAdd($sql_array, 'punct', $punct_data, $arraysize); 00414 } 00415 else { 00416 // Bad luck, lets split it up more 00417 $first = $punct_data[0]; 00418 $first2 = $punct_data[0] . $punct_data[1]; 00419 $last2 = $punct_data[$l - 2] . $punct_data[$l - 1]; 00420 $last = $punct_data[$l - 1]; 00421 if (($first == ',') || ($first == ';') || ($first == '.') || ($first == '*')) { 00422 $count2 = $count1 + 1; 00423 $punct_data = $first; 00424 } else if (($last2 == '/*') || ($last2 == '--')) { 00425 $count2 -= 2; 00426 $punct_data = $GLOBALS['PMA_substr']($sql, $count1, $count2 - $count1); 00427 } else if (($last == '-') || ($last == '+') || ($last == '!')) { 00428 $count2--; 00429 $punct_data = $GLOBALS['PMA_substr']($sql, $count1, $count2 - $count1); 00430 // TODO: for negation operator, split in 2 tokens ? 00431 // "select x&~1 from t" 00432 // becomes "select x & ~ 1 from t" ? 00433 00434 } else if ($last != '~') { 00435 $debugstr = $GLOBALS['strSQPBugUnknownPunctuation'] . ' @ ' . ($count1+1) . "\n" 00436 . 'STR: ' . $punct_data; 00437 PMA_SQP_throwError($debugstr, $sql); 00438 return $sql; 00439 } 00440 PMA_SQP_arrayAdd($sql_array, 'punct', $punct_data, $arraysize); 00441 continue; 00442 } // end if... else if... else 00443 continue; 00444 } 00445 00446 // Checks for alpha 00447 if (PMA_STR_isSqlIdentifier($c, FALSE) || ($c == '@')) { 00448 $count2 ++; 00449 00450 //TODO: a @ can also be present in expressions like 00451 // FROM 'user'@'%' 00452 // in this case, the @ is wrongly marked as alpha_variable 00453 00454 $is_sql_variable = ($c == '@'); 00455 $is_digit = (!$is_sql_variable) && PMA_STR_isDigit($c); 00456 $is_hex_digit = ($is_digit) && ($c == '0') && ($count2 < $len) && ($sql[$count2] == 'x'); 00457 $is_float_digit = FALSE; 00458 $is_float_digit_exponent = FALSE; 00459 00460 if ($is_hex_digit) { 00461 $count2++; 00462 } 00463 00464 while (($count2 < $len) && PMA_STR_isSqlIdentifier($sql[$count2], ($is_sql_variable || $is_digit))) { 00465 $c2 = $sql[$count2]; 00466 if ($is_sql_variable && ($c2 == '.')) { 00467 $count2++; 00468 continue; 00469 } 00470 if ($is_digit && (!$is_hex_digit) && ($c2 == '.')) { 00471 $count2++; 00472 if (!$is_float_digit) { 00473 $is_float_digit = TRUE; 00474 continue; 00475 } else { 00476 $debugstr = $GLOBALS['strSQPBugInvalidIdentifer'] . ' @ ' . ($count1+1) . "\n" 00477 . 'STR: ' . $GLOBALS['PMA_substr']($sql, $count1, $count2 - $count1); 00478 PMA_SQP_throwError($debugstr, $sql); 00479 return $sql; 00480 } 00481 } 00482 if ($is_digit && (!$is_hex_digit) && (($c2 == 'e') || ($c2 == 'E'))) { 00483 if (!$is_float_digit_exponent) { 00484 $is_float_digit_exponent = TRUE; 00485 $is_float_digit = TRUE; 00486 $count2++; 00487 continue; 00488 } else { 00489 $is_digit = FALSE; 00490 $is_float_digit = FALSE; 00491 } 00492 } 00493 if (($is_hex_digit && PMA_STR_isHexDigit($c2)) || ($is_digit && PMA_STR_isDigit($c2))) { 00494 $count2++; 00495 continue; 00496 } else { 00497 $is_digit = FALSE; 00498 $is_hex_digit = FALSE; 00499 } 00500 00501 $count2++; 00502 } // end while 00503 00504 $l = $count2 - $count1; 00505 $str = $GLOBALS['PMA_substr']($sql, $count1, $l); 00506 00507 $type = ''; 00508 if ($is_digit) { 00509 $type = 'digit'; 00510 if ($is_float_digit) { 00511 $type .= '_float'; 00512 } else if ($is_hex_digit) { 00513 $type .= '_hex'; 00514 } else { 00515 $type .= '_integer'; 00516 } 00517 } 00518 else { 00519 if ($is_sql_variable != FALSE) { 00520 $type = 'alpha_variable'; 00521 } else { 00522 $type = 'alpha'; 00523 } 00524 } // end if... else.... 00525 PMA_SQP_arrayAdd($sql_array, $type, $str, $arraysize); 00526 00527 continue; 00528 } 00529 00530 // DEBUG 00531 $count2++; 00532 00533 $debugstr = 'C1 C2 LEN: ' . $count1 . ' ' . $count2 . ' ' . $len . "\n" 00534 . 'STR: ' . $GLOBALS['PMA_substr']($sql, $count1, $count2 - $count1) . "\n"; 00535 PMA_SQP_bug($debugstr, $sql); 00536 return $sql; 00537 00538 } // end while ($count2 < $len) 00539 00540 00541 if ($arraysize > 0) { 00542 $t_next = $sql_array[0]['type']; 00543 $t_prev = ''; 00544 $t_bef_prev = ''; 00545 $t_cur = ''; 00546 $d_next = $sql_array[0]['data']; 00547 $d_prev = ''; 00548 $d_bef_prev = ''; 00549 $d_cur = ''; 00550 $d_next_upper = $t_next == 'alpha' ? strtoupper($d_next) : $d_next; 00551 $d_prev_upper = ''; 00552 $d_bef_prev_upper = ''; 00553 $d_cur_upper = ''; 00554 } 00555 00556 for ($i = 0; $i < $arraysize; $i++) { 00557 $t_bef_prev = $t_prev; 00558 $t_prev = $t_cur; 00559 $t_cur = $t_next; 00560 $d_bef_prev = $d_prev; 00561 $d_prev = $d_cur; 00562 $d_cur = $d_next; 00563 $d_bef_prev_upper = $d_prev_upper; 00564 $d_prev_upper = $d_cur_upper; 00565 $d_cur_upper = $d_next_upper; 00566 if (($i + 1) < $arraysize) { 00567 $t_next = $sql_array[$i + 1]['type']; 00568 $d_next = $sql_array[$i + 1]['data']; 00569 $d_next_upper = $t_next == 'alpha' ? strtoupper($d_next) : $d_next; 00570 } else { 00571 $t_next = ''; 00572 $d_next = ''; 00573 $d_next_upper = ''; 00574 } 00575 00576 //DEBUG echo "[prev: <b>".$d_prev."</b> ".$t_prev."][cur: <b>".$d_cur."</b> ".$t_cur."][next: <b>".$d_next."</b> ".$t_next."]<br>"; 00577 00578 if ($t_cur == 'alpha') { 00579 $t_suffix = '_identifier'; 00580 if (($t_next == 'punct_qualifier') || ($t_prev == 'punct_qualifier')) { 00581 $t_suffix = '_identifier'; 00582 } else if (($t_next == 'punct_bracket_open_round') 00583 && PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_function_name, $PMA_SQPdata_function_name_cnt)) { 00584 $t_suffix = '_functionName'; 00585 } else if (PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_column_type, $PMA_SQPdata_column_type_cnt)) { 00586 $t_suffix = '_columnType'; 00587 00588 // Temporary fix for BUG #621357 00589 //TODO FIX PROPERLY NEEDS OVERHAUL OF SQL TOKENIZER 00590 if ($d_cur_upper == 'SET' && $t_next != 'punct_bracket_open_round') { 00591 $t_suffix = '_reservedWord'; 00592 } 00593 //END OF TEMPORARY FIX 00594 00595 // CHARACTER is a synonym for CHAR, but can also be meant as 00596 // CHARACTER SET. In this case, we have a reserved word. 00597 if ($d_cur_upper == 'CHARACTER' && $d_next_upper == 'SET') { 00598 $t_suffix = '_reservedWord'; 00599 } 00600 00601 // experimental 00602 // current is a column type, so previous must not be 00603 // a reserved word but an identifier 00604 // CREATE TABLE SG_Persons (first varchar(64)) 00605 00606 //if ($sql_array[$i-1]['type'] =='alpha_reservedWord') { 00607 // $sql_array[$i-1]['type'] = 'alpha_identifier'; 00608 //} 00609 00610 } else if (PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_reserved_word, $PMA_SQPdata_reserved_word_cnt)) { 00611 $t_suffix = '_reservedWord'; 00612 } else if (PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_column_attrib, $PMA_SQPdata_column_attrib_cnt)) { 00613 $t_suffix = '_columnAttrib'; 00614 // INNODB is a MySQL table type, but in "SHOW INNODB STATUS", 00615 // it should be regarded as a reserved word. 00616 if ($d_cur_upper == 'INNODB' && $d_prev_upper == 'SHOW' && $d_next_upper == 'STATUS') { 00617 $t_suffix = '_reservedWord'; 00618 } 00619 00620 if ($d_cur_upper == 'DEFAULT' && $d_next_upper == 'CHARACTER') { 00621 $t_suffix = '_reservedWord'; 00622 } 00623 // Binary as character set 00624 if ($d_cur_upper == 'BINARY' && ( 00625 ($d_bef_prev_upper == 'CHARACTER' && $d_prev_upper == 'SET') 00626 || ($d_bef_prev_upper == 'SET' && $d_prev_upper == '=') 00627 || ($d_bef_prev_upper == 'CHARSET' && $d_prev_upper == '=') 00628 || $prev_upper == 'CHARSET' 00629 ) && PMA_STR_binarySearchInArr($d_cur, $mysql_charsets, count($mysql_charsets))) { 00630 $t_suffix = '_charset'; 00631 } 00632 } elseif (PMA_STR_binarySearchInArr($d_cur, $mysql_charsets, $mysql_charsets_count) 00633 || PMA_STR_binarySearchInArr($d_cur, $mysql_collations_flat, $mysql_collations_count) 00634 || ($d_cur{0} == '_' && PMA_STR_binarySearchInArr(substr($d_cur, 1), $mysql_charsets, $mysql_charsets_count))) { 00635 $t_suffix = '_charset'; 00636 } else { 00637 // Do nothing 00638 } 00639 $sql_array[$i]['type'] .= $t_suffix; 00640 } 00641 } // end for 00642 00643 // Stores the size of the array inside the array, as count() is a slow 00644 // operation. 00645 $sql_array['len'] = $arraysize; 00646 00647 // Sends the data back 00648 return $sql_array; 00649 } // end of the "PMA_SQP_parse()" function 00650 00661 function PMA_SQP_typeCheck($toCheck, $whatWeWant) 00662 { 00663 $typeSeperator = '_'; 00664 if(strcmp($whatWeWant, $toCheck) == 0) { 00665 return TRUE; 00666 } else { 00667 //if(strpos($whatWeWant, $typeSeperator) === FALSE) { 00668 // PHP3 compatible (works unless there is a real ff character) 00669 if(!strpos("\xff" . $whatWeWant, $typeSeperator)) { 00670 return strncmp($whatWeWant, $toCheck , strpos($toCheck, $typeSeperator)) == 0; 00671 } else { 00672 return FALSE; 00673 } 00674 } 00675 } 00676 00677 00687 function PMA_SQP_analyze($arr) 00688 { 00689 $result = array(); 00690 $size = $arr['len']; 00691 $subresult = array( 00692 'querytype' => '', 00693 'select_expr_clause'=> '', // the whole stuff between SELECT and FROM , except DISTINCT 00694 'position_of_first_select' => '', // the array index 00695 'from_clause'=> '', 00696 'group_by_clause'=> '', 00697 'order_by_clause'=> '', 00698 'having_clause' => '', 00699 'where_clause' => '', 00700 'where_clause_identifiers' => array(), 00701 'queryflags' => array(), 00702 'select_expr' => array(), 00703 'table_ref' => array(), 00704 'foreign_keys' => array() 00705 ); 00706 $subresult_empty = $subresult; 00707 $seek_queryend = FALSE; 00708 $seen_end_of_table_ref = FALSE; 00709 00710 // for SELECT EXTRACT(YEAR_MONTH FROM CURDATE()) 00711 // we must not use CURDATE as a table_ref 00712 // so we track wether we are in the EXTRACT() 00713 $in_extract = FALSE; 00714 00715 /* Description of analyzer results 00716 * 00717 * lem9: db, table, column, alias 00718 * ------------------------ 00719 * 00720 * Inside the $subresult array, we create ['select_expr'] and ['table_ref'] arrays. 00721 * 00722 * The SELECT syntax (simplified) is 00723 * 00724 * SELECT 00725 * select_expression,... 00726 * [FROM [table_references] 00727 * 00728 * 00729 * ['select_expr'] is filled with each expression, the key represents the 00730 * expression position in the list (0-based) (so we don't lose track of 00731 * multiple occurences of the same column). 00732 * 00733 * ['table_ref'] is filled with each table ref, same thing for the key. 00734 * 00735 * I create all sub-values empty, even if they are 00736 * not present (for example no select_expression alias). 00737 * 00738 * There is a debug section at the end of loop #1, if you want to 00739 * see the exact contents of select_expr and table_ref 00740 * 00741 * lem9: queryflags 00742 * ---------- 00743 * 00744 * In $subresult, array 'queryflags' is filled, according to what we 00745 * find in the query. 00746 * 00747 * Currently, those are generated: 00748 * 00749 * ['queryflags']['need_confirm'] = 1; if the query needs confirmation 00750 * ['queryflags']['select_from'] = 1; if this is a real SELECT...FROM 00751 * ['queryflags']['distinct'] = 1; for a DISTINCT 00752 * ['queryflags']['union'] = 1; for a UNION 00753 * 00754 * lem9: query clauses 00755 * ------------- 00756 * 00757 * The select is splitted in those clauses: 00758 * ['select_expr_clause'] 00759 * ['from_clause'] 00760 * ['group_by_clause'] 00761 * ['order_by_clause'] 00762 * ['having_clause'] 00763 * ['where_clause'] 00764 * 00765 * and the identifiers of the where clause are put into the array 00766 * ['where_clause_identifier'] 00767 * 00768 * lem9: foreign keys 00769 * ------------ 00770 * The CREATE TABLE may contain FOREIGN KEY clauses, so they get 00771 * analyzed and ['foreign_keys'] is an array filled with 00772 * the constraint name, the index list, 00773 * the REFERENCES table name and REFERENCES index list, 00774 * and ON UPDATE | ON DELETE clauses 00775 * 00776 * lem9: position_of_first_select 00777 * ------------------------ 00778 * 00779 * The array index of the first SELECT we find. Will be used to 00780 * insert a SQL_CALC_FOUND_ROWS. 00781 */ 00782 00783 // must be sorted 00784 // TODO: current logic checks for only one word, so I put only the 00785 // first word of the reserved expressions that end a table ref; 00786 // maybe this is not ok (the first word might mean something else) 00787 // $words_ending_table_ref = array( 00788 // 'FOR UPDATE', 00789 // 'GROUP BY', 00790 // 'HAVING', 00791 // 'LIMIT', 00792 // 'LOCK IN SHARE MODE', 00793 // 'ORDER BY', 00794 // 'PROCEDURE', 00795 // 'UNION', 00796 // 'WHERE' 00797 // ); 00798 $words_ending_table_ref = array( 00799 'FOR', 00800 'GROUP', 00801 'HAVING', 00802 'LIMIT', 00803 'LOCK', 00804 'ORDER', 00805 'PROCEDURE', 00806 'UNION', 00807 'WHERE' 00808 ); 00809 $words_ending_table_ref_cnt = 9; //count($words_ending_table_ref); 00810 00811 $words_ending_clauses = array( 00812 'FOR', 00813 'LIMIT', 00814 'LOCK', 00815 'PROCEDURE', 00816 'UNION' 00817 ); 00818 $words_ending_clauses_cnt = 5; //count($words_ending_clauses); 00819 00820 00821 00822 00823 // must be sorted 00824 $supported_query_types = array( 00825 'SELECT' 00826 /* 00827 // Support for these additional query types will come later on. 00828 'DELETE', 00829 'INSERT', 00830 'REPLACE', 00831 'TRUNCATE', 00832 'UPDATE' 00833 'EXPLAIN', 00834 'DESCRIBE', 00835 'SHOW', 00836 'CREATE', 00837 'SET', 00838 'ALTER' 00839 */ 00840 ); 00841 $supported_query_types_cnt = count($supported_query_types); 00842 00843 // loop #1 for each token: select_expr, table_ref for SELECT 00844 00845 for ($i = 0; $i < $size; $i++) { 00846 //echo "trace <b>" . $arr[$i]['data'] . "</b> (" . $arr[$i]['type'] . ")<br>"; 00847 00848 // High speed seek for locating the end of the current query 00849 if ($seek_queryend == TRUE) { 00850 if ($arr[$i]['type'] == 'punct_queryend') { 00851 $seek_queryend = FALSE; 00852 } else { 00853 continue; 00854 } // end if (type == punct_queryend) 00855 } // end if ($seek_queryend) 00856 00857 // TODO: when we find a UNION, should we split 00858 // in another subresult? 00859 if ($arr[$i]['type'] == 'punct_queryend') { 00860 $result[] = $subresult; 00861 $subresult = $subresult_empty; 00862 continue; 00863 } // end if (type == punct_queryend) 00864 00865 // ============================================================== 00866 if ($arr[$i]['type'] == 'punct_bracket_open_round') { 00867 if ($in_extract) { 00868 $number_of_brackets_in_extract++; 00869 } 00870 } 00871 // ============================================================== 00872 if ($arr[$i]['type'] == 'punct_bracket_close_round') { 00873 if ($in_extract) { 00874 $number_of_brackets_in_extract--; 00875 if ($number_of_brackets_in_extract == 0) { 00876 $in_extract = FALSE; 00877 } 00878 } 00879 } 00880 // ============================================================== 00881 if ($arr[$i]['type'] == 'alpha_functionName') { 00882 $upper_data = strtoupper($arr[$i]['data']); 00883 if ($upper_data =='EXTRACT') { 00884 $in_extract = TRUE; 00885 $number_of_brackets_in_extract = 0; 00886 } 00887 } 00888 00889 // ============================================================== 00890 if ($arr[$i]['type'] == 'alpha_reservedWord') { 00891 // We don't know what type of query yet, so run this 00892 if ($subresult['querytype'] == '') { 00893 $subresult['querytype'] = strtoupper($arr[$i]['data']); 00894 } // end if (querytype was empty) 00895 00896 // Check if we support this type of query 00897 if (!PMA_STR_binarySearchInArr($subresult['querytype'], $supported_query_types, $supported_query_types_cnt)) { 00898 // Skip ahead to the next one if we don't 00899 $seek_queryend = TRUE; 00900 continue; 00901 } // end if (query not supported) 00902 00903 // upper once 00904 $upper_data = strtoupper($arr[$i]['data']); 00905 //TODO: reset for each query? 00906 00907 if ($upper_data == 'SELECT') { 00908 $seen_from = FALSE; 00909 $previous_was_identifier = FALSE; 00910 $current_select_expr = -1; 00911 $seen_end_of_table_ref = FALSE; 00912 } // end if ( data == SELECT) 00913 00914 if ($upper_data =='FROM' && !$in_extract) { 00915 $current_table_ref = -1; 00916 $seen_from = TRUE; 00917 $previous_was_identifier = FALSE; 00918 $save_table_ref = TRUE; 00919 } // end if (data == FROM) 00920 00921 // here, do not 'continue' the loop, as we have more work for 00922 // reserved words below 00923 } // end if (type == alpha_reservedWord) 00924 00925 // ============================== 00926 if (($arr[$i]['type'] == 'quote_backtick') 00927 || ($arr[$i]['type'] == 'quote_double') 00928 || ($arr[$i]['type'] == 'quote_single') 00929 || ($arr[$i]['type'] == 'alpha_identifier')) { 00930 00931 switch ($arr[$i]['type']) { 00932 case 'alpha_identifier': 00933 $identifier = $arr[$i]['data']; 00934 break; 00935 00936 //TODO: check embedded double quotes or backticks? 00937 // and/or remove just the first and last character? 00938 case 'quote_backtick': 00939 $identifier = str_replace('`','',$arr[$i]['data']); 00940 break; 00941 case 'quote_double': 00942 $identifier = str_replace('"','',$arr[$i]['data']); 00943 break; 00944 case 'quote_single': 00945 $identifier = str_replace("'","",$arr[$i]['data']); 00946 break; 00947 } // end switch 00948 00949 if ($subresult['querytype'] == 'SELECT') { 00950 if (!$seen_from) { 00951 if ($previous_was_identifier && isset($chain)) { 00952 // found alias for this select_expr, save it 00953 // but only if we got something in $chain 00954 // (for example, SELECT COUNT(*) AS cnt 00955 // puts nothing in $chain, so we avoid 00956 // setting the alias) 00957 $alias_for_select_expr = $identifier; 00958 } else { 00959 $chain[] = $identifier; 00960 $previous_was_identifier = TRUE; 00961 00962 } // end if !$previous_was_identifier 00963 } else { 00964 // ($seen_from) 00965 if ($save_table_ref && !$seen_end_of_table_ref) { 00966 if ($previous_was_identifier) { 00967 // found alias for table ref 00968 // save it for later 00969 $alias_for_table_ref = $identifier; 00970 } else { 00971 $chain[] = $identifier; 00972 $previous_was_identifier = TRUE; 00973 00974 } // end if ($previous_was_identifier) 00975 } // end if ($save_table_ref &&!$seen_end_of_table_ref) 00976 } // end if (!$seen_from) 00977 } // end if (querytype SELECT) 00978 } // end if ( quote_backtick or double quote or alpha_identifier) 00979 00980 // =================================== 00981 if ($arr[$i]['type'] == 'punct_qualifier') { 00982 // to be able to detect an identifier following another 00983 $previous_was_identifier = FALSE; 00984 continue; 00985 } // end if (punct_qualifier) 00986 00987 // TODO: check if 3 identifiers following one another -> error 00988 00989 // s a v e a s e l e c t e x p r 00990 // finding a list separator or FROM 00991 // means that we must save the current chain of identifiers 00992 // into a select expression 00993 00994 // for now, we only save a select expression if it contains 00995 // at least one identifier, as we are interested in checking 00996 // the columns and table names, so in "select * from persons", 00997 // the "*" is not saved 00998 00999 if (isset($chain) && !$seen_end_of_table_ref 01000 && ( (!$seen_from 01001 && $arr[$i]['type'] == 'punct_listsep') 01002 || ($arr[$i]['type'] == 'alpha_reservedWord' && $upper_data == 'FROM')) ) { 01003 $size_chain = count($chain); 01004 $current_select_expr++; 01005 $subresult['select_expr'][$current_select_expr] = array( 01006 'expr' => '', 01007 'alias' => '', 01008 'db' => '', 01009 'table_name' => '', 01010 'table_true_name' => '', 01011 'column' => '' 01012 ); 01013 01014 if (!empty($alias_for_select_expr)) { 01015 // we had found an alias for this select expression 01016 $subresult['select_expr'][$current_select_expr]['alias'] = $alias_for_select_expr; 01017 unset($alias_for_select_expr); 01018 } 01019 // there is at least a column 01020 $subresult['select_expr'][$current_select_expr]['column'] = $chain[$size_chain - 1]; 01021 $subresult['select_expr'][$current_select_expr]['expr'] = $chain[$size_chain - 1]; 01022 01023 // maybe a table 01024 if ($size_chain > 1) { 01025 $subresult['select_expr'][$current_select_expr]['table_name'] = $chain[$size_chain - 2]; 01026 // we assume for now that this is also the true name 01027 $subresult['select_expr'][$current_select_expr]['table_true_name'] = $chain[$size_chain - 2]; 01028 $subresult['select_expr'][$current_select_expr]['expr'] 01029 = $subresult['select_expr'][$current_select_expr]['table_name'] 01030 . '.' . $subresult['select_expr'][$current_select_expr]['expr']; 01031 } // end if ($size_chain > 1) 01032 01033 // maybe a db 01034 if ($size_chain > 2) { 01035 $subresult['select_expr'][$current_select_expr]['db'] = $chain[$size_chain - 3]; 01036 $subresult['select_expr'][$current_select_expr]['expr'] 01037 = $subresult['select_expr'][$current_select_expr]['db'] 01038 . '.' . $subresult['select_expr'][$current_select_expr]['expr']; 01039 } // end if ($size_chain > 2) 01040 unset($chain); 01041 01042 // TODO: explain this: 01043 if (($arr[$i]['type'] == 'alpha_reservedWord') 01044 && ($upper_data != 'FROM')) { 01045 $previous_was_identifier = TRUE; 01046 } 01047 01048 } // end if (save a select expr) 01049 01050 01051 //====================================== 01052 // s a v e a t a b l e r e f 01053 //====================================== 01054 01055 // maybe we just saw the end of table refs 01056 // but the last table ref has to be saved 01057 // or we are at the last token (TODO: there could be another 01058 // query after this one) 01059 // or we just got a reserved word 01060 01061 if (isset($chain) && $seen_from && $save_table_ref 01062 && ($arr[$i]['type'] == 'punct_listsep' 01063 || ($arr[$i]['type'] == 'alpha_reservedWord' && $upper_data!="AS") 01064 || $seen_end_of_table_ref 01065 || $i==$size-1 )) { 01066 01067 $size_chain = count($chain); 01068 $current_table_ref++; 01069 $subresult['table_ref'][$current_table_ref] = array( 01070 'expr' => '', 01071 'db' => '', 01072 'table_name' => '', 01073 'table_alias' => '', 01074 'table_true_name' => '' 01075 ); 01076 if (!empty($alias_for_table_ref)) { 01077 $subresult['table_ref'][$current_table_ref]['table_alias'] = $alias_for_table_ref; 01078 unset($alias_for_table_ref); 01079 } 01080 $subresult['table_ref'][$current_table_ref]['table_name'] = $chain[$size_chain - 1]; 01081 // we assume for now that this is also the true name 01082 $subresult['table_ref'][$current_table_ref]['table_true_name'] = $chain[$size_chain - 1]; 01083 $subresult['table_ref'][$current_table_ref]['expr'] 01084 = $subresult['table_ref'][$current_table_ref]['table_name']; 01085 // maybe a db 01086 if ($size_chain > 1) { 01087 $subresult['table_ref'][$current_table_ref]['db'] = $chain[$size_chain - 2]; 01088 $subresult['table_ref'][$current_table_ref]['expr'] 01089 = $subresult['table_ref'][$current_table_ref]['db'] 01090 . '.' . $subresult['table_ref'][$current_table_ref]['expr']; 01091 } // end if ($size_chain > 1) 01092 01093 // add the table alias into the whole expression 01094 $subresult['table_ref'][$current_table_ref]['expr'] 01095 .= ' ' . $subresult['table_ref'][$current_table_ref]['table_alias']; 01096 01097 unset($chain); 01098 $previous_was_identifier = TRUE; 01099 //continue; 01100 01101 } // end if (save a table ref) 01102 01103 01104 // when we have found all table refs, 01105 // for each table_ref alias, put the true name of the table 01106 // in the corresponding select expressions 01107 01108 if (isset($current_table_ref) && ($seen_end_of_table_ref || $i == $size-1)) { 01109 for ($tr=0; $tr <= $current_table_ref; $tr++) { 01110 $alias = $subresult['table_ref'][$tr]['table_alias']; 01111 $truename = $subresult['table_ref'][$tr]['table_true_name']; 01112 for ($se=0; $se <= $current_select_expr; $se++) { 01113 if (!empty($alias) && $subresult['select_expr'][$se]['table_true_name'] 01114 == $alias) { 01115 $subresult['select_expr'][$se]['table_true_name'] 01116 = $truename; 01117 } // end if (found the alias) 01118 } // end for (select expressions) 01119 01120 } // end for (table refs) 01121 } // end if (set the true names) 01122 01123 01124 // e n d i n g l o o p #1 01125 // set the $previous_was_identifier to FALSE if the current 01126 // token is not an identifier 01127 if (($arr[$i]['type'] != 'alpha_identifier') 01128 && ($arr[$i]['type'] != 'quote_double') 01129 && ($arr[$i]['type'] != 'quote_single') 01130 && ($arr[$i]['type'] != 'quote_backtick')) { 01131 $previous_was_identifier = FALSE; 01132 } // end if 01133 01134 // however, if we are on AS, we must keep the $previous_was_identifier 01135 if (($arr[$i]['type'] == 'alpha_reservedWord') 01136 && ($upper_data == 'AS')) { 01137 $previous_was_identifier = TRUE; 01138 } 01139 01140 if (($arr[$i]['type'] == 'alpha_reservedWord') 01141 && ($upper_data =='ON' || $upper_data =='USING')) { 01142 $save_table_ref = FALSE; 01143 } // end if (data == ON) 01144 01145 if (($arr[$i]['type'] == 'alpha_reservedWord') 01146 && ($upper_data =='JOIN' || $upper_data =='FROM')) { 01147 $save_table_ref = TRUE; 01148 } // end if (data == JOIN) 01149 01150 // no need to check the end of table ref if we already did 01151 // TODO: maybe add "&& $seen_from" 01152 if (!$seen_end_of_table_ref) { 01153 // if this is the last token, it implies that we have 01154 // seen the end of table references 01155 // Check for the end of table references 01156 if (($i == $size-1) 01157 || ($arr[$i]['type'] == 'alpha_reservedWord' 01158 && PMA_STR_binarySearchInArr($upper_data, $words_ending_table_ref, $words_ending_table_ref_cnt))) { 01159 $seen_end_of_table_ref = TRUE; 01160 01161 // to be able to save the last table ref, but do not 01162 // set it true if we found a word like "ON" that has 01163 // already set it to false 01164 if (isset($save_table_ref) && $save_table_ref != FALSE) { 01165 $save_table_ref = TRUE; 01166 } //end if 01167 01168 } // end if (check for end of table ref) 01169 } //end if (!$seen_end_of_table_ref) 01170 01171 if ($seen_end_of_table_ref) { 01172 $save_table_ref = FALSE; 01173 } // end if 01174 01175 } // end for $i (loop #1) 01176 01177 // ------------------------------------------------------- 01178 // This is a big hunk of debugging code by Marc for this. 01179 // ------------------------------------------------------- 01180 /* 01181 if (isset($current_select_expr)) { 01182 for ($trace=0; $trace<=$current_select_expr; $trace++) { 01183 01184 echo "<br>"; 01185 reset ($subresult['select_expr'][$trace]); 01186 while (list ($key, $val) = each ($subresult['select_expr'][$trace])) 01187 echo "sel expr $trace $key => $val<br />\n"; 01188 } 01189 } 01190 01191 if (isset($current_table_ref)) { 01192 for ($trace=0; $trace<=$current_table_ref; $trace++) { 01193 01194 echo "<br>"; 01195 reset ($subresult['table_ref'][$trace]); 01196 while (list ($key, $val) = each ($subresult['table_ref'][$trace])) 01197 echo "table ref $trace $key => $val<br />\n"; 01198 } 01199 } 01200 */ 01201 // ------------------------------------------------------- 01202 01203 01204 // loop #2: for queryflags 01205 // ,querytype (for queries != 'SELECT') 01206 // 01207 // we will also need this queryflag in loop 2 01208 // so set it here 01209 if (isset($current_table_ref) && $current_table_ref > -1) { 01210 $subresult['queryflags']['select_from'] = 1; 01211 } 01212 01213 $seen_reserved_word = FALSE; 01214 $seen_group = FALSE; 01215 $seen_order = FALSE; 01216 $in_group_by = FALSE; // true when we are into the GROUP BY clause 01217 $in_order_by = FALSE; // true when we are into the ORDER BY clause 01218 $in_having = FALSE; // true when we are into the HAVING clause 01219 $in_select_expr = FALSE; // true when we are into the select expr clause 01220 $in_where = FALSE; // true when we are into the WHERE clause 01221 $in_from = FALSE; 01222 01223 for ($i = 0; $i < $size; $i++) { 01224 //DEBUG echo "trace loop2 <b>" . $arr[$i]['data'] . "</b> (" . $arr[$i]['type'] . ")<br>"; 01225 01226 // need_confirm 01227 // 01228 // check for reserved words that will have to generate 01229 // a confirmation request later in sql.php 01230 // the cases are: 01231 // DROP TABLE 01232 // DROP DATABASE 01233 // ALTER TABLE... DROP 01234 // DELETE FROM... 01235 // 01236 // this code is not used for confirmations coming from functions.js 01237 01238 // TODO: check for punct_queryend 01239 01240 if ($arr[$i]['type'] == 'alpha_reservedWord') { 01241 $upper_data = strtoupper($arr[$i]['data']); 01242 if (!$seen_reserved_word) { 01243 $first_reserved_word = $upper_data; 01244 $subresult['querytype'] = $upper_data; 01245 $seen_reserved_word = TRUE; 01246 01247 // if the first reserved word is DROP or DELETE, 01248 // we know this is a query that needs to be confirmed 01249 if ($first_reserved_word=='DROP' 01250 || $first_reserved_word == 'DELETE' 01251 || $first_reserved_word == 'TRUNCATE') { 01252 $subresult['queryflags']['need_confirm'] = 1; 01253 } 01254 01255 if ($first_reserved_word=='SELECT'){ 01256 $position_of_first_select = $i; 01257 } 01258 01259 } else { 01260 if ($upper_data=='DROP' && $first_reserved_word=='ALTER') { 01261 $subresult['queryflags']['need_confirm'] = 1; 01262 } 01263 } 01264 01265 if ($upper_data == 'SELECT') { 01266 $in_select_expr = TRUE; 01267 $select_expr_clause = ''; 01268 } 01269 if ($upper_data == 'DISTINCT') { 01270 $subresult['queryflags']['distinct'] = 1; 01271 } 01272 01273 if ($upper_data == 'UNION') { 01274 $subresult['queryflags']['union'] = 1; 01275 } 01276 01277 // if this is a real SELECT...FROM 01278 if ($upper_data == 'FROM' && isset($subresult['queryflags']['select_from']) && $subresult['queryflags']['select_from'] == 1) { 01279 $in_from = TRUE; 01280 $from_clause = ''; 01281 $in_select_expr = FALSE; 01282 } 01283 01284 01285 // (we could have less resetting of variables to FALSE 01286 // if we trust that the query respects the standard 01287 // MySQL order for clauses) 01288 01289 // we use $seen_group and $seen_order because we are looking 01290 // for the BY 01291 if ($upper_data == 'GROUP') { 01292 $seen_group = TRUE; 01293 $seen_order = FALSE; 01294 $in_having = FALSE; 01295 $in_order_by = FALSE; 01296 $in_where = FALSE; 01297 $in_select_expr = FALSE; 01298 $in_from = FALSE; 01299 } 01300 if ($upper_data == 'ORDER') { 01301 $seen_order = TRUE; 01302 $seen_group = FALSE; 01303 $in_having = FALSE; 01304 $in_group_by = FALSE; 01305 $in_where = FALSE; 01306 $in_select_expr = FALSE; 01307 $in_from = FALSE; 01308 } 01309 if ($upper_data == 'HAVING') { 01310 $in_having = TRUE; 01311 $having_clause = ''; 01312 $seen_group = FALSE; 01313 $seen_order = FALSE; 01314 $in_group_by = FALSE; 01315 $in_order_by = FALSE; 01316 $in_where = FALSE; 01317 $in_select_expr = FALSE; 01318 $in_from = FALSE; 01319 } 01320 01321 if ($upper_data == 'WHERE') { 01322 $in_where = TRUE; 01323 $where_clause = ''; 01324 $where_clause_identifiers = array(); 01325 $seen_group = FALSE; 01326 $seen_order = FALSE; 01327 $in_group_by = FALSE; 01328 $in_order_by = FALSE; 01329 $in_having = FALSE; 01330 $in_select_expr = FALSE; 01331 $in_from = FALSE; 01332 } 01333 01334 if ($upper_data == 'BY') { 01335 if ($seen_group) { 01336 $in_group_by = TRUE; 01337 $group_by_clause = ''; 01338 } 01339 if ($seen_order) { 01340 $in_order_by = TRUE; 01341 $order_by_clause = ''; 01342 } 01343 } 01344 01345 // if we find one of the words that could end the clause 01346 if (PMA_STR_binarySearchInArr($upper_data, $words_ending_clauses, $words_ending_clauses_cnt)) { 01347 01348 $in_group_by = FALSE; 01349 $in_order_by = FALSE; 01350 $in_having = FALSE; 01351 $in_where = FALSE; 01352 $in_select_expr = FALSE; 01353 $in_from = FALSE; 01354 } 01355 01356 } // endif (reservedWord) 01357 01358 01359 // do not add a blank after a function name 01360 01361 $sep=' '; 01362 if ($arr[$i]['type'] == 'alpha_functionName') { 01363 $sep=''; 01364 } 01365 01366 if ($in_select_expr && $upper_data != 'SELECT' && $upper_data != 'DISTINCT') { 01367 $select_expr_clause .= $arr[$i]['data'] . $sep; 01368 } 01369 if ($in_from && $upper_data != 'FROM') { 01370 $from_clause .= $arr[$i]['data'] . $sep; 01371 } 01372 if ($in_group_by && $upper_data != 'GROUP' && $upper_data != 'BY') { 01373 $group_by_clause .= $arr[$i]['data'] . $sep; 01374 } 01375 if ($in_order_by && $upper_data != 'ORDER' && $upper_data != 'BY') { 01376 $order_by_clause .= $arr[$i]['data'] . $sep; 01377 } 01378 if ($in_having && $upper_data != 'HAVING') { 01379 $having_clause .= $arr[$i]['data'] . $sep; 01380 } 01381 if ($in_where && $upper_data != 'WHERE') { 01382 $where_clause .= $arr[$i]['data'] . $sep; 01383 01384 if (($arr[$i]['type'] == 'quote_backtick') 01385 || ($arr[$i]['type'] == 'alpha_identifier')) { 01386 $where_clause_identifiers[] = $arr[$i]['data']; 01387 } 01388 } 01389 01390 // clear $upper_data for next iteration 01391 $upper_data=''; 01392 01393 } // end for $i (loop #2) 01394 01395 // ----------------------------------------------------- 01396 // loop #3: foreign keys 01397 // (for now, check only the first query) 01398 // (for now, identifiers must be backquoted) 01399 01400 $seen_foreign = FALSE; 01401 $seen_references = FALSE; 01402 $seen_constraint = FALSE; 01403 $in_bracket = FALSE; 01404 $foreign_key_number = -1; 01405 01406 for ($i = 0; $i < $size; $i++) { 01407 // DEBUG echo "<b>" . $arr[$i]['data'] . "</b> " . $arr[$i]['type'] . "<br>"; 01408 if ($arr[$i]['type'] == 'alpha_reservedWord') { 01409 $upper_data = strtoupper($arr[$i]['data']); 01410 01411 if ($upper_data == 'CONSTRAINT') { 01412 $foreign_key_number++; 01413 $seen_foreign = FALSE; 01414 $seen_references = FALSE; 01415 $seen_constraint = TRUE; 01416 } 01417 if ($upper_data == 'FOREIGN') { 01418 $seen_foreign = TRUE; 01419 $seen_references = FALSE; 01420 $seen_constraint = FALSE; 01421 } 01422 if ($upper_data == 'REFERENCES') { 01423 $seen_foreign = FALSE; 01424 $seen_references = TRUE; 01425 $seen_constraint = FALSE; 01426 } 01427 01428 01429 // [ON DELETE {CASCADE | SET NULL | NO ACTION | RESTRICT}] 01430 // [ON UPDATE {CASCADE | SET NULL | NO ACTION | RESTRICT}] 01431 01432 // but we set ['on_delete'] or ['on_cascade'] to 01433 // CASCADE | SET_NULL | NO_ACTION | RESTRICT 01434 01435 if ($upper_data == 'ON') { 01436 unset($clause); 01437 if ($arr[$i+1]['type'] == 'alpha_reservedWord') { 01438 $second_upper_data = strtoupper($arr[$i+1]['data']); 01439 if ($second_upper_data == 'DELETE') { 01440 $clause = 'on_delete'; 01441 } 01442 if ($second_upper_data == 'UPDATE') { 01443 $clause = 'on_update'; 01444 } 01445 if (isset($clause) 01446 && ($arr[$i+2]['type'] == 'alpha_reservedWord' 01447 01448 // ugly workaround because currently, NO is not 01449 // in the list of reserved words in sqlparser.data 01450 // (we got a bug report about not being able to use 01451 // 'no' as an identifier) 01452 || ($arr[$i+2]['type'] == 'alpha_identifier' 01453 && strtoupper($arr[$i+2]['data'])=='NO') ) 01454 ) { 01455 $third_upper_data = strtoupper($arr[$i+2]['data']); 01456 if ($third_upper_data == 'CASCADE' 01457 || $third_upper_data == 'RESTRICT') { 01458 $value = $third_upper_data; 01459 } elseif ($third_upper_data == 'SET' 01460 || $third_upper_data == 'NO') { 01461 if ($arr[$i+3]['type'] == 'alpha_reservedWord') { 01462 $value = $third_upper_data . '_' . strtoupper($arr[$i+3]['data']); 01463 } 01464 } 01465 $foreign[$foreign_key_number][$clause] = $value; 01466 } 01467 } 01468 } 01469 01470 } 01471 01472 if ($arr[$i]['type'] == 'punct_bracket_open_round') { 01473 $in_bracket = TRUE; 01474 } 01475 01476 if ($arr[$i]['type'] == 'punct_bracket_close_round') { 01477 $in_bracket = FALSE; 01478 if ($seen_references) { 01479 $seen_references = FALSE; 01480 } 01481 } 01482 01483 if (($arr[$i]['type'] == 'quote_backtick')) { 01484 01485 if ($seen_constraint) { 01486 // remove backquotes 01487 $identifier = str_replace('`','',$arr[$i]['data']); 01488 $foreign[$foreign_key_number]['constraint'] = $identifier; 01489 } 01490 if ($seen_foreign && $in_bracket) { 01491 // remove backquotes 01492 $identifier = str_replace('`','',$arr[$i]['data']); 01493 $foreign[$foreign_key_number]['index_list'][] = $identifier; 01494 } 01495 01496 if ($seen_references) { 01497 $identifier = str_replace('`','',$arr[$i]['data']); 01498 if ($in_bracket) { 01499 $foreign[$foreign_key_number]['ref_index_list'][] = $identifier; 01500 } else { 01501 // identifier can be table or db.table 01502 $db_table = explode('.',$identifier); 01503 if (isset($db_table[1])) { 01504 $foreign[$foreign_key_number]['ref_db_name'] = $db_table[0]; 01505 $foreign[$foreign_key_number]['ref_table_name'] = $db_table[1]; 01506 } else { 01507 $foreign[$foreign_key_number]['ref_table_name'] = $db_table[0]; 01508 } 01509 } 01510 } 01511 } 01512 } // end for $i (loop #3) 01513 01514 if (isset($foreign)) { 01515 $subresult['foreign_keys'] = $foreign; 01516 } 01517 //DEBUG print_r($foreign); 01518 01519 if (isset($select_expr_clause)) { 01520 $subresult['select_expr_clause'] = $select_expr_clause; 01521 } 01522 if (isset($from_clause)) { 01523 $subresult['from_clause'] = $from_clause; 01524 } 01525 if (isset($group_by_clause)) { 01526 $subresult['group_by_clause'] = $group_by_clause; 01527 } 01528 if (isset($order_by_clause)) { 01529 $subresult['order_by_clause'] = $order_by_clause; 01530 } 01531 if (isset($having_clause)) { 01532 $subresult['having_clause'] = $having_clause; 01533 } 01534 if (isset($where_clause)) { 01535 $subresult['where_clause'] = $where_clause; 01536 } 01537 if (isset($where_clause_identifiers)) { 01538 $subresult['where_clause_identifiers'] = $where_clause_identifiers; 01539 } 01540 01541 if (isset($position_of_first_select)) { 01542 $subresult['position_of_first_select'] = $position_of_first_select; 01543 } 01544 01545 01546 // They are naughty and didn't have a trailing semi-colon, 01547 // then still handle it properly 01548 if ($subresult['querytype'] != '') { 01549 $result[] = $subresult; 01550 } 01551 return $result; 01552 } // end of the "PMA_SQP_analyze()" function 01553 01554 01564 function PMA_SQP_formatHtml_colorize($arr) 01565 { 01566 $i = $GLOBALS['PMA_strpos']($arr['type'], '_'); 01567 $class = ''; 01568 if ($i > 0) { 01569 $class = 'syntax_' . $GLOBALS['PMA_substr']($arr['type'], 0, $i) . ' '; 01570 } 01571 01572 $class .= 'syntax_' . $arr['type']; 01573 01574 //TODO: check why adding a "\n" after the </span> would cause extra 01575 // blanks to be displayed: 01576 // SELECT p . person_name 01577 01578 return '<span class="' . $class . '">' . htmlspecialchars($arr['data']) . '</span>'; 01579 } // end of the "PMA_SQP_formatHtml_colorize()" function 01580 01581 01594 function PMA_SQP_formatHtml($arr, $mode='color', $start_token=0, 01595 $number_of_tokens=-1) 01596 { 01597 // first check for the SQL parser having hit an error 01598 if (PMA_SQP_isError()) { 01599 return $arr; 01600 } 01601 // then check for an array 01602 if (!is_array($arr)) { 01603 return $arr; 01604 } 01605 // else do it properly 01606 switch ($mode) { 01607 case 'color': 01608 $str = '<span class="syntax">'; 01609 $html_line_break = '<br />'; 01610 break; 01611 case 'query_only': 01612 $str = ''; 01613 $html_line_break = "\n"; 01614 break; 01615 case 'text': 01616 $str = ''; 01617 $html_line_break = '<br />'; 01618 break; 01619 } // end switch 01620 $indent = 0; 01621 $bracketlevel = 0; 01622 $functionlevel = 0; 01623 $infunction = FALSE; 01624 $space_punct_listsep = ' '; 01625 $space_punct_listsep_function_name = ' '; 01626 // $space_alpha_reserved_word = '<br />'."\n"; 01627 $space_alpha_reserved_word = ' '; 01628 01629 $keywords_with_brackets_1before = array( 01630 'INDEX', 01631 'KEY', 01632 'ON', 01633 'USING' 01634 ); 01635 $keywords_with_brackets_1before_cnt = 4; 01636 01637 $keywords_with_brackets_2before = array( 01638 'IGNORE', 01639 'INDEX', 01640 'INTO', 01641 'KEY', 01642 'PRIMARY', 01643 'PROCEDURE', 01644 'REFERENCES', 01645 'UNIQUE', 01646 'USE' 01647 ); 01648 // $keywords_with_brackets_2before_cnt = count($keywords_with_brackets_2before); 01649 $keywords_with_brackets_2before_cnt = 9; 01650 01651 // These reserved words do NOT get a newline placed near them. 01652 $keywords_no_newline = array( 01653 'AND', 01654 'AS', 01655 'ASC', 01656 'DESC', 01657 'DISTINCT', 01658 'HOUR', 01659 'INTERVAL', 01660 'IS', 01661 'NOT', 01662 'NULL', 01663 'ON', 01664 'OR' 01665 ); 01666 $keywords_no_newline_cnt = 12; 01667 01668 // These reserved words introduce a privilege list 01669 $keywords_priv_list = array( 01670 'GRANT', 01671 'REVOKE' 01672 ); 01673 $keywords_priv_list_cnt = 2; 01674 01675 if ($number_of_tokens == -1) { 01676 $arraysize = $arr['len']; 01677 } else { 01678 $arraysize = $number_of_tokens; 01679 } 01680 $typearr = array(); 01681 if ($arraysize >= 0) { 01682 $typearr[0] = ''; 01683 $typearr[1] = ''; 01684 $typearr[2] = ''; 01685 //$typearr[3] = $arr[0]['type']; 01686 $typearr[3] = $arr[$start_token]['type']; 01687 } 01688 01689 $in_priv_list = FALSE; 01690 for ($i = $start_token; $i < $arraysize; $i++) { 01691 // DEBUG echo "<b>" . $arr[$i]['data'] . "</b> " . $arr[$i]['type'] . "<br />"; 01692 $before = ''; 01693 $after = ''; 01694 $indent = 0; 01695 // array_shift($typearr); 01696 /* 01697 0 prev2 01698 1 prev 01699 2 current 01700 3 next 01701 */ 01702 if (($i + 1) < $arraysize) { 01703 // array_push($typearr, $arr[$i + 1]['type']); 01704 $typearr[4] = $arr[$i + 1]['type']; 01705 } else { 01706 //array_push($typearr, NULL); 01707 $typearr[4] = ''; 01708 } 01709 01710 for ($j=0; $j<4; $j++) { 01711 $typearr[$j] = $typearr[$j + 1]; 01712 } 01713 01714 switch ($typearr[2]) { 01715 case 'white_newline': 01716 // $after = '<br />'; 01717 $before = ''; 01718 break; 01719 case 'punct_bracket_open_round': 01720 $bracketlevel++; 01721 $infunction = FALSE; 01722 // Make sure this array is sorted! 01723 if (($typearr[1] == 'alpha_functionName') || ($typearr[1] == 'alpha_columnType') || ($typearr[1] == 'punct') 01724 || ($typearr[3] == 'digit_integer') || ($typearr[3] == 'digit_hex') || ($typearr[3] == 'digit_float') 01725 || (($typearr[0] == 'alpha_reservedWord') 01726 && PMA_STR_binarySearchInArr(strtoupper($arr[$i - 2]['data']), $keywords_with_brackets_2before, $keywords_with_brackets_2before_cnt)) 01727 || (($typearr[1] == 'alpha_reservedWord') 01728 && PMA_STR_binarySearchInArr(strtoupper($arr[$i - 1]['data']), $keywords_with_brackets_1before, $keywords_with_brackets_1before_cnt)) 01729 ) { 01730 $functionlevel++; 01731 $infunction = TRUE; 01732 $after .= ' '; 01733 } else { 01734 $indent++; 01735 $after .= ($mode != 'query_only' ? '<div class="syntax_indent' . $indent . '">' : ' '); 01736 } 01737 break; 01738 case 'alpha_identifier': 01739 if (($typearr[1] == 'punct_qualifier') || ($typearr[3] == 'punct_qualifier')) { 01740 $after = ''; 01741 $before = ''; 01742 } 01743 if (($typearr[3] == 'alpha_columnType') || ($typearr[3] == 'alpha_identifier')) { 01744 $after .= ' '; 01745 } 01746 break; 01747 case 'punct_qualifier': 01748 $before = ''; 01749 $after = ''; 01750 break; 01751 case 'punct_listsep': 01752 if ($infunction == TRUE) { 01753 $after .= $space_punct_listsep_function_name; 01754 } else { 01755 $after .= $space_punct_listsep; 01756 } 01757 break; 01758 case 'punct_queryend': 01759 if (($typearr[3] != 'comment_mysql') && ($typearr[3] != 'comment_ansi') && $typearr[3] != 'comment_c') { 01760 $after .= $html_line_break; 01761 $after .= $html_line_break; 01762 } 01763 $space_punct_listsep = ' '; 01764 $space_punct_listsep_function_name = ' '; 01765 $space_alpha_reserved_word = ' '; 01766 $in_priv_list = FALSE; 01767 break; 01768 case 'comment_mysql': 01769 case 'comment_ansi': 01770 $after .= $html_line_break; 01771 break; 01772 case 'punct': 01773 $before .= ' '; 01774 // workaround for 01775 // select * from mytable limit 0,-1 01776 // (a side effect of this workaround is that 01777 // select 20 - 9 01778 // becomes 01779 // select 20 -9 01780 // ) 01781 if ($typearr[3] != 'digit_integer') { 01782 $after .= ' '; 01783 } 01784 break; 01785 case 'punct_bracket_close_round': 01786 $bracketlevel--; 01787 if ($infunction == TRUE) { 01788 $functionlevel--; 01789 $after .= ' '; 01790 $before .= ' '; 01791 } else { 01792 $indent--; 01793 $before .= ($mode != 'query_only' ? '</div>' : ' '); 01794 } 01795 $infunction = ($functionlevel > 0) ? TRUE : FALSE; 01796 break; 01797 case 'alpha_columnType': 01798 if ($typearr[3] == 'alpha_columnAttrib') { 01799 $after .= ' '; 01800 } 01801 if ($typearr[1] == 'alpha_columnType') { 01802 $before .= ' '; 01803 } 01804 break; 01805 case 'alpha_columnAttrib': 01806 01807 // ALTER TABLE tbl_name AUTO_INCREMENT = 1 01808 if ($typearr[1] == 'alpha_identifier') { 01809 $before .= ' '; 01810 } 01811 if (($typearr[3] == 'alpha_columnAttrib') || ($typearr[3] == 'quote_single') || ($typearr[3] == 'digit_integer')) { 01812 $after .= ' '; 01813 } 01814 // workaround for 01815 // select * from mysql.user where binary user="root" 01816 // binary is marked as alpha_columnAttrib 01817 // but should be marked as a reserved word 01818 if (strtoupper($arr[$i]['data']) == 'BINARY' 01819 && $typearr[3] == 'alpha_identifier') { 01820 $after .= ' '; 01821 } 01822 break; 01823 case 'alpha_reservedWord': 01824 $arr[$i]['data'] = strtoupper($arr[$i]['data']); 01825 if ((($typearr[1] != 'alpha_reservedWord') 01826 || (($typearr[1] == 'alpha_reservedWord') 01827 && PMA_STR_binarySearchInArr(strtoupper($arr[$i - 1]['data']), $keywords_no_newline, $keywords_no_newline_cnt))) 01828 && ($typearr[1] != 'punct_level_plus') 01829 && (!PMA_STR_binarySearchInArr($arr[$i]['data'], $keywords_no_newline, $keywords_no_newline_cnt))) { 01830 // do not put a space before the first token, because 01831 // we use a lot of eregi() checking for the first 01832 // reserved word at beginning of query 01833 // so do not put a newline before 01834 // 01835 // also we must not be inside a privilege list 01836 if ($i > 0) { 01837 if (!$in_priv_list) { 01838 $before .= $space_alpha_reserved_word; 01839 } 01840 } else { 01841 // on first keyword, check if it introduces a 01842 // privilege list 01843 if (PMA_STR_binarySearchInArr($arr[$i]['data'], $keywords_priv_list, $keywords_priv_list_cnt)) { 01844 $in_priv_list = TRUE; 01845 } 01846 } 01847 } else { 01848 $before .= ' '; 01849 } 01850 01851 switch ($arr[$i]['data']) { 01852 case 'CREATE': 01853 if (!$in_priv_list) { 01854 $space_punct_listsep = $html_line_break; 01855 $space_alpha_reserved_word = ' '; 01856 } 01857 break; 01858 case 'EXPLAIN': 01859 case 'DESCRIBE': 01860 case 'SET': 01861 case 'ALTER': 01862 case 'DELETE': 01863 case 'SHOW': 01864 case 'DROP': 01865 case 'UPDATE': 01866 case 'TRUNCATE': 01867 case 'ANALYZE': 01868 case 'ANALYSE': 01869 if (!$in_priv_list) { 01870 $space_punct_listsep = $html_line_break; 01871 $space_alpha_reserved_word = ' '; 01872 } 01873 break; 01874 case 'INSERT': 01875 case 'REPLACE': 01876 if (!$in_priv_list) { 01877 $space_punct_listsep = $html_line_break; 01878 $space_alpha_reserved_word = $html_line_break; 01879 } 01880 break; 01881 case 'VALUES': 01882 $space_punct_listsep = ' '; 01883 $space_alpha_reserved_word = $html_line_break; 01884 break; 01885 case 'SELECT': 01886 $space_punct_listsep = ' '; 01887 $space_alpha_reserved_word = $html_line_break; 01888 break; 01889 default: 01890 break; 01891 } // end switch ($arr[$i]['data']) 01892 01893 $after .= ' '; 01894 break; 01895 case 'digit_integer': 01896 case 'digit_float': 01897 case 'digit_hex': 01898 //TODO: could there be other types preceding a digit? 01899 if ($typearr[1] == 'alpha_reservedWord') { 01900 $after .= ' '; 01901 } 01902 if ($infunction && $typearr[3] == 'punct_bracket_close_round') { 01903 $after .= ' '; 01904 } 01905 break; 01906 case 'alpha_variable': 01907 $after = ' '; 01908 break; 01909 case 'quote_double': 01910 case 'quote_single': 01911 // workaround: for the query 01912 // REVOKE SELECT ON `base2\_db`.* FROM 'user'@'%' 01913 // the @ is incorrectly marked as alpha_variable 01914 // in the parser, and here, the '%' gets a blank before, 01915 // which is a syntax error 01916 if ($typearr[1]!='alpha_variable') { 01917 $before .= ' '; 01918 } 01919 if ($infunction && $typearr[3] == 'punct_bracket_close_round') { 01920 $after .= ' '; 01921 } 01922 break; 01923 case 'quote_backtick': 01924 if ($typearr[3] != 'punct_qualifier') { 01925 $after .= ' '; 01926 } 01927 if ($typearr[1] != 'punct_qualifier') { 01928 $before .= ' '; 01929 } 01930 break; 01931 default: 01932 break; 01933 } // end switch ($typearr[2]) 01934 01935 /* 01936 if ($typearr[3] != 'punct_qualifier') { 01937 $after .= ' '; 01938 } 01939 $after .= "\n"; 01940 */ 01941 $str .= $before . ($mode=='color' ? PMA_SQP_formatHTML_colorize($arr[$i]) : $arr[$i]['data']). $after; 01942 } // end for 01943 if ($mode=='color') { 01944 $str .= '</span>'; 01945 } 01946 01947 return $str; 01948 } // end of the "PMA_SQP_formatHtml()" function 01949 } 01950 01964 function PMA_SQP_buildCssRule($classname, $property, $value) 01965 { 01966 $str = '.' . $classname . ' {'; 01967 if ($value != '') { 01968 $str .= $property . ': ' . $value . ';'; 01969 } 01970 $str .= '}' . "\n"; 01971 01972 return $str; 01973 } // end of the "PMA_SQP_buildCssRule()" function 01974 01975 01987 function PMA_SQP_buildCssData() 01988 { 01989 global $cfg; 01990 01991 $css_string = ''; 01992 foreach($cfg['SQP']['fmtColor'] AS $key => $col) { 01993 $css_string .= PMA_SQP_buildCssRule('syntax_' . $key, 'color', $col); 01994 } 01995 01996 for ($i = 0; $i < 8; $i++) { 01997 $css_string .= PMA_SQP_buildCssRule('syntax_indent' . $i, 'margin-left', ($i * $cfg['SQP']['fmtInd']) . $cfg['SQP']['fmtIndUnit']); 01998 } 01999 02000 return $css_string; 02001 } // end of the "PMA_SQP_buildCssData()" function 02002 02003 if ($is_minimum_common == FALSE) { 02013 function PMA_SQP_formatNone($arr) 02014 { 02015 $formatted_sql = htmlspecialchars($arr['raw']); 02016 $formatted_sql = preg_replace("@((\015\012)|(\015)|(\012)){3,}@", "\n\n", $formatted_sql); 02017 02018 return $formatted_sql; 02019 } // end of the "PMA_SQP_formatNone()" function 02020 02021 02031 function PMA_SQP_formatText($arr) 02032 { 02036 return PMA_SQP_formatNone($arr); 02037 } // end of the "PMA_SQP_formatText()" function 02038 } // end if: minimal common.lib needed? 02039 02040 ?>