Sql.php 87 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * Set of functions for the SQL executor
  5. *
  6. * @package PhpMyAdmin
  7. */
  8. namespace PhpMyAdmin;
  9. use PhpMyAdmin\Bookmark;
  10. use PhpMyAdmin\Core;
  11. use PhpMyAdmin\DatabaseInterface;
  12. use PhpMyAdmin\Display\Results as DisplayResults;
  13. use PhpMyAdmin\Index;
  14. use PhpMyAdmin\Message;
  15. use PhpMyAdmin\Operations;
  16. use PhpMyAdmin\ParseAnalyze;
  17. use PhpMyAdmin\Relation;
  18. use PhpMyAdmin\RelationCleanup;
  19. use PhpMyAdmin\Response;
  20. use PhpMyAdmin\SqlParser\Statements\AlterStatement;
  21. use PhpMyAdmin\SqlParser\Statements\DropStatement;
  22. use PhpMyAdmin\SqlParser\Statements\SelectStatement;
  23. use PhpMyAdmin\SqlParser\Utils\Query;
  24. use PhpMyAdmin\Table;
  25. use PhpMyAdmin\Transformations;
  26. use PhpMyAdmin\Url;
  27. use PhpMyAdmin\Util;
  28. /**
  29. * Set of functions for the SQL executor
  30. *
  31. * @package PhpMyAdmin
  32. */
  33. class Sql
  34. {
  35. /**
  36. * @var Relation $relation
  37. */
  38. private $relation;
  39. /**
  40. * Constructor
  41. */
  42. public function __construct()
  43. {
  44. $this->relation = new Relation();
  45. }
  46. /**
  47. * Parses and analyzes the given SQL query.
  48. *
  49. * @param string $sql_query SQL query
  50. * @param string $db DB name
  51. *
  52. * @return mixed
  53. */
  54. public function parseAndAnalyze($sql_query, $db = null)
  55. {
  56. if (is_null($db) && isset($GLOBALS['db']) && strlen($GLOBALS['db'])) {
  57. $db = $GLOBALS['db'];
  58. }
  59. list($analyzed_sql_results,,) = ParseAnalyze::sqlQuery($sql_query, $db);
  60. return $analyzed_sql_results;
  61. }
  62. /**
  63. * Handle remembered sorting order, only for single table query
  64. *
  65. * @param string $db database name
  66. * @param string $table table name
  67. * @param array &$analyzed_sql_results the analyzed query results
  68. * @param string &$full_sql_query SQL query
  69. *
  70. * @return void
  71. */
  72. private function handleSortOrder(
  73. $db, $table, array &$analyzed_sql_results, &$full_sql_query
  74. ) {
  75. $pmatable = new Table($table, $db);
  76. if (empty($analyzed_sql_results['order'])) {
  77. // Retrieving the name of the column we should sort after.
  78. $sortCol = $pmatable->getUiProp(Table::PROP_SORTED_COLUMN);
  79. if (empty($sortCol)) {
  80. return;
  81. }
  82. // Remove the name of the table from the retrieved field name.
  83. $sortCol = str_replace(
  84. Util::backquote($table) . '.',
  85. '',
  86. $sortCol
  87. );
  88. // Create the new query.
  89. $full_sql_query = Query::replaceClause(
  90. $analyzed_sql_results['statement'],
  91. $analyzed_sql_results['parser']->list,
  92. 'ORDER BY ' . $sortCol
  93. );
  94. // TODO: Avoid reparsing the query.
  95. $analyzed_sql_results = Query::getAll($full_sql_query);
  96. } else {
  97. // Store the remembered table into session.
  98. $pmatable->setUiProp(
  99. Table::PROP_SORTED_COLUMN,
  100. Query::getClause(
  101. $analyzed_sql_results['statement'],
  102. $analyzed_sql_results['parser']->list,
  103. 'ORDER BY'
  104. )
  105. );
  106. }
  107. }
  108. /**
  109. * Append limit clause to SQL query
  110. *
  111. * @param array &$analyzed_sql_results the analyzed query results
  112. *
  113. * @return string limit clause appended SQL query
  114. */
  115. private function getSqlWithLimitClause(array &$analyzed_sql_results)
  116. {
  117. return Query::replaceClause(
  118. $analyzed_sql_results['statement'],
  119. $analyzed_sql_results['parser']->list,
  120. 'LIMIT ' . $_SESSION['tmpval']['pos'] . ', '
  121. . $_SESSION['tmpval']['max_rows']
  122. );
  123. }
  124. /**
  125. * Verify whether the result set has columns from just one table
  126. *
  127. * @param array $fields_meta meta fields
  128. *
  129. * @return boolean whether the result set has columns from just one table
  130. */
  131. private function resultSetHasJustOneTable(array $fields_meta)
  132. {
  133. $just_one_table = true;
  134. $prev_table = '';
  135. foreach ($fields_meta as $one_field_meta) {
  136. if ($one_field_meta->table != ''
  137. && $prev_table != ''
  138. && $one_field_meta->table != $prev_table
  139. ) {
  140. $just_one_table = false;
  141. }
  142. if ($one_field_meta->table != '') {
  143. $prev_table = $one_field_meta->table;
  144. }
  145. }
  146. return $just_one_table && $prev_table != '';
  147. }
  148. /**
  149. * Verify whether the result set contains all the columns
  150. * of at least one unique key
  151. *
  152. * @param string $db database name
  153. * @param string $table table name
  154. * @param array $fields_meta meta fields
  155. *
  156. * @return boolean whether the result set contains a unique key
  157. */
  158. private function resultSetContainsUniqueKey($db, $table, array $fields_meta)
  159. {
  160. $resultSetColumnNames = array();
  161. foreach ($fields_meta as $oneMeta) {
  162. $resultSetColumnNames[] = $oneMeta->name;
  163. }
  164. foreach (Index::getFromTable($table, $db) as $index) {
  165. if ($index->isUnique()) {
  166. $indexColumns = $index->getColumns();
  167. $numberFound = 0;
  168. foreach ($indexColumns as $indexColumnName => $dummy) {
  169. if (in_array($indexColumnName, $resultSetColumnNames)) {
  170. $numberFound++;
  171. }
  172. }
  173. if ($numberFound == count($indexColumns)) {
  174. return true;
  175. }
  176. }
  177. }
  178. return false;
  179. }
  180. /**
  181. * Get the HTML for relational column dropdown
  182. * During grid edit, if we have a relational field, returns the html for the
  183. * dropdown
  184. *
  185. * @param string $db current database
  186. * @param string $table current table
  187. * @param string $column current column
  188. * @param string $curr_value current selected value
  189. *
  190. * @return string $dropdown html for the dropdown
  191. */
  192. private function getHtmlForRelationalColumnDropdown($db, $table, $column, $curr_value)
  193. {
  194. $foreigners = $this->relation->getForeigners($db, $table, $column);
  195. $foreignData = $this->relation->getForeignData($foreigners, $column, false, '', '');
  196. if ($foreignData['disp_row'] == null) {
  197. //Handle the case when number of values
  198. //is more than $cfg['ForeignKeyMaxLimit']
  199. $_url_params = array(
  200. 'db' => $db,
  201. 'table' => $table,
  202. 'field' => $column
  203. );
  204. $dropdown = '<span class="curr_value">'
  205. . htmlspecialchars($_POST['curr_value'])
  206. . '</span>'
  207. . '<a href="browse_foreigners.php" data-post="'
  208. . Url::getCommon($_url_params, '') . '"'
  209. . 'class="ajax browse_foreign" ' . '>'
  210. . __('Browse foreign values')
  211. . '</a>';
  212. } else {
  213. $dropdown = $this->relation->foreignDropdown(
  214. $foreignData['disp_row'],
  215. $foreignData['foreign_field'],
  216. $foreignData['foreign_display'],
  217. $curr_value,
  218. $GLOBALS['cfg']['ForeignKeyMaxLimit']
  219. );
  220. $dropdown = '<select>' . $dropdown . '</select>';
  221. }
  222. return $dropdown;
  223. }
  224. /**
  225. * Get the HTML for the profiling table and accompanying chart if profiling is set.
  226. * Otherwise returns null
  227. *
  228. * @param string $url_query url query
  229. * @param string $db current database
  230. * @param array $profiling_results array containing the profiling info
  231. *
  232. * @return string $profiling_table html for the profiling table and chart
  233. */
  234. private function getHtmlForProfilingChart($url_query, $db, $profiling_results)
  235. {
  236. if (! empty($profiling_results)) {
  237. $url_query = isset($url_query)
  238. ? $url_query
  239. : Url::getCommon(array('db' => $db));
  240. $profiling_table = '';
  241. $profiling_table .= '<fieldset><legend>' . __('Profiling')
  242. . '</legend>' . "\n";
  243. $profiling_table .= '<div class="floatleft">';
  244. $profiling_table .= '<h3>' . __('Detailed profile') . '</h3>';
  245. $profiling_table .= '<table id="profiletable"><thead>' . "\n";
  246. $profiling_table .= ' <tr>' . "\n";
  247. $profiling_table .= ' <th>' . __('Order')
  248. . '<div class="sorticon"></div></th>' . "\n";
  249. $profiling_table .= ' <th>' . __('State')
  250. . Util::showMySQLDocu('general-thread-states')
  251. . '<div class="sorticon"></div></th>' . "\n";
  252. $profiling_table .= ' <th>' . __('Time')
  253. . '<div class="sorticon"></div></th>' . "\n";
  254. $profiling_table .= ' </tr></thead><tbody>' . "\n";
  255. list($detailed_table, $chart_json, $profiling_stats)
  256. = $this->analyzeAndGetTableHtmlForProfilingResults($profiling_results);
  257. $profiling_table .= $detailed_table;
  258. $profiling_table .= '</tbody></table>' . "\n";
  259. $profiling_table .= '</div>';
  260. $profiling_table .= '<div class="floatleft">';
  261. $profiling_table .= '<h3>' . __('Summary by state') . '</h3>';
  262. $profiling_table .= '<table id="profilesummarytable"><thead>' . "\n";
  263. $profiling_table .= ' <tr>' . "\n";
  264. $profiling_table .= ' <th>' . __('State')
  265. . Util::showMySQLDocu('general-thread-states')
  266. . '<div class="sorticon"></div></th>' . "\n";
  267. $profiling_table .= ' <th>' . __('Total Time')
  268. . '<div class="sorticon"></div></th>' . "\n";
  269. $profiling_table .= ' <th>' . __('% Time')
  270. . '<div class="sorticon"></div></th>' . "\n";
  271. $profiling_table .= ' <th>' . __('Calls')
  272. . '<div class="sorticon"></div></th>' . "\n";
  273. $profiling_table .= ' <th>' . __('ø Time')
  274. . '<div class="sorticon"></div></th>' . "\n";
  275. $profiling_table .= ' </tr></thead><tbody>' . "\n";
  276. $profiling_table .= $this->getTableHtmlForProfilingSummaryByState(
  277. $profiling_stats
  278. );
  279. $profiling_table .= '</tbody></table>' . "\n";
  280. $profiling_table .= <<<EOT
  281. <script type="text/javascript">
  282. url_query = '$url_query';
  283. </script>
  284. EOT;
  285. $profiling_table .= "</div>";
  286. $profiling_table .= "<div class='clearfloat'></div>";
  287. //require_once 'libraries/chart.lib.php';
  288. $profiling_table .= '<div id="profilingChartData" class="hide">';
  289. $profiling_table .= json_encode($chart_json);
  290. $profiling_table .= '</div>';
  291. $profiling_table .= '<div id="profilingchart" class="hide">';
  292. $profiling_table .= '</div>';
  293. $profiling_table .= '<script type="text/javascript">';
  294. $profiling_table .= "AJAX.registerOnload('sql.js', function () {";
  295. $profiling_table .= 'makeProfilingChart();';
  296. $profiling_table .= 'initProfilingTables();';
  297. $profiling_table .= '});';
  298. $profiling_table .= '</script>';
  299. $profiling_table .= '</fieldset>' . "\n";
  300. } else {
  301. $profiling_table = null;
  302. }
  303. return $profiling_table;
  304. }
  305. /**
  306. * Function to get HTML for detailed profiling results table, profiling stats, and
  307. * $chart_json for displaying the chart.
  308. *
  309. * @param array $profiling_results profiling results
  310. *
  311. * @return mixed
  312. */
  313. private function analyzeAndGetTableHtmlForProfilingResults(
  314. $profiling_results
  315. ) {
  316. $profiling_stats = array(
  317. 'total_time' => 0,
  318. 'states' => array(),
  319. );
  320. $chart_json = Array();
  321. $i = 1;
  322. $table = '';
  323. foreach ($profiling_results as $one_result) {
  324. if (isset($profiling_stats['states'][ucwords($one_result['Status'])])) {
  325. $states = $profiling_stats['states'];
  326. $states[ucwords($one_result['Status'])]['total_time']
  327. += $one_result['Duration'];
  328. $states[ucwords($one_result['Status'])]['calls']++;
  329. } else {
  330. $profiling_stats['states'][ucwords($one_result['Status'])] = array(
  331. 'total_time' => $one_result['Duration'],
  332. 'calls' => 1,
  333. );
  334. }
  335. $profiling_stats['total_time'] += $one_result['Duration'];
  336. $table .= ' <tr>' . "\n";
  337. $table .= '<td>' . $i++ . '</td>' . "\n";
  338. $table .= '<td>' . ucwords($one_result['Status'])
  339. . '</td>' . "\n";
  340. $table .= '<td class="right">'
  341. . (Util::formatNumber($one_result['Duration'], 3, 1))
  342. . 's<span class="rawvalue hide">'
  343. . $one_result['Duration'] . '</span></td>' . "\n";
  344. if (isset($chart_json[ucwords($one_result['Status'])])) {
  345. $chart_json[ucwords($one_result['Status'])]
  346. += $one_result['Duration'];
  347. } else {
  348. $chart_json[ucwords($one_result['Status'])]
  349. = $one_result['Duration'];
  350. }
  351. }
  352. return array($table, $chart_json, $profiling_stats);
  353. }
  354. /**
  355. * Function to get HTML for summary by state table
  356. *
  357. * @param array $profiling_stats profiling stats
  358. *
  359. * @return string $table html for the table
  360. */
  361. private function getTableHtmlForProfilingSummaryByState(array $profiling_stats)
  362. {
  363. $table = '';
  364. foreach ($profiling_stats['states'] as $name => $stats) {
  365. $table .= ' <tr>' . "\n";
  366. $table .= '<td>' . $name . '</td>' . "\n";
  367. $table .= '<td align="right">'
  368. . Util::formatNumber($stats['total_time'], 3, 1)
  369. . 's<span class="rawvalue hide">'
  370. . $stats['total_time'] . '</span></td>' . "\n";
  371. $table .= '<td align="right">'
  372. . Util::formatNumber(
  373. 100 * ($stats['total_time'] / $profiling_stats['total_time']),
  374. 0, 2
  375. )
  376. . '%</td>' . "\n";
  377. $table .= '<td align="right">' . $stats['calls'] . '</td>'
  378. . "\n";
  379. $table .= '<td align="right">'
  380. . Util::formatNumber(
  381. $stats['total_time'] / $stats['calls'], 3, 1
  382. )
  383. . 's<span class="rawvalue hide">'
  384. . number_format($stats['total_time'] / $stats['calls'], 8, '.', '')
  385. . '</span></td>' . "\n";
  386. $table .= ' </tr>' . "\n";
  387. }
  388. return $table;
  389. }
  390. /**
  391. * Get the HTML for the enum column dropdown
  392. * During grid edit, if we have a enum field, returns the html for the
  393. * dropdown
  394. *
  395. * @param string $db current database
  396. * @param string $table current table
  397. * @param string $column current column
  398. * @param string $curr_value currently selected value
  399. *
  400. * @return string $dropdown html for the dropdown
  401. */
  402. private function getHtmlForEnumColumnDropdown($db, $table, $column, $curr_value)
  403. {
  404. $values = $this->getValuesForColumn($db, $table, $column);
  405. $dropdown = '<option value="">&nbsp;</option>';
  406. $dropdown .= $this->getHtmlForOptionsList($values, array($curr_value));
  407. $dropdown = '<select>' . $dropdown . '</select>';
  408. return $dropdown;
  409. }
  410. /**
  411. * Get value of a column for a specific row (marked by $where_clause)
  412. *
  413. * @param string $db current database
  414. * @param string $table current table
  415. * @param string $column current column
  416. * @param string $where_clause where clause to select a particular row
  417. *
  418. * @return string with value
  419. */
  420. private function getFullValuesForSetColumn($db, $table, $column, $where_clause)
  421. {
  422. $result = $GLOBALS['dbi']->fetchSingleRow(
  423. "SELECT `$column` FROM `$db`.`$table` WHERE $where_clause"
  424. );
  425. return $result[$column];
  426. }
  427. /**
  428. * Get the HTML for the set column dropdown
  429. * During grid edit, if we have a set field, returns the html for the
  430. * dropdown
  431. *
  432. * @param string $db current database
  433. * @param string $table current table
  434. * @param string $column current column
  435. * @param string $curr_value currently selected value
  436. *
  437. * @return string $dropdown html for the set column
  438. */
  439. private function getHtmlForSetColumn($db, $table, $column, $curr_value)
  440. {
  441. $values = $this->getValuesForColumn($db, $table, $column);
  442. $dropdown = '';
  443. $full_values =
  444. isset($_POST['get_full_values']) ? $_POST['get_full_values'] : false;
  445. $where_clause =
  446. isset($_POST['where_clause']) ? $_POST['where_clause'] : null;
  447. // If the $curr_value was truncated, we should
  448. // fetch the correct full values from the table
  449. if ($full_values && ! empty($where_clause)) {
  450. $curr_value = $this->getFullValuesForSetColumn(
  451. $db, $table, $column, $where_clause
  452. );
  453. }
  454. //converts characters of $curr_value to HTML entities
  455. $converted_curr_value = htmlentities(
  456. $curr_value, ENT_COMPAT, "UTF-8"
  457. );
  458. $selected_values = explode(',', $converted_curr_value);
  459. $dropdown .= $this->getHtmlForOptionsList($values, $selected_values);
  460. $select_size = (sizeof($values) > 10) ? 10 : sizeof($values);
  461. $dropdown = '<select multiple="multiple" size="' . $select_size . '">'
  462. . $dropdown . '</select>';
  463. return $dropdown;
  464. }
  465. /**
  466. * Get all the values for a enum column or set column in a table
  467. *
  468. * @param string $db current database
  469. * @param string $table current table
  470. * @param string $column current column
  471. *
  472. * @return array $values array containing the value list for the column
  473. */
  474. private function getValuesForColumn($db, $table, $column)
  475. {
  476. $field_info_query = $GLOBALS['dbi']->getColumnsSql($db, $table, $column);
  477. $field_info_result = $GLOBALS['dbi']->fetchResult(
  478. $field_info_query,
  479. null,
  480. null,
  481. DatabaseInterface::CONNECT_USER,
  482. DatabaseInterface::QUERY_STORE
  483. );
  484. $values = Util::parseEnumSetValues($field_info_result[0]['Type']);
  485. return $values;
  486. }
  487. /**
  488. * Get HTML for options list
  489. *
  490. * @param array $values set of values
  491. * @param array $selected_values currently selected values
  492. *
  493. * @return string $options HTML for options list
  494. */
  495. private function getHtmlForOptionsList(array $values, array $selected_values)
  496. {
  497. $options = '';
  498. foreach ($values as $value) {
  499. $options .= '<option value="' . $value . '"';
  500. if (in_array($value, $selected_values, true)) {
  501. $options .= ' selected="selected" ';
  502. }
  503. $options .= '>' . $value . '</option>';
  504. }
  505. return $options;
  506. }
  507. /**
  508. * Function to get html for bookmark support if bookmarks are enabled. Else will
  509. * return null
  510. *
  511. * @param array $displayParts the parts to display
  512. * @param array $cfgBookmark configuration setting for bookmarking
  513. * @param string $sql_query sql query
  514. * @param string $db current database
  515. * @param string $table current table
  516. * @param string $complete_query complete query
  517. * @param string $bkm_user bookmarking user
  518. *
  519. * @return string $html
  520. */
  521. public function getHtmlForBookmark(array $displayParts, array $cfgBookmark, $sql_query, $db,
  522. $table, $complete_query, $bkm_user
  523. ) {
  524. if ($displayParts['bkm_form'] == '1'
  525. && (! empty($cfgBookmark) && empty($_GET['id_bookmark']))
  526. && ! empty($sql_query)
  527. ) {
  528. $goto = 'sql.php'
  529. . Url::getCommon(
  530. array(
  531. 'db' => $db,
  532. 'table' => $table,
  533. 'sql_query' => $sql_query,
  534. 'id_bookmark'=> 1,
  535. )
  536. );
  537. $bkm_sql_query = isset($complete_query) ? $complete_query : $sql_query;
  538. $html = '<form action="sql.php" method="post"'
  539. . ' onsubmit="return ! emptyCheckTheField(this,'
  540. . '\'bkm_fields[bkm_label]\');"'
  541. . ' class="bookmarkQueryForm print_ignore">';
  542. $html .= Url::getHiddenInputs();
  543. $html .= '<input type="hidden" name="db"'
  544. . ' value="' . htmlspecialchars($db) . '" />';
  545. $html .= '<input type="hidden" name="goto" value="' . $goto . '" />';
  546. $html .= '<input type="hidden" name="bkm_fields[bkm_database]"'
  547. . ' value="' . htmlspecialchars($db) . '" />';
  548. $html .= '<input type="hidden" name="bkm_fields[bkm_user]"'
  549. . ' value="' . $bkm_user . '" />';
  550. $html .= '<input type="hidden" name="bkm_fields[bkm_sql_query]"'
  551. . ' value="'
  552. . htmlspecialchars($bkm_sql_query)
  553. . '" />';
  554. $html .= '<fieldset>';
  555. $html .= '<legend>';
  556. $html .= Util::getIcon(
  557. 'b_bookmark', __('Bookmark this SQL query'), true
  558. );
  559. $html .= '</legend>';
  560. $html .= '<div class="formelement">';
  561. $html .= '<label>' . __('Label:');
  562. $html .= '<input type="text" name="bkm_fields[bkm_label]" value="" />' .
  563. '</label>';
  564. $html .= '</div>';
  565. $html .= '<div class="formelement">';
  566. $html .= '<label>' .
  567. '<input type="checkbox" name="bkm_all_users" value="true" />';
  568. $html .= __('Let every user access this bookmark') . '</label>';
  569. $html .= '</div>';
  570. $html .= '<div class="clearfloat"></div>';
  571. $html .= '</fieldset>';
  572. $html .= '<fieldset class="tblFooters">';
  573. $html .= '<input type="hidden" name="store_bkm" value="1" />';
  574. $html .= '<input type="submit"'
  575. . ' value="' . __('Bookmark this SQL query') . '" />';
  576. $html .= '</fieldset>';
  577. $html .= '</form>';
  578. } else {
  579. $html = null;
  580. }
  581. return $html;
  582. }
  583. /**
  584. * Function to check whether to remember the sorting order or not
  585. *
  586. * @param array $analyzed_sql_results the analyzed query and other variables set
  587. * after analyzing the query
  588. *
  589. * @return boolean
  590. */
  591. private function isRememberSortingOrder(array $analyzed_sql_results)
  592. {
  593. return $GLOBALS['cfg']['RememberSorting']
  594. && ! ($analyzed_sql_results['is_count']
  595. || $analyzed_sql_results['is_export']
  596. || $analyzed_sql_results['is_func']
  597. || $analyzed_sql_results['is_analyse'])
  598. && $analyzed_sql_results['select_from']
  599. && isset($analyzed_sql_results['select_expr'])
  600. && isset($analyzed_sql_results['select_tables'])
  601. && ((empty($analyzed_sql_results['select_expr']))
  602. || ((count($analyzed_sql_results['select_expr']) == 1)
  603. && ($analyzed_sql_results['select_expr'][0] == '*')))
  604. && count($analyzed_sql_results['select_tables']) == 1;
  605. }
  606. /**
  607. * Function to check whether the LIMIT clause should be appended or not
  608. *
  609. * @param array $analyzed_sql_results the analyzed query and other variables set
  610. * after analyzing the query
  611. *
  612. * @return boolean
  613. */
  614. private function isAppendLimitClause(array $analyzed_sql_results)
  615. {
  616. // Assigning LIMIT clause to an syntactically-wrong query
  617. // is not needed. Also we would want to show the true query
  618. // and the true error message to the query executor
  619. return (isset($analyzed_sql_results['parser'])
  620. && count($analyzed_sql_results['parser']->errors) === 0)
  621. && ($_SESSION['tmpval']['max_rows'] != 'all')
  622. && ! ($analyzed_sql_results['is_export']
  623. || $analyzed_sql_results['is_analyse'])
  624. && ($analyzed_sql_results['select_from']
  625. || $analyzed_sql_results['is_subquery'])
  626. && empty($analyzed_sql_results['limit']);
  627. }
  628. /**
  629. * Function to check whether this query is for just browsing
  630. *
  631. * @param array $analyzed_sql_results the analyzed query and other variables set
  632. * after analyzing the query
  633. * @param boolean $find_real_end whether the real end should be found
  634. *
  635. * @return boolean
  636. */
  637. public function isJustBrowsing(array $analyzed_sql_results, $find_real_end)
  638. {
  639. return ! $analyzed_sql_results['is_group']
  640. && ! $analyzed_sql_results['is_func']
  641. && empty($analyzed_sql_results['union'])
  642. && empty($analyzed_sql_results['distinct'])
  643. && $analyzed_sql_results['select_from']
  644. && (count($analyzed_sql_results['select_tables']) === 1)
  645. && (empty($analyzed_sql_results['statement']->where)
  646. || (count($analyzed_sql_results['statement']->where) == 1
  647. && $analyzed_sql_results['statement']->where[0]->expr ==='1'))
  648. && empty($analyzed_sql_results['group'])
  649. && ! isset($find_real_end)
  650. && ! $analyzed_sql_results['is_subquery']
  651. && ! $analyzed_sql_results['join']
  652. && empty($analyzed_sql_results['having']);
  653. }
  654. /**
  655. * Function to check whether the related transformation information should be deleted
  656. *
  657. * @param array $analyzed_sql_results the analyzed query and other variables set
  658. * after analyzing the query
  659. *
  660. * @return boolean
  661. */
  662. private function isDeleteTransformationInfo(array $analyzed_sql_results)
  663. {
  664. return !empty($analyzed_sql_results['querytype'])
  665. && (($analyzed_sql_results['querytype'] == 'ALTER')
  666. || ($analyzed_sql_results['querytype'] == 'DROP'));
  667. }
  668. /**
  669. * Function to check whether the user has rights to drop the database
  670. *
  671. * @param array $analyzed_sql_results the analyzed query and other variables set
  672. * after analyzing the query
  673. * @param boolean $allowUserDropDatabase whether the user is allowed to drop db
  674. * @param boolean $is_superuser whether this user is a superuser
  675. *
  676. * @return boolean
  677. */
  678. public function hasNoRightsToDropDatabase(array $analyzed_sql_results,
  679. $allowUserDropDatabase, $is_superuser
  680. ) {
  681. return ! $allowUserDropDatabase
  682. && isset($analyzed_sql_results['drop_database'])
  683. && $analyzed_sql_results['drop_database']
  684. && ! $is_superuser;
  685. }
  686. /**
  687. * Function to set a column property
  688. *
  689. * @param Table $pmatable Table instance
  690. * @param string $request_index col_order|col_visib
  691. *
  692. * @return boolean $retval
  693. */
  694. private function setColumnProperty($pmatable, $request_index)
  695. {
  696. $property_value = array_map('intval', explode(',', $_POST[$request_index]));
  697. switch($request_index) {
  698. case 'col_order':
  699. $property_to_set = Table::PROP_COLUMN_ORDER;
  700. break;
  701. case 'col_visib':
  702. $property_to_set = Table::PROP_COLUMN_VISIB;
  703. break;
  704. default:
  705. $property_to_set = '';
  706. }
  707. $retval = $pmatable->setUiProp(
  708. $property_to_set,
  709. $property_value,
  710. $_POST['table_create_time']
  711. );
  712. if (gettype($retval) != 'boolean') {
  713. $response = Response::getInstance();
  714. $response->setRequestStatus(false);
  715. $response->addJSON('message', $retval->getString());
  716. exit;
  717. }
  718. return $retval;
  719. }
  720. /**
  721. * Function to check the request for setting the column order or visibility
  722. *
  723. * @param string $table the current table
  724. * @param string $db the current database
  725. *
  726. * @return void
  727. */
  728. public function setColumnOrderOrVisibility($table, $db)
  729. {
  730. $pmatable = new Table($table, $db);
  731. $retval = false;
  732. // set column order
  733. if (isset($_POST['col_order'])) {
  734. $retval = $this->setColumnProperty($pmatable, 'col_order');
  735. }
  736. // set column visibility
  737. if ($retval === true && isset($_POST['col_visib'])) {
  738. $retval = $this->setColumnProperty($pmatable, 'col_visib');
  739. }
  740. $response = Response::getInstance();
  741. $response->setRequestStatus($retval == true);
  742. exit;
  743. }
  744. /**
  745. * Function to add a bookmark
  746. *
  747. * @param string $goto goto page URL
  748. *
  749. * @return void
  750. */
  751. public function addBookmark($goto)
  752. {
  753. $bookmark = Bookmark::createBookmark(
  754. $GLOBALS['dbi'],
  755. $GLOBALS['cfg']['Server']['user'],
  756. $_POST['bkm_fields'],
  757. (isset($_POST['bkm_all_users'])
  758. && $_POST['bkm_all_users'] == 'true' ? true : false
  759. )
  760. );
  761. $result = $bookmark->save();
  762. $response = Response::getInstance();
  763. if ($response->isAjax()) {
  764. if ($result) {
  765. $msg = Message::success(__('Bookmark %s has been created.'));
  766. $msg->addParam($_POST['bkm_fields']['bkm_label']);
  767. $response->addJSON('message', $msg);
  768. } else {
  769. $msg = Message::error(__('Bookmark not created!'));
  770. $response->setRequestStatus(false);
  771. $response->addJSON('message', $msg);
  772. }
  773. exit;
  774. } else {
  775. // go back to sql.php to redisplay query; do not use &amp; in this case:
  776. /**
  777. * @todo In which scenario does this happen?
  778. */
  779. Core::sendHeaderLocation(
  780. './' . $goto
  781. . '&label=' . $_POST['bkm_fields']['bkm_label']
  782. );
  783. }
  784. }
  785. /**
  786. * Function to find the real end of rows
  787. *
  788. * @param string $db the current database
  789. * @param string $table the current table
  790. *
  791. * @return mixed the number of rows if "retain" param is true, otherwise true
  792. */
  793. public function findRealEndOfRows($db, $table)
  794. {
  795. $unlim_num_rows = $GLOBALS['dbi']->getTable($db, $table)->countRecords(true);
  796. $_SESSION['tmpval']['pos'] = $this->getStartPosToDisplayRow($unlim_num_rows);
  797. return $unlim_num_rows;
  798. }
  799. /**
  800. * Function to get values for the relational columns
  801. *
  802. * @param string $db the current database
  803. * @param string $table the current table
  804. *
  805. * @return void
  806. */
  807. public function getRelationalValues($db, $table)
  808. {
  809. $column = $_POST['column'];
  810. if ($_SESSION['tmpval']['relational_display'] == 'D'
  811. && isset($_POST['relation_key_or_display_column'])
  812. && $_POST['relation_key_or_display_column']
  813. ) {
  814. $curr_value = $_POST['relation_key_or_display_column'];
  815. } else {
  816. $curr_value = $_POST['curr_value'];
  817. }
  818. $dropdown = $this->getHtmlForRelationalColumnDropdown(
  819. $db, $table, $column, $curr_value
  820. );
  821. $response = Response::getInstance();
  822. $response->addJSON('dropdown', $dropdown);
  823. exit;
  824. }
  825. /**
  826. * Function to get values for Enum or Set Columns
  827. *
  828. * @param string $db the current database
  829. * @param string $table the current table
  830. * @param string $columnType whether enum or set
  831. *
  832. * @return void
  833. */
  834. public function getEnumOrSetValues($db, $table, $columnType)
  835. {
  836. $column = $_POST['column'];
  837. $curr_value = $_POST['curr_value'];
  838. $response = Response::getInstance();
  839. if ($columnType == "enum") {
  840. $dropdown = $this->getHtmlForEnumColumnDropdown(
  841. $db, $table, $column, $curr_value
  842. );
  843. $response->addJSON('dropdown', $dropdown);
  844. } else {
  845. $select = $this->getHtmlForSetColumn(
  846. $db, $table, $column, $curr_value
  847. );
  848. $response->addJSON('select', $select);
  849. }
  850. exit;
  851. }
  852. /**
  853. * Function to get the default sql query for browsing page
  854. *
  855. * @param string $db the current database
  856. * @param string $table the current table
  857. *
  858. * @return string $sql_query the default $sql_query for browse page
  859. */
  860. public function getDefaultSqlQueryForBrowse($db, $table)
  861. {
  862. $bookmark = Bookmark::get(
  863. $GLOBALS['dbi'],
  864. $GLOBALS['cfg']['Server']['user'],
  865. $db,
  866. $table,
  867. 'label',
  868. false,
  869. true
  870. );
  871. if (! empty($bookmark) && ! empty($bookmark->getQuery())) {
  872. $GLOBALS['using_bookmark_message'] = Message::notice(
  873. __('Using bookmark "%s" as default browse query.')
  874. );
  875. $GLOBALS['using_bookmark_message']->addParam($table);
  876. $GLOBALS['using_bookmark_message']->addHtml(
  877. Util::showDocu('faq', 'faq6-22')
  878. );
  879. $sql_query = $bookmark->getQuery();
  880. } else {
  881. $defaultOrderByClause = '';
  882. if (isset($GLOBALS['cfg']['TablePrimaryKeyOrder'])
  883. && ($GLOBALS['cfg']['TablePrimaryKeyOrder'] !== 'NONE')
  884. ) {
  885. $primaryKey = null;
  886. $primary = Index::getPrimary($table, $db);
  887. if ($primary !== false) {
  888. $primarycols = $primary->getColumns();
  889. foreach ($primarycols as $col) {
  890. $primaryKey = $col->getName();
  891. break;
  892. }
  893. if ($primaryKey != null) {
  894. $defaultOrderByClause = ' ORDER BY '
  895. . Util::backquote($table) . '.'
  896. . Util::backquote($primaryKey) . ' '
  897. . $GLOBALS['cfg']['TablePrimaryKeyOrder'];
  898. }
  899. }
  900. }
  901. $sql_query = 'SELECT * FROM ' . Util::backquote($table)
  902. . $defaultOrderByClause;
  903. }
  904. return $sql_query;
  905. }
  906. /**
  907. * Responds an error when an error happens when executing the query
  908. *
  909. * @param boolean $is_gotofile whether goto file or not
  910. * @param string $error error after executing the query
  911. * @param string $full_sql_query full sql query
  912. *
  913. * @return void
  914. */
  915. private function handleQueryExecuteError($is_gotofile, $error, $full_sql_query)
  916. {
  917. if ($is_gotofile) {
  918. $message = Message::rawError($error);
  919. $response = Response::getInstance();
  920. $response->setRequestStatus(false);
  921. $response->addJSON('message', $message);
  922. } else {
  923. Util::mysqlDie($error, $full_sql_query, '', '');
  924. }
  925. exit;
  926. }
  927. /**
  928. * Function to store the query as a bookmark
  929. *
  930. * @param string $db the current database
  931. * @param string $bkm_user the bookmarking user
  932. * @param string $sql_query_for_bookmark the query to be stored in bookmark
  933. * @param string $bkm_label bookmark label
  934. * @param boolean $bkm_replace whether to replace existing bookmarks
  935. *
  936. * @return void
  937. */
  938. public function storeTheQueryAsBookmark($db, $bkm_user, $sql_query_for_bookmark,
  939. $bkm_label, $bkm_replace
  940. ) {
  941. $bfields = array(
  942. 'bkm_database' => $db,
  943. 'bkm_user' => $bkm_user,
  944. 'bkm_sql_query' => $sql_query_for_bookmark,
  945. 'bkm_label' => $bkm_label,
  946. );
  947. // Should we replace bookmark?
  948. if (isset($bkm_replace)) {
  949. $bookmarks = Bookmark::getList(
  950. $GLOBALS['dbi'],
  951. $GLOBALS['cfg']['Server']['user'],
  952. $db
  953. );
  954. foreach ($bookmarks as $bookmark) {
  955. if ($bookmark->getLabel() == $bkm_label) {
  956. $bookmark->delete();
  957. }
  958. }
  959. }
  960. $bookmark = Bookmark::createBookmark(
  961. $GLOBALS['dbi'],
  962. $GLOBALS['cfg']['Server']['user'],
  963. $bfields,
  964. isset($_POST['bkm_all_users'])
  965. );
  966. $bookmark->save();
  967. }
  968. /**
  969. * Executes the SQL query and measures its execution time
  970. *
  971. * @param string $full_sql_query the full sql query
  972. *
  973. * @return array ($result, $querytime)
  974. */
  975. private function executeQueryAndMeasureTime($full_sql_query)
  976. {
  977. // close session in case the query takes too long
  978. session_write_close();
  979. // Measure query time.
  980. $querytime_before = array_sum(explode(' ', microtime()));
  981. $result = @$GLOBALS['dbi']->tryQuery(
  982. $full_sql_query, DatabaseInterface::CONNECT_USER, DatabaseInterface::QUERY_STORE
  983. );
  984. $querytime_after = array_sum(explode(' ', microtime()));
  985. // reopen session
  986. session_start();
  987. return array($result, $querytime_after - $querytime_before);
  988. }
  989. /**
  990. * Function to get the affected or changed number of rows after executing a query
  991. *
  992. * @param boolean $is_affected whether the query affected a table
  993. * @param mixed $result results of executing the query
  994. *
  995. * @return int $num_rows number of rows affected or changed
  996. */
  997. private function getNumberOfRowsAffectedOrChanged($is_affected, $result)
  998. {
  999. if (! $is_affected) {
  1000. $num_rows = ($result) ? @$GLOBALS['dbi']->numRows($result) : 0;
  1001. } else {
  1002. $num_rows = @$GLOBALS['dbi']->affectedRows();
  1003. }
  1004. return $num_rows;
  1005. }
  1006. /**
  1007. * Checks if the current database has changed
  1008. * This could happen if the user sends a query like "USE `database`;"
  1009. *
  1010. * @param string $db the database in the query
  1011. *
  1012. * @return int $reload whether to reload the navigation(1) or not(0)
  1013. */
  1014. private function hasCurrentDbChanged($db)
  1015. {
  1016. if (strlen($db) > 0) {
  1017. $current_db = $GLOBALS['dbi']->fetchValue('SELECT DATABASE()');
  1018. // $current_db is false, except when a USE statement was sent
  1019. return ($current_db != false) && ($db !== $current_db);
  1020. }
  1021. return false;
  1022. }
  1023. /**
  1024. * If a table, database or column gets dropped, clean comments.
  1025. *
  1026. * @param string $db current database
  1027. * @param string $table current table
  1028. * @param string $column current column
  1029. * @param bool $purge whether purge set or not
  1030. *
  1031. * @return array $extra_data
  1032. */
  1033. private function cleanupRelations($db, $table, $column, $purge)
  1034. {
  1035. if (! empty($purge) && strlen($db) > 0) {
  1036. if (strlen($table) > 0) {
  1037. if (isset($column) && strlen($column) > 0) {
  1038. RelationCleanup::column($db, $table, $column);
  1039. } else {
  1040. RelationCleanup::table($db, $table);
  1041. }
  1042. } else {
  1043. RelationCleanup::database($db);
  1044. }
  1045. }
  1046. }
  1047. /**
  1048. * Function to count the total number of rows for the same 'SELECT' query without
  1049. * the 'LIMIT' clause that may have been programatically added
  1050. *
  1051. * @param int $num_rows number of rows affected/changed by the query
  1052. * @param bool $justBrowsing whether just browsing or not
  1053. * @param string $db the current database
  1054. * @param string $table the current table
  1055. * @param array $analyzed_sql_results the analyzed query and other variables set
  1056. * after analyzing the query
  1057. *
  1058. * @return int $unlim_num_rows unlimited number of rows
  1059. */
  1060. private function countQueryResults(
  1061. $num_rows, $justBrowsing, $db, $table, array $analyzed_sql_results
  1062. ) {
  1063. /* Shortcut for not analyzed/empty query */
  1064. if (empty($analyzed_sql_results)) {
  1065. return 0;
  1066. }
  1067. if (!$this->isAppendLimitClause($analyzed_sql_results)) {
  1068. // if we did not append a limit, set this to get a correct
  1069. // "Showing rows..." message
  1070. // $_SESSION['tmpval']['max_rows'] = 'all';
  1071. $unlim_num_rows = $num_rows;
  1072. } elseif ($analyzed_sql_results['querytype'] == 'SELECT'
  1073. || $analyzed_sql_results['is_subquery']
  1074. ) {
  1075. // c o u n t q u e r y
  1076. // If we are "just browsing", there is only one table (and no join),
  1077. // and no WHERE clause (or just 'WHERE 1 '),
  1078. // we do a quick count (which uses MaxExactCount) because
  1079. // SQL_CALC_FOUND_ROWS is not quick on large InnoDB tables
  1080. // However, do not count again if we did it previously
  1081. // due to $find_real_end == true
  1082. if ($justBrowsing) {
  1083. // Get row count (is approximate for InnoDB)
  1084. $unlim_num_rows = $GLOBALS['dbi']->getTable($db, $table)->countRecords();
  1085. /**
  1086. * @todo Can we know at this point that this is InnoDB,
  1087. * (in this case there would be no need for getting
  1088. * an exact count)?
  1089. */
  1090. if ($unlim_num_rows < $GLOBALS['cfg']['MaxExactCount']) {
  1091. // Get the exact count if approximate count
  1092. // is less than MaxExactCount
  1093. /**
  1094. * @todo In countRecords(), MaxExactCount is also verified,
  1095. * so can we avoid checking it twice?
  1096. */
  1097. $unlim_num_rows = $GLOBALS['dbi']->getTable($db, $table)
  1098. ->countRecords(true);
  1099. }
  1100. } else {
  1101. // The SQL_CALC_FOUND_ROWS option of the SELECT statement is used.
  1102. // For UNION statements, only a SQL_CALC_FOUND_ROWS is required
  1103. // after the first SELECT.
  1104. $count_query = Query::replaceClause(
  1105. $analyzed_sql_results['statement'],
  1106. $analyzed_sql_results['parser']->list,
  1107. 'SELECT SQL_CALC_FOUND_ROWS',
  1108. null,
  1109. true
  1110. );
  1111. // Another LIMIT clause is added to avoid long delays.
  1112. // A complete result will be returned anyway, but the LIMIT would
  1113. // stop the query as soon as the result that is required has been
  1114. // computed.
  1115. if (empty($analyzed_sql_results['union'])) {
  1116. $count_query .= ' LIMIT 1';
  1117. }
  1118. // Running the count query.
  1119. $GLOBALS['dbi']->tryQuery($count_query);
  1120. $unlim_num_rows = $GLOBALS['dbi']->fetchValue('SELECT FOUND_ROWS()');
  1121. } // end else "just browsing"
  1122. } else {// not $is_select
  1123. $unlim_num_rows = 0;
  1124. }
  1125. return $unlim_num_rows;
  1126. }
  1127. /**
  1128. * Function to handle all aspects relating to executing the query
  1129. *
  1130. * @param array $analyzed_sql_results analyzed sql results
  1131. * @param string $full_sql_query full sql query
  1132. * @param boolean $is_gotofile whether to go to a file
  1133. * @param string $db current database
  1134. * @param string $table current table
  1135. * @param boolean $find_real_end whether to find the real end
  1136. * @param string $sql_query_for_bookmark sql query to be stored as bookmark
  1137. * @param array $extra_data extra data
  1138. *
  1139. * @return mixed
  1140. */
  1141. private function executeTheQuery(array $analyzed_sql_results, $full_sql_query, $is_gotofile,
  1142. $db, $table, $find_real_end, $sql_query_for_bookmark, $extra_data
  1143. ) {
  1144. $response = Response::getInstance();
  1145. $response->getHeader()->getMenu()->setTable($table);
  1146. // Only if we ask to see the php code
  1147. if (isset($GLOBALS['show_as_php'])) {
  1148. $result = null;
  1149. $num_rows = 0;
  1150. $unlim_num_rows = 0;
  1151. } else { // If we don't ask to see the php code
  1152. if (isset($_SESSION['profiling'])
  1153. && Util::profilingSupported()
  1154. ) {
  1155. $GLOBALS['dbi']->query('SET PROFILING=1;');
  1156. }
  1157. list(
  1158. $result,
  1159. $GLOBALS['querytime']
  1160. ) = $this->executeQueryAndMeasureTime($full_sql_query);
  1161. // Displays an error message if required and stop parsing the script
  1162. $error = $GLOBALS['dbi']->getError();
  1163. if ($error && $GLOBALS['cfg']['IgnoreMultiSubmitErrors']) {
  1164. $extra_data['error'] = $error;
  1165. } elseif ($error) {
  1166. $this->handleQueryExecuteError($is_gotofile, $error, $full_sql_query);
  1167. }
  1168. // If there are no errors and bookmarklabel was given,
  1169. // store the query as a bookmark
  1170. if (! empty($_POST['bkm_label']) && ! empty($sql_query_for_bookmark)) {
  1171. $cfgBookmark = Bookmark::getParams($GLOBALS['cfg']['Server']['user']);
  1172. $this->storeTheQueryAsBookmark(
  1173. $db, $cfgBookmark['user'],
  1174. $sql_query_for_bookmark, $_POST['bkm_label'],
  1175. isset($_POST['bkm_replace']) ? $_POST['bkm_replace'] : null
  1176. );
  1177. } // end store bookmarks
  1178. // Gets the number of rows affected/returned
  1179. // (This must be done immediately after the query because
  1180. // mysql_affected_rows() reports about the last query done)
  1181. $num_rows = $this->getNumberOfRowsAffectedOrChanged(
  1182. $analyzed_sql_results['is_affected'], $result
  1183. );
  1184. // Grabs the profiling results
  1185. if (isset($_SESSION['profiling'])
  1186. && Util::profilingSupported()
  1187. ) {
  1188. $profiling_results = $GLOBALS['dbi']->fetchResult('SHOW PROFILE;');
  1189. }
  1190. $justBrowsing = $this->isJustBrowsing(
  1191. $analyzed_sql_results, isset($find_real_end) ? $find_real_end : null
  1192. );
  1193. $unlim_num_rows = $this->countQueryResults(
  1194. $num_rows, $justBrowsing, $db, $table, $analyzed_sql_results
  1195. );
  1196. $this->cleanupRelations(
  1197. isset($db) ? $db : '',
  1198. isset($table) ? $table : '',
  1199. isset($_POST['dropped_column']) ? $_POST['dropped_column'] : null,
  1200. isset($_POST['purge']) ? $_POST['purge'] : null
  1201. );
  1202. if (isset($_POST['dropped_column'])
  1203. && strlen($db) > 0
  1204. && strlen($table) > 0
  1205. ) {
  1206. // to refresh the list of indexes (Ajax mode)
  1207. $extra_data['indexes_list'] = Index::getHtmlForIndexes(
  1208. $table,
  1209. $db
  1210. );
  1211. }
  1212. }
  1213. return array($result, $num_rows, $unlim_num_rows,
  1214. isset($profiling_results) ? $profiling_results : null, $extra_data
  1215. );
  1216. }
  1217. /**
  1218. * Delete related transformation information
  1219. *
  1220. * @param string $db current database
  1221. * @param string $table current table
  1222. * @param array $analyzed_sql_results analyzed sql results
  1223. *
  1224. * @return void
  1225. */
  1226. private function deleteTransformationInfo($db, $table, array $analyzed_sql_results)
  1227. {
  1228. if (! isset($analyzed_sql_results['statement'])) {
  1229. return;
  1230. }
  1231. $statement = $analyzed_sql_results['statement'];
  1232. if ($statement instanceof AlterStatement) {
  1233. if (!empty($statement->altered[0])
  1234. && $statement->altered[0]->options->has('DROP')
  1235. ) {
  1236. if (!empty($statement->altered[0]->field->column)) {
  1237. Transformations::clear(
  1238. $db,
  1239. $table,
  1240. $statement->altered[0]->field->column
  1241. );
  1242. }
  1243. }
  1244. } elseif ($statement instanceof DropStatement) {
  1245. Transformations::clear($db, $table);
  1246. }
  1247. }
  1248. /**
  1249. * Function to get the message for the no rows returned case
  1250. *
  1251. * @param string $message_to_show message to show
  1252. * @param array $analyzed_sql_results analyzed sql results
  1253. * @param int $num_rows number of rows
  1254. *
  1255. * @return string $message
  1256. */
  1257. private function getMessageForNoRowsReturned($message_to_show,
  1258. array $analyzed_sql_results, $num_rows
  1259. ) {
  1260. if ($analyzed_sql_results['querytype'] == 'DELETE"') {
  1261. $message = Message::getMessageForDeletedRows($num_rows);
  1262. } elseif ($analyzed_sql_results['is_insert']) {
  1263. if ($analyzed_sql_results['querytype'] == 'REPLACE') {
  1264. // For REPLACE we get DELETED + INSERTED row count,
  1265. // so we have to call it affected
  1266. $message = Message::getMessageForAffectedRows($num_rows);
  1267. } else {
  1268. $message = Message::getMessageForInsertedRows($num_rows);
  1269. }
  1270. $insert_id = $GLOBALS['dbi']->insertId();
  1271. if ($insert_id != 0) {
  1272. // insert_id is id of FIRST record inserted in one insert,
  1273. // so if we inserted multiple rows, we had to increment this
  1274. $message->addText('[br]');
  1275. // need to use a temporary because the Message class
  1276. // currently supports adding parameters only to the first
  1277. // message
  1278. $_inserted = Message::notice(__('Inserted row id: %1$d'));
  1279. $_inserted->addParam($insert_id + $num_rows - 1);
  1280. $message->addMessage($_inserted);
  1281. }
  1282. } elseif ($analyzed_sql_results['is_affected']) {
  1283. $message = Message::getMessageForAffectedRows($num_rows);
  1284. // Ok, here is an explanation for the !$is_select.
  1285. // The form generated by PhpMyAdmin\SqlQueryForm
  1286. // and db_sql.php has many submit buttons
  1287. // on the same form, and some confusion arises from the
  1288. // fact that $message_to_show is sent for every case.
  1289. // The $message_to_show containing a success message and sent with
  1290. // the form should not have priority over errors
  1291. } elseif (! empty($message_to_show)
  1292. && $analyzed_sql_results['querytype'] != 'SELECT'
  1293. ) {
  1294. $message = Message::rawSuccess(htmlspecialchars($message_to_show));
  1295. } elseif (! empty($GLOBALS['show_as_php'])) {
  1296. $message = Message::success(__('Showing as PHP code'));
  1297. } elseif (isset($GLOBALS['show_as_php'])) {
  1298. /* User disable showing as PHP, query is only displayed */
  1299. $message = Message::notice(__('Showing SQL query'));
  1300. } else {
  1301. $message = Message::success(
  1302. __('MySQL returned an empty result set (i.e. zero rows).')
  1303. );
  1304. }
  1305. if (isset($GLOBALS['querytime'])) {
  1306. $_querytime = Message::notice(
  1307. '(' . __('Query took %01.4f seconds.') . ')'
  1308. );
  1309. $_querytime->addParam($GLOBALS['querytime']);
  1310. $message->addMessage($_querytime);
  1311. }
  1312. // In case of ROLLBACK, notify the user.
  1313. if (isset($_POST['rollback_query'])) {
  1314. $message->addText(__('[ROLLBACK occurred.]'));
  1315. }
  1316. return $message;
  1317. }
  1318. /**
  1319. * Function to respond back when the query returns zero rows
  1320. * This method is called
  1321. * 1-> When browsing an empty table
  1322. * 2-> When executing a query on a non empty table which returns zero results
  1323. * 3-> When executing a query on an empty table
  1324. * 4-> When executing an INSERT, UPDATE, DELETE query from the SQL tab
  1325. * 5-> When deleting a row from BROWSE tab
  1326. * 6-> When searching using the SEARCH tab which returns zero results
  1327. * 7-> When changing the structure of the table except change operation
  1328. *
  1329. * @param array $analyzed_sql_results analyzed sql results
  1330. * @param string $db current database
  1331. * @param string $table current table
  1332. * @param string $message_to_show message to show
  1333. * @param int $num_rows number of rows
  1334. * @param DisplayResults $displayResultsObject DisplayResult instance
  1335. * @param array $extra_data extra data
  1336. * @param string $pmaThemeImage uri of the theme image
  1337. * @param object $result executed query results
  1338. * @param string $sql_query sql query
  1339. * @param string $complete_query complete sql query
  1340. *
  1341. * @return string html
  1342. */
  1343. private function getQueryResponseForNoResultsReturned(array $analyzed_sql_results, $db,
  1344. $table, $message_to_show, $num_rows, $displayResultsObject, $extra_data,
  1345. $pmaThemeImage, $result, $sql_query, $complete_query
  1346. ) {
  1347. if ($this->isDeleteTransformationInfo($analyzed_sql_results)) {
  1348. $this->deleteTransformationInfo($db, $table, $analyzed_sql_results);
  1349. }
  1350. if (isset($extra_data['error'])) {
  1351. $message = Message::rawError($extra_data['error']);
  1352. } else {
  1353. $message = $this->getMessageForNoRowsReturned(
  1354. isset($message_to_show) ? $message_to_show : null,
  1355. $analyzed_sql_results, $num_rows
  1356. );
  1357. }
  1358. $html_output = '';
  1359. $html_message = Util::getMessage(
  1360. $message, $GLOBALS['sql_query'], 'success'
  1361. );
  1362. $html_output .= $html_message;
  1363. if (!isset($GLOBALS['show_as_php'])) {
  1364. if (! empty($GLOBALS['reload'])) {
  1365. $extra_data['reload'] = 1;
  1366. $extra_data['db'] = $GLOBALS['db'];
  1367. }
  1368. // For ajax requests add message and sql_query as JSON
  1369. if (empty($_REQUEST['ajax_page_request'])) {
  1370. $extra_data['message'] = $message;
  1371. if ($GLOBALS['cfg']['ShowSQL']) {
  1372. $extra_data['sql_query'] = $html_message;
  1373. }
  1374. }
  1375. $response = Response::getInstance();
  1376. $response->addJSON(isset($extra_data) ? $extra_data : array());
  1377. if (!empty($analyzed_sql_results['is_select']) &&
  1378. !isset($extra_data['error'])) {
  1379. $url_query = isset($url_query) ? $url_query : null;
  1380. $displayParts = array(
  1381. 'edit_lnk' => null,
  1382. 'del_lnk' => null,
  1383. 'sort_lnk' => '1',
  1384. 'nav_bar' => '0',
  1385. 'bkm_form' => '1',
  1386. 'text_btn' => '1',
  1387. 'pview_lnk' => '1'
  1388. );
  1389. $html_output .= $this->getHtmlForSqlQueryResultsTable(
  1390. $displayResultsObject,
  1391. $pmaThemeImage, $url_query, $displayParts,
  1392. false, 0, $num_rows, true, $result,
  1393. $analyzed_sql_results, true
  1394. );
  1395. $html_output .= $displayResultsObject->getCreateViewQueryResultOp(
  1396. $analyzed_sql_results
  1397. );
  1398. $cfgBookmark = Bookmark::getParams($GLOBALS['cfg']['Server']['user']);
  1399. if ($cfgBookmark) {
  1400. $html_output .= $this->getHtmlForBookmark(
  1401. $displayParts,
  1402. $cfgBookmark,
  1403. $sql_query, $db, $table,
  1404. isset($complete_query) ? $complete_query : $sql_query,
  1405. $cfgBookmark['user']
  1406. );
  1407. }
  1408. }
  1409. }
  1410. return $html_output;
  1411. }
  1412. /**
  1413. * Function to send response for ajax grid edit
  1414. *
  1415. * @param object $result result of the executed query
  1416. *
  1417. * @return void
  1418. */
  1419. private function sendResponseForGridEdit($result)
  1420. {
  1421. $row = $GLOBALS['dbi']->fetchRow($result);
  1422. $field_flags = $GLOBALS['dbi']->fieldFlags($result, 0);
  1423. if (stristr($field_flags, DisplayResults::BINARY_FIELD)) {
  1424. $row[0] = bin2hex($row[0]);
  1425. }
  1426. $response = Response::getInstance();
  1427. $response->addJSON('value', $row[0]);
  1428. exit;
  1429. }
  1430. /**
  1431. * Function to get html for the sql query results div
  1432. *
  1433. * @param string $previous_update_query_html html for the previously executed query
  1434. * @param string $profiling_chart_html html for profiling
  1435. * @param Message $missing_unique_column_msg message for the missing unique column
  1436. * @param Message $bookmark_created_msg message for bookmark creation
  1437. * @param string $table_html html for the table for displaying sql
  1438. * results
  1439. * @param string $indexes_problems_html html for displaying errors in indexes
  1440. * @param string $bookmark_support_html html for displaying bookmark form
  1441. *
  1442. * @return string $html_output
  1443. */
  1444. private function getHtmlForSqlQueryResults($previous_update_query_html,
  1445. $profiling_chart_html, $missing_unique_column_msg, $bookmark_created_msg,
  1446. $table_html, $indexes_problems_html, $bookmark_support_html
  1447. ) {
  1448. //begin the sqlqueryresults div here. container div
  1449. $html_output = '<div class="sqlqueryresults ajax">';
  1450. $html_output .= isset($previous_update_query_html)
  1451. ? $previous_update_query_html : '';
  1452. $html_output .= isset($profiling_chart_html) ? $profiling_chart_html : '';
  1453. $html_output .= isset($missing_unique_column_msg)
  1454. ? $missing_unique_column_msg->getDisplay() : '';
  1455. $html_output .= isset($bookmark_created_msg)
  1456. ? $bookmark_created_msg->getDisplay() : '';
  1457. $html_output .= $table_html;
  1458. $html_output .= isset($indexes_problems_html) ? $indexes_problems_html : '';
  1459. $html_output .= isset($bookmark_support_html) ? $bookmark_support_html : '';
  1460. $html_output .= '</div>'; // end sqlqueryresults div
  1461. return $html_output;
  1462. }
  1463. /**
  1464. * Returns a message for successful creation of a bookmark or null if a bookmark
  1465. * was not created
  1466. *
  1467. * @return Message $bookmark_created_msg
  1468. */
  1469. private function getBookmarkCreatedMessage()
  1470. {
  1471. if (isset($_GET['label'])) {
  1472. $bookmark_created_msg = Message::success(
  1473. __('Bookmark %s has been created.')
  1474. );
  1475. $bookmark_created_msg->addParam($_GET['label']);
  1476. } else {
  1477. $bookmark_created_msg = null;
  1478. }
  1479. return $bookmark_created_msg;
  1480. }
  1481. /**
  1482. * Function to get html for the sql query results table
  1483. *
  1484. * @param DisplayResults $displayResultsObject instance of DisplayResult
  1485. * @param string $pmaThemeImage theme image uri
  1486. * @param string $url_query url query
  1487. * @param array $displayParts the parts to display
  1488. * @param bool $editable whether the result table is
  1489. * editable or not
  1490. * @param int $unlim_num_rows unlimited number of rows
  1491. * @param int $num_rows number of rows
  1492. * @param bool $showtable whether to show table or not
  1493. * @param object $result result of the executed query
  1494. * @param array $analyzed_sql_results analyzed sql results
  1495. * @param bool $is_limited_display Show only limited operations or not
  1496. *
  1497. * @return string
  1498. */
  1499. private function getHtmlForSqlQueryResultsTable($displayResultsObject,
  1500. $pmaThemeImage, $url_query, array $displayParts,
  1501. $editable, $unlim_num_rows, $num_rows, $showtable, $result,
  1502. array $analyzed_sql_results, $is_limited_display = false
  1503. ) {
  1504. $printview = isset($_POST['printview']) && $_POST['printview'] == '1' ? '1' : null;
  1505. $table_html = '';
  1506. $browse_dist = ! empty($_POST['is_browse_distinct']);
  1507. if ($analyzed_sql_results['is_procedure']) {
  1508. do {
  1509. if (! isset($result)) {
  1510. $result = $GLOBALS['dbi']->storeResult();
  1511. }
  1512. $num_rows = $GLOBALS['dbi']->numRows($result);
  1513. if ($result !== false && $num_rows > 0) {
  1514. $fields_meta = $GLOBALS['dbi']->getFieldsMeta($result);
  1515. if (! is_array($fields_meta)) {
  1516. $fields_cnt = 0;
  1517. } else {
  1518. $fields_cnt = count($fields_meta);
  1519. }
  1520. $displayResultsObject->setProperties(
  1521. $num_rows,
  1522. $fields_meta,
  1523. $analyzed_sql_results['is_count'],
  1524. $analyzed_sql_results['is_export'],
  1525. $analyzed_sql_results['is_func'],
  1526. $analyzed_sql_results['is_analyse'],
  1527. $num_rows,
  1528. $fields_cnt,
  1529. $GLOBALS['querytime'],
  1530. $pmaThemeImage,
  1531. $GLOBALS['text_dir'],
  1532. $analyzed_sql_results['is_maint'],
  1533. $analyzed_sql_results['is_explain'],
  1534. $analyzed_sql_results['is_show'],
  1535. $showtable,
  1536. $printview,
  1537. $url_query,
  1538. $editable,
  1539. $browse_dist
  1540. );
  1541. $displayParts = array(
  1542. 'edit_lnk' => $displayResultsObject::NO_EDIT_OR_DELETE,
  1543. 'del_lnk' => $displayResultsObject::NO_EDIT_OR_DELETE,
  1544. 'sort_lnk' => '1',
  1545. 'nav_bar' => '1',
  1546. 'bkm_form' => '1',
  1547. 'text_btn' => '1',
  1548. 'pview_lnk' => '1'
  1549. );
  1550. $table_html .= $displayResultsObject->getTable(
  1551. $result,
  1552. $displayParts,
  1553. $analyzed_sql_results,
  1554. $is_limited_display
  1555. );
  1556. }
  1557. $GLOBALS['dbi']->freeResult($result);
  1558. unset($result);
  1559. } while ($GLOBALS['dbi']->moreResults() && $GLOBALS['dbi']->nextResult());
  1560. } else {
  1561. if (isset($result) && $result !== false) {
  1562. $fields_meta = $GLOBALS['dbi']->getFieldsMeta($result);
  1563. $fields_cnt = count($fields_meta);
  1564. }
  1565. $_SESSION['is_multi_query'] = false;
  1566. $displayResultsObject->setProperties(
  1567. $unlim_num_rows,
  1568. $fields_meta,
  1569. $analyzed_sql_results['is_count'],
  1570. $analyzed_sql_results['is_export'],
  1571. $analyzed_sql_results['is_func'],
  1572. $analyzed_sql_results['is_analyse'],
  1573. $num_rows,
  1574. $fields_cnt, $GLOBALS['querytime'],
  1575. $pmaThemeImage, $GLOBALS['text_dir'],
  1576. $analyzed_sql_results['is_maint'],
  1577. $analyzed_sql_results['is_explain'],
  1578. $analyzed_sql_results['is_show'],
  1579. $showtable,
  1580. $printview,
  1581. $url_query,
  1582. $editable,
  1583. $browse_dist
  1584. );
  1585. $table_html .= $displayResultsObject->getTable(
  1586. $result,
  1587. $displayParts,
  1588. $analyzed_sql_results,
  1589. $is_limited_display
  1590. );
  1591. $GLOBALS['dbi']->freeResult($result);
  1592. }
  1593. return $table_html;
  1594. }
  1595. /**
  1596. * Function to get html for the previous query if there is such. If not will return
  1597. * null
  1598. *
  1599. * @param string $disp_query display query
  1600. * @param bool $showSql whether to show sql
  1601. * @param array $sql_data sql data
  1602. * @param string $disp_message display message
  1603. *
  1604. * @return string $previous_update_query_html
  1605. */
  1606. private function getHtmlForPreviousUpdateQuery($disp_query, $showSql, $sql_data,
  1607. $disp_message
  1608. ) {
  1609. // previous update query (from tbl_replace)
  1610. if (isset($disp_query) && ($showSql == true) && empty($sql_data)) {
  1611. $previous_update_query_html = Util::getMessage(
  1612. $disp_message, $disp_query, 'success'
  1613. );
  1614. } else {
  1615. $previous_update_query_html = null;
  1616. }
  1617. return $previous_update_query_html;
  1618. }
  1619. /**
  1620. * To get the message if a column index is missing. If not will return null
  1621. *
  1622. * @param string $table current table
  1623. * @param string $db current database
  1624. * @param boolean $editable whether the results table can be editable or not
  1625. * @param boolean $has_unique whether there is a unique key
  1626. *
  1627. * @return Message $message
  1628. */
  1629. private function getMessageIfMissingColumnIndex($table, $db, $editable, $has_unique)
  1630. {
  1631. if (!empty($table) && ($GLOBALS['dbi']->isSystemSchema($db) || !$editable)) {
  1632. $missing_unique_column_msg = Message::notice(
  1633. sprintf(
  1634. __(
  1635. 'Current selection does not contain a unique column.'
  1636. . ' Grid edit, checkbox, Edit, Copy and Delete features'
  1637. . ' are not available. %s'
  1638. ),
  1639. Util::showDocu(
  1640. 'config',
  1641. 'cfg_RowActionLinksWithoutUnique'
  1642. )
  1643. )
  1644. );
  1645. } elseif (! empty($table) && ! $has_unique) {
  1646. $missing_unique_column_msg = Message::notice(
  1647. sprintf(
  1648. __(
  1649. 'Current selection does not contain a unique column.'
  1650. . ' Grid edit, Edit, Copy and Delete features may result in'
  1651. . ' undesired behavior. %s'
  1652. ),
  1653. Util::showDocu(
  1654. 'config',
  1655. 'cfg_RowActionLinksWithoutUnique'
  1656. )
  1657. )
  1658. );
  1659. } else {
  1660. $missing_unique_column_msg = null;
  1661. }
  1662. return $missing_unique_column_msg;
  1663. }
  1664. /**
  1665. * Function to get html to display problems in indexes
  1666. *
  1667. * @param string $query_type query type
  1668. * @param array|null $selectedTables array of table names selected from the
  1669. * database structure page, for an action
  1670. * like check table, optimize table,
  1671. * analyze table or repair table
  1672. * @param string $db current database
  1673. *
  1674. * @return string
  1675. */
  1676. private function getHtmlForIndexesProblems($query_type, $selectedTables, $db)
  1677. {
  1678. // BEGIN INDEX CHECK See if indexes should be checked.
  1679. if (isset($query_type)
  1680. && $query_type == 'check_tbl'
  1681. && isset($selectedTables)
  1682. && is_array($selectedTables)
  1683. ) {
  1684. $indexes_problems_html = '';
  1685. foreach ($selectedTables as $tbl_name) {
  1686. $check = Index::findDuplicates($tbl_name, $db);
  1687. if (! empty($check)) {
  1688. $indexes_problems_html .= sprintf(
  1689. __('Problems with indexes of table `%s`'), $tbl_name
  1690. );
  1691. $indexes_problems_html .= $check;
  1692. }
  1693. }
  1694. } else {
  1695. $indexes_problems_html = null;
  1696. }
  1697. return $indexes_problems_html;
  1698. }
  1699. /**
  1700. * Function to display results when the executed query returns non empty results
  1701. *
  1702. * @param object $result executed query results
  1703. * @param array $analyzed_sql_results analysed sql results
  1704. * @param string $db current database
  1705. * @param string $table current table
  1706. * @param string $message message to show
  1707. * @param array $sql_data sql data
  1708. * @param DisplayResults $displayResultsObject Instance of DisplayResults
  1709. * @param string $pmaThemeImage uri of the theme image
  1710. * @param int $unlim_num_rows unlimited number of rows
  1711. * @param int $num_rows number of rows
  1712. * @param string $disp_query display query
  1713. * @param string $disp_message display message
  1714. * @param array $profiling_results profiling results
  1715. * @param string $query_type query type
  1716. * @param array|null $selectedTables array of table names selected
  1717. * from the database structure page, for
  1718. * an action like check table,
  1719. * optimize table, analyze table or
  1720. * repair table
  1721. * @param string $sql_query sql query
  1722. * @param string $complete_query complete sql query
  1723. *
  1724. * @return string html
  1725. */
  1726. private function getQueryResponseForResultsReturned($result, array $analyzed_sql_results,
  1727. $db, $table, $message, $sql_data, $displayResultsObject, $pmaThemeImage,
  1728. $unlim_num_rows, $num_rows, $disp_query, $disp_message, $profiling_results,
  1729. $query_type, $selectedTables, $sql_query, $complete_query
  1730. ) {
  1731. // If we are retrieving the full value of a truncated field or the original
  1732. // value of a transformed field, show it here
  1733. if (isset($_POST['grid_edit']) && $_POST['grid_edit'] == true) {
  1734. $this->sendResponseForGridEdit($result);
  1735. // script has exited at this point
  1736. }
  1737. // Gets the list of fields properties
  1738. if (isset($result) && $result) {
  1739. $fields_meta = $GLOBALS['dbi']->getFieldsMeta($result);
  1740. }
  1741. // Should be initialized these parameters before parsing
  1742. $showtable = isset($showtable) ? $showtable : null;
  1743. $url_query = isset($url_query) ? $url_query : null;
  1744. $response = Response::getInstance();
  1745. $header = $response->getHeader();
  1746. $scripts = $header->getScripts();
  1747. $just_one_table = $this->resultSetHasJustOneTable($fields_meta);
  1748. // hide edit and delete links:
  1749. // - for information_schema
  1750. // - if the result set does not contain all the columns of a unique key
  1751. // (unless this is an updatable view)
  1752. // - if the SELECT query contains a join or a subquery
  1753. $updatableView = false;
  1754. $statement = isset($analyzed_sql_results['statement']) ? $analyzed_sql_results['statement'] : null;
  1755. if ($statement instanceof SelectStatement) {
  1756. if (!empty($statement->expr)) {
  1757. if ($statement->expr[0]->expr === '*') {
  1758. $_table = new Table($table, $db);
  1759. $updatableView = $_table->isUpdatableView();
  1760. }
  1761. }
  1762. if ($analyzed_sql_results['join']
  1763. || $analyzed_sql_results['is_subquery']
  1764. || count($analyzed_sql_results['select_tables']) !== 1
  1765. ) {
  1766. $just_one_table = false;
  1767. }
  1768. }
  1769. $has_unique = $this->resultSetContainsUniqueKey(
  1770. $db, $table, $fields_meta
  1771. );
  1772. $editable = ($has_unique
  1773. || $GLOBALS['cfg']['RowActionLinksWithoutUnique']
  1774. || $updatableView)
  1775. && $just_one_table;
  1776. $_SESSION['tmpval']['possible_as_geometry'] = $editable;
  1777. $displayParts = array(
  1778. 'edit_lnk' => $displayResultsObject::UPDATE_ROW,
  1779. 'del_lnk' => $displayResultsObject::DELETE_ROW,
  1780. 'sort_lnk' => '1',
  1781. 'nav_bar' => '1',
  1782. 'bkm_form' => '1',
  1783. 'text_btn' => '0',
  1784. 'pview_lnk' => '1'
  1785. );
  1786. if ($GLOBALS['dbi']->isSystemSchema($db) || !$editable) {
  1787. $displayParts = array(
  1788. 'edit_lnk' => $displayResultsObject::NO_EDIT_OR_DELETE,
  1789. 'del_lnk' => $displayResultsObject::NO_EDIT_OR_DELETE,
  1790. 'sort_lnk' => '1',
  1791. 'nav_bar' => '1',
  1792. 'bkm_form' => '1',
  1793. 'text_btn' => '1',
  1794. 'pview_lnk' => '1'
  1795. );
  1796. }
  1797. if (isset($_POST['printview']) && $_POST['printview'] == '1') {
  1798. $displayParts = array(
  1799. 'edit_lnk' => $displayResultsObject::NO_EDIT_OR_DELETE,
  1800. 'del_lnk' => $displayResultsObject::NO_EDIT_OR_DELETE,
  1801. 'sort_lnk' => '0',
  1802. 'nav_bar' => '0',
  1803. 'bkm_form' => '0',
  1804. 'text_btn' => '0',
  1805. 'pview_lnk' => '0'
  1806. );
  1807. }
  1808. if (isset($_POST['table_maintenance'])) {
  1809. $scripts->addFile('makegrid.js');
  1810. $scripts->addFile('sql.js');
  1811. $table_maintenance_html = '';
  1812. if (isset($message)) {
  1813. $message = Message::success($message);
  1814. $table_maintenance_html = Util::getMessage(
  1815. $message, $GLOBALS['sql_query'], 'success'
  1816. );
  1817. }
  1818. $table_maintenance_html .= $this->getHtmlForSqlQueryResultsTable(
  1819. $displayResultsObject,
  1820. $pmaThemeImage, $url_query, $displayParts,
  1821. false, $unlim_num_rows, $num_rows, $showtable, $result,
  1822. $analyzed_sql_results
  1823. );
  1824. if (empty($sql_data) || ($sql_data['valid_queries'] = 1)) {
  1825. $response->addHTML($table_maintenance_html);
  1826. exit();
  1827. }
  1828. }
  1829. if (!isset($_POST['printview']) || $_POST['printview'] != '1') {
  1830. $scripts->addFile('makegrid.js');
  1831. $scripts->addFile('sql.js');
  1832. unset($GLOBALS['message']);
  1833. //we don't need to buffer the output in getMessage here.
  1834. //set a global variable and check against it in the function
  1835. $GLOBALS['buffer_message'] = false;
  1836. }
  1837. $previous_update_query_html = $this->getHtmlForPreviousUpdateQuery(
  1838. isset($disp_query) ? $disp_query : null,
  1839. $GLOBALS['cfg']['ShowSQL'], isset($sql_data) ? $sql_data : null,
  1840. isset($disp_message) ? $disp_message : null
  1841. );
  1842. $profiling_chart_html = $this->getHtmlForProfilingChart(
  1843. $url_query, $db, isset($profiling_results) ? $profiling_results :array()
  1844. );
  1845. $missing_unique_column_msg = $this->getMessageIfMissingColumnIndex(
  1846. $table, $db, $editable, $has_unique
  1847. );
  1848. $bookmark_created_msg = $this->getBookmarkCreatedMessage();
  1849. $table_html = $this->getHtmlForSqlQueryResultsTable(
  1850. $displayResultsObject,
  1851. $pmaThemeImage, $url_query, $displayParts,
  1852. $editable, $unlim_num_rows, $num_rows, $showtable, $result,
  1853. $analyzed_sql_results
  1854. );
  1855. $indexes_problems_html = $this->getHtmlForIndexesProblems(
  1856. isset($query_type) ? $query_type : null,
  1857. isset($selectedTables) ? $selectedTables : null, $db
  1858. );
  1859. $cfgBookmark = Bookmark::getParams($GLOBALS['cfg']['Server']['user']);
  1860. if ($cfgBookmark) {
  1861. $bookmark_support_html = $this->getHtmlForBookmark(
  1862. $displayParts,
  1863. $cfgBookmark,
  1864. $sql_query, $db, $table,
  1865. isset($complete_query) ? $complete_query : $sql_query,
  1866. $cfgBookmark['user']
  1867. );
  1868. } else {
  1869. $bookmark_support_html = '';
  1870. }
  1871. $html_output = isset($table_maintenance_html) ? $table_maintenance_html : '';
  1872. $html_output .= $this->getHtmlForSqlQueryResults(
  1873. $previous_update_query_html, $profiling_chart_html,
  1874. $missing_unique_column_msg, $bookmark_created_msg,
  1875. $table_html, $indexes_problems_html, $bookmark_support_html
  1876. );
  1877. return $html_output;
  1878. }
  1879. /**
  1880. * Function to execute the query and send the response
  1881. *
  1882. * @param array $analyzed_sql_results analysed sql results
  1883. * @param bool $is_gotofile whether goto file or not
  1884. * @param string $db current database
  1885. * @param string $table current table
  1886. * @param bool|null $find_real_end whether to find real end or not
  1887. * @param string $sql_query_for_bookmark the sql query to be stored as bookmark
  1888. * @param array|null $extra_data extra data
  1889. * @param string $message_to_show message to show
  1890. * @param string $message message
  1891. * @param array|null $sql_data sql data
  1892. * @param string $goto goto page url
  1893. * @param string $pmaThemeImage uri of the PMA theme image
  1894. * @param string $disp_query display query
  1895. * @param string $disp_message display message
  1896. * @param string $query_type query type
  1897. * @param string $sql_query sql query
  1898. * @param array|null $selectedTables array of table names selected from the
  1899. * database structure page, for an action
  1900. * like check table, optimize table,
  1901. * analyze table or repair table
  1902. * @param string $complete_query complete query
  1903. *
  1904. * @return void
  1905. */
  1906. public function executeQueryAndSendQueryResponse($analyzed_sql_results,
  1907. $is_gotofile, $db, $table, $find_real_end, $sql_query_for_bookmark,
  1908. $extra_data, $message_to_show, $message, $sql_data, $goto, $pmaThemeImage,
  1909. $disp_query, $disp_message, $query_type, $sql_query, $selectedTables,
  1910. $complete_query
  1911. ) {
  1912. if ($analyzed_sql_results == null) {
  1913. // Parse and analyze the query
  1914. list(
  1915. $analyzed_sql_results,
  1916. $db,
  1917. $table_from_sql
  1918. ) = ParseAnalyze::sqlQuery($sql_query, $db);
  1919. // @todo: possibly refactor
  1920. extract($analyzed_sql_results);
  1921. if ($table != $table_from_sql && !empty($table_from_sql)) {
  1922. $table = $table_from_sql;
  1923. }
  1924. }
  1925. $html_output = $this->executeQueryAndGetQueryResponse(
  1926. $analyzed_sql_results, // analyzed_sql_results
  1927. $is_gotofile, // is_gotofile
  1928. $db, // db
  1929. $table, // table
  1930. $find_real_end, // find_real_end
  1931. $sql_query_for_bookmark, // sql_query_for_bookmark
  1932. $extra_data, // extra_data
  1933. $message_to_show, // message_to_show
  1934. $message, // message
  1935. $sql_data, // sql_data
  1936. $goto, // goto
  1937. $pmaThemeImage, // pmaThemeImage
  1938. $disp_query, // disp_query
  1939. $disp_message, // disp_message
  1940. $query_type, // query_type
  1941. $sql_query, // sql_query
  1942. $selectedTables, // selectedTables
  1943. $complete_query // complete_query
  1944. );
  1945. $response = Response::getInstance();
  1946. $response->addHTML($html_output);
  1947. }
  1948. /**
  1949. * Function to execute the query and send the response
  1950. *
  1951. * @param array $analyzed_sql_results analysed sql results
  1952. * @param bool $is_gotofile whether goto file or not
  1953. * @param string $db current database
  1954. * @param string $table current table
  1955. * @param bool|null $find_real_end whether to find real end or not
  1956. * @param string $sql_query_for_bookmark the sql query to be stored as bookmark
  1957. * @param array|null $extra_data extra data
  1958. * @param string $message_to_show message to show
  1959. * @param string $message message
  1960. * @param array|null $sql_data sql data
  1961. * @param string $goto goto page url
  1962. * @param string $pmaThemeImage uri of the PMA theme image
  1963. * @param string $disp_query display query
  1964. * @param string $disp_message display message
  1965. * @param string $query_type query type
  1966. * @param string $sql_query sql query
  1967. * @param array|null $selectedTables array of table names selected from the
  1968. * database structure page, for an action
  1969. * like check table, optimize table,
  1970. * analyze table or repair table
  1971. * @param string $complete_query complete query
  1972. *
  1973. * @return string html
  1974. */
  1975. public function executeQueryAndGetQueryResponse(array $analyzed_sql_results,
  1976. $is_gotofile, $db, $table, $find_real_end, $sql_query_for_bookmark,
  1977. $extra_data, $message_to_show, $message, $sql_data, $goto, $pmaThemeImage,
  1978. $disp_query, $disp_message, $query_type, $sql_query, $selectedTables,
  1979. $complete_query
  1980. ) {
  1981. // Handle disable/enable foreign key checks
  1982. $default_fk_check = Util::handleDisableFKCheckInit();
  1983. // Handle remembered sorting order, only for single table query.
  1984. // Handling is not required when it's a union query
  1985. // (the parser never sets the 'union' key to 0).
  1986. // Handling is also not required if we came from the "Sort by key"
  1987. // drop-down.
  1988. if (! empty($analyzed_sql_results)
  1989. && $this->isRememberSortingOrder($analyzed_sql_results)
  1990. && empty($analyzed_sql_results['union'])
  1991. && ! isset($_POST['sort_by_key'])
  1992. ) {
  1993. if (! isset($_SESSION['sql_from_query_box'])) {
  1994. $this->handleSortOrder($db, $table, $analyzed_sql_results, $sql_query);
  1995. } else {
  1996. unset($_SESSION['sql_from_query_box']);
  1997. }
  1998. }
  1999. $displayResultsObject = new DisplayResults(
  2000. $GLOBALS['db'], $GLOBALS['table'], $goto, $sql_query
  2001. );
  2002. $displayResultsObject->setConfigParamsForDisplayTable();
  2003. // assign default full_sql_query
  2004. $full_sql_query = $sql_query;
  2005. // Do append a "LIMIT" clause?
  2006. if ($this->isAppendLimitClause($analyzed_sql_results)) {
  2007. $full_sql_query = $this->getSqlWithLimitClause($analyzed_sql_results);
  2008. }
  2009. $GLOBALS['reload'] = $this->hasCurrentDbChanged($db);
  2010. $GLOBALS['dbi']->selectDb($db);
  2011. // Execute the query
  2012. list($result, $num_rows, $unlim_num_rows, $profiling_results, $extra_data)
  2013. = $this->executeTheQuery(
  2014. $analyzed_sql_results,
  2015. $full_sql_query,
  2016. $is_gotofile,
  2017. $db,
  2018. $table,
  2019. isset($find_real_end) ? $find_real_end : null,
  2020. isset($sql_query_for_bookmark) ? $sql_query_for_bookmark : null,
  2021. isset($extra_data) ? $extra_data : null
  2022. );
  2023. $operations = new Operations();
  2024. $warning_messages = $operations->getWarningMessagesArray();
  2025. // No rows returned -> move back to the calling page
  2026. if ((0 == $num_rows && 0 == $unlim_num_rows)
  2027. || $analyzed_sql_results['is_affected']
  2028. ) {
  2029. $html_output = $this->getQueryResponseForNoResultsReturned(
  2030. $analyzed_sql_results, $db, $table,
  2031. isset($message_to_show) ? $message_to_show : null,
  2032. $num_rows, $displayResultsObject, $extra_data,
  2033. $pmaThemeImage, isset($result) ? $result : null,
  2034. $sql_query, isset($complete_query) ? $complete_query : null
  2035. );
  2036. } else {
  2037. // At least one row is returned -> displays a table with results
  2038. $html_output = $this->getQueryResponseForResultsReturned(
  2039. isset($result) ? $result : null,
  2040. $analyzed_sql_results,
  2041. $db,
  2042. $table,
  2043. isset($message) ? $message : null,
  2044. isset($sql_data) ? $sql_data : null,
  2045. $displayResultsObject,
  2046. $pmaThemeImage,
  2047. $unlim_num_rows,
  2048. $num_rows,
  2049. isset($disp_query) ? $disp_query : null,
  2050. isset($disp_message) ? $disp_message : null,
  2051. $profiling_results,
  2052. isset($query_type) ? $query_type : null,
  2053. isset($selectedTables) ? $selectedTables : null,
  2054. $sql_query,
  2055. isset($complete_query) ? $complete_query : null
  2056. );
  2057. }
  2058. // Handle disable/enable foreign key checks
  2059. Util::handleDisableFKCheckCleanup($default_fk_check);
  2060. foreach ($warning_messages as $warning) {
  2061. $message = Message::notice(Message::sanitize($warning));
  2062. $html_output .= $message->getDisplay();
  2063. }
  2064. return $html_output;
  2065. }
  2066. /**
  2067. * Function to define pos to display a row
  2068. *
  2069. * @param int $number_of_line Number of the line to display
  2070. * @param int $max_rows Number of rows by page
  2071. *
  2072. * @return int Start position to display the line
  2073. */
  2074. private function getStartPosToDisplayRow($number_of_line, $max_rows = null)
  2075. {
  2076. if (null === $max_rows) {
  2077. $max_rows = $_SESSION['tmpval']['max_rows'];
  2078. }
  2079. return @((ceil($number_of_line / $max_rows) - 1) * $max_rows);
  2080. }
  2081. /**
  2082. * Function to calculate new pos if pos is higher than number of rows
  2083. * of displayed table
  2084. *
  2085. * @param string $db Database name
  2086. * @param string $table Table name
  2087. * @param int|null $pos Initial position
  2088. *
  2089. * @return int Number of pos to display last page
  2090. */
  2091. public function calculatePosForLastPage($db, $table, $pos)
  2092. {
  2093. if (null === $pos) {
  2094. $pos = $_SESSION['tmpval']['pos'];
  2095. }
  2096. $_table = new Table($table, $db);
  2097. $unlim_num_rows = $_table->countRecords(true);
  2098. //If position is higher than number of rows
  2099. if ($unlim_num_rows <= $pos && 0 != $pos) {
  2100. $pos = $this->getStartPosToDisplayRow($unlim_num_rows);
  2101. }
  2102. return $pos;
  2103. }
  2104. }