From 04d6d5ca99ebfd1cebb8ce06618fb3811fc1a8aa Mon Sep 17 00:00:00 2001 From: Charles Date: Thu, 9 Jan 2020 10:55:03 +0100 Subject: phpmyadmin working --- srcs/phpmyadmin/libraries/classes/Operations.php | 2263 ++++++++++++++++++++++ 1 file changed, 2263 insertions(+) create mode 100644 srcs/phpmyadmin/libraries/classes/Operations.php (limited to 'srcs/phpmyadmin/libraries/classes/Operations.php') diff --git a/srcs/phpmyadmin/libraries/classes/Operations.php b/srcs/phpmyadmin/libraries/classes/Operations.php new file mode 100644 index 0000000..7cedd17 --- /dev/null +++ b/srcs/phpmyadmin/libraries/classes/Operations.php @@ -0,0 +1,2263 @@ +dbi = $dbi; + $this->relation = $relation; + } + + /** + * Get HTML output for database comment + * + * @param string $db database name + * + * @return string + */ + public function getHtmlForDatabaseComment($db) + { + $html_output = '
' + . '
' + . Url::getHiddenInputs($db) + . '
' + . ''; + if (Util::showIcons('ActionLinksMode')) { + $html_output .= Util::getImage('b_comment') . ' '; + } + $html_output .= __('Database comment'); + $html_output .= ''; + $html_output .= '' + . '
'; + $html_output .= '
' + . '' + . '
' + . '
' + . '
'; + + return $html_output; + } + + /** + * Get HTML output for rename database + * + * @param string $db database name + * @param string $db_collation dataset collation + * + * @return string + */ + public function getHtmlForRenameDatabase($db, $db_collation) + { + $html_output = '
' + . '
'; + if ($db_collation !== null) { + $html_output .= '' . "\n"; + } + $html_output .= '' + . '' + . Url::getHiddenInputs($db) + . '
' + . ''; + + if (Util::showIcons('ActionLinksMode')) { + $html_output .= Util::getImage('b_edit') . ' '; + } + $html_output .= __('Rename database to') + . ''; + + $html_output .= ''; + $html_output .= '
'; + + if ($GLOBALS['db_priv'] && $GLOBALS['table_priv'] + && $GLOBALS['col_priv'] && $GLOBALS['proc_priv'] + && $GLOBALS['is_reload_priv'] + ) { + $html_output .= ''; + } else { + $html_output .= ''; + } + + $html_output .= '
'; + + $html_output .= '' + . '
' + . '
' + . '' + . '
' + . '
' + . '
'; + + return $html_output; + } + + /** + * Get HTML for database drop link + * + * @param string $db database name + * + * @return string + */ + public function getHtmlForDropDatabaseLink($db) + { + $this_sql_query = 'DROP DATABASE ' . Util::backquote($db); + $this_url_params = [ + 'sql_query' => $this_sql_query, + 'back' => 'db_operations.php', + 'goto' => 'index.php', + 'reload' => '1', + 'purge' => '1', + 'message_to_show' => sprintf( + __('Database %s has been dropped.'), + htmlspecialchars(Util::backquote($db)) + ), + 'db' => null, + ]; + + $html_output = '
' + . '
'; + $html_output .= ''; + if (Util::showIcons('ActionLinksMode')) { + $html_output .= Util::getImage('b_deltbl') . ' '; + } + $html_output .= __('Remove database') + . ''; + $html_output .= '
    '; + $html_output .= $this->getDeleteDataOrTablelink( + $this_url_params, + 'DROP_DATABASE', + __('Drop the database (DROP)'), + 'drop_db_anchor' + ); + $html_output .= '
' + . '
'; + + return $html_output; + } + + /** + * Get HTML snippet for copy database + * + * @param string $db database name + * @param string $db_collation dataset collation + * + * @return string + */ + public function getHtmlForCopyDatabase($db, $db_collation) + { + $drop_clause = 'DROP TABLE / DROP VIEW'; + $choices = [ + 'structure' => __('Structure only'), + 'data' => __('Structure and data'), + 'dataonly' => __('Data only'), + ]; + + $pma_switch_to_new = isset($_SESSION['pma_switch_to_new']) && $_SESSION['pma_switch_to_new']; + + $html_output = '
'; + $html_output .= '
'; + + if ($db_collation !== null) { + $html_output .= '' . "\n"; + } + $html_output .= '' . "\n" + . Url::getHiddenInputs($db); + $html_output .= '
' + . ''; + + if (Util::showIcons('ActionLinksMode')) { + $html_output .= Util::getImage('b_edit') . ' '; + } + $html_output .= __('Copy database to') + . '' + . '
' + . Util::getRadioFields( + 'what', + $choices, + 'data', + true + ); + $html_output .= '
'; + $html_output .= ''; + $html_output .= '
'; + $html_output .= ''; + $html_output .= '
'; + $html_output .= ''; + $html_output .= '
'; + $html_output .= ''; + $html_output .= '
'; + $html_output .= '
'; + + if ($GLOBALS['db_priv'] && $GLOBALS['table_priv'] + && $GLOBALS['col_priv'] && $GLOBALS['proc_priv'] + && $GLOBALS['is_reload_priv'] + ) { + $html_output .= ''; + } else { + $html_output .= ''; + } + $html_output .= '
'; + + $html_output .= ''; + $html_output .= '' + . '
'; + $html_output .= '
' + . '' + . '
' + . '
' + . '
'; + + return $html_output; + } + + /** + * Get HTML snippet for change database charset + * + * @param string $db database name + * @param string $db_collation dataset collation + * + * @return string + */ + public function getHtmlForChangeDatabaseCharset($db, $db_collation) + { + $html_output = '
' + . '
'; + if (Util::showIcons('ActionLinksMode')) { + $html_output .= Util::getImage('s_asci') . ' '; + } + $html_output .= '' . "\n" + . '' . "\n"; + $html_output .= '' . "\n"; + $html_output .= '
' + . '' + . '' + . '
' + . '' + . '' + . '' + . '
' + . '' . "\n" + . '
' . "\n" + . '
' . "\n"; + + return $html_output; + } + + /** + * Run the Procedure definitions and function definitions + * + * to avoid selecting alternatively the current and new db + * we would need to modify the CREATE definitions to qualify + * the db name + * + * @param string $db database name + * + * @return void + */ + public function runProcedureAndFunctionDefinitions($db) + { + $procedure_names = $this->dbi->getProceduresOrFunctions($db, 'PROCEDURE'); + if ($procedure_names) { + foreach ($procedure_names as $procedure_name) { + $this->dbi->selectDb($db); + $tmp_query = $this->dbi->getDefinition( + $db, + 'PROCEDURE', + $procedure_name + ); + if ($tmp_query !== null) { + // collect for later display + $GLOBALS['sql_query'] .= "\n" . $tmp_query; + $this->dbi->selectDb($_POST['newname']); + $this->dbi->query($tmp_query); + } + } + } + + $function_names = $this->dbi->getProceduresOrFunctions($db, 'FUNCTION'); + if ($function_names) { + foreach ($function_names as $function_name) { + $this->dbi->selectDb($db); + $tmp_query = $this->dbi->getDefinition( + $db, + 'FUNCTION', + $function_name + ); + if ($tmp_query !== null) { + // collect for later display + $GLOBALS['sql_query'] .= "\n" . $tmp_query; + $this->dbi->selectDb($_POST['newname']); + $this->dbi->query($tmp_query); + } + } + } + } + + /** + * Create database before copy + * + * @return void + */ + public function createDbBeforeCopy() + { + $local_query = 'CREATE DATABASE IF NOT EXISTS ' + . Util::backquote($_POST['newname']); + if (isset($_POST['db_collation'])) { + $local_query .= ' DEFAULT' + . Util::getCharsetQueryPart($_POST['db_collation']); + } + $local_query .= ';'; + $GLOBALS['sql_query'] .= $local_query; + + // save the original db name because Tracker.php which + // may be called under $this->dbi->query() changes $GLOBALS['db'] + // for some statements, one of which being CREATE DATABASE + $original_db = $GLOBALS['db']; + $this->dbi->query($local_query); + $GLOBALS['db'] = $original_db; + + // Set the SQL mode to NO_AUTO_VALUE_ON_ZERO to prevent MySQL from creating + // export statements it cannot import + $sql_set_mode = "SET SQL_MODE='NO_AUTO_VALUE_ON_ZERO'"; + $this->dbi->query($sql_set_mode); + + // rebuild the database list because Table::moveCopy + // checks in this list if the target db exists + $GLOBALS['dblist']->databases->build(); + } + + /** + * Get views as an array and create SQL view stand-in + * + * @param array $tables_full array of all tables in given db or dbs + * @param ExportSql $export_sql_plugin export plugin instance + * @param string $db database name + * + * @return array + */ + public function getViewsAndCreateSqlViewStandIn( + array $tables_full, + $export_sql_plugin, + $db + ) { + $views = []; + foreach ($tables_full as $each_table => $tmp) { + // to be able to rename a db containing views, + // first all the views are collected and a stand-in is created + // the real views are created after the tables + if ($this->dbi->getTable($db, (string) $each_table)->isView()) { + // If view exists, and 'add drop view' is selected: Drop it! + if ($_POST['what'] != 'nocopy' + && isset($_POST['drop_if_exists']) + && $_POST['drop_if_exists'] == 'true' + ) { + $drop_query = 'DROP VIEW IF EXISTS ' + . Util::backquote($_POST['newname']) . '.' + . Util::backquote($each_table); + $this->dbi->query($drop_query); + + $GLOBALS['sql_query'] .= "\n" . $drop_query . ';'; + } + + $views[] = $each_table; + // Create stand-in definition to resolve view dependencies + $sql_view_standin = $export_sql_plugin->getTableDefStandIn( + $db, + $each_table, + "\n" + ); + $this->dbi->selectDb($_POST['newname']); + $this->dbi->query($sql_view_standin); + $GLOBALS['sql_query'] .= "\n" . $sql_view_standin; + } + } + return $views; + } + + /** + * Get sql query for copy/rename table and boolean for whether copy/rename or not + * + * @param array $tables_full array of all tables in given db or dbs + * @param boolean $move whether database name is empty or not + * @param string $db database name + * + * @return array SQL queries for the constraints + */ + public function copyTables(array $tables_full, $move, $db) + { + $sqlContraints = []; + foreach ($tables_full as $each_table => $tmp) { + // skip the views; we have created stand-in definitions + if ($this->dbi->getTable($db, (string) $each_table)->isView()) { + continue; + } + + // value of $what for this table only + $this_what = $_POST['what']; + + // do not copy the data from a Merge table + // note: on the calling FORM, 'data' means 'structure and data' + if ($this->dbi->getTable($db, (string) $each_table)->isMerge()) { + if ($this_what == 'data') { + $this_what = 'structure'; + } + if ($this_what == 'dataonly') { + $this_what = 'nocopy'; + } + } + + if ($this_what != 'nocopy') { + // keep the triggers from the original db+table + // (third param is empty because delimiters are only intended + // for importing via the mysql client or our Import feature) + $triggers = $this->dbi->getTriggers($db, (string) $each_table, ''); + + if (! Table::moveCopy( + $db, + $each_table, + $_POST['newname'], + $each_table, + (isset($this_what) ? $this_what : 'data'), + $move, + 'db_copy' + )) { + $GLOBALS['_error'] = true; + break; + } + // apply the triggers to the destination db+table + if ($triggers) { + $this->dbi->selectDb($_POST['newname']); + foreach ($triggers as $trigger) { + $this->dbi->query($trigger['create']); + $GLOBALS['sql_query'] .= "\n" . $trigger['create'] . ';'; + } + } + + // this does not apply to a rename operation + if (isset($_POST['add_constraints']) + && ! empty($GLOBALS['sql_constraints_query']) + ) { + $sqlContraints[] = $GLOBALS['sql_constraints_query']; + unset($GLOBALS['sql_constraints_query']); + } + } + } + return $sqlContraints; + } + + /** + * Run the EVENT definition for selected database + * + * to avoid selecting alternatively the current and new db + * we would need to modify the CREATE definitions to qualify + * the db name + * + * @param string $db database name + * + * @return void + */ + public function runEventDefinitionsForDb($db) + { + $event_names = $this->dbi->fetchResult( + 'SELECT EVENT_NAME FROM information_schema.EVENTS WHERE EVENT_SCHEMA= \'' + . $this->dbi->escapeString($db) . '\';' + ); + if ($event_names) { + foreach ($event_names as $event_name) { + $this->dbi->selectDb($db); + $tmp_query = $this->dbi->getDefinition($db, 'EVENT', $event_name); + // collect for later display + $GLOBALS['sql_query'] .= "\n" . $tmp_query; + $this->dbi->selectDb($_POST['newname']); + $this->dbi->query($tmp_query); + } + } + } + + /** + * Handle the views, return the boolean value whether table rename/copy or not + * + * @param array $views views as an array + * @param boolean $move whether database name is empty or not + * @param string $db database name + * + * @return void + */ + public function handleTheViews(array $views, $move, $db) + { + // temporarily force to add DROP IF EXIST to CREATE VIEW query, + // to remove stand-in VIEW that was created earlier + // ( $_POST['drop_if_exists'] is used in moveCopy() ) + if (isset($_POST['drop_if_exists'])) { + $temp_drop_if_exists = $_POST['drop_if_exists']; + } + + $_POST['drop_if_exists'] = 'true'; + foreach ($views as $view) { + $copying_succeeded = Table::moveCopy( + $db, + $view, + $_POST['newname'], + $view, + 'structure', + $move, + 'db_copy' + ); + if (! $copying_succeeded) { + $GLOBALS['_error'] = true; + break; + } + } + unset($_POST['drop_if_exists']); + + if (isset($temp_drop_if_exists)) { + // restore previous value + $_POST['drop_if_exists'] = $temp_drop_if_exists; + } + } + + /** + * Adjust the privileges after Renaming the db + * + * @param string $oldDb Database name before renaming + * @param string $newname New Database name requested + * + * @return void + */ + public function adjustPrivilegesMoveDb($oldDb, $newname) + { + if ($GLOBALS['db_priv'] && $GLOBALS['table_priv'] + && $GLOBALS['col_priv'] && $GLOBALS['proc_priv'] + && $GLOBALS['is_reload_priv'] + ) { + $this->dbi->selectDb('mysql'); + $newname = str_replace("_", "\_", $newname); + $oldDb = str_replace("_", "\_", $oldDb); + + // For Db specific privileges + $query_db_specific = 'UPDATE ' . Util::backquote('db') + . 'SET Db = \'' . $this->dbi->escapeString($newname) + . '\' where Db = \'' . $this->dbi->escapeString($oldDb) . '\';'; + $this->dbi->query($query_db_specific); + + // For table specific privileges + $query_table_specific = 'UPDATE ' . Util::backquote('tables_priv') + . 'SET Db = \'' . $this->dbi->escapeString($newname) + . '\' where Db = \'' . $this->dbi->escapeString($oldDb) . '\';'; + $this->dbi->query($query_table_specific); + + // For column specific privileges + $query_col_specific = 'UPDATE ' . Util::backquote('columns_priv') + . 'SET Db = \'' . $this->dbi->escapeString($newname) + . '\' where Db = \'' . $this->dbi->escapeString($oldDb) . '\';'; + $this->dbi->query($query_col_specific); + + // For procedures specific privileges + $query_proc_specific = 'UPDATE ' . Util::backquote('procs_priv') + . 'SET Db = \'' . $this->dbi->escapeString($newname) + . '\' where Db = \'' . $this->dbi->escapeString($oldDb) . '\';'; + $this->dbi->query($query_proc_specific); + + // Finally FLUSH the new privileges + $flush_query = "FLUSH PRIVILEGES;"; + $this->dbi->query($flush_query); + } + } + + /** + * Adjust the privileges after Copying the db + * + * @param string $oldDb Database name before copying + * @param string $newname New Database name requested + * + * @return void + */ + public function adjustPrivilegesCopyDb($oldDb, $newname) + { + if ($GLOBALS['db_priv'] && $GLOBALS['table_priv'] + && $GLOBALS['col_priv'] && $GLOBALS['proc_priv'] + && $GLOBALS['is_reload_priv'] + ) { + $this->dbi->selectDb('mysql'); + $newname = str_replace("_", "\_", $newname); + $oldDb = str_replace("_", "\_", $oldDb); + + $query_db_specific_old = 'SELECT * FROM ' + . Util::backquote('db') . ' WHERE ' + . 'Db = "' . $oldDb . '";'; + + $old_privs_db = $this->dbi->fetchResult($query_db_specific_old, 0); + + foreach ($old_privs_db as $old_priv) { + $newDb_db_privs_query = 'INSERT INTO ' . Util::backquote('db') + . ' VALUES("' . $old_priv[0] . '", "' . $newname . '"'; + for ($i = 2; $i < count($old_priv); $i++) { + $newDb_db_privs_query .= ', "' . $old_priv[$i] . '"'; + } + $newDb_db_privs_query .= ')'; + + $this->dbi->query($newDb_db_privs_query); + } + + // For Table Specific privileges + $query_table_specific_old = 'SELECT * FROM ' + . Util::backquote('tables_priv') . ' WHERE ' + . 'Db = "' . $oldDb . '";'; + + $old_privs_table = $this->dbi->fetchResult( + $query_table_specific_old, + 0 + ); + + foreach ($old_privs_table as $old_priv) { + $newDb_table_privs_query = 'INSERT INTO ' . Util::backquote( + 'tables_priv' + ) . ' VALUES("' . $old_priv[0] . '", "' . $newname . '", "' + . $old_priv[2] . '", "' . $old_priv[3] . '", "' . $old_priv[4] + . '", "' . $old_priv[5] . '", "' . $old_priv[6] . '", "' + . $old_priv[7] . '");'; + + $this->dbi->query($newDb_table_privs_query); + } + + // For Column Specific privileges + $query_col_specific_old = 'SELECT * FROM ' + . Util::backquote('columns_priv') . ' WHERE ' + . 'Db = "' . $oldDb . '";'; + + $old_privs_col = $this->dbi->fetchResult( + $query_col_specific_old, + 0 + ); + + foreach ($old_privs_col as $old_priv) { + $newDb_col_privs_query = 'INSERT INTO ' . Util::backquote( + 'columns_priv' + ) . ' VALUES("' . $old_priv[0] . '", "' . $newname . '", "' + . $old_priv[2] . '", "' . $old_priv[3] . '", "' . $old_priv[4] + . '", "' . $old_priv[5] . '", "' . $old_priv[6] . '");'; + + $this->dbi->query($newDb_col_privs_query); + } + + // For Procedure Specific privileges + $query_proc_specific_old = 'SELECT * FROM ' + . Util::backquote('procs_priv') . ' WHERE ' + . 'Db = "' . $oldDb . '";'; + + $old_privs_proc = $this->dbi->fetchResult( + $query_proc_specific_old, + 0 + ); + + foreach ($old_privs_proc as $old_priv) { + $newDb_proc_privs_query = 'INSERT INTO ' . Util::backquote( + 'procs_priv' + ) . ' VALUES("' . $old_priv[0] . '", "' . $newname . '", "' + . $old_priv[2] . '", "' . $old_priv[3] . '", "' . $old_priv[4] + . '", "' . $old_priv[5] . '", "' . $old_priv[6] . '", "' + . $old_priv[7] . '");'; + + $this->dbi->query($newDb_proc_privs_query); + } + + // Finally FLUSH the new privileges + $flush_query = "FLUSH PRIVILEGES;"; + $this->dbi->query($flush_query); + } + } + + /** + * Create all accumulated constraints + * + * @param array $sqlConstratints array of sql constraints for the database + * + * @return void + */ + public function createAllAccumulatedConstraints(array $sqlConstratints) + { + $this->dbi->selectDb($_POST['newname']); + foreach ($sqlConstratints as $one_query) { + $this->dbi->query($one_query); + // and prepare to display them + $GLOBALS['sql_query'] .= "\n" . $one_query; + } + } + + /** + * Duplicate the bookmarks for the db (done once for each db) + * + * @param boolean $_error whether table rename/copy or not + * @param string $db database name + * + * @return void + */ + public function duplicateBookmarks($_error, $db) + { + if (! $_error && $db != $_POST['newname']) { + $get_fields = [ + 'user', + 'label', + 'query', + ]; + $where_fields = ['dbase' => $db]; + $new_fields = ['dbase' => $_POST['newname']]; + Table::duplicateInfo( + 'bookmarkwork', + 'bookmark', + $get_fields, + $where_fields, + $new_fields + ); + } + } + + /** + * Get the HTML snippet for order the table + * + * @param array $columns columns array + * + * @return string + */ + public function getHtmlForOrderTheTable(array $columns) + { + $html_output = '
'; + $html_output .= '
'; + $html_output .= Url::getHiddenInputs( + $GLOBALS['db'], + $GLOBALS['table'] + ); + $html_output .= '
' + . '' . __('Alter table order by') . '' + . ' ' . __('(singly)') . ' ' + . '
' + . '' + . '' + . '' + . '' + . '
' + . '
' + . '' + . '' + . '
' + . '
' + . '
'; + + return $html_output; + } + + /** + * Get the HTML snippet for move table + * + * @return string + */ + public function getHtmlForMoveTable() + { + $html_output = '
'; + $html_output .= '
' + . Url::getHiddenInputs($GLOBALS['db'], $GLOBALS['table']); + + $html_output .= '' + . '' + . '
'; + + $html_output .= '' . __('Move table to (database.table)') + . ''; + + if (count($GLOBALS['dblist']->databases) > $GLOBALS['cfg']['MaxDbList']) { + $html_output .= ''; + } else { + $html_output .= ''; + } + $html_output .= ' . '; + $html_output .= '
'; + + // starting with MySQL 5.0.24, SHOW CREATE TABLE includes the AUTO_INCREMENT + // next value but users can decide if they want it or not for the operation + + $html_output .= '' + . '
'; + + if ($GLOBALS['table_priv'] && $GLOBALS['col_priv'] + && $GLOBALS['is_reload_priv'] + ) { + $html_output .= ''; + } else { + $html_output .= ''; + } + $html_output .= '
'; + + $html_output .= '
' + . '' + . '
' + . '
' + . '
'; + + return $html_output; + } + + /** + * Get the HTML div for Table option + * + * @param Table $pma_table Table object + * @param string $comment Comment + * @param string $tbl_collation table collation + * @param string $tbl_storage_engine table storage engine + * @param string $pack_keys pack keys + * @param string $auto_increment value of auto increment + * @param string $delay_key_write delay key write + * @param string $transactional value of transactional + * @param string $page_checksum value of page checksum + * @param string $checksum the checksum + * + * @return string + */ + public function getTableOptionDiv( + $pma_table, + $comment, + $tbl_collation, + $tbl_storage_engine, + $pack_keys, + $auto_increment, + $delay_key_write, + $transactional, + $page_checksum, + $checksum + ) { + $html_output = '
'; + $html_output .= '
getTableOptionFieldset( + $pma_table, + $comment, + $tbl_collation, + $tbl_storage_engine, + $pack_keys, + $delay_key_write, + $auto_increment, + $transactional, + $page_checksum, + $checksum + ); + + $html_output .= '
' + . '' + . '' + . '
' + . '
' + . '
'; + + return $html_output; + } + + /** + * Get HTML for the rename table part of table options + * + * @return string + */ + private function getHtmlForRenameTable() + { + $html_output = '' . __('Rename table to') . '' + . '' + . '' + . '' + . ''; + + if ($GLOBALS['table_priv'] && $GLOBALS['col_priv'] + && $GLOBALS['is_reload_priv'] + ) { + $html_output .= ''; + } else { + $html_output .= ''; + } + $html_output .= ''; + + $html_output .= ''; + return $html_output; + } + + /** + * Get HTML for the table comments part of table options + * + * @param string $current_value of the table comments + * + * @return string + */ + private function getHtmlForTableComments($current_value) + { + $commentLength = $this->dbi->getVersion() >= 50503 ? 2048 : 60; + return '' . __('Table comments') . '' + . '' + . '' + . '' + . ''; + } + + /** + * Get HTML for the PACK KEYS part of table options + * + * @param string $current_value of the pack keys option + * + * @return string + */ + private function getHtmlForPackKeys($current_value) + { + $html_output = '' + . '' + . '' . "\n"; + $html_output .= '' . "\n"; + + $charsets = Charsets::getCharsets($this->dbi, $GLOBALS['cfg']['Server']['DisableIS']); + $collations = Charsets::getCollations($this->dbi, $GLOBALS['cfg']['Server']['DisableIS']); + /** @var Charset $charset */ + foreach ($charsets as $charset) { + $html_output .= '' . "\n"; + /** @var Collation $collation */ + foreach ($collations[$charset->getName()] as $collation) { + $html_output .= '' . "\n"; + } + $html_output .= '' . "\n"; + } + $html_output .= '' . "\n"; + $html_output .= '' + . ''; + + // Change all Column collations + $html_output .= '' + . '' + . '' + . ''; + + if ($pma_table->isEngine(['MYISAM', 'ARIA', 'ISAM'])) { + $html_output .= $this->getHtmlForPackKeys($pack_keys); + } // end if (MYISAM|ISAM) + + if ($pma_table->isEngine(['MYISAM', 'ARIA'])) { + $html_output .= $this->getHtmlForTableRow( + 'new_checksum', + 'CHECKSUM', + $checksum + ); + + $html_output .= $this->getHtmlForTableRow( + 'new_delay_key_write', + 'DELAY_KEY_WRITE', + $delay_key_write + ); + } // end if (MYISAM) + + if ($pma_table->isEngine('ARIA')) { + $html_output .= $this->getHtmlForTableRow( + 'new_transactional', + 'TRANSACTIONAL', + $transactional + ); + + $html_output .= $this->getHtmlForTableRow( + 'new_page_checksum', + 'PAGE_CHECKSUM', + $page_checksum + ); + } // end if (ARIA) + + if (strlen($auto_increment) > 0 + && $pma_table->isEngine(['MYISAM', 'ARIA', 'INNODB', 'PBXT', 'ROCKSDB']) + ) { + $html_output .= '' + . '' + . '' + . ' '; + } // end if (MYISAM|INNODB) + + $possible_row_formats = $this->getPossibleRowFormat(); + + // for MYISAM there is also COMPRESSED but it can be set only by the + // myisampack utility, so don't offer here the choice because if we + // try it inside an ALTER TABLE, MySQL (at least in 5.1.23-maria) + // does not return a warning + // (if the table was compressed, it can be seen on the Structure page) + + if (isset($possible_row_formats[$tbl_storage_engine])) { + $current_row_format + = mb_strtoupper($GLOBALS['showtable']['Row_format']); + $html_output .= '' + . '' + . ''; + $html_output .= Util::getDropdown( + 'new_row_format', + $possible_row_formats[$tbl_storage_engine], + $current_row_format, + 'new_row_format' + ); + $html_output .= ''; + } + $html_output .= '' + . ''; + + return $html_output; + } + + /** + * Get the common HTML table row (tr) for new_checksum, new_delay_key_write, + * new_transactional and new_page_checksum + * + * @param string $attribute class, name and id attribute + * @param string $label label value + * @param string $val checksum, delay_key_write, transactional, page_checksum + * + * @return string + */ + private function getHtmlForTableRow($attribute, $label, $val) + { + return '' + . '' + . '' + . '' + . '' + . '' + . '' + . ''; + } + + /** + * Get array of possible row formats + * + * @return array + */ + private function getPossibleRowFormat() + { + // the outer array is for engines, the inner array contains the dropdown + // option values as keys then the dropdown option labels + + $possible_row_formats = [ + 'ARCHIVE' => [ + 'COMPRESSED' => 'COMPRESSED', + ], + 'ARIA' => [ + 'FIXED' => 'FIXED', + 'DYNAMIC' => 'DYNAMIC', + 'PAGE' => 'PAGE', + ], + 'MARIA' => [ + 'FIXED' => 'FIXED', + 'DYNAMIC' => 'DYNAMIC', + 'PAGE' => 'PAGE', + ], + 'MYISAM' => [ + 'FIXED' => 'FIXED', + 'DYNAMIC' => 'DYNAMIC', + ], + 'PBXT' => [ + 'FIXED' => 'FIXED', + 'DYNAMIC' => 'DYNAMIC', + ], + 'INNODB' => [ + 'COMPACT' => 'COMPACT', + 'REDUNDANT' => 'REDUNDANT', + ], + ]; + + /** @var Innodb $innodbEnginePlugin */ + $innodbEnginePlugin = StorageEngine::getEngine('Innodb'); + $innodbPluginVersion = $innodbEnginePlugin->getInnodbPluginVersion(); + if (! empty($innodbPluginVersion)) { + $innodb_file_format = $innodbEnginePlugin->getInnodbFileFormat(); + } else { + $innodb_file_format = ''; + } + /** + * Newer MySQL/MariaDB always return empty a.k.a '' on $innodb_file_format otherwise + * old versions of MySQL/MariaDB must be returning something or not empty. + * This patch is to support newer MySQL/MariaDB while also for backward compatibilities. + */ + if (( ('Barracuda' == $innodb_file_format) || ($innodb_file_format == '') ) + && $innodbEnginePlugin->supportsFilePerTable() + ) { + $possible_row_formats['INNODB']['DYNAMIC'] = 'DYNAMIC'; + $possible_row_formats['INNODB']['COMPRESSED'] = 'COMPRESSED'; + } + + return $possible_row_formats; + } + + /** + * Get HTML div for copy table + * + * @return string + */ + public function getHtmlForCopytable() + { + $html_output = '
'; + $html_output .= '
' + . Url::getHiddenInputs($GLOBALS['db'], $GLOBALS['table']) + . ''; + + $html_output .= '
'; + $html_output .= '' + . __('Copy table to (database.table)') . ''; + + if (count($GLOBALS['dblist']->databases) > $GLOBALS['cfg']['MaxDbList']) { + $html_output .= ''; + } else { + $html_output .= ''; + } + $html_output .= ' . '; + $html_output .= '
'; + + $choices = [ + 'structure' => __('Structure only'), + 'data' => __('Structure and data'), + 'dataonly' => __('Data only'), + ]; + + $html_output .= Util::getRadioFields( + 'what', + $choices, + 'data', + true + ); + $html_output .= '
'; + + $html_output .= '' + . '
' + . '' + . '
'; + + // display "Add constraints" choice only if there are + // foreign keys + if ($this->relation->getForeigners($GLOBALS['db'], $GLOBALS['table'], '', 'foreign')) { + $html_output .= ''; + $html_output .= '
'; + } // endif + + $html_output .= '
'; + + if ($GLOBALS['table_priv'] && $GLOBALS['col_priv'] + && $GLOBALS['is_reload_priv'] + ) { + $html_output .= ''; + } else { + $html_output .= ''; + } + $html_output .= '
'; + + $pma_switch_to_new = isset($_SESSION['pma_switch_to_new']) && $_SESSION['pma_switch_to_new']; + + $html_output .= ''; + $html_output .= '' + . '
'; + + $html_output .= '
' + . '' + . '
' + . '
' + . '
'; + + return $html_output; + } + + /** + * Get HTML snippet for table maintenance + * + * @param Table $pma_table Table object + * @param array $url_params array of URL parameters + * + * @return string + */ + public function getHtmlForTableMaintenance($pma_table, array $url_params) + { + $html_output = '
'; + $html_output .= '
' + . '' . __('Table maintenance') . ''; + $html_output .= '
    '; + + // Note: BERKELEY (BDB) is no longer supported, starting with MySQL 5.1 + $html_output .= $this->getListofMaintainActionLink($pma_table, $url_params); + + $html_output .= '
' + . '
' + . '
'; + + return $html_output; + } + + /** + * Get HTML 'li' having a link of maintain action + * + * @param Table $pma_table Table object + * @param array $url_params Array of URL parameters + * + * @return string + */ + private function getListofMaintainActionLink($pma_table, array $url_params) + { + $html_output = ''; + + // analyze table + if ($pma_table->isEngine(['MYISAM', 'ARIA', 'INNODB', 'BERKELEYDB', 'TOKUDB'])) { + $params = [ + 'sql_query' => 'ANALYZE TABLE ' + . Util::backquote($GLOBALS['table']), + 'table_maintenance' => 'Go', + ]; + $html_output .= $this->getMaintainActionlink( + __('Analyze table'), + $params, + $url_params, + 'ANALYZE_TABLE' + ); + } + + // check table + if ($pma_table->isEngine(['MYISAM', 'ARIA', 'INNODB', 'TOKUDB'])) { + $params = [ + 'sql_query' => 'CHECK TABLE ' + . Util::backquote($GLOBALS['table']), + 'table_maintenance' => 'Go', + ]; + $html_output .= $this->getMaintainActionlink( + __('Check table'), + $params, + $url_params, + 'CHECK_TABLE' + ); + } + + // checksum table + $params = [ + 'sql_query' => 'CHECKSUM TABLE ' + . Util::backquote($GLOBALS['table']), + 'table_maintenance' => 'Go', + ]; + $html_output .= $this->getMaintainActionlink( + __('Checksum table'), + $params, + $url_params, + 'CHECKSUM_TABLE' + ); + + // defragment table + if ($pma_table->isEngine(['INNODB'])) { + $params = [ + 'sql_query' => 'ALTER TABLE ' + . Util::backquote($GLOBALS['table']) + . ' ENGINE = InnoDB;', + ]; + $html_output .= $this->getMaintainActionlink( + __('Defragment table'), + $params, + $url_params, + 'InnoDB_File_Defragmenting' + ); + } + + // flush table + $params = [ + 'sql_query' => 'FLUSH TABLE ' + . Util::backquote($GLOBALS['table']), + 'message_to_show' => sprintf( + __('Table %s has been flushed.'), + htmlspecialchars($GLOBALS['table']) + ), + 'reload' => 1, + ]; + $html_output .= $this->getMaintainActionlink( + __('Flush the table (FLUSH)'), + $params, + $url_params, + 'FLUSH' + ); + + // optimize table + if ($pma_table->isEngine(['MYISAM', 'ARIA', 'INNODB', 'BERKELEYDB', 'TOKUDB'])) { + $params = [ + 'sql_query' => 'OPTIMIZE TABLE ' + . Util::backquote($GLOBALS['table']), + 'table_maintenance' => 'Go', + ]; + $html_output .= $this->getMaintainActionlink( + __('Optimize table'), + $params, + $url_params, + 'OPTIMIZE_TABLE' + ); + } + + // repair table + if ($pma_table->isEngine(['MYISAM', 'ARIA'])) { + $params = [ + 'sql_query' => 'REPAIR TABLE ' + . Util::backquote($GLOBALS['table']), + 'table_maintenance' => 'Go', + ]; + $html_output .= $this->getMaintainActionlink( + __('Repair table'), + $params, + $url_params, + 'REPAIR_TABLE' + ); + } + + return $html_output; + } + + /** + * Get maintain action HTML link + * + * @param string $action_message action message + * @param array $params url parameters array + * @param array $url_params additional url parameters + * @param string $link contains name of page/anchor that is being linked + * + * @return string + */ + private function getMaintainActionlink($action_message, array $params, array $url_params, $link) + { + return '
  • ' + . Util::linkOrButton( + 'sql.php' . Url::getCommon(array_merge($url_params, $params)), + $action_message, + ['class' => 'maintain_action ajax'] + ) + . Util::showMySQLDocu($link) + . '
  • '; + } + + /** + * Get HTML for Delete data or table (truncate table, drop table) + * + * @param array $truncate_table_url_params url parameter array for truncate table + * @param array $dropTableUrlParams url parameter array for drop table + * + * @return string + */ + public function getHtmlForDeleteDataOrTable( + array $truncate_table_url_params, + array $dropTableUrlParams + ) { + $html_output = '
    ' + . '
    ' + . '' . __('Delete data or table') . ''; + + $html_output .= '
      '; + + if (! empty($truncate_table_url_params)) { + $html_output .= $this->getDeleteDataOrTablelink( + $truncate_table_url_params, + 'TRUNCATE_TABLE', + __('Empty the table (TRUNCATE)'), + 'truncate_tbl_anchor' + ); + } + if (! empty($dropTableUrlParams)) { + $html_output .= $this->getDeleteDataOrTablelink( + $dropTableUrlParams, + 'DROP_TABLE', + __('Delete the table (DROP)'), + 'drop_tbl_anchor' + ); + } + $html_output .= '
    '; + + return $html_output; + } + + /** + * Get the HTML link for Truncate table, Drop table and Drop db + * + * @param array $url_params url parameter array for delete data or table + * @param string $syntax TRUNCATE_TABLE or DROP_TABLE or DROP_DATABASE + * @param string $link link to be shown + * @param string $htmlId id of the link + * + * @return string html output + */ + public function getDeleteDataOrTablelink(array $url_params, $syntax, $link, $htmlId) + { + return '
  • ' . Util::linkOrButton( + 'sql.php' . Url::getCommon($url_params), + $link, + [ + 'id' => $htmlId, + 'class' => 'ajax', + ] + ) + . Util::showMySQLDocu($syntax) + . '
  • '; + } + + /** + * Get HTML snippet for partition maintenance + * + * @param array $partition_names array of partition names for a specific db/table + * @param array $url_params url parameters + * + * @return string + */ + public function getHtmlForPartitionMaintenance(array $partition_names, array $url_params) + { + $choices = [ + 'ANALYZE' => __('Analyze'), + 'CHECK' => __('Check'), + 'OPTIMIZE' => __('Optimize'), + 'REBUILD' => __('Rebuild'), + 'REPAIR' => __('Repair'), + 'TRUNCATE' => __('Truncate'), + ]; + + $partition_method = Partition::getPartitionMethod( + $GLOBALS['db'], + $GLOBALS['table'] + ); + // add COALESCE or DROP option to choices array depeding on Partition method + if ($partition_method == 'RANGE' + || $partition_method == 'RANGE COLUMNS' + || $partition_method == 'LIST' + || $partition_method == 'LIST COLUMNS' + ) { + $choices['DROP'] = __('Drop'); + } else { + $choices['COALESCE'] = __('Coalesce'); + } + + $html_output = '
    ' + . '
    ' + . Url::getHiddenInputs($GLOBALS['db'], $GLOBALS['table']) + . '
    ' + . '' + . __('Partition maintenance') + . Util::showMySQLDocu('partitioning_maintenance') + . ''; + + $html_select = '