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