sqlparser.lib.php 102 KB

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