tbl_relation.lib.php 36 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * Functions for the table relation page
  5. *
  6. * @package PhpMyAdmin
  7. */
  8. /**
  9. * Generate dropdown choices
  10. *
  11. * @param string $dropdown_question Message to display
  12. * @param string $select_name Name of the <select> field
  13. * @param array $choices Choices for dropdown
  14. * @param string $selected_value Selected value
  15. *
  16. * @return string The html code for existing value (for selected)
  17. *
  18. * @access public
  19. */
  20. function PMA_generateDropdown(
  21. $dropdown_question, $select_name, $choices, $selected_value
  22. ) {
  23. $html_output = htmlspecialchars($dropdown_question) . '&nbsp;&nbsp;'
  24. . '<select name="' . htmlspecialchars($select_name) . '">' . "\n";
  25. foreach ($choices as $one_value => $one_label) {
  26. $html_output .= '<option value="' . htmlspecialchars($one_value) . '"';
  27. if ($selected_value == $one_value) {
  28. $html_output .= ' selected="selected" ';
  29. }
  30. $html_output .= '>' . htmlspecialchars($one_label) . '</option>' . "\n";
  31. }
  32. $html_output .= '</select>' . "\n";
  33. return $html_output;
  34. }
  35. /**
  36. * Split a string on backquote pairs
  37. *
  38. * @param string $text original string
  39. *
  40. * @return array containing the elements (and their surrounding backquotes)
  41. *
  42. * @access public
  43. */
  44. function PMA_backquoteSplit($text)
  45. {
  46. $elements = array();
  47. $final_pos = strlen($text) - 1;
  48. $pos = 0;
  49. while ($pos <= $final_pos) {
  50. $first_backquote = strpos($text, '`', $pos);
  51. $second_backquote = strpos($text, '`', $first_backquote + 1);
  52. // after the second one, there might be another one which means
  53. // this is an escaped backquote
  54. if ($second_backquote < $final_pos && '`' == $text[$second_backquote + 1]) {
  55. $second_backquote = strpos($text, '`', $second_backquote + 2);
  56. }
  57. if (false === $first_backquote || false === $second_backquote) {
  58. break;
  59. }
  60. $elements[] = substr(
  61. $text, $first_backquote, $second_backquote - $first_backquote + 1
  62. );
  63. $pos = $second_backquote + 1;
  64. }
  65. return($elements);
  66. }
  67. /**
  68. * Returns the DROP query for a foreign key constraint
  69. *
  70. * @param string $table table of the foreign key
  71. * @param string $fk foreign key name
  72. *
  73. * @return string DROP query for the foreign key constraint
  74. */
  75. function PMA_getSQLToDropForeignKey($table, $fk)
  76. {
  77. return 'ALTER TABLE ' . PMA_Util::backquote($table)
  78. . ' DROP FOREIGN KEY ' . PMA_Util::backquote($fk) . ';';
  79. }
  80. /**
  81. * Returns the SQL query for foreign key constraint creation
  82. *
  83. * @param string $table table name
  84. * @param string $field field name
  85. * @param string $foreignDb foreign database name
  86. * @param string $foreignTable foreign table name
  87. * @param string $foreignField foreign field name
  88. * @param string $name name of the constraint
  89. * @param string $onDelete on delete action
  90. * @param string $onUpdate on update action
  91. *
  92. * @return string SQL query for foreign key constraint creation
  93. */
  94. function PMA_getSQLToCreateForeignKey($table, $field, $foreignDb, $foreignTable,
  95. $foreignField, $name = null, $onDelete = null, $onUpdate = null
  96. ) {
  97. $sql_query = 'ALTER TABLE ' . PMA_Util::backquote($table) . ' ADD ';
  98. // if user entered a constraint name
  99. if (! empty($name)) {
  100. $sql_query .= ' CONSTRAINT ' . PMA_Util::backquote($name);
  101. }
  102. $sql_query .= ' FOREIGN KEY (' . PMA_Util::backquote($field) . ')'
  103. . ' REFERENCES ' . PMA_Util::backquote($foreignDb)
  104. . '.' . PMA_Util::backquote($foreignTable)
  105. . '(' . PMA_Util::backquote($foreignField) . ')';
  106. if (! empty($onDelete)) {
  107. $sql_query .= ' ON DELETE ' . $onDelete;
  108. }
  109. if (! empty($onUpdate)) {
  110. $sql_query .= ' ON UPDATE ' . $onUpdate;
  111. }
  112. $sql_query .= ';';
  113. return $sql_query;
  114. }
  115. /**
  116. * Creates and populates dropdowns to select foreign db/table/column
  117. *
  118. * @param string $name name of the dropdowns
  119. * @param array $values dropdown values
  120. * @param string|boolean $foreign value of the item to be selected
  121. * @param string $title title to show on hovering the dropdown
  122. *
  123. * @return string HTML for the dropdown
  124. */
  125. function PMA_generateRelationalDropdown(
  126. $name, $values = array(), $foreign = false, $title = ''
  127. ) {
  128. $html_output = '<select name="' . $name . '" title="' . $title . '">';
  129. $html_output .= '<option value=""></option>';
  130. $seen_key = false;
  131. foreach ($values as $value) {
  132. $html_output .= '<option value="' . htmlspecialchars($value) . '"';
  133. if ($foreign && $value == $foreign) {
  134. $html_output .= ' selected="selected"';
  135. $seen_key = true;
  136. }
  137. $html_output .= '>' . htmlspecialchars($value) . '</option>';
  138. }
  139. if ($foreign && ! $seen_key) {
  140. $html_output .= '<option value="' . htmlspecialchars($foreign) . '"'
  141. . ' selected="selected">' . htmlspecialchars($foreign) . '</option>';
  142. }
  143. $html_output .= '</select>';
  144. return $html_output;
  145. }
  146. /**
  147. * Function to get html for the common form
  148. *
  149. * @param string $db current database
  150. * @param string $table current table
  151. * @param array $columns columns
  152. * @param array $cfgRelation configuration relation
  153. * @param string $tbl_storage_engine table storage engine
  154. * @param array $existrel db, table, column
  155. * @param array $existrel_foreign db, table, column
  156. * @param array $options_array options array
  157. *
  158. * @return string
  159. */
  160. function PMA_getHtmlForCommonForm($db, $table, $columns, $cfgRelation,
  161. $tbl_storage_engine, $existrel, $existrel_foreign, $options_array
  162. ) {
  163. $html_output = PMA_getHtmlForCommonFormHeader($db, $table);
  164. if (count($columns) > 0) {
  165. $html_output .= PMA_getHtmlForCommonFormRows(
  166. $columns, $cfgRelation, $tbl_storage_engine,
  167. $existrel, $existrel_foreign, $options_array, $db, $table
  168. );
  169. } // end if (we have columns in this table)
  170. $html_output .= PMA_getHtmlForCommonFormFooter();
  171. return $html_output;
  172. }
  173. /**
  174. * Function to get html for the common form rows
  175. *
  176. * @param array $columns columns
  177. * @param array $cfgRelation configuration relation
  178. * @param string $tbl_storage_engine table storage engine
  179. * @param array $existrel existed relations
  180. * @param array $existrel_foreign existed relations for foreign keys
  181. * @param array $options_array options array
  182. * @param string $db current database
  183. * @param string $table current table
  184. *
  185. * @return string
  186. */
  187. function PMA_getHtmlForCommonFormRows($columns, $cfgRelation, $tbl_storage_engine,
  188. $existrel, $existrel_foreign, $options_array, $db, $table
  189. ) {
  190. $save_row = array();
  191. foreach ($columns as $row) {
  192. $save_row[] = $row;
  193. }
  194. $saved_row_cnt = count($save_row);
  195. $html_output = '<fieldset>'
  196. . '<legend>' . __('Relations') . '</legend>'
  197. . '<table id="relationalTable">';
  198. $html_output .= PMA_getHtmlForCommonFormTableHeaders(
  199. $cfgRelation, $tbl_storage_engine
  200. );
  201. $odd_row = true;
  202. for ($i = 0; $i < $saved_row_cnt; $i++) {
  203. $html_output .= PMA_getHtmlForRow(
  204. $save_row, $i, $odd_row, $cfgRelation, $existrel, $db,
  205. $tbl_storage_engine, $existrel_foreign, $options_array
  206. );
  207. $odd_row = ! $odd_row;
  208. } // end for
  209. $html_output .= '</table>' . "\n"
  210. . '</fieldset>' . "\n";
  211. if ($cfgRelation['displaywork']) {
  212. $html_output .= PMA_getHtmlForDisplayFieldInfos($db, $table, $save_row);
  213. }
  214. return $html_output;
  215. }
  216. /**
  217. * Function to get html for an entire row in common form
  218. *
  219. * @param array $save_row save row
  220. * @param int $i counter
  221. * @param bool $odd_row whether odd row or not
  222. * @param array $cfgRelation configuration relation
  223. * @param array $existrel db, table, column
  224. * @param string $db current db
  225. * @param string $tbl_storage_engine table storage engine
  226. * @param array $existrel_foreign db, table, column
  227. * @param array $options_array options array
  228. *
  229. * @return string
  230. */
  231. function PMA_getHtmlForRow($save_row, $i, $odd_row, $cfgRelation, $existrel, $db,
  232. $tbl_storage_engine, $existrel_foreign, $options_array
  233. ) {
  234. $myfield = $save_row[$i]['Field'];
  235. // Use an md5 as array index to avoid having special characters
  236. // in the name attribute (see bug #1746964 )
  237. $myfield_md5 = md5($myfield);
  238. $myfield_html = htmlspecialchars($myfield);
  239. $html_output = '<tr class="' . ($odd_row ? 'odd' : 'even') . '">'
  240. . '<td class="center">'
  241. . '<strong>' . $myfield_html . '</strong>'
  242. . '<input type="hidden" name="fields_name[' . $myfield_md5 . ']"'
  243. . ' value="' . $myfield_html . '"/>'
  244. . '</td>';
  245. if ($cfgRelation['relwork']) {
  246. $html_output .= '<td>';
  247. $foreign_db = false;
  248. $foreign_table = false;
  249. $foreign_column = false;
  250. // database dropdown
  251. if (isset($existrel[$myfield])) {
  252. $foreign_db = $existrel[$myfield]['foreign_db'];
  253. } else {
  254. $foreign_db = $db;
  255. }
  256. $html_output .= PMA_generateRelationalDropdown(
  257. 'destination_db[' . $myfield_md5 . ']',
  258. $GLOBALS['pma']->databases,
  259. $foreign_db,
  260. __('Database')
  261. );
  262. // end of database dropdown
  263. // table dropdown
  264. $tables = array();
  265. if ($foreign_db) {
  266. if (isset($existrel[$myfield])) {
  267. $foreign_table = $existrel[$myfield]['foreign_table'];
  268. }
  269. $tables_rs = $GLOBALS['dbi']->query(
  270. 'SHOW TABLES FROM ' . PMA_Util::backquote($foreign_db),
  271. null,
  272. PMA_DatabaseInterface::QUERY_STORE
  273. );
  274. while ($row = $GLOBALS['dbi']->fetchRow($tables_rs)) {
  275. $tables[] = $row[0];
  276. }
  277. }
  278. $html_output .= PMA_generateRelationalDropdown(
  279. 'destination_table[' . $myfield_md5 . ']',
  280. $tables,
  281. $foreign_table,
  282. __('Table')
  283. );
  284. // end of table dropdown
  285. // column dropdown
  286. $columns = array();
  287. if ($foreign_db && $foreign_table) {
  288. if (isset($existrel[$myfield])) {
  289. $foreign_column = $existrel[$myfield]['foreign_field'];
  290. }
  291. $table_obj = new PMA_Table($foreign_table, $foreign_db);
  292. $columns = $table_obj->getUniqueColumns(false, false);
  293. }
  294. $html_output .= PMA_generateRelationalDropdown(
  295. 'destination_column[' . $myfield_md5 . ']',
  296. $columns,
  297. $foreign_column,
  298. __('Column')
  299. );
  300. // end of column dropdown
  301. $html_output .= '</td>';
  302. } // end if (internal relations)
  303. if (PMA_Util::isForeignKeySupported($tbl_storage_engine)) {
  304. $html_output .= PMA_getHtmlForForeignKey(
  305. $save_row, $i, $existrel_foreign, $myfield, $db,
  306. $myfield_md5, $tbl_storage_engine, $options_array
  307. );
  308. } // end if (InnoDB)
  309. $html_output .= '</tr>';
  310. return $html_output;
  311. }
  312. /**
  313. * Function to get html for the common form header
  314. *
  315. * @param string $db current database
  316. * @param string $table current table
  317. *
  318. * @return string
  319. */
  320. function PMA_getHtmlForCommonFormHeader($db, $table)
  321. {
  322. return '<form method="post" action="tbl_relation.php">' . "\n"
  323. . PMA_URL_getHiddenInputs($db, $table);
  324. }
  325. /**
  326. * Function to get html for the common form footer
  327. *
  328. * @return string
  329. */
  330. function PMA_getHtmlForCommonFormFooter()
  331. {
  332. return '<fieldset class="tblFooters">'
  333. . '<input type="submit" value="' . __('Save') . '" />'
  334. . '</fieldset>'
  335. . '</form>';
  336. }
  337. /**
  338. * Function to get html for display field infos
  339. *
  340. * @param string $db current database
  341. * @param string $table current table
  342. * @param array $save_row save row
  343. *
  344. * @return string
  345. */
  346. function PMA_getHtmlForDisplayFieldInfos($db, $table, $save_row)
  347. {
  348. $disp = PMA_getDisplayField($db, $table);
  349. $html_output = '<fieldset>'
  350. . '<label>' . __('Choose column to display:') . '</label>'
  351. . '<select name="display_field">'
  352. . '<option value="">---</option>';
  353. foreach ($save_row as $row) {
  354. $html_output .= '<option value="'
  355. . htmlspecialchars($row['Field']) . '"';
  356. if (isset($disp) && $row['Field'] == $disp) {
  357. $html_output .= ' selected="selected"';
  358. }
  359. $html_output .= '>' . htmlspecialchars($row['Field'])
  360. . '</option>' . "\n";
  361. } // end while
  362. $html_output .= '</select>'
  363. . '</fieldset>';
  364. return $html_output;
  365. }
  366. /**
  367. * Function to get html for the common form title headers
  368. *
  369. * @param array $cfgRelation configuration relation
  370. * @param string $tbl_storage_engine table storage engine
  371. *
  372. * @return string
  373. */
  374. function PMA_getHtmlForCommonFormTableHeaders($cfgRelation, $tbl_storage_engine)
  375. {
  376. $html_output = '<tr><th>' . __('Column') . '</th>';
  377. if ($cfgRelation['relwork']) {
  378. $html_output .= '<th>' . __('Internal relation');
  379. if (PMA_Util::isForeignKeySupported($tbl_storage_engine)) {
  380. $html_output .= PMA_Util::showHint(
  381. __(
  382. 'An internal relation is not necessary when a corresponding'
  383. . ' FOREIGN KEY relation exists.'
  384. )
  385. );
  386. }
  387. $html_output .= '</th>';
  388. }
  389. if (PMA_Util::isForeignKeySupported($tbl_storage_engine)) {
  390. // this does not have to be translated, it's part of the MySQL syntax
  391. $html_output .= '<th colspan="2">' . __('Foreign key constraint')
  392. . ' (' . $tbl_storage_engine . ')';
  393. $html_output .= '</th>';
  394. }
  395. $html_output .= '</tr>';
  396. return $html_output;
  397. }
  398. /**
  399. * Function to get html for the foreign key
  400. *
  401. * @param array $save_row save row
  402. * @param int $i counter
  403. * @param array $existrel_foreign db, table, columns
  404. * @param string $myfield my field
  405. * @param string $db current database
  406. * @param string $myfield_md5 my field md5
  407. * @param string $tbl_storage_engine table storage engine
  408. * @param array $options_array options array
  409. *
  410. * @return string
  411. */
  412. function PMA_getHtmlForForeignKey($save_row, $i, $existrel_foreign, $myfield, $db,
  413. $myfield_md5, $tbl_storage_engine, $options_array
  414. ) {
  415. $html_output = '<td>';
  416. if (! empty($save_row[$i]['Key'])) {
  417. $foreign_db = false;
  418. $foreign_table = false;
  419. $foreign_column = false;
  420. // foreign database dropdown
  421. if (isset($existrel_foreign[$myfield])) {
  422. $foreign_db = $existrel_foreign[$myfield]['foreign_db'];
  423. } else {
  424. $foreign_db = $db;
  425. }
  426. $html_output .= '<span class="formelement clearfloat">';
  427. $html_output .= PMA_generateRelationalDropdown(
  428. 'destination_foreign_db[' . $myfield_md5 . ']',
  429. $GLOBALS['pma']->databases,
  430. $foreign_db,
  431. __('Database')
  432. );
  433. // end of foreign database dropdown
  434. // foreign table dropdown
  435. $tables = array();
  436. if ($foreign_db) {
  437. if (isset($existrel_foreign[$myfield])) {
  438. $foreign_table = $existrel_foreign[$myfield]['foreign_table'];
  439. }
  440. // In Drizzle, 'SHOW TABLE STATUS' will show status only for the tables
  441. // which are currently in the table cache. Hence we have to use
  442. // 'SHOW TABLES' and manully retrieve table engine values.
  443. if (PMA_DRIZZLE) {
  444. $tables_rs = $GLOBALS['dbi']->query(
  445. 'SHOW TABLES FROM ' . PMA_Util::backquote($foreign_db),
  446. null,
  447. PMA_DatabaseInterface::QUERY_STORE
  448. );
  449. while ($row = $GLOBALS['dbi']->fetchArray($tables_rs)) {
  450. $engine = PMA_Table::sGetStatusInfo(
  451. $foreign_db,
  452. $row[0],
  453. 'Engine'
  454. );
  455. if (isset($engine)
  456. && strtoupper($engine) == $tbl_storage_engine
  457. ) {
  458. $tables[] = $row[0];
  459. }
  460. }
  461. } else {
  462. $tables_rs = $GLOBALS['dbi']->query(
  463. 'SHOW TABLE STATUS FROM ' . PMA_Util::backquote($foreign_db),
  464. null,
  465. PMA_DatabaseInterface::QUERY_STORE
  466. );
  467. while ($row = $GLOBALS['dbi']->fetchRow($tables_rs)) {
  468. if (isset($row[1])
  469. && strtoupper($row[1]) == $tbl_storage_engine
  470. ) {
  471. $tables[] = $row[0];
  472. }
  473. }
  474. }
  475. }
  476. $html_output .= PMA_generateRelationalDropdown(
  477. 'destination_foreign_table[' . $myfield_md5 . ']',
  478. $tables,
  479. $foreign_table,
  480. __('Table')
  481. );
  482. // end of foreign table dropdown
  483. // foreign column dropdown
  484. $columns = array();
  485. if ($foreign_db && $foreign_table) {
  486. if (isset($existrel_foreign[$myfield])) {
  487. $foreign_column = $existrel_foreign[$myfield]['foreign_field'];
  488. }
  489. $table_obj = new PMA_Table($foreign_table, $foreign_db);
  490. $columns = $table_obj->getUniqueColumns(false, false);
  491. }
  492. $html_output .= PMA_generateRelationalDropdown(
  493. 'destination_foreign_column[' . $myfield_md5 . ']',
  494. $columns,
  495. $foreign_column,
  496. __('Column')
  497. );
  498. $html_output .= '</span>';
  499. // end of foreign column dropdown
  500. // For constraint name
  501. $html_output .= '<span class="formelement clearfloat">';
  502. $constraint_name = isset($existrel_foreign[$myfield]['constraint'])
  503. ? $existrel_foreign[$myfield]['constraint'] : '';
  504. $html_output .= __('Constraint name');
  505. $html_output .= '<input type="text" name="constraint_name['
  506. . $myfield_md5 . ']"'
  507. . ' value="' . htmlspecialchars($constraint_name) . '"/>';
  508. $html_output .= '</span>' . "\n";
  509. $html_output .= '<span class="formelement clearfloat">';
  510. // For ON DELETE and ON UPDATE, the default action
  511. // is RESTRICT as per MySQL doc; however, a SHOW CREATE TABLE
  512. // won't display the clause if it's set as RESTRICT.
  513. $on_delete = isset($existrel_foreign[$myfield]['on_delete'])
  514. ? $existrel_foreign[$myfield]['on_delete'] : 'RESTRICT';
  515. $html_output .= PMA_generateDropdown(
  516. 'ON DELETE',
  517. 'on_delete[' . $myfield_md5 . ']',
  518. $options_array,
  519. $on_delete
  520. );
  521. $html_output .= '</span>' . "\n";
  522. $html_output .= '<span class="formelement clearfloat">' . "\n";
  523. $on_update = isset($existrel_foreign[$myfield]['on_update'])
  524. ? $existrel_foreign[$myfield]['on_update'] : 'RESTRICT';
  525. $html_output .= PMA_generateDropdown(
  526. 'ON UPDATE',
  527. 'on_update[' . $myfield_md5 . ']',
  528. $options_array,
  529. $on_update
  530. );
  531. $html_output .= '</span>' . "\n";
  532. } else {
  533. $html_output .= __('No index defined! Create one below');
  534. } // end if (a key exists)
  535. $html_output .= '</td>';
  536. return $html_output;
  537. }
  538. /**
  539. * Function to send html for table or column dropdown list
  540. *
  541. * @return void
  542. */
  543. function PMA_sendHtmlForTableOrColumnDropdownList()
  544. {
  545. if (isset($_REQUEST['foreignTable'])) { // if both db and table are selected
  546. PMA_sendHtmlForColumnDropdownList();
  547. } else { // if only the db is selected
  548. PMA_sendHtmlForTableDropdownList();
  549. }
  550. exit;
  551. }
  552. /**
  553. * Function to send html for column dropdown list
  554. *
  555. * @return void
  556. */
  557. function PMA_sendHtmlForColumnDropdownList()
  558. {
  559. $response = PMA_Response::getInstance();
  560. $foreignTable = $_REQUEST['foreignTable'];
  561. $table_obj = new PMA_Table($foreignTable, $_REQUEST['foreignDb']);
  562. $columns = array();
  563. foreach ($table_obj->getUniqueColumns(false, false) as $column) {
  564. $columns[] = htmlspecialchars($column);
  565. }
  566. $response->addJSON('columns', $columns);
  567. }
  568. /**
  569. * Function to send html for table dropdown list
  570. *
  571. * @return void
  572. */
  573. function PMA_sendHtmlForTableDropdownList()
  574. {
  575. $response = PMA_Response::getInstance();
  576. $tables = array();
  577. $foreign = isset($_REQUEST['foreign']) && $_REQUEST['foreign'] === 'true';
  578. if ($foreign) {
  579. $tbl_storage_engine = strtoupper(
  580. PMA_Table::sGetStatusInfo(
  581. $_REQUEST['db'],
  582. $_REQUEST['table'],
  583. 'Engine'
  584. )
  585. );
  586. }
  587. // In Drizzle, 'SHOW TABLE STATUS' will show status only for the tables
  588. // which are currently in the table cache. Hence we have to use 'SHOW TABLES'
  589. // and manully retrieve table engine values.
  590. if ($foreign && ! PMA_DRIZZLE) {
  591. $query = 'SHOW TABLE STATUS FROM '
  592. . PMA_Util::backquote($_REQUEST['foreignDb']);
  593. $tables_rs = $GLOBALS['dbi']->query(
  594. $query,
  595. null,
  596. PMA_DatabaseInterface::QUERY_STORE
  597. );
  598. while ($row = $GLOBALS['dbi']->fetchArray($tables_rs)) {
  599. if (isset($row['Engine'])
  600. && strtoupper($row['Engine']) == $tbl_storage_engine
  601. ) {
  602. $tables[] = htmlspecialchars($row['Name']);
  603. }
  604. }
  605. } else {
  606. $query = 'SHOW TABLES FROM '
  607. . PMA_Util::backquote($_REQUEST['foreignDb']);
  608. $tables_rs = $GLOBALS['dbi']->query(
  609. $query,
  610. null,
  611. PMA_DatabaseInterface::QUERY_STORE
  612. );
  613. while ($row = $GLOBALS['dbi']->fetchArray($tables_rs)) {
  614. if ($foreign && PMA_DRIZZLE) {
  615. $engine = strtoupper(
  616. PMA_Table::sGetStatusInfo(
  617. $_REQUEST['foreignDb'],
  618. $row[0],
  619. 'Engine'
  620. )
  621. );
  622. if (isset($engine) && $engine == $tbl_storage_engine) {
  623. $tables[] = htmlspecialchars($row[0]);
  624. }
  625. } else {
  626. $tables[] = htmlspecialchars($row[0]);
  627. }
  628. }
  629. }
  630. $response->addJSON('tables', $tables);
  631. }
  632. /**
  633. * Function to handle update for display field
  634. *
  635. * @param string $disp field name
  636. * @param string $display_field display field
  637. * @param string $db current database
  638. * @param string $table current table
  639. * @param array $cfgRelation configuration relation
  640. *
  641. * @return void
  642. */
  643. function PMA_handleUpdateForDisplayField($disp, $display_field, $db, $table,
  644. $cfgRelation
  645. ) {
  646. $upd_query = PMA_getQueryForDisplayUpdate(
  647. $disp, $display_field, $db, $table, $cfgRelation
  648. );
  649. if ($upd_query) {
  650. PMA_queryAsControlUser($upd_query);
  651. }
  652. }
  653. /**
  654. * Function to get display query for handlingdisplay update
  655. *
  656. * @param string $disp field name
  657. * @param string $display_field display field
  658. * @param string $db current database
  659. * @param string $table current table
  660. * @param array $cfgRelation configuration relation
  661. *
  662. * @return string
  663. */
  664. function PMA_getQueryForDisplayUpdate($disp, $display_field, $db, $table,
  665. $cfgRelation
  666. ) {
  667. $upd_query = false;
  668. if ($disp) {
  669. if ($display_field != '') {
  670. $upd_query = 'UPDATE '
  671. . PMA_Util::backquote($GLOBALS['cfgRelation']['db'])
  672. . '.' . PMA_Util::backquote($cfgRelation['table_info'])
  673. . ' SET display_field = \''
  674. . PMA_Util::sqlAddSlashes($display_field) . '\''
  675. . ' WHERE db_name = \'' . PMA_Util::sqlAddSlashes($db) . '\''
  676. . ' AND table_name = \'' . PMA_Util::sqlAddSlashes($table) . '\'';
  677. } else {
  678. $upd_query = 'DELETE FROM '
  679. . PMA_Util::backquote($GLOBALS['cfgRelation']['db'])
  680. . '.' . PMA_Util::backquote($cfgRelation['table_info'])
  681. . ' WHERE db_name = \'' . PMA_Util::sqlAddSlashes($db) . '\''
  682. . ' AND table_name = \'' . PMA_Util::sqlAddSlashes($table) . '\'';
  683. }
  684. } elseif ($display_field != '') {
  685. $upd_query = 'INSERT INTO '
  686. . PMA_Util::backquote($GLOBALS['cfgRelation']['db'])
  687. . '.' . PMA_Util::backquote($cfgRelation['table_info'])
  688. . '(db_name, table_name, display_field) VALUES('
  689. . '\'' . PMA_Util::sqlAddSlashes($db) . '\','
  690. . '\'' . PMA_Util::sqlAddSlashes($table) . '\','
  691. . '\'' . PMA_Util::sqlAddSlashes($display_field) . '\')';
  692. }
  693. return $upd_query;
  694. }
  695. /**
  696. * Function to handle updates for internal relations
  697. *
  698. * @param string $destination_db destination database
  699. * @param string $multi_edit_columns_name multi edit column name
  700. * @param string $destination_table destination table
  701. * @param string $destination_column destination column
  702. * @param array $cfgRelation configuration relation
  703. * @param string $db current database
  704. * @param string $table current table
  705. * @param array $existrel db, table, column
  706. *
  707. * @return void
  708. */
  709. function PMA_handleUpdatesForInternalRelations($destination_db,
  710. $multi_edit_columns_name, $destination_table, $destination_column, $cfgRelation,
  711. $db, $table, $existrel
  712. ) {
  713. foreach ($destination_db as $master_field_md5 => $foreign_db) {
  714. $upd_query = PMA_getQueryForInternalRelationUpdate(
  715. $multi_edit_columns_name,
  716. $master_field_md5, $foreign_db, $destination_table, $destination_column,
  717. $cfgRelation, $db, $table, isset($existrel) ? $existrel : null
  718. );
  719. if ($upd_query) {
  720. PMA_queryAsControlUser($upd_query);
  721. }
  722. }
  723. }
  724. /**
  725. * Function to get update query for updating internal relations
  726. *
  727. * @param string $multi_edit_columns_name multi edit column names
  728. * @param string $master_field_md5 master field md5
  729. * @param string $foreign_db foreign database
  730. * @param string $destination_table destination table
  731. * @param string $destination_column destination column
  732. * @param array $cfgRelation configuration relation
  733. * @param string $db current database
  734. * @param string $table current table
  735. * @param array $existrel db, table, column
  736. *
  737. * @return string
  738. */
  739. function PMA_getQueryForInternalRelationUpdate($multi_edit_columns_name,
  740. $master_field_md5, $foreign_db, $destination_table, $destination_column,
  741. $cfgRelation, $db, $table, $existrel
  742. ) {
  743. $upd_query = false;
  744. // Map the fieldname's md5 back to its real name
  745. $master_field = $multi_edit_columns_name[$master_field_md5];
  746. $foreign_table = $destination_table[$master_field_md5];
  747. $foreign_field = $destination_column[$master_field_md5];
  748. if (! empty($foreign_db)
  749. && ! empty($foreign_table)
  750. && ! empty($foreign_field)
  751. ) {
  752. if (! isset($existrel[$master_field])) {
  753. $upd_query = 'INSERT INTO '
  754. . PMA_Util::backquote($GLOBALS['cfgRelation']['db'])
  755. . '.' . PMA_Util::backquote($cfgRelation['relation'])
  756. . '(master_db, master_table, master_field, foreign_db,'
  757. . ' foreign_table, foreign_field)'
  758. . ' values('
  759. . '\'' . PMA_Util::sqlAddSlashes($db) . '\', '
  760. . '\'' . PMA_Util::sqlAddSlashes($table) . '\', '
  761. . '\'' . PMA_Util::sqlAddSlashes($master_field) . '\', '
  762. . '\'' . PMA_Util::sqlAddSlashes($foreign_db) . '\', '
  763. . '\'' . PMA_Util::sqlAddSlashes($foreign_table) . '\','
  764. . '\'' . PMA_Util::sqlAddSlashes($foreign_field) . '\')';
  765. } elseif ($existrel[$master_field]['foreign_db'] != $foreign_db
  766. || $existrel[$master_field]['foreign_table'] != $foreign_table
  767. || $existrel[$master_field]['foreign_field'] != $foreign_field
  768. ) {
  769. $upd_query = 'UPDATE '
  770. . PMA_Util::backquote($GLOBALS['cfgRelation']['db'])
  771. . '.' . PMA_Util::backquote($cfgRelation['relation']) . ' SET'
  772. . ' foreign_db = \''
  773. . PMA_Util::sqlAddSlashes($foreign_db) . '\', '
  774. . ' foreign_table = \''
  775. . PMA_Util::sqlAddSlashes($foreign_table) . '\', '
  776. . ' foreign_field = \''
  777. . PMA_Util::sqlAddSlashes($foreign_field) . '\' '
  778. . ' WHERE master_db = \''
  779. . PMA_Util::sqlAddSlashes($db) . '\''
  780. . ' AND master_table = \''
  781. . PMA_Util::sqlAddSlashes($table) . '\''
  782. . ' AND master_field = \''
  783. . PMA_Util::sqlAddSlashes($master_field) . '\'';
  784. } // end if... else....
  785. } elseif (isset($existrel[$master_field])) {
  786. $upd_query = 'DELETE FROM '
  787. . PMA_Util::backquote($GLOBALS['cfgRelation']['db'])
  788. . '.' . PMA_Util::backquote($cfgRelation['relation'])
  789. . ' WHERE master_db = \'' . PMA_Util::sqlAddSlashes($db) . '\''
  790. . ' AND master_table = \'' . PMA_Util::sqlAddSlashes($table) . '\''
  791. . ' AND master_field = \'' . PMA_Util::sqlAddSlashes($master_field)
  792. . '\'';
  793. } // end if... else....
  794. return $upd_query;
  795. }
  796. /**
  797. * Function to handle foreign key updates
  798. *
  799. * @param string $destination_foreign_db destination foreign database
  800. * @param string $multi_edit_columns_name multi edit column names
  801. * @param string $destination_foreign_table destination foreign table
  802. * @param string $destination_foreign_column destination foreign column
  803. * @param array $options_array options array
  804. * @param string $table current table
  805. * @param array $existrel_foreign db, table, column
  806. *
  807. * @return string
  808. */
  809. function PMA_handleUpdatesForForeignKeys($destination_foreign_db,
  810. $multi_edit_columns_name, $destination_foreign_table,
  811. $destination_foreign_column, $options_array, $table, $existrel_foreign
  812. ) {
  813. $html_output = '';
  814. $display_query = '';
  815. $seen_error = false;
  816. foreach ($destination_foreign_db as $master_field_md5 => $foreign_db) {
  817. $html_output .= PMA_handleUpdateForForeignKey(
  818. $multi_edit_columns_name, $master_field_md5,
  819. $destination_foreign_table, $destination_foreign_column, $options_array,
  820. $existrel_foreign, $table, $seen_error, $display_query, $foreign_db
  821. );
  822. } // end foreach
  823. if (! empty($display_query) && ! $seen_error) {
  824. $GLOBALS['display_query'] = $display_query;
  825. $html_output = PMA_Util::getMessage(
  826. __('Your SQL query has been executed successfully.'),
  827. null, 'success'
  828. );
  829. }
  830. return $html_output;
  831. }
  832. /**
  833. * Function to handle update for a foreign key
  834. *
  835. * @param array $multi_edit_columns_name multu edit columns name
  836. * @param string $master_field_md5 master field md5
  837. * @param string $destination_foreign_table destination foreign table
  838. * @param string $destination_foreign_column destination foreign column
  839. * @param array $options_array options array
  840. * @param array $existrel_foreign db, table, column
  841. * @param string $table current table
  842. * @param bool &$seen_error whether seen error
  843. * @param string &$display_query display query
  844. * @param string $foreign_db foreign database
  845. *
  846. * @return string
  847. */
  848. function PMA_handleUpdateForForeignKey($multi_edit_columns_name, $master_field_md5,
  849. $destination_foreign_table, $destination_foreign_column, $options_array,
  850. $existrel_foreign, $table, &$seen_error, &$display_query, $foreign_db
  851. ) {
  852. $html_output = '';
  853. $create = false;
  854. $drop = false;
  855. // Map the fieldname's md5 back to its real name
  856. $master_field = $multi_edit_columns_name[$master_field_md5];
  857. $foreign_table = $destination_foreign_table[$master_field_md5];
  858. $foreign_field = $destination_foreign_column[$master_field_md5];
  859. if (! empty($foreign_db)
  860. && ! empty($foreign_table)
  861. && ! empty($foreign_field)
  862. ) {
  863. if ( isset($existrel_foreign[$master_field])) {
  864. $constraint_name = $existrel_foreign[$master_field]['constraint'];
  865. $on_delete = ! empty(
  866. $existrel_foreign[$master_field]['on_delete'])
  867. ? $existrel_foreign[$master_field]['on_delete'] : 'RESTRICT';
  868. $on_update = ! empty(
  869. $existrel_foreign[$master_field]['on_update'])
  870. ? $existrel_foreign[$master_field]['on_update'] : 'RESTRICT';
  871. }
  872. if (! isset($existrel_foreign[$master_field])) {
  873. // no key defined for this field
  874. $create = true;
  875. } elseif ($existrel_foreign[$master_field]['foreign_db'] != $foreign_db
  876. || $existrel_foreign[$master_field]['foreign_table'] != $foreign_table
  877. || $existrel_foreign[$master_field]['foreign_field'] != $foreign_field
  878. || $_REQUEST['constraint_name'][$master_field_md5] != $constraint_name
  879. || ($_REQUEST['on_delete'][$master_field_md5] != $on_delete)
  880. || ($_REQUEST['on_update'][$master_field_md5] != $on_update)
  881. ) {
  882. // another foreign key is already defined for this field
  883. // or an option has been changed for ON DELETE or ON UPDATE
  884. $drop = true;
  885. $create = true;
  886. } // end if... else....
  887. } elseif (isset($existrel_foreign[$master_field])) {
  888. $drop = true;
  889. } // end if... else....
  890. $tmp_error_drop = false;
  891. if ($drop) {
  892. $drop_query = PMA_getSQLToDropForeignKey(
  893. $table, $existrel_foreign[$master_field]['constraint']
  894. );
  895. $display_query .= $drop_query . "\n";
  896. $GLOBALS['dbi']->tryQuery($drop_query);
  897. $tmp_error_drop = $GLOBALS['dbi']->getError();
  898. if (! empty($tmp_error_drop)) {
  899. $seen_error = true;
  900. $html_output .= PMA_Util::mysqlDie(
  901. $tmp_error_drop, $drop_query, false, '', false
  902. );
  903. return $html_output;
  904. }
  905. }
  906. $tmp_error_create = false;
  907. if ($create) {
  908. $create_query = PMA_getSQLToCreateForeignKey(
  909. $table, $master_field, $foreign_db, $foreign_table, $foreign_field,
  910. $_REQUEST['constraint_name'][$master_field_md5],
  911. $options_array[$_REQUEST['on_delete'][$master_field_md5]],
  912. $options_array[$_REQUEST['on_update'][$master_field_md5]]
  913. );
  914. $display_query .= $create_query . "\n";
  915. $GLOBALS['dbi']->tryQuery($create_query);
  916. $tmp_error_create = $GLOBALS['dbi']->getError();
  917. if (! empty($tmp_error_create)) {
  918. $seen_error = true;
  919. if (substr($tmp_error_create, 1, 4) == '1005') {
  920. $message = PMA_Message::error(
  921. __('Error creating foreign key on %1$s (check data types)')
  922. );
  923. $message->addParam($master_field);
  924. $html_output .= $message->getDisplay();
  925. } else {
  926. $html_output .= PMA_Util::mysqlDie(
  927. $tmp_error_create, $create_query, false, '', false
  928. );
  929. }
  930. $html_output .= PMA_Util::showMySQLDocu(
  931. 'InnoDB_foreign_key_constraints'
  932. ) . "\n";
  933. }
  934. // this is an alteration and the old constraint has been dropped
  935. // without creation of a new one
  936. if ($drop && $create && empty($tmp_error_drop)
  937. && ! empty($tmp_error_create)
  938. ) {
  939. // a rollback may be better here
  940. $sql_query_recreate = '# Restoring the dropped constraint...' . "\n";
  941. $sql_query_recreate .= PMA_getSQLToCreateForeignKey(
  942. $table,
  943. $master_field,
  944. $existrel_foreign[$master_field]['foreign_db'],
  945. $existrel_foreign[$master_field]['foreign_table'],
  946. $existrel_foreign[$master_field]['foreign_field'],
  947. $existrel_foreign[$master_field]['constraint'],
  948. $options_array[$existrel_foreign[$master_field]['on_delete']],
  949. $options_array[$existrel_foreign[$master_field]['on_update']]
  950. );
  951. $display_query .= $sql_query_recreate . "\n";
  952. $GLOBALS['dbi']->tryQuery($sql_query_recreate);
  953. }
  954. }
  955. return $html_output;
  956. }
  957. ?>