Operations.php 81 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * Holds the PhpMyAdmin\Operations class
  5. *
  6. * @package PhpMyAdmin
  7. */
  8. declare(strict_types=1);
  9. namespace PhpMyAdmin;
  10. use PhpMyAdmin\Charsets\Charset;
  11. use PhpMyAdmin\Charsets\Collation;
  12. use PhpMyAdmin\Engines\Innodb;
  13. use PhpMyAdmin\Plugins\Export\ExportSql;
  14. /**
  15. * Set of functions with the operations section in phpMyAdmin
  16. *
  17. * @package PhpMyAdmin
  18. */
  19. class Operations
  20. {
  21. /**
  22. * @var Relation
  23. */
  24. private $relation;
  25. /**
  26. * @var DatabaseInterface
  27. */
  28. private $dbi;
  29. /**
  30. * Operations constructor.
  31. *
  32. * @param DatabaseInterface $dbi DatabaseInterface object
  33. * @param Relation $relation Relation object
  34. */
  35. public function __construct(DatabaseInterface $dbi, Relation $relation)
  36. {
  37. $this->dbi = $dbi;
  38. $this->relation = $relation;
  39. }
  40. /**
  41. * Get HTML output for database comment
  42. *
  43. * @param string $db database name
  44. *
  45. * @return string
  46. */
  47. public function getHtmlForDatabaseComment($db)
  48. {
  49. $html_output = '<div>'
  50. . '<form method="post" action="db_operations.php" id="formDatabaseComment">'
  51. . Url::getHiddenInputs($db)
  52. . '<fieldset>'
  53. . '<legend>';
  54. if (Util::showIcons('ActionLinksMode')) {
  55. $html_output .= Util::getImage('b_comment') . '&nbsp;';
  56. }
  57. $html_output .= __('Database comment');
  58. $html_output .= '</legend>';
  59. $html_output .= '<input type="text" name="comment" '
  60. . 'class="textfield"'
  61. . 'value="' . htmlspecialchars($this->relation->getDbComment($db)) . '">'
  62. . '</fieldset>';
  63. $html_output .= '<fieldset class="tblFooters">'
  64. . '<input class="btn btn-primary" type="submit" value="' . __('Go') . '">'
  65. . '</fieldset>'
  66. . '</form>'
  67. . '</div>';
  68. return $html_output;
  69. }
  70. /**
  71. * Get HTML output for rename database
  72. *
  73. * @param string $db database name
  74. * @param string $db_collation dataset collation
  75. *
  76. * @return string
  77. */
  78. public function getHtmlForRenameDatabase($db, $db_collation)
  79. {
  80. $html_output = '<div>'
  81. . '<form id="rename_db_form" '
  82. . 'class="ajax" '
  83. . 'method="post" action="db_operations.php" '
  84. . 'onsubmit="return Functions.emptyCheckTheField(this, \'newname\')">';
  85. if ($db_collation !== null) {
  86. $html_output .= '<input type="hidden" name="db_collation" '
  87. . 'value="' . $db_collation
  88. . '">' . "\n";
  89. }
  90. $html_output .= '<input type="hidden" name="what" value="data">'
  91. . '<input type="hidden" name="db_rename" value="true">'
  92. . Url::getHiddenInputs($db)
  93. . '<fieldset>'
  94. . '<legend>';
  95. if (Util::showIcons('ActionLinksMode')) {
  96. $html_output .= Util::getImage('b_edit') . '&nbsp;';
  97. }
  98. $html_output .= __('Rename database to')
  99. . '</legend>';
  100. $html_output .= '<input id="new_db_name" type="text" name="newname" '
  101. . 'maxlength="64" class="textfield" required="required">';
  102. $html_output .= '<br>';
  103. if ($GLOBALS['db_priv'] && $GLOBALS['table_priv']
  104. && $GLOBALS['col_priv'] && $GLOBALS['proc_priv']
  105. && $GLOBALS['is_reload_priv']
  106. ) {
  107. $html_output .= '<input type="checkbox" name="adjust_privileges" '
  108. . 'value="1" id="checkbox_adjust_privileges" checked="checked">';
  109. } else {
  110. $html_output .= '<input type="checkbox" name="adjust_privileges" '
  111. . 'value="1" id="checkbox_adjust_privileges" title="' . __(
  112. 'You don\'t have sufficient privileges to perform this '
  113. . 'operation; Please refer to the documentation for more details'
  114. )
  115. . '" disabled>';
  116. }
  117. $html_output .= '<label for="checkbox_adjust_privileges">'
  118. . __('Adjust privileges') . Util::showDocu('faq', 'faq6-39')
  119. . '</label><br>';
  120. $html_output .= ''
  121. . '</fieldset>'
  122. . '<fieldset class="tblFooters">'
  123. . '<input id="rename_db_input" class="btn btn-primary" type="submit" value="' . __('Go') . '">'
  124. . '</fieldset>'
  125. . '</form>'
  126. . '</div>';
  127. return $html_output;
  128. }
  129. /**
  130. * Get HTML for database drop link
  131. *
  132. * @param string $db database name
  133. *
  134. * @return string
  135. */
  136. public function getHtmlForDropDatabaseLink($db)
  137. {
  138. $this_sql_query = 'DROP DATABASE ' . Util::backquote($db);
  139. $this_url_params = [
  140. 'sql_query' => $this_sql_query,
  141. 'back' => 'db_operations.php',
  142. 'goto' => 'index.php',
  143. 'reload' => '1',
  144. 'purge' => '1',
  145. 'message_to_show' => sprintf(
  146. __('Database %s has been dropped.'),
  147. htmlspecialchars(Util::backquote($db))
  148. ),
  149. 'db' => null,
  150. ];
  151. $html_output = '<div>'
  152. . '<fieldset class="caution">';
  153. $html_output .= '<legend>';
  154. if (Util::showIcons('ActionLinksMode')) {
  155. $html_output .= Util::getImage('b_deltbl') . '&nbsp';
  156. }
  157. $html_output .= __('Remove database')
  158. . '</legend>';
  159. $html_output .= '<ul>';
  160. $html_output .= $this->getDeleteDataOrTablelink(
  161. $this_url_params,
  162. 'DROP_DATABASE',
  163. __('Drop the database (DROP)'),
  164. 'drop_db_anchor'
  165. );
  166. $html_output .= '</ul></fieldset>'
  167. . '</div>';
  168. return $html_output;
  169. }
  170. /**
  171. * Get HTML snippet for copy database
  172. *
  173. * @param string $db database name
  174. * @param string $db_collation dataset collation
  175. *
  176. * @return string
  177. */
  178. public function getHtmlForCopyDatabase($db, $db_collation)
  179. {
  180. $drop_clause = 'DROP TABLE / DROP VIEW';
  181. $choices = [
  182. 'structure' => __('Structure only'),
  183. 'data' => __('Structure and data'),
  184. 'dataonly' => __('Data only'),
  185. ];
  186. $pma_switch_to_new = isset($_SESSION['pma_switch_to_new']) && $_SESSION['pma_switch_to_new'];
  187. $html_output = '<div>';
  188. $html_output .= '<form id="copy_db_form" '
  189. . 'class="ajax" '
  190. . 'method="post" action="db_operations.php" '
  191. . 'onsubmit="return Functions.emptyCheckTheField(this, \'newname\')">';
  192. if ($db_collation !== null) {
  193. $html_output .= '<input type="hidden" name="db_collation" '
  194. . 'value="' . $db_collation . '">' . "\n";
  195. }
  196. $html_output .= '<input type="hidden" name="db_copy" value="true">' . "\n"
  197. . Url::getHiddenInputs($db);
  198. $html_output .= '<fieldset>'
  199. . '<legend>';
  200. if (Util::showIcons('ActionLinksMode')) {
  201. $html_output .= Util::getImage('b_edit') . '&nbsp';
  202. }
  203. $html_output .= __('Copy database to')
  204. . '</legend>'
  205. . '<input type="text" maxlength="64" name="newname" '
  206. . 'class="textfield" required="required"><br>'
  207. . Util::getRadioFields(
  208. 'what',
  209. $choices,
  210. 'data',
  211. true
  212. );
  213. $html_output .= '<br>';
  214. $html_output .= '<input type="checkbox" name="create_database_before_copying" '
  215. . 'value="1" id="checkbox_create_database_before_copying"'
  216. . 'checked="checked">';
  217. $html_output .= '<label for="checkbox_create_database_before_copying">'
  218. . __('CREATE DATABASE before copying') . '</label><br>';
  219. $html_output .= '<input type="checkbox" name="drop_if_exists" value="true"'
  220. . 'id="checkbox_drop">';
  221. $html_output .= '<label for="checkbox_drop">'
  222. . sprintf(__('Add %s'), $drop_clause)
  223. . '</label><br>';
  224. $html_output .= '<input type="checkbox" name="sql_auto_increment" value="1" '
  225. . 'checked="checked" id="checkbox_auto_increment">';
  226. $html_output .= '<label for="checkbox_auto_increment">'
  227. . __('Add AUTO_INCREMENT value') . '</label><br>';
  228. $html_output .= '<input type="checkbox" name="add_constraints" value="1"'
  229. . 'id="checkbox_constraints" checked="checked">';
  230. $html_output .= '<label for="checkbox_constraints">'
  231. . __('Add constraints') . '</label><br>';
  232. $html_output .= '<br>';
  233. if ($GLOBALS['db_priv'] && $GLOBALS['table_priv']
  234. && $GLOBALS['col_priv'] && $GLOBALS['proc_priv']
  235. && $GLOBALS['is_reload_priv']
  236. ) {
  237. $html_output .= '<input type="checkbox" name="adjust_privileges" '
  238. . 'value="1" id="checkbox_privileges" checked="checked">';
  239. } else {
  240. $html_output .= '<input type="checkbox" name="adjust_privileges" '
  241. . 'value="1" id="checkbox_privileges" title="' . __(
  242. 'You don\'t have sufficient privileges to perform this '
  243. . 'operation; Please refer to the documentation for more details'
  244. )
  245. . '" disabled>';
  246. }
  247. $html_output .= '<label for="checkbox_privileges">'
  248. . __('Adjust privileges') . Util::showDocu('faq', 'faq6-39')
  249. . '</label><br>';
  250. $html_output .= '<input type="checkbox" name="switch_to_new" value="true"'
  251. . 'id="checkbox_switch"'
  252. . ($pma_switch_to_new ? ' checked="checked"' : '') . '>';
  253. $html_output .= '<label for="checkbox_switch">'
  254. . __('Switch to copied database') . '</label>'
  255. . '</fieldset>';
  256. $html_output .= '<fieldset class="tblFooters">'
  257. . '<input class="btn btn-primary" type="submit" name="submit_copy" value="' . __('Go') . '">'
  258. . '</fieldset>'
  259. . '</form>'
  260. . '</div>';
  261. return $html_output;
  262. }
  263. /**
  264. * Get HTML snippet for change database charset
  265. *
  266. * @param string $db database name
  267. * @param string $db_collation dataset collation
  268. *
  269. * @return string
  270. */
  271. public function getHtmlForChangeDatabaseCharset($db, $db_collation)
  272. {
  273. $html_output = '<div>'
  274. . '<form id="change_db_charset_form" ';
  275. $html_output .= 'class="ajax" ';
  276. $html_output .= 'method="post" action="db_operations.php">';
  277. $html_output .= Url::getHiddenInputs($db);
  278. $html_output .= '<fieldset>' . "\n"
  279. . ' <legend>';
  280. if (Util::showIcons('ActionLinksMode')) {
  281. $html_output .= Util::getImage('s_asci') . '&nbsp';
  282. }
  283. $html_output .= '<label for="select_db_collation">' . __('Collation')
  284. . '</label>' . "\n"
  285. . '</legend>' . "\n";
  286. $html_output .= '<select lang="en" dir="ltr" name="db_collation" id="select_db_collation">' . "\n";
  287. $html_output .= '<option value=""></option>' . "\n";
  288. $charsets = Charsets::getCharsets($this->dbi, $GLOBALS['cfg']['Server']['DisableIS']);
  289. $collations = Charsets::getCollations($this->dbi, $GLOBALS['cfg']['Server']['DisableIS']);
  290. /** @var Charset $charset */
  291. foreach ($charsets as $charset) {
  292. $html_output .= '<optgroup label="' . $charset->getName()
  293. . '" title="' . $charset->getDescription() . '">' . "\n";
  294. /** @var Collation $collation */
  295. foreach ($collations[$charset->getName()] as $collation) {
  296. $html_output .= '<option value="' . $collation->getName()
  297. . '" title="' . $collation->getDescription() . '"'
  298. . ($db_collation == $collation->getName() ? ' selected' : '') . '>'
  299. . $collation->getName() . '</option>' . "\n";
  300. }
  301. $html_output .= '</optgroup>' . "\n";
  302. }
  303. $html_output .= '</select>' . "\n";
  304. $html_output .= '<br>'
  305. . '<input type="checkbox" name="change_all_tables_collations"'
  306. . 'id="checkbox_change_all_tables_collations">'
  307. . '<label for="checkbox_change_all_tables_collations">'
  308. . __('Change all tables collations')
  309. . '</label>'
  310. . '<br>'
  311. . '<span id="span_change_all_tables_columns_collations"><input type="checkbox" name="change_all_tables_columns_collations"'
  312. . 'id="checkbox_change_all_tables_columns_collations">'
  313. . '<label for="checkbox_change_all_tables_columns_collations">'
  314. . __('Change all tables columns collations')
  315. . '</label></span>'
  316. . '</fieldset>'
  317. . '<fieldset class="tblFooters">'
  318. . '<input class="btn btn-primary" type="submit" name="submitcollation"'
  319. . ' value="' . __('Go') . '">' . "\n"
  320. . '</fieldset>' . "\n"
  321. . '</form></div>' . "\n";
  322. return $html_output;
  323. }
  324. /**
  325. * Run the Procedure definitions and function definitions
  326. *
  327. * to avoid selecting alternatively the current and new db
  328. * we would need to modify the CREATE definitions to qualify
  329. * the db name
  330. *
  331. * @param string $db database name
  332. *
  333. * @return void
  334. */
  335. public function runProcedureAndFunctionDefinitions($db)
  336. {
  337. $procedure_names = $this->dbi->getProceduresOrFunctions($db, 'PROCEDURE');
  338. if ($procedure_names) {
  339. foreach ($procedure_names as $procedure_name) {
  340. $this->dbi->selectDb($db);
  341. $tmp_query = $this->dbi->getDefinition(
  342. $db,
  343. 'PROCEDURE',
  344. $procedure_name
  345. );
  346. if ($tmp_query !== null) {
  347. // collect for later display
  348. $GLOBALS['sql_query'] .= "\n" . $tmp_query;
  349. $this->dbi->selectDb($_POST['newname']);
  350. $this->dbi->query($tmp_query);
  351. }
  352. }
  353. }
  354. $function_names = $this->dbi->getProceduresOrFunctions($db, 'FUNCTION');
  355. if ($function_names) {
  356. foreach ($function_names as $function_name) {
  357. $this->dbi->selectDb($db);
  358. $tmp_query = $this->dbi->getDefinition(
  359. $db,
  360. 'FUNCTION',
  361. $function_name
  362. );
  363. if ($tmp_query !== null) {
  364. // collect for later display
  365. $GLOBALS['sql_query'] .= "\n" . $tmp_query;
  366. $this->dbi->selectDb($_POST['newname']);
  367. $this->dbi->query($tmp_query);
  368. }
  369. }
  370. }
  371. }
  372. /**
  373. * Create database before copy
  374. *
  375. * @return void
  376. */
  377. public function createDbBeforeCopy()
  378. {
  379. $local_query = 'CREATE DATABASE IF NOT EXISTS '
  380. . Util::backquote($_POST['newname']);
  381. if (isset($_POST['db_collation'])) {
  382. $local_query .= ' DEFAULT'
  383. . Util::getCharsetQueryPart($_POST['db_collation']);
  384. }
  385. $local_query .= ';';
  386. $GLOBALS['sql_query'] .= $local_query;
  387. // save the original db name because Tracker.php which
  388. // may be called under $this->dbi->query() changes $GLOBALS['db']
  389. // for some statements, one of which being CREATE DATABASE
  390. $original_db = $GLOBALS['db'];
  391. $this->dbi->query($local_query);
  392. $GLOBALS['db'] = $original_db;
  393. // Set the SQL mode to NO_AUTO_VALUE_ON_ZERO to prevent MySQL from creating
  394. // export statements it cannot import
  395. $sql_set_mode = "SET SQL_MODE='NO_AUTO_VALUE_ON_ZERO'";
  396. $this->dbi->query($sql_set_mode);
  397. // rebuild the database list because Table::moveCopy
  398. // checks in this list if the target db exists
  399. $GLOBALS['dblist']->databases->build();
  400. }
  401. /**
  402. * Get views as an array and create SQL view stand-in
  403. *
  404. * @param array $tables_full array of all tables in given db or dbs
  405. * @param ExportSql $export_sql_plugin export plugin instance
  406. * @param string $db database name
  407. *
  408. * @return array
  409. */
  410. public function getViewsAndCreateSqlViewStandIn(
  411. array $tables_full,
  412. $export_sql_plugin,
  413. $db
  414. ) {
  415. $views = [];
  416. foreach ($tables_full as $each_table => $tmp) {
  417. // to be able to rename a db containing views,
  418. // first all the views are collected and a stand-in is created
  419. // the real views are created after the tables
  420. if ($this->dbi->getTable($db, (string) $each_table)->isView()) {
  421. // If view exists, and 'add drop view' is selected: Drop it!
  422. if ($_POST['what'] != 'nocopy'
  423. && isset($_POST['drop_if_exists'])
  424. && $_POST['drop_if_exists'] == 'true'
  425. ) {
  426. $drop_query = 'DROP VIEW IF EXISTS '
  427. . Util::backquote($_POST['newname']) . '.'
  428. . Util::backquote($each_table);
  429. $this->dbi->query($drop_query);
  430. $GLOBALS['sql_query'] .= "\n" . $drop_query . ';';
  431. }
  432. $views[] = $each_table;
  433. // Create stand-in definition to resolve view dependencies
  434. $sql_view_standin = $export_sql_plugin->getTableDefStandIn(
  435. $db,
  436. $each_table,
  437. "\n"
  438. );
  439. $this->dbi->selectDb($_POST['newname']);
  440. $this->dbi->query($sql_view_standin);
  441. $GLOBALS['sql_query'] .= "\n" . $sql_view_standin;
  442. }
  443. }
  444. return $views;
  445. }
  446. /**
  447. * Get sql query for copy/rename table and boolean for whether copy/rename or not
  448. *
  449. * @param array $tables_full array of all tables in given db or dbs
  450. * @param boolean $move whether database name is empty or not
  451. * @param string $db database name
  452. *
  453. * @return array SQL queries for the constraints
  454. */
  455. public function copyTables(array $tables_full, $move, $db)
  456. {
  457. $sqlContraints = [];
  458. foreach ($tables_full as $each_table => $tmp) {
  459. // skip the views; we have created stand-in definitions
  460. if ($this->dbi->getTable($db, (string) $each_table)->isView()) {
  461. continue;
  462. }
  463. // value of $what for this table only
  464. $this_what = $_POST['what'];
  465. // do not copy the data from a Merge table
  466. // note: on the calling FORM, 'data' means 'structure and data'
  467. if ($this->dbi->getTable($db, (string) $each_table)->isMerge()) {
  468. if ($this_what == 'data') {
  469. $this_what = 'structure';
  470. }
  471. if ($this_what == 'dataonly') {
  472. $this_what = 'nocopy';
  473. }
  474. }
  475. if ($this_what != 'nocopy') {
  476. // keep the triggers from the original db+table
  477. // (third param is empty because delimiters are only intended
  478. // for importing via the mysql client or our Import feature)
  479. $triggers = $this->dbi->getTriggers($db, (string) $each_table, '');
  480. if (! Table::moveCopy(
  481. $db,
  482. $each_table,
  483. $_POST['newname'],
  484. $each_table,
  485. (isset($this_what) ? $this_what : 'data'),
  486. $move,
  487. 'db_copy'
  488. )) {
  489. $GLOBALS['_error'] = true;
  490. break;
  491. }
  492. // apply the triggers to the destination db+table
  493. if ($triggers) {
  494. $this->dbi->selectDb($_POST['newname']);
  495. foreach ($triggers as $trigger) {
  496. $this->dbi->query($trigger['create']);
  497. $GLOBALS['sql_query'] .= "\n" . $trigger['create'] . ';';
  498. }
  499. }
  500. // this does not apply to a rename operation
  501. if (isset($_POST['add_constraints'])
  502. && ! empty($GLOBALS['sql_constraints_query'])
  503. ) {
  504. $sqlContraints[] = $GLOBALS['sql_constraints_query'];
  505. unset($GLOBALS['sql_constraints_query']);
  506. }
  507. }
  508. }
  509. return $sqlContraints;
  510. }
  511. /**
  512. * Run the EVENT definition for selected database
  513. *
  514. * to avoid selecting alternatively the current and new db
  515. * we would need to modify the CREATE definitions to qualify
  516. * the db name
  517. *
  518. * @param string $db database name
  519. *
  520. * @return void
  521. */
  522. public function runEventDefinitionsForDb($db)
  523. {
  524. $event_names = $this->dbi->fetchResult(
  525. 'SELECT EVENT_NAME FROM information_schema.EVENTS WHERE EVENT_SCHEMA= \''
  526. . $this->dbi->escapeString($db) . '\';'
  527. );
  528. if ($event_names) {
  529. foreach ($event_names as $event_name) {
  530. $this->dbi->selectDb($db);
  531. $tmp_query = $this->dbi->getDefinition($db, 'EVENT', $event_name);
  532. // collect for later display
  533. $GLOBALS['sql_query'] .= "\n" . $tmp_query;
  534. $this->dbi->selectDb($_POST['newname']);
  535. $this->dbi->query($tmp_query);
  536. }
  537. }
  538. }
  539. /**
  540. * Handle the views, return the boolean value whether table rename/copy or not
  541. *
  542. * @param array $views views as an array
  543. * @param boolean $move whether database name is empty or not
  544. * @param string $db database name
  545. *
  546. * @return void
  547. */
  548. public function handleTheViews(array $views, $move, $db)
  549. {
  550. // temporarily force to add DROP IF EXIST to CREATE VIEW query,
  551. // to remove stand-in VIEW that was created earlier
  552. // ( $_POST['drop_if_exists'] is used in moveCopy() )
  553. if (isset($_POST['drop_if_exists'])) {
  554. $temp_drop_if_exists = $_POST['drop_if_exists'];
  555. }
  556. $_POST['drop_if_exists'] = 'true';
  557. foreach ($views as $view) {
  558. $copying_succeeded = Table::moveCopy(
  559. $db,
  560. $view,
  561. $_POST['newname'],
  562. $view,
  563. 'structure',
  564. $move,
  565. 'db_copy'
  566. );
  567. if (! $copying_succeeded) {
  568. $GLOBALS['_error'] = true;
  569. break;
  570. }
  571. }
  572. unset($_POST['drop_if_exists']);
  573. if (isset($temp_drop_if_exists)) {
  574. // restore previous value
  575. $_POST['drop_if_exists'] = $temp_drop_if_exists;
  576. }
  577. }
  578. /**
  579. * Adjust the privileges after Renaming the db
  580. *
  581. * @param string $oldDb Database name before renaming
  582. * @param string $newname New Database name requested
  583. *
  584. * @return void
  585. */
  586. public function adjustPrivilegesMoveDb($oldDb, $newname)
  587. {
  588. if ($GLOBALS['db_priv'] && $GLOBALS['table_priv']
  589. && $GLOBALS['col_priv'] && $GLOBALS['proc_priv']
  590. && $GLOBALS['is_reload_priv']
  591. ) {
  592. $this->dbi->selectDb('mysql');
  593. $newname = str_replace("_", "\_", $newname);
  594. $oldDb = str_replace("_", "\_", $oldDb);
  595. // For Db specific privileges
  596. $query_db_specific = 'UPDATE ' . Util::backquote('db')
  597. . 'SET Db = \'' . $this->dbi->escapeString($newname)
  598. . '\' where Db = \'' . $this->dbi->escapeString($oldDb) . '\';';
  599. $this->dbi->query($query_db_specific);
  600. // For table specific privileges
  601. $query_table_specific = 'UPDATE ' . Util::backquote('tables_priv')
  602. . 'SET Db = \'' . $this->dbi->escapeString($newname)
  603. . '\' where Db = \'' . $this->dbi->escapeString($oldDb) . '\';';
  604. $this->dbi->query($query_table_specific);
  605. // For column specific privileges
  606. $query_col_specific = 'UPDATE ' . Util::backquote('columns_priv')
  607. . 'SET Db = \'' . $this->dbi->escapeString($newname)
  608. . '\' where Db = \'' . $this->dbi->escapeString($oldDb) . '\';';
  609. $this->dbi->query($query_col_specific);
  610. // For procedures specific privileges
  611. $query_proc_specific = 'UPDATE ' . Util::backquote('procs_priv')
  612. . 'SET Db = \'' . $this->dbi->escapeString($newname)
  613. . '\' where Db = \'' . $this->dbi->escapeString($oldDb) . '\';';
  614. $this->dbi->query($query_proc_specific);
  615. // Finally FLUSH the new privileges
  616. $flush_query = "FLUSH PRIVILEGES;";
  617. $this->dbi->query($flush_query);
  618. }
  619. }
  620. /**
  621. * Adjust the privileges after Copying the db
  622. *
  623. * @param string $oldDb Database name before copying
  624. * @param string $newname New Database name requested
  625. *
  626. * @return void
  627. */
  628. public function adjustPrivilegesCopyDb($oldDb, $newname)
  629. {
  630. if ($GLOBALS['db_priv'] && $GLOBALS['table_priv']
  631. && $GLOBALS['col_priv'] && $GLOBALS['proc_priv']
  632. && $GLOBALS['is_reload_priv']
  633. ) {
  634. $this->dbi->selectDb('mysql');
  635. $newname = str_replace("_", "\_", $newname);
  636. $oldDb = str_replace("_", "\_", $oldDb);
  637. $query_db_specific_old = 'SELECT * FROM '
  638. . Util::backquote('db') . ' WHERE '
  639. . 'Db = "' . $oldDb . '";';
  640. $old_privs_db = $this->dbi->fetchResult($query_db_specific_old, 0);
  641. foreach ($old_privs_db as $old_priv) {
  642. $newDb_db_privs_query = 'INSERT INTO ' . Util::backquote('db')
  643. . ' VALUES("' . $old_priv[0] . '", "' . $newname . '"';
  644. for ($i = 2; $i < count($old_priv); $i++) {
  645. $newDb_db_privs_query .= ', "' . $old_priv[$i] . '"';
  646. }
  647. $newDb_db_privs_query .= ')';
  648. $this->dbi->query($newDb_db_privs_query);
  649. }
  650. // For Table Specific privileges
  651. $query_table_specific_old = 'SELECT * FROM '
  652. . Util::backquote('tables_priv') . ' WHERE '
  653. . 'Db = "' . $oldDb . '";';
  654. $old_privs_table = $this->dbi->fetchResult(
  655. $query_table_specific_old,
  656. 0
  657. );
  658. foreach ($old_privs_table as $old_priv) {
  659. $newDb_table_privs_query = 'INSERT INTO ' . Util::backquote(
  660. 'tables_priv'
  661. ) . ' VALUES("' . $old_priv[0] . '", "' . $newname . '", "'
  662. . $old_priv[2] . '", "' . $old_priv[3] . '", "' . $old_priv[4]
  663. . '", "' . $old_priv[5] . '", "' . $old_priv[6] . '", "'
  664. . $old_priv[7] . '");';
  665. $this->dbi->query($newDb_table_privs_query);
  666. }
  667. // For Column Specific privileges
  668. $query_col_specific_old = 'SELECT * FROM '
  669. . Util::backquote('columns_priv') . ' WHERE '
  670. . 'Db = "' . $oldDb . '";';
  671. $old_privs_col = $this->dbi->fetchResult(
  672. $query_col_specific_old,
  673. 0
  674. );
  675. foreach ($old_privs_col as $old_priv) {
  676. $newDb_col_privs_query = 'INSERT INTO ' . Util::backquote(
  677. 'columns_priv'
  678. ) . ' VALUES("' . $old_priv[0] . '", "' . $newname . '", "'
  679. . $old_priv[2] . '", "' . $old_priv[3] . '", "' . $old_priv[4]
  680. . '", "' . $old_priv[5] . '", "' . $old_priv[6] . '");';
  681. $this->dbi->query($newDb_col_privs_query);
  682. }
  683. // For Procedure Specific privileges
  684. $query_proc_specific_old = 'SELECT * FROM '
  685. . Util::backquote('procs_priv') . ' WHERE '
  686. . 'Db = "' . $oldDb . '";';
  687. $old_privs_proc = $this->dbi->fetchResult(
  688. $query_proc_specific_old,
  689. 0
  690. );
  691. foreach ($old_privs_proc as $old_priv) {
  692. $newDb_proc_privs_query = 'INSERT INTO ' . Util::backquote(
  693. 'procs_priv'
  694. ) . ' VALUES("' . $old_priv[0] . '", "' . $newname . '", "'
  695. . $old_priv[2] . '", "' . $old_priv[3] . '", "' . $old_priv[4]
  696. . '", "' . $old_priv[5] . '", "' . $old_priv[6] . '", "'
  697. . $old_priv[7] . '");';
  698. $this->dbi->query($newDb_proc_privs_query);
  699. }
  700. // Finally FLUSH the new privileges
  701. $flush_query = "FLUSH PRIVILEGES;";
  702. $this->dbi->query($flush_query);
  703. }
  704. }
  705. /**
  706. * Create all accumulated constraints
  707. *
  708. * @param array $sqlConstratints array of sql constraints for the database
  709. *
  710. * @return void
  711. */
  712. public function createAllAccumulatedConstraints(array $sqlConstratints)
  713. {
  714. $this->dbi->selectDb($_POST['newname']);
  715. foreach ($sqlConstratints as $one_query) {
  716. $this->dbi->query($one_query);
  717. // and prepare to display them
  718. $GLOBALS['sql_query'] .= "\n" . $one_query;
  719. }
  720. }
  721. /**
  722. * Duplicate the bookmarks for the db (done once for each db)
  723. *
  724. * @param boolean $_error whether table rename/copy or not
  725. * @param string $db database name
  726. *
  727. * @return void
  728. */
  729. public function duplicateBookmarks($_error, $db)
  730. {
  731. if (! $_error && $db != $_POST['newname']) {
  732. $get_fields = [
  733. 'user',
  734. 'label',
  735. 'query',
  736. ];
  737. $where_fields = ['dbase' => $db];
  738. $new_fields = ['dbase' => $_POST['newname']];
  739. Table::duplicateInfo(
  740. 'bookmarkwork',
  741. 'bookmark',
  742. $get_fields,
  743. $where_fields,
  744. $new_fields
  745. );
  746. }
  747. }
  748. /**
  749. * Get the HTML snippet for order the table
  750. *
  751. * @param array $columns columns array
  752. *
  753. * @return string
  754. */
  755. public function getHtmlForOrderTheTable(array $columns)
  756. {
  757. $html_output = '<div>';
  758. $html_output .= '<form method="post" id="alterTableOrderby" '
  759. . 'action="tbl_operations.php">';
  760. $html_output .= Url::getHiddenInputs(
  761. $GLOBALS['db'],
  762. $GLOBALS['table']
  763. );
  764. $html_output .= '<fieldset id="fieldset_table_order">'
  765. . '<legend>' . __('Alter table order by') . '</legend>'
  766. . '<select name="order_field">';
  767. foreach ($columns as $fieldname) {
  768. $html_output .= '<option '
  769. . 'value="' . htmlspecialchars($fieldname['Field']) . '">'
  770. . htmlspecialchars($fieldname['Field']) . '</option>' . "\n";
  771. }
  772. $html_output .= '</select> ' . __('(singly)') . ' '
  773. . '<br>'
  774. . '<input id="order_order_asc" name="order_order"'
  775. . ' type="radio" value="asc" checked="checked">'
  776. . '<label for="order_order_asc">' . __('Ascending') . '</label>'
  777. . '<input id="order_order_desc" name="order_order"'
  778. . ' type="radio" value="desc">'
  779. . '<label for="order_order_desc">' . __('Descending') . '</label>'
  780. . '</fieldset>'
  781. . '<fieldset class="tblFooters">'
  782. . '<input type="hidden" name="submitorderby" value="1">'
  783. . '<input class="btn btn-primary" type="submit" value="' . __('Go') . '">'
  784. . '</fieldset>'
  785. . '</form>'
  786. . '</div>';
  787. return $html_output;
  788. }
  789. /**
  790. * Get the HTML snippet for move table
  791. *
  792. * @return string
  793. */
  794. public function getHtmlForMoveTable()
  795. {
  796. $html_output = '<div>';
  797. $html_output .= '<form method="post" action="tbl_operations.php"'
  798. . ' id="moveTableForm" class="ajax"'
  799. . ' onsubmit="return Functions.emptyCheckTheField(this, \'new_name\')">'
  800. . Url::getHiddenInputs($GLOBALS['db'], $GLOBALS['table']);
  801. $html_output .= '<input type="hidden" name="reload" value="1">'
  802. . '<input type="hidden" name="what" value="data">'
  803. . '<fieldset id="fieldset_table_rename">';
  804. $html_output .= '<legend>' . __('Move table to (database<b>.</b>table)')
  805. . '</legend>';
  806. if (count($GLOBALS['dblist']->databases) > $GLOBALS['cfg']['MaxDbList']) {
  807. $html_output .= '<input type="text" maxlength="100" '
  808. . 'name="target_db" value="' . htmlspecialchars($GLOBALS['db'])
  809. . '">';
  810. } else {
  811. $html_output .= '<select class="halfWidth" name="target_db">'
  812. . $GLOBALS['dblist']->databases->getHtmlOptions(true, false)
  813. . '</select>';
  814. }
  815. $html_output .= '&nbsp;<strong>.</strong>&nbsp;';
  816. $html_output .= '<input class="halfWidth" type="text" name="new_name"'
  817. . ' maxlength="64" required="required" '
  818. . 'value="' . htmlspecialchars($GLOBALS['table']) . '"><br>';
  819. // starting with MySQL 5.0.24, SHOW CREATE TABLE includes the AUTO_INCREMENT
  820. // next value but users can decide if they want it or not for the operation
  821. $html_output .= '<input type="checkbox" name="sql_auto_increment" '
  822. . 'value="1" id="checkbox_auto_increment_mv" checked="checked">'
  823. . '<label for="checkbox_auto_increment_mv">'
  824. . __('Add AUTO_INCREMENT value')
  825. . '</label><br>';
  826. if ($GLOBALS['table_priv'] && $GLOBALS['col_priv']
  827. && $GLOBALS['is_reload_priv']
  828. ) {
  829. $html_output .= '<input type="checkbox" name="adjust_privileges" '
  830. . 'value="1" id="checkbox_privileges_tables_move" '
  831. . 'checked="checked">';
  832. } else {
  833. $html_output .= '<input type="checkbox" name="adjust_privileges" '
  834. . 'value="1" id="checkbox_privileges_tables_move" title="' . __(
  835. 'You don\'t have sufficient privileges to perform this '
  836. . 'operation; Please refer to the documentation for more details'
  837. )
  838. . '" disabled>';
  839. }
  840. $html_output .= '<label for="checkbox_privileges_tables_move">'
  841. . __('Adjust privileges') . Util::showDocu('faq', 'faq6-39')
  842. . '</label><br>';
  843. $html_output .= '</fieldset><fieldset class="tblFooters">'
  844. . '<input class="btn btn-primary" type="submit" name="submit_move" value="' . __('Go') . '">'
  845. . '</fieldset>'
  846. . '</form>'
  847. . '</div>';
  848. return $html_output;
  849. }
  850. /**
  851. * Get the HTML div for Table option
  852. *
  853. * @param Table $pma_table Table object
  854. * @param string $comment Comment
  855. * @param string $tbl_collation table collation
  856. * @param string $tbl_storage_engine table storage engine
  857. * @param string $pack_keys pack keys
  858. * @param string $auto_increment value of auto increment
  859. * @param string $delay_key_write delay key write
  860. * @param string $transactional value of transactional
  861. * @param string $page_checksum value of page checksum
  862. * @param string $checksum the checksum
  863. *
  864. * @return string
  865. */
  866. public function getTableOptionDiv(
  867. $pma_table,
  868. $comment,
  869. $tbl_collation,
  870. $tbl_storage_engine,
  871. $pack_keys,
  872. $auto_increment,
  873. $delay_key_write,
  874. $transactional,
  875. $page_checksum,
  876. $checksum
  877. ) {
  878. $html_output = '<div>';
  879. $html_output .= '<form method="post" action="tbl_operations.php"';
  880. $html_output .= ' id="tableOptionsForm" class="ajax">';
  881. $html_output .= Url::getHiddenInputs(
  882. $GLOBALS['db'],
  883. $GLOBALS['table']
  884. );
  885. $html_output .= '<input type="hidden" name="reload" value="1">';
  886. $html_output .= $this->getTableOptionFieldset(
  887. $pma_table,
  888. $comment,
  889. $tbl_collation,
  890. $tbl_storage_engine,
  891. $pack_keys,
  892. $delay_key_write,
  893. $auto_increment,
  894. $transactional,
  895. $page_checksum,
  896. $checksum
  897. );
  898. $html_output .= '<fieldset class="tblFooters">'
  899. . '<input type="hidden" name="submitoptions" value="1">'
  900. . '<input class="btn btn-primary" type="submit" value="' . __('Go') . '">'
  901. . '</fieldset>'
  902. . '</form>'
  903. . '</div>';
  904. return $html_output;
  905. }
  906. /**
  907. * Get HTML for the rename table part of table options
  908. *
  909. * @return string
  910. */
  911. private function getHtmlForRenameTable()
  912. {
  913. $html_output = '<tr><td class="vmiddle">' . __('Rename table to') . '</td>'
  914. . '<td>'
  915. . '<input type="text" name="new_name" maxlength="64" '
  916. . 'value="' . htmlspecialchars($GLOBALS['table'])
  917. . '" required="required">'
  918. . '</td></tr>'
  919. . '<tr><td></td><td>';
  920. if ($GLOBALS['table_priv'] && $GLOBALS['col_priv']
  921. && $GLOBALS['is_reload_priv']
  922. ) {
  923. $html_output .= '<input type="checkbox" name="adjust_privileges" '
  924. . 'value="1" id="checkbox_privileges_table_options" '
  925. . 'checked="checked">';
  926. } else {
  927. $html_output .= '<input type="checkbox" name="adjust_privileges" '
  928. . 'value="1" id="checkbox_privileges_table_options" title="' . __(
  929. 'You don\'t have sufficient privileges to perform this '
  930. . 'operation; Please refer to the documentation for more details'
  931. )
  932. . '" disabled>';
  933. }
  934. $html_output .= '<label for="checkbox_privileges_table_options">'
  935. . __('Adjust privileges') . '&nbsp;'
  936. . Util::showDocu('faq', 'faq6-39') . '</label>';
  937. $html_output .= '</td></tr>';
  938. return $html_output;
  939. }
  940. /**
  941. * Get HTML for the table comments part of table options
  942. *
  943. * @param string $current_value of the table comments
  944. *
  945. * @return string
  946. */
  947. private function getHtmlForTableComments($current_value)
  948. {
  949. $commentLength = $this->dbi->getVersion() >= 50503 ? 2048 : 60;
  950. return '<tr><td class="vmiddle">' . __('Table comments') . '</td>'
  951. . '<td><input type="text" name="comment" '
  952. . 'maxlength="' . $commentLength . '"'
  953. . 'value="' . htmlspecialchars($current_value) . '">'
  954. . '<input type="hidden" name="prev_comment" value="'
  955. . htmlspecialchars($current_value) . '">'
  956. . '</td>'
  957. . '</tr>';
  958. }
  959. /**
  960. * Get HTML for the PACK KEYS part of table options
  961. *
  962. * @param string $current_value of the pack keys option
  963. *
  964. * @return string
  965. */
  966. private function getHtmlForPackKeys($current_value)
  967. {
  968. $html_output = '<tr>'
  969. . '<td class="vmiddle"><label for="new_pack_keys">PACK_KEYS</label></td>'
  970. . '<td><select name="new_pack_keys" id="new_pack_keys">';
  971. $html_output .= '<option value="DEFAULT"';
  972. if ($current_value == 'DEFAULT') {
  973. $html_output .= 'selected="selected"';
  974. }
  975. $html_output .= '>DEFAULT</option>
  976. <option value="0"';
  977. if ($current_value == '0') {
  978. $html_output .= 'selected="selected"';
  979. }
  980. $html_output .= '>0</option>
  981. <option value="1" ';
  982. if ($current_value == '1') {
  983. $html_output .= 'selected="selected"';
  984. }
  985. $html_output .= '>1</option>'
  986. . '</select>'
  987. . '</td>'
  988. . '</tr>';
  989. return $html_output;
  990. }
  991. /**
  992. * Get HTML fieldset for Table option, it contains HTML table for options
  993. *
  994. * @param Table $pma_table Table object
  995. * @param string $comment Comment
  996. * @param string $tbl_collation table collation
  997. * @param string $tbl_storage_engine table storage engine
  998. * @param string $pack_keys pack keys
  999. * @param string $delay_key_write delay key write
  1000. * @param string $auto_increment value of auto increment
  1001. * @param string $transactional value of transactional
  1002. * @param string $page_checksum value of page checksum
  1003. * @param string $checksum the checksum
  1004. *
  1005. * @return string
  1006. */
  1007. private function getTableOptionFieldset(
  1008. $pma_table,
  1009. $comment,
  1010. $tbl_collation,
  1011. $tbl_storage_engine,
  1012. $pack_keys,
  1013. $delay_key_write,
  1014. $auto_increment,
  1015. $transactional,
  1016. $page_checksum,
  1017. $checksum
  1018. ) {
  1019. $html_output = '<fieldset>'
  1020. . '<legend>' . __('Table options') . '</legend>';
  1021. $html_output .= '<table>';
  1022. $html_output .= $this->getHtmlForRenameTable();
  1023. $html_output .= $this->getHtmlForTableComments($comment);
  1024. //Storage engine
  1025. $html_output .= '<tr><td class="vmiddle">' . __('Storage Engine')
  1026. . '&nbsp;' . Util::showMySQLDocu('Storage_engines')
  1027. . '</td>'
  1028. . '<td>'
  1029. . StorageEngine::getHtmlSelect(
  1030. 'new_tbl_storage_engine',
  1031. null,
  1032. $tbl_storage_engine
  1033. )
  1034. . '</td>'
  1035. . '</tr>';
  1036. //Table character set
  1037. $html_output .= '<tr><td class="vmiddle">' . __('Collation') . '</td>'
  1038. . '<td>';
  1039. $html_output .= '<select lang="en" dir="ltr" name="tbl_collation">' . "\n";
  1040. $html_output .= '<option value=""></option>' . "\n";
  1041. $charsets = Charsets::getCharsets($this->dbi, $GLOBALS['cfg']['Server']['DisableIS']);
  1042. $collations = Charsets::getCollations($this->dbi, $GLOBALS['cfg']['Server']['DisableIS']);
  1043. /** @var Charset $charset */
  1044. foreach ($charsets as $charset) {
  1045. $html_output .= '<optgroup label="' . $charset->getName()
  1046. . '" title="' . $charset->getDescription() . '">' . "\n";
  1047. /** @var Collation $collation */
  1048. foreach ($collations[$charset->getName()] as $collation) {
  1049. $html_output .= '<option value="' . $collation->getName()
  1050. . '" title="' . $collation->getDescription() . '"'
  1051. . ($tbl_collation == $collation->getName() ? ' selected' : '') . '>'
  1052. . $collation->getName() . '</option>' . "\n";
  1053. }
  1054. $html_output .= '</optgroup>' . "\n";
  1055. }
  1056. $html_output .= '</select>' . "\n";
  1057. $html_output .= '</td>'
  1058. . '</tr>';
  1059. // Change all Column collations
  1060. $html_output .= '<tr><td></td><td>'
  1061. . '<input type="checkbox" name="change_all_collations" value="1" '
  1062. . 'id="checkbox_change_all_collations">'
  1063. . '<label for="checkbox_change_all_collations">'
  1064. . __('Change all column collations')
  1065. . '</label>'
  1066. . '</td></tr>';
  1067. if ($pma_table->isEngine(['MYISAM', 'ARIA', 'ISAM'])) {
  1068. $html_output .= $this->getHtmlForPackKeys($pack_keys);
  1069. } // end if (MYISAM|ISAM)
  1070. if ($pma_table->isEngine(['MYISAM', 'ARIA'])) {
  1071. $html_output .= $this->getHtmlForTableRow(
  1072. 'new_checksum',
  1073. 'CHECKSUM',
  1074. $checksum
  1075. );
  1076. $html_output .= $this->getHtmlForTableRow(
  1077. 'new_delay_key_write',
  1078. 'DELAY_KEY_WRITE',
  1079. $delay_key_write
  1080. );
  1081. } // end if (MYISAM)
  1082. if ($pma_table->isEngine('ARIA')) {
  1083. $html_output .= $this->getHtmlForTableRow(
  1084. 'new_transactional',
  1085. 'TRANSACTIONAL',
  1086. $transactional
  1087. );
  1088. $html_output .= $this->getHtmlForTableRow(
  1089. 'new_page_checksum',
  1090. 'PAGE_CHECKSUM',
  1091. $page_checksum
  1092. );
  1093. } // end if (ARIA)
  1094. if (strlen($auto_increment) > 0
  1095. && $pma_table->isEngine(['MYISAM', 'ARIA', 'INNODB', 'PBXT', 'ROCKSDB'])
  1096. ) {
  1097. $html_output .= '<tr><td class="vmiddle">'
  1098. . '<label for="auto_increment_opt">AUTO_INCREMENT</label></td>'
  1099. . '<td><input type="number" name="new_auto_increment" '
  1100. . 'id="auto_increment_opt"'
  1101. . 'value="' . $auto_increment . '">'
  1102. . '<input type="hidden" name="hidden_auto_increment" '
  1103. . 'value="' . $auto_increment . '"></td>'
  1104. . '</tr> ';
  1105. } // end if (MYISAM|INNODB)
  1106. $possible_row_formats = $this->getPossibleRowFormat();
  1107. // for MYISAM there is also COMPRESSED but it can be set only by the
  1108. // myisampack utility, so don't offer here the choice because if we
  1109. // try it inside an ALTER TABLE, MySQL (at least in 5.1.23-maria)
  1110. // does not return a warning
  1111. // (if the table was compressed, it can be seen on the Structure page)
  1112. if (isset($possible_row_formats[$tbl_storage_engine])) {
  1113. $current_row_format
  1114. = mb_strtoupper($GLOBALS['showtable']['Row_format']);
  1115. $html_output .= '<tr><td class="vmiddle">'
  1116. . '<label for="new_row_format">ROW_FORMAT</label></td>'
  1117. . '<td>';
  1118. $html_output .= Util::getDropdown(
  1119. 'new_row_format',
  1120. $possible_row_formats[$tbl_storage_engine],
  1121. $current_row_format,
  1122. 'new_row_format'
  1123. );
  1124. $html_output .= '</td></tr>';
  1125. }
  1126. $html_output .= '</table>'
  1127. . '</fieldset>';
  1128. return $html_output;
  1129. }
  1130. /**
  1131. * Get the common HTML table row (tr) for new_checksum, new_delay_key_write,
  1132. * new_transactional and new_page_checksum
  1133. *
  1134. * @param string $attribute class, name and id attribute
  1135. * @param string $label label value
  1136. * @param string $val checksum, delay_key_write, transactional, page_checksum
  1137. *
  1138. * @return string
  1139. */
  1140. private function getHtmlForTableRow($attribute, $label, $val)
  1141. {
  1142. return '<tr>'
  1143. . '<td class="vmiddle">'
  1144. . '<label for="' . $attribute . '">' . $label . '</label>'
  1145. . '</td>'
  1146. . '<td>'
  1147. . '<input type="checkbox" name="' . $attribute . '" id="' . $attribute . '"'
  1148. . ' value="1"' . (! empty($val) && $val == 1 ? ' checked="checked"' : '')
  1149. . '>'
  1150. . '</td>'
  1151. . '</tr>';
  1152. }
  1153. /**
  1154. * Get array of possible row formats
  1155. *
  1156. * @return array
  1157. */
  1158. private function getPossibleRowFormat()
  1159. {
  1160. // the outer array is for engines, the inner array contains the dropdown
  1161. // option values as keys then the dropdown option labels
  1162. $possible_row_formats = [
  1163. 'ARCHIVE' => [
  1164. 'COMPRESSED' => 'COMPRESSED',
  1165. ],
  1166. 'ARIA' => [
  1167. 'FIXED' => 'FIXED',
  1168. 'DYNAMIC' => 'DYNAMIC',
  1169. 'PAGE' => 'PAGE',
  1170. ],
  1171. 'MARIA' => [
  1172. 'FIXED' => 'FIXED',
  1173. 'DYNAMIC' => 'DYNAMIC',
  1174. 'PAGE' => 'PAGE',
  1175. ],
  1176. 'MYISAM' => [
  1177. 'FIXED' => 'FIXED',
  1178. 'DYNAMIC' => 'DYNAMIC',
  1179. ],
  1180. 'PBXT' => [
  1181. 'FIXED' => 'FIXED',
  1182. 'DYNAMIC' => 'DYNAMIC',
  1183. ],
  1184. 'INNODB' => [
  1185. 'COMPACT' => 'COMPACT',
  1186. 'REDUNDANT' => 'REDUNDANT',
  1187. ],
  1188. ];
  1189. /** @var Innodb $innodbEnginePlugin */
  1190. $innodbEnginePlugin = StorageEngine::getEngine('Innodb');
  1191. $innodbPluginVersion = $innodbEnginePlugin->getInnodbPluginVersion();
  1192. if (! empty($innodbPluginVersion)) {
  1193. $innodb_file_format = $innodbEnginePlugin->getInnodbFileFormat();
  1194. } else {
  1195. $innodb_file_format = '';
  1196. }
  1197. /**
  1198. * Newer MySQL/MariaDB always return empty a.k.a '' on $innodb_file_format otherwise
  1199. * old versions of MySQL/MariaDB must be returning something or not empty.
  1200. * This patch is to support newer MySQL/MariaDB while also for backward compatibilities.
  1201. */
  1202. if (( ('Barracuda' == $innodb_file_format) || ($innodb_file_format == '') )
  1203. && $innodbEnginePlugin->supportsFilePerTable()
  1204. ) {
  1205. $possible_row_formats['INNODB']['DYNAMIC'] = 'DYNAMIC';
  1206. $possible_row_formats['INNODB']['COMPRESSED'] = 'COMPRESSED';
  1207. }
  1208. return $possible_row_formats;
  1209. }
  1210. /**
  1211. * Get HTML div for copy table
  1212. *
  1213. * @return string
  1214. */
  1215. public function getHtmlForCopytable()
  1216. {
  1217. $html_output = '<div>';
  1218. $html_output .= '<form method="post" action="tbl_operations.php" '
  1219. . 'name="copyTable" '
  1220. . 'id="copyTable" '
  1221. . ' class="ajax" '
  1222. . 'onsubmit="return Functions.emptyCheckTheField(this, \'new_name\')">'
  1223. . Url::getHiddenInputs($GLOBALS['db'], $GLOBALS['table'])
  1224. . '<input type="hidden" name="reload" value="1">';
  1225. $html_output .= '<fieldset>';
  1226. $html_output .= '<legend>'
  1227. . __('Copy table to (database<b>.</b>table)') . '</legend>';
  1228. if (count($GLOBALS['dblist']->databases) > $GLOBALS['cfg']['MaxDbList']) {
  1229. $html_output .= '<input class="halfWidth" type="text" maxlength="100" '
  1230. . 'name="target_db" '
  1231. . 'value="' . htmlspecialchars($GLOBALS['db']) . '">';
  1232. } else {
  1233. $html_output .= '<select class="halfWidth" name="target_db">'
  1234. . $GLOBALS['dblist']->databases->getHtmlOptions(true, false)
  1235. . '</select>';
  1236. }
  1237. $html_output .= '&nbsp;<strong>.</strong>&nbsp;';
  1238. $html_output .= '<input class="halfWidth" type="text" required="required" '
  1239. . 'name="new_name" maxlength="64" '
  1240. . 'value="' . htmlspecialchars($GLOBALS['table']) . '"><br>';
  1241. $choices = [
  1242. 'structure' => __('Structure only'),
  1243. 'data' => __('Structure and data'),
  1244. 'dataonly' => __('Data only'),
  1245. ];
  1246. $html_output .= Util::getRadioFields(
  1247. 'what',
  1248. $choices,
  1249. 'data',
  1250. true
  1251. );
  1252. $html_output .= '<br>';
  1253. $html_output .= '<input type="checkbox" name="drop_if_exists" '
  1254. . 'value="true" id="checkbox_drop">'
  1255. . '<label for="checkbox_drop">'
  1256. . sprintf(__('Add %s'), 'DROP TABLE') . '</label><br>'
  1257. . '<input type="checkbox" name="sql_auto_increment" '
  1258. . 'value="1" id="checkbox_auto_increment_cp">'
  1259. . '<label for="checkbox_auto_increment_cp">'
  1260. . __('Add AUTO_INCREMENT value') . '</label><br>';
  1261. // display "Add constraints" choice only if there are
  1262. // foreign keys
  1263. if ($this->relation->getForeigners($GLOBALS['db'], $GLOBALS['table'], '', 'foreign')) {
  1264. $html_output .= '<input type="checkbox" name="add_constraints" '
  1265. . 'value="1" id="checkbox_constraints" checked="checked">';
  1266. $html_output .= '<label for="checkbox_constraints">'
  1267. . __('Add constraints') . '</label><br>';
  1268. } // endif
  1269. $html_output .= '<br>';
  1270. if ($GLOBALS['table_priv'] && $GLOBALS['col_priv']
  1271. && $GLOBALS['is_reload_priv']
  1272. ) {
  1273. $html_output .= '<input type="checkbox" name="adjust_privileges" '
  1274. . 'value="1" id="checkbox_adjust_privileges" checked="checked">';
  1275. } else {
  1276. $html_output .= '<input type="checkbox" name="adjust_privileges" '
  1277. . 'value="1" id="checkbox_adjust_privileges" title="' . __(
  1278. 'You don\'t have sufficient privileges to perform this '
  1279. . 'operation; Please refer to the documentation for more details'
  1280. )
  1281. . '" disabled>';
  1282. }
  1283. $html_output .= '<label for="checkbox_adjust_privileges">'
  1284. . __('Adjust privileges') . Util::showDocu('faq', 'faq6-39')
  1285. . '</label><br>';
  1286. $pma_switch_to_new = isset($_SESSION['pma_switch_to_new']) && $_SESSION['pma_switch_to_new'];
  1287. $html_output .= '<input type="checkbox" name="switch_to_new" value="true"'
  1288. . 'id="checkbox_switch"'
  1289. . ($pma_switch_to_new ? ' checked="checked"' : '') . '>';
  1290. $html_output .= '<label for="checkbox_switch">'
  1291. . __('Switch to copied table') . '</label>'
  1292. . '</fieldset>';
  1293. $html_output .= '<fieldset class="tblFooters">'
  1294. . '<input class="btn btn-primary" type="submit" name="submit_copy" value="' . __('Go') . '">'
  1295. . '</fieldset>'
  1296. . '</form>'
  1297. . '</div>';
  1298. return $html_output;
  1299. }
  1300. /**
  1301. * Get HTML snippet for table maintenance
  1302. *
  1303. * @param Table $pma_table Table object
  1304. * @param array $url_params array of URL parameters
  1305. *
  1306. * @return string
  1307. */
  1308. public function getHtmlForTableMaintenance($pma_table, array $url_params)
  1309. {
  1310. $html_output = '<div>';
  1311. $html_output .= '<fieldset>'
  1312. . '<legend>' . __('Table maintenance') . '</legend>';
  1313. $html_output .= '<ul id="tbl_maintenance">';
  1314. // Note: BERKELEY (BDB) is no longer supported, starting with MySQL 5.1
  1315. $html_output .= $this->getListofMaintainActionLink($pma_table, $url_params);
  1316. $html_output .= '</ul>'
  1317. . '</fieldset>'
  1318. . '</div>';
  1319. return $html_output;
  1320. }
  1321. /**
  1322. * Get HTML 'li' having a link of maintain action
  1323. *
  1324. * @param Table $pma_table Table object
  1325. * @param array $url_params Array of URL parameters
  1326. *
  1327. * @return string
  1328. */
  1329. private function getListofMaintainActionLink($pma_table, array $url_params)
  1330. {
  1331. $html_output = '';
  1332. // analyze table
  1333. if ($pma_table->isEngine(['MYISAM', 'ARIA', 'INNODB', 'BERKELEYDB', 'TOKUDB'])) {
  1334. $params = [
  1335. 'sql_query' => 'ANALYZE TABLE '
  1336. . Util::backquote($GLOBALS['table']),
  1337. 'table_maintenance' => 'Go',
  1338. ];
  1339. $html_output .= $this->getMaintainActionlink(
  1340. __('Analyze table'),
  1341. $params,
  1342. $url_params,
  1343. 'ANALYZE_TABLE'
  1344. );
  1345. }
  1346. // check table
  1347. if ($pma_table->isEngine(['MYISAM', 'ARIA', 'INNODB', 'TOKUDB'])) {
  1348. $params = [
  1349. 'sql_query' => 'CHECK TABLE '
  1350. . Util::backquote($GLOBALS['table']),
  1351. 'table_maintenance' => 'Go',
  1352. ];
  1353. $html_output .= $this->getMaintainActionlink(
  1354. __('Check table'),
  1355. $params,
  1356. $url_params,
  1357. 'CHECK_TABLE'
  1358. );
  1359. }
  1360. // checksum table
  1361. $params = [
  1362. 'sql_query' => 'CHECKSUM TABLE '
  1363. . Util::backquote($GLOBALS['table']),
  1364. 'table_maintenance' => 'Go',
  1365. ];
  1366. $html_output .= $this->getMaintainActionlink(
  1367. __('Checksum table'),
  1368. $params,
  1369. $url_params,
  1370. 'CHECKSUM_TABLE'
  1371. );
  1372. // defragment table
  1373. if ($pma_table->isEngine(['INNODB'])) {
  1374. $params = [
  1375. 'sql_query' => 'ALTER TABLE '
  1376. . Util::backquote($GLOBALS['table'])
  1377. . ' ENGINE = InnoDB;',
  1378. ];
  1379. $html_output .= $this->getMaintainActionlink(
  1380. __('Defragment table'),
  1381. $params,
  1382. $url_params,
  1383. 'InnoDB_File_Defragmenting'
  1384. );
  1385. }
  1386. // flush table
  1387. $params = [
  1388. 'sql_query' => 'FLUSH TABLE '
  1389. . Util::backquote($GLOBALS['table']),
  1390. 'message_to_show' => sprintf(
  1391. __('Table %s has been flushed.'),
  1392. htmlspecialchars($GLOBALS['table'])
  1393. ),
  1394. 'reload' => 1,
  1395. ];
  1396. $html_output .= $this->getMaintainActionlink(
  1397. __('Flush the table (FLUSH)'),
  1398. $params,
  1399. $url_params,
  1400. 'FLUSH'
  1401. );
  1402. // optimize table
  1403. if ($pma_table->isEngine(['MYISAM', 'ARIA', 'INNODB', 'BERKELEYDB', 'TOKUDB'])) {
  1404. $params = [
  1405. 'sql_query' => 'OPTIMIZE TABLE '
  1406. . Util::backquote($GLOBALS['table']),
  1407. 'table_maintenance' => 'Go',
  1408. ];
  1409. $html_output .= $this->getMaintainActionlink(
  1410. __('Optimize table'),
  1411. $params,
  1412. $url_params,
  1413. 'OPTIMIZE_TABLE'
  1414. );
  1415. }
  1416. // repair table
  1417. if ($pma_table->isEngine(['MYISAM', 'ARIA'])) {
  1418. $params = [
  1419. 'sql_query' => 'REPAIR TABLE '
  1420. . Util::backquote($GLOBALS['table']),
  1421. 'table_maintenance' => 'Go',
  1422. ];
  1423. $html_output .= $this->getMaintainActionlink(
  1424. __('Repair table'),
  1425. $params,
  1426. $url_params,
  1427. 'REPAIR_TABLE'
  1428. );
  1429. }
  1430. return $html_output;
  1431. }
  1432. /**
  1433. * Get maintain action HTML link
  1434. *
  1435. * @param string $action_message action message
  1436. * @param array $params url parameters array
  1437. * @param array $url_params additional url parameters
  1438. * @param string $link contains name of page/anchor that is being linked
  1439. *
  1440. * @return string
  1441. */
  1442. private function getMaintainActionlink($action_message, array $params, array $url_params, $link)
  1443. {
  1444. return '<li>'
  1445. . Util::linkOrButton(
  1446. 'sql.php' . Url::getCommon(array_merge($url_params, $params)),
  1447. $action_message,
  1448. ['class' => 'maintain_action ajax']
  1449. )
  1450. . Util::showMySQLDocu($link)
  1451. . '</li>';
  1452. }
  1453. /**
  1454. * Get HTML for Delete data or table (truncate table, drop table)
  1455. *
  1456. * @param array $truncate_table_url_params url parameter array for truncate table
  1457. * @param array $dropTableUrlParams url parameter array for drop table
  1458. *
  1459. * @return string
  1460. */
  1461. public function getHtmlForDeleteDataOrTable(
  1462. array $truncate_table_url_params,
  1463. array $dropTableUrlParams
  1464. ) {
  1465. $html_output = '<div>'
  1466. . '<fieldset class="caution">'
  1467. . '<legend>' . __('Delete data or table') . '</legend>';
  1468. $html_output .= '<ul>';
  1469. if (! empty($truncate_table_url_params)) {
  1470. $html_output .= $this->getDeleteDataOrTablelink(
  1471. $truncate_table_url_params,
  1472. 'TRUNCATE_TABLE',
  1473. __('Empty the table (TRUNCATE)'),
  1474. 'truncate_tbl_anchor'
  1475. );
  1476. }
  1477. if (! empty($dropTableUrlParams)) {
  1478. $html_output .= $this->getDeleteDataOrTablelink(
  1479. $dropTableUrlParams,
  1480. 'DROP_TABLE',
  1481. __('Delete the table (DROP)'),
  1482. 'drop_tbl_anchor'
  1483. );
  1484. }
  1485. $html_output .= '</ul></fieldset></div>';
  1486. return $html_output;
  1487. }
  1488. /**
  1489. * Get the HTML link for Truncate table, Drop table and Drop db
  1490. *
  1491. * @param array $url_params url parameter array for delete data or table
  1492. * @param string $syntax TRUNCATE_TABLE or DROP_TABLE or DROP_DATABASE
  1493. * @param string $link link to be shown
  1494. * @param string $htmlId id of the link
  1495. *
  1496. * @return string html output
  1497. */
  1498. public function getDeleteDataOrTablelink(array $url_params, $syntax, $link, $htmlId)
  1499. {
  1500. return '<li>' . Util::linkOrButton(
  1501. 'sql.php' . Url::getCommon($url_params),
  1502. $link,
  1503. [
  1504. 'id' => $htmlId,
  1505. 'class' => 'ajax',
  1506. ]
  1507. )
  1508. . Util::showMySQLDocu($syntax)
  1509. . '</li>';
  1510. }
  1511. /**
  1512. * Get HTML snippet for partition maintenance
  1513. *
  1514. * @param array $partition_names array of partition names for a specific db/table
  1515. * @param array $url_params url parameters
  1516. *
  1517. * @return string
  1518. */
  1519. public function getHtmlForPartitionMaintenance(array $partition_names, array $url_params)
  1520. {
  1521. $choices = [
  1522. 'ANALYZE' => __('Analyze'),
  1523. 'CHECK' => __('Check'),
  1524. 'OPTIMIZE' => __('Optimize'),
  1525. 'REBUILD' => __('Rebuild'),
  1526. 'REPAIR' => __('Repair'),
  1527. 'TRUNCATE' => __('Truncate'),
  1528. ];
  1529. $partition_method = Partition::getPartitionMethod(
  1530. $GLOBALS['db'],
  1531. $GLOBALS['table']
  1532. );
  1533. // add COALESCE or DROP option to choices array depeding on Partition method
  1534. if ($partition_method == 'RANGE'
  1535. || $partition_method == 'RANGE COLUMNS'
  1536. || $partition_method == 'LIST'
  1537. || $partition_method == 'LIST COLUMNS'
  1538. ) {
  1539. $choices['DROP'] = __('Drop');
  1540. } else {
  1541. $choices['COALESCE'] = __('Coalesce');
  1542. }
  1543. $html_output = '<div>'
  1544. . '<form id="partitionsForm" class="ajax" '
  1545. . 'method="post" action="tbl_operations.php" >'
  1546. . Url::getHiddenInputs($GLOBALS['db'], $GLOBALS['table'])
  1547. . '<fieldset>'
  1548. . '<legend>'
  1549. . __('Partition maintenance')
  1550. . Util::showMySQLDocu('partitioning_maintenance')
  1551. . '</legend>';
  1552. $html_select = '<select id="partition_name" name="partition_name[]"'
  1553. . ' multiple="multiple" required="required">' . "\n";
  1554. $first = true;
  1555. foreach ($partition_names as $one_partition) {
  1556. $one_partition = htmlspecialchars($one_partition);
  1557. $html_select .= '<option value="' . $one_partition . '"';
  1558. if ($first) {
  1559. $html_select .= ' selected="selected"';
  1560. $first = false;
  1561. }
  1562. $html_select .= '>' . $one_partition . '</option>' . "\n";
  1563. }
  1564. $html_select .= '</select>' . "\n";
  1565. $html_output .= sprintf(__('Partition %s'), $html_select);
  1566. $html_output .= '<div class="clearfloat">';
  1567. $html_output .= Util::getRadioFields(
  1568. 'partition_operation',
  1569. $choices,
  1570. 'ANALYZE',
  1571. false,
  1572. true,
  1573. 'floatleft'
  1574. );
  1575. $this_url_params = array_merge(
  1576. $url_params,
  1577. [
  1578. 'sql_query' => 'ALTER TABLE '
  1579. . Util::backquote($GLOBALS['table'])
  1580. . ' REMOVE PARTITIONING;',
  1581. ]
  1582. );
  1583. $html_output .= '<div class="clearfloat"><br>';
  1584. $html_output .= '<a href="sql.php'
  1585. . Url::getCommon($this_url_params) . '">'
  1586. . __('Remove partitioning') . '</a>';
  1587. $html_output .= '</fieldset>'
  1588. . '<fieldset class="tblFooters">'
  1589. . '<input type="hidden" name="submit_partition" value="1">'
  1590. . '<input class="btn btn-primary" type="submit" value="' . __('Go') . '">'
  1591. . '</fieldset>'
  1592. . '</form>'
  1593. . '</div>';
  1594. return $html_output;
  1595. }
  1596. /**
  1597. * Get the HTML for Referential Integrity check
  1598. *
  1599. * @param array $foreign all Relations to foreign tables for a given table
  1600. * or optionally a given column in a table
  1601. * @param array $url_params array of url parameters
  1602. *
  1603. * @return string
  1604. */
  1605. public function getHtmlForReferentialIntegrityCheck(array $foreign, array $url_params)
  1606. {
  1607. $html_output = '<div>'
  1608. . '<fieldset>'
  1609. . '<legend>' . __('Check referential integrity:') . '</legend>';
  1610. $html_output .= '<ul>';
  1611. foreach ($foreign as $master => $arr) {
  1612. $join_query = 'SELECT '
  1613. . Util::backquote($GLOBALS['table']) . '.*'
  1614. . ' FROM ' . Util::backquote($GLOBALS['table'])
  1615. . ' LEFT JOIN '
  1616. . Util::backquote($arr['foreign_db'])
  1617. . '.'
  1618. . Util::backquote($arr['foreign_table']);
  1619. if ($arr['foreign_table'] == $GLOBALS['table']) {
  1620. $foreign_table = $GLOBALS['table'] . '1';
  1621. $join_query .= ' AS ' . Util::backquote($foreign_table);
  1622. } else {
  1623. $foreign_table = $arr['foreign_table'];
  1624. }
  1625. $join_query .= ' ON '
  1626. . Util::backquote($GLOBALS['table']) . '.'
  1627. . Util::backquote($master)
  1628. . ' = '
  1629. . Util::backquote($arr['foreign_db'])
  1630. . '.'
  1631. . Util::backquote($foreign_table) . '.'
  1632. . Util::backquote($arr['foreign_field'])
  1633. . ' WHERE '
  1634. . Util::backquote($arr['foreign_db'])
  1635. . '.'
  1636. . Util::backquote($foreign_table) . '.'
  1637. . Util::backquote($arr['foreign_field'])
  1638. . ' IS NULL AND '
  1639. . Util::backquote($GLOBALS['table']) . '.'
  1640. . Util::backquote($master)
  1641. . ' IS NOT NULL';
  1642. $this_url_params = array_merge(
  1643. $url_params,
  1644. [
  1645. 'sql_query' => $join_query,
  1646. 'sql_signature' => Core::signSqlQuery($join_query),
  1647. ]
  1648. );
  1649. $html_output .= '<li>'
  1650. . '<a href="sql.php'
  1651. . Url::getCommon($this_url_params)
  1652. . '">'
  1653. . $master . '&nbsp;->&nbsp;' . $arr['foreign_db'] . '.'
  1654. . $arr['foreign_table'] . '.' . $arr['foreign_field']
  1655. . '</a></li>' . "\n";
  1656. } // foreach $foreign
  1657. $html_output .= '</ul></fieldset></div>';
  1658. return $html_output;
  1659. }
  1660. /**
  1661. * Reorder table based on request params
  1662. *
  1663. * @return array SQL query and result
  1664. */
  1665. public function getQueryAndResultForReorderingTable()
  1666. {
  1667. $sql_query = 'ALTER TABLE '
  1668. . Util::backquote($GLOBALS['table'])
  1669. . ' ORDER BY '
  1670. . Util::backquote(urldecode($_POST['order_field']));
  1671. if (isset($_POST['order_order'])
  1672. && $_POST['order_order'] === 'desc'
  1673. ) {
  1674. $sql_query .= ' DESC';
  1675. } else {
  1676. $sql_query .= ' ASC';
  1677. }
  1678. $sql_query .= ';';
  1679. $result = $this->dbi->query($sql_query);
  1680. return [
  1681. $sql_query,
  1682. $result,
  1683. ];
  1684. }
  1685. /**
  1686. * Get table alters array
  1687. *
  1688. * @param Table $pma_table The Table object
  1689. * @param string $pack_keys pack keys
  1690. * @param string $checksum value of checksum
  1691. * @param string $page_checksum value of page checksum
  1692. * @param string $delay_key_write delay key write
  1693. * @param string $row_format row format
  1694. * @param string $newTblStorageEngine table storage engine
  1695. * @param string $transactional value of transactional
  1696. * @param string $tbl_collation collation of the table
  1697. *
  1698. * @return array
  1699. */
  1700. public function getTableAltersArray(
  1701. $pma_table,
  1702. $pack_keys,
  1703. $checksum,
  1704. $page_checksum,
  1705. $delay_key_write,
  1706. $row_format,
  1707. $newTblStorageEngine,
  1708. $transactional,
  1709. $tbl_collation
  1710. ) {
  1711. global $auto_increment;
  1712. $table_alters = [];
  1713. if (isset($_POST['comment'])
  1714. && urldecode($_POST['prev_comment']) !== $_POST['comment']
  1715. ) {
  1716. $table_alters[] = 'COMMENT = \''
  1717. . $this->dbi->escapeString($_POST['comment']) . '\'';
  1718. }
  1719. if (! empty($newTblStorageEngine)
  1720. && mb_strtolower($newTblStorageEngine) !== mb_strtolower($GLOBALS['tbl_storage_engine'])
  1721. ) {
  1722. $table_alters[] = 'ENGINE = ' . $newTblStorageEngine;
  1723. }
  1724. if (! empty($_POST['tbl_collation'])
  1725. && $_POST['tbl_collation'] !== $tbl_collation
  1726. ) {
  1727. $table_alters[] = 'DEFAULT '
  1728. . Util::getCharsetQueryPart($_POST['tbl_collation']);
  1729. }
  1730. if ($pma_table->isEngine(['MYISAM', 'ARIA', 'ISAM'])
  1731. && isset($_POST['new_pack_keys'])
  1732. && $_POST['new_pack_keys'] != (string) $pack_keys
  1733. ) {
  1734. $table_alters[] = 'pack_keys = ' . $_POST['new_pack_keys'];
  1735. }
  1736. $_POST['new_checksum'] = empty($_POST['new_checksum']) ? '0' : '1';
  1737. if ($pma_table->isEngine(['MYISAM', 'ARIA'])
  1738. && $_POST['new_checksum'] !== $checksum
  1739. ) {
  1740. $table_alters[] = 'checksum = ' . $_POST['new_checksum'];
  1741. }
  1742. $_POST['new_transactional']
  1743. = empty($_POST['new_transactional']) ? '0' : '1';
  1744. if ($pma_table->isEngine('ARIA')
  1745. && $_POST['new_transactional'] !== $transactional
  1746. ) {
  1747. $table_alters[] = 'TRANSACTIONAL = ' . $_POST['new_transactional'];
  1748. }
  1749. $_POST['new_page_checksum']
  1750. = empty($_POST['new_page_checksum']) ? '0' : '1';
  1751. if ($pma_table->isEngine('ARIA')
  1752. && $_POST['new_page_checksum'] !== $page_checksum
  1753. ) {
  1754. $table_alters[] = 'PAGE_CHECKSUM = ' . $_POST['new_page_checksum'];
  1755. }
  1756. $_POST['new_delay_key_write']
  1757. = empty($_POST['new_delay_key_write']) ? '0' : '1';
  1758. if ($pma_table->isEngine(['MYISAM', 'ARIA'])
  1759. && $_POST['new_delay_key_write'] !== $delay_key_write
  1760. ) {
  1761. $table_alters[] = 'delay_key_write = ' . $_POST['new_delay_key_write'];
  1762. }
  1763. if ($pma_table->isEngine(['MYISAM', 'ARIA', 'INNODB', 'PBXT', 'ROCKSDB'])
  1764. && ! empty($_POST['new_auto_increment'])
  1765. && (! isset($auto_increment)
  1766. || $_POST['new_auto_increment'] !== $auto_increment)
  1767. && $_POST['new_auto_increment'] !== $_POST['hidden_auto_increment']
  1768. ) {
  1769. $table_alters[] = 'auto_increment = '
  1770. . $this->dbi->escapeString($_POST['new_auto_increment']);
  1771. }
  1772. if (! empty($_POST['new_row_format'])) {
  1773. $newRowFormat = $_POST['new_row_format'];
  1774. $newRowFormatLower = mb_strtolower($newRowFormat);
  1775. if ($pma_table->isEngine(['MYISAM', 'ARIA', 'INNODB', 'PBXT'])
  1776. && (strlen($row_format) === 0
  1777. || $newRowFormatLower !== mb_strtolower($row_format))
  1778. ) {
  1779. $table_alters[] = 'ROW_FORMAT = '
  1780. . $this->dbi->escapeString($newRowFormat);
  1781. }
  1782. }
  1783. return $table_alters;
  1784. }
  1785. /**
  1786. * Get warning messages array
  1787. *
  1788. * @return array
  1789. */
  1790. public function getWarningMessagesArray()
  1791. {
  1792. $warning_messages = [];
  1793. foreach ($this->dbi->getWarnings() as $warning) {
  1794. // In MariaDB 5.1.44, when altering a table from Maria to MyISAM
  1795. // and if TRANSACTIONAL was set, the system reports an error;
  1796. // I discussed with a Maria developer and he agrees that this
  1797. // should not be reported with a Level of Error, so here
  1798. // I just ignore it. But there are other 1478 messages
  1799. // that it's better to show.
  1800. if (! (isset($_POST['new_tbl_storage_engine'])
  1801. && $_POST['new_tbl_storage_engine'] == 'MyISAM'
  1802. && $warning['Code'] == '1478'
  1803. && $warning['Level'] == 'Error')
  1804. ) {
  1805. $warning_messages[] = $warning['Level'] . ': #' . $warning['Code']
  1806. . ' ' . $warning['Message'];
  1807. }
  1808. }
  1809. return $warning_messages;
  1810. }
  1811. /**
  1812. * Get SQL query and result after ran this SQL query for a partition operation
  1813. * has been requested by the user
  1814. *
  1815. * @return array $sql_query, $result
  1816. */
  1817. public function getQueryAndResultForPartition()
  1818. {
  1819. $sql_query = 'ALTER TABLE '
  1820. . Util::backquote($GLOBALS['table']) . ' '
  1821. . $_POST['partition_operation']
  1822. . ' PARTITION ';
  1823. if ($_POST['partition_operation'] == 'COALESCE') {
  1824. $sql_query .= count($_POST['partition_name']);
  1825. } else {
  1826. $sql_query .= implode(', ', $_POST['partition_name']) . ';';
  1827. }
  1828. $result = $this->dbi->query($sql_query);
  1829. return [
  1830. $sql_query,
  1831. $result,
  1832. ];
  1833. }
  1834. /**
  1835. * Adjust the privileges after renaming/moving a table
  1836. *
  1837. * @param string $oldDb Database name before table renaming/moving table
  1838. * @param string $oldTable Table name before table renaming/moving table
  1839. * @param string $newDb Database name after table renaming/ moving table
  1840. * @param string $newTable Table name after table renaming/moving table
  1841. *
  1842. * @return void
  1843. */
  1844. public function adjustPrivilegesRenameOrMoveTable($oldDb, $oldTable, $newDb, $newTable)
  1845. {
  1846. if ($GLOBALS['table_priv'] && $GLOBALS['col_priv']
  1847. && $GLOBALS['is_reload_priv']
  1848. ) {
  1849. $this->dbi->selectDb('mysql');
  1850. // For table specific privileges
  1851. $query_table_specific = 'UPDATE ' . Util::backquote('tables_priv')
  1852. . 'SET Db = \'' . $this->dbi->escapeString($newDb) . '\', Table_name = \'' . $this->dbi->escapeString($newTable)
  1853. . '\' where Db = \'' . $this->dbi->escapeString($oldDb) . '\' AND Table_name = \'' . $this->dbi->escapeString($oldTable)
  1854. . '\';';
  1855. $this->dbi->query($query_table_specific);
  1856. // For column specific privileges
  1857. $query_col_specific = 'UPDATE ' . Util::backquote('columns_priv')
  1858. . 'SET Db = \'' . $this->dbi->escapeString($newDb) . '\', Table_name = \'' . $this->dbi->escapeString($newTable)
  1859. . '\' where Db = \'' . $this->dbi->escapeString($oldDb) . '\' AND Table_name = \'' . $this->dbi->escapeString($oldTable)
  1860. . '\';';
  1861. $this->dbi->query($query_col_specific);
  1862. // Finally FLUSH the new privileges
  1863. $flush_query = "FLUSH PRIVILEGES;";
  1864. $this->dbi->query($flush_query);
  1865. }
  1866. }
  1867. /**
  1868. * Adjust the privileges after copying a table
  1869. *
  1870. * @param string $oldDb Database name before table copying
  1871. * @param string $oldTable Table name before table copying
  1872. * @param string $newDb Database name after table copying
  1873. * @param string $newTable Table name after table copying
  1874. *
  1875. * @return void
  1876. */
  1877. public function adjustPrivilegesCopyTable($oldDb, $oldTable, $newDb, $newTable)
  1878. {
  1879. if ($GLOBALS['table_priv'] && $GLOBALS['col_priv']
  1880. && $GLOBALS['is_reload_priv']
  1881. ) {
  1882. $this->dbi->selectDb('mysql');
  1883. // For Table Specific privileges
  1884. $query_table_specific_old = 'SELECT * FROM '
  1885. . Util::backquote('tables_priv') . ' where '
  1886. . 'Db = "' . $oldDb . '" AND Table_name = "' . $oldTable . '";';
  1887. $old_privs_table = $this->dbi->fetchResult(
  1888. $query_table_specific_old,
  1889. 0
  1890. );
  1891. foreach ($old_privs_table as $old_priv) {
  1892. $newDb_table_privs_query = 'INSERT INTO '
  1893. . Util::backquote('tables_priv') . ' VALUES("'
  1894. . $old_priv[0] . '", "' . $newDb . '", "' . $old_priv[2] . '", "'
  1895. . $newTable . '", "' . $old_priv[4] . '", "' . $old_priv[5]
  1896. . '", "' . $old_priv[6] . '", "' . $old_priv[7] . '");';
  1897. $this->dbi->query($newDb_table_privs_query);
  1898. }
  1899. // For Column Specific privileges
  1900. $query_col_specific_old = 'SELECT * FROM '
  1901. . Util::backquote('columns_priv') . ' WHERE '
  1902. . 'Db = "' . $oldDb . '" AND Table_name = "' . $oldTable . '";';
  1903. $old_privs_col = $this->dbi->fetchResult(
  1904. $query_col_specific_old,
  1905. 0
  1906. );
  1907. foreach ($old_privs_col as $old_priv) {
  1908. $newDb_col_privs_query = 'INSERT INTO '
  1909. . Util::backquote('columns_priv') . ' VALUES("'
  1910. . $old_priv[0] . '", "' . $newDb . '", "' . $old_priv[2] . '", "'
  1911. . $newTable . '", "' . $old_priv[4] . '", "' . $old_priv[5]
  1912. . '", "' . $old_priv[6] . '");';
  1913. $this->dbi->query($newDb_col_privs_query);
  1914. }
  1915. // Finally FLUSH the new privileges
  1916. $flush_query = "FLUSH PRIVILEGES;";
  1917. $this->dbi->query($flush_query);
  1918. }
  1919. }
  1920. /**
  1921. * Change all collations and character sets of all columns in table
  1922. *
  1923. * @param string $db Database name
  1924. * @param string $table Table name
  1925. * @param string $tbl_collation Collation Name
  1926. *
  1927. * @return void
  1928. */
  1929. public function changeAllColumnsCollation($db, $table, $tbl_collation)
  1930. {
  1931. $this->dbi->selectDb($db);
  1932. $change_all_collations_query = 'ALTER TABLE '
  1933. . Util::backquote($table)
  1934. . ' CONVERT TO';
  1935. list($charset) = explode('_', $tbl_collation);
  1936. $change_all_collations_query .= ' CHARACTER SET ' . $charset
  1937. . ($charset == $tbl_collation ? '' : ' COLLATE ' . $tbl_collation);
  1938. $this->dbi->query($change_all_collations_query);
  1939. }
  1940. /**
  1941. * Move or copy a table
  1942. *
  1943. * @param string $db current database name
  1944. * @param string $table current table name
  1945. *
  1946. * @return void
  1947. */
  1948. public function moveOrCopyTable($db, $table)
  1949. {
  1950. /**
  1951. * Selects the database to work with
  1952. */
  1953. $this->dbi->selectDb($db);
  1954. /**
  1955. * $_POST['target_db'] could be empty in case we came from an input field
  1956. * (when there are many databases, no drop-down)
  1957. */
  1958. if (empty($_POST['target_db'])) {
  1959. $_POST['target_db'] = $db;
  1960. }
  1961. /**
  1962. * A target table name has been sent to this script -> do the work
  1963. */
  1964. if (Core::isValid($_POST['new_name'])) {
  1965. if ($db == $_POST['target_db'] && $table == $_POST['new_name']) {
  1966. if (isset($_POST['submit_move'])) {
  1967. $message = Message::error(__('Can\'t move table to same one!'));
  1968. } else {
  1969. $message = Message::error(__('Can\'t copy table to same one!'));
  1970. }
  1971. } else {
  1972. Table::moveCopy(
  1973. $db,
  1974. $table,
  1975. $_POST['target_db'],
  1976. $_POST['new_name'],
  1977. $_POST['what'],
  1978. isset($_POST['submit_move']),
  1979. 'one_table'
  1980. );
  1981. if (isset($_POST['adjust_privileges'])
  1982. && ! empty($_POST['adjust_privileges'])
  1983. ) {
  1984. if (isset($_POST['submit_move'])) {
  1985. $this->adjustPrivilegesRenameOrMoveTable(
  1986. $db,
  1987. $table,
  1988. $_POST['target_db'],
  1989. $_POST['new_name']
  1990. );
  1991. } else {
  1992. $this->adjustPrivilegesCopyTable(
  1993. $db,
  1994. $table,
  1995. $_POST['target_db'],
  1996. $_POST['new_name']
  1997. );
  1998. }
  1999. if (isset($_POST['submit_move'])) {
  2000. $message = Message::success(
  2001. __(
  2002. 'Table %s has been moved to %s. Privileges have been '
  2003. . 'adjusted.'
  2004. )
  2005. );
  2006. } else {
  2007. $message = Message::success(
  2008. __(
  2009. 'Table %s has been copied to %s. Privileges have been '
  2010. . 'adjusted.'
  2011. )
  2012. );
  2013. }
  2014. } else {
  2015. if (isset($_POST['submit_move'])) {
  2016. $message = Message::success(
  2017. __('Table %s has been moved to %s.')
  2018. );
  2019. } else {
  2020. $message = Message::success(
  2021. __('Table %s has been copied to %s.')
  2022. );
  2023. }
  2024. }
  2025. $old = Util::backquote($db) . '.'
  2026. . Util::backquote($table);
  2027. $message->addParam($old);
  2028. $new_name = $_POST['new_name'];
  2029. if ($this->dbi->getLowerCaseNames() === '1') {
  2030. $new_name = strtolower($new_name);
  2031. }
  2032. $GLOBALS['table'] = $new_name;
  2033. $new = Util::backquote($_POST['target_db']) . '.'
  2034. . Util::backquote($new_name);
  2035. $message->addParam($new);
  2036. }
  2037. } else {
  2038. /**
  2039. * No new name for the table!
  2040. */
  2041. $message = Message::error(__('The table name is empty!'));
  2042. }
  2043. $response = Response::getInstance();
  2044. if ($response->isAjax()) {
  2045. $response->addJSON('message', $message);
  2046. if ($message->isSuccess()) {
  2047. $response->addJSON('db', $GLOBALS['db']);
  2048. } else {
  2049. $response->setRequestStatus(false);
  2050. }
  2051. exit;
  2052. }
  2053. }
  2054. }