sqlparser.lib.php 103 KB

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