/* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "program.h" #include "i_connection_provider.h" #include "thread_specific_connection_provider.h" #include "single_transaction_connection_provider.h" #include "simple_id_generator.h" #include "i_progress_watcher.h" #include "standard_progress_watcher.h" #include "i_crawler.h" #include "mysql_crawler.h" #include "i_chain_maker.h" #include "mysqldump_tool_chain_maker.h" #include using namespace Mysql::Tools::Dump; void Program::close_redirected_stderr() { if (m_stderr != NULL) fclose(m_stderr); } void Program::error_log_file_callback(char*) { if (!m_error_log_file.has_value()) return; this->close_redirected_stderr(); m_stderr= freopen(m_error_log_file.value().c_str(), "a", stderr); if (m_stderr == NULL) { this->error(Mysql::Tools::Base::Message_data(errno, "Cannot append error log to specified file: \"" + m_error_log_file.value() + "\"", Mysql::Tools::Base::Message_type_error)); } } bool Program::message_handler(const Mysql::Tools::Base::Message_data& message) { this->error(message); return false; } void Program::error(const Mysql::Tools::Base::Message_data& message) { std::cerr << this->get_name() << ": [" << message.get_message_type_string() << "] (" << message.get_code() << ") " << message.get_message() << std::endl; if (message.get_message_type() == Mysql::Tools::Base::Message_type_error) { std::cerr << "Dump process encountered error and will not continue." << std::endl; m_error_code.store((int)message.get_code()); } } void Program::create_options() { this->create_new_option(&m_error_log_file, "log-error-file", "Append warnings and errors to specified file.") ->add_callback(new Mysql::Instance_callback( this, &Program::error_log_file_callback)); this->create_new_option(&m_watch_progress, "watch-progress", "Shows periodically dump process progress information on error output. " "Progress information include both completed and total number of " "tables, rows and other objects collected.") ->set_value(true); this->create_new_option(&m_single_transaction, "single-transaction", "Creates a consistent snapshot by dumping all tables in a single " "transaction. Works ONLY for tables stored in storage engines which " "support multiversioning (currently only InnoDB does); the dump is NOT " "guaranteed to be consistent for other storage engines. " "While a --single-transaction dump is in process, to ensure a valid " "dump file (correct table contents and binary log position), no other " "connection should use the following statements: ALTER TABLE, DROP " "TABLE, RENAME TABLE, TRUNCATE TABLE, as consistent snapshot is not " "isolated from them. This option is mutually exclusive with " "--add-locks option."); } void Program::check_mutually_exclusive_options() { /* In case of --add-locks we dont allow parallelism */ if (m_mysqldump_tool_chain_maker_options->m_default_parallelism || m_mysqldump_tool_chain_maker_options->get_parallel_schemas_thread_count()) { if (m_mysqldump_tool_chain_maker_options->m_formatter_options->m_add_locks) m_mysql_chain_element_options->get_program()->error( Mysql::Tools::Base::Message_data(1, "Usage of --add-locks " "is mutually exclusive with parallelism.", Mysql::Tools::Base::Message_type_error)); } } int Program::get_total_connections() { /* total thread count for mysqlpump would be as below: 1 main thread + default queues thread (specified by default parallelism) + total parallel-schemas without threads specified * dp + total threads mentioned in parallel-schemas */ int dp= m_mysqldump_tool_chain_maker_options->m_default_parallelism; return (1 + dp + m_mysqldump_tool_chain_maker_options->get_parallel_schemas_thread_count() + (m_mysqldump_tool_chain_maker_options-> get_parallel_schemas_with_default_thread_count() * dp)); } int Program::get_error_code() { return m_error_code.load(); } int Program::execute(std::vector positional_options) { I_connection_provider* connection_provider= NULL; int num_connections= get_total_connections(); Mysql::I_callable* message_handler= new Mysql::Instance_callback ( this, &Program::message_handler); try { connection_provider= m_single_transaction ? new Single_transaction_connection_provider(this, num_connections, message_handler) : new Thread_specific_connection_provider(this); } catch (const std::exception &e) { this->error(Mysql::Tools::Base::Message_data( 0, "Error during creating connection.", Mysql::Tools::Base::Message_type_error)); } Mysql::Tools::Base::Mysql_query_runner* runner= connection_provider ->get_runner(message_handler); if (mysql_get_server_version(runner->get_low_level_connection()) < 50708) { std::cerr << "Server version is not compatible. Server version should " "be 5.7.8 or above."; delete runner; delete message_handler; delete connection_provider; return 0; } Simple_id_generator* id_generator= new Simple_id_generator(); boost::chrono::high_resolution_clock::time_point start_time= boost::chrono::high_resolution_clock::now(); I_progress_watcher* progress_watcher= NULL; if (m_watch_progress) { progress_watcher= new Standard_progress_watcher( message_handler, id_generator); } I_crawler* crawler= new Mysql_crawler( connection_provider, message_handler, id_generator, m_mysql_chain_element_options, this); m_mysqldump_tool_chain_maker_options->process_positional_options( positional_options); check_mutually_exclusive_options(); I_chain_maker* chain_maker= new Mysqldump_tool_chain_maker( connection_provider, message_handler, id_generator, m_mysqldump_tool_chain_maker_options, this); crawler->register_chain_maker(chain_maker); if (progress_watcher != NULL) { crawler->register_progress_watcher(progress_watcher); chain_maker->register_progress_watcher(progress_watcher); } crawler->enumerate_objects(); delete runner; delete crawler; if (progress_watcher != NULL) delete progress_watcher; delete id_generator; delete connection_provider; delete message_handler; delete chain_maker; if (!get_error_code()) { std::cerr << "Dump completed in " << boost::chrono::duration_cast( boost::chrono::high_resolution_clock::now() - start_time) << std::endl; } return get_error_code(); } std::string Program::get_description() { return "MySQL utility for dumping data from databases to external file."; } int Program::get_first_release_year() { return 2014; } std::string Program::get_version() { return "1.0.0"; } Program::~Program() { delete m_mysql_chain_element_options; delete m_mysqldump_tool_chain_maker_options; this->close_redirected_stderr(); } void Program::short_usage() { std::cout << "Usage: " << get_name() <<" [OPTIONS] [--all-databases]" << std::endl; std::cout << "OR " << get_name() <<" [OPTIONS] --databases DB1 [DB2 DB3...]" << std::endl; std::cout << "OR " << get_name() <<" [OPTIONS] database [tables]" << std::endl; } Program::Program() : Abstract_connection_program(), m_stderr(NULL), m_error_code(0) { m_mysql_chain_element_options= new Mysql_chain_element_options(this); m_mysqldump_tool_chain_maker_options= new Mysqldump_tool_chain_maker_options(m_mysql_chain_element_options); this->add_provider(m_mysql_chain_element_options); this->add_provider(m_mysqldump_tool_chain_maker_options); } const char *load_default_groups[]= { "client", /* Read settings how to connect to server. */ "mysql_dump", /* Read special settings for mysql_dump. */ 0 }; static Program program; int main(int argc, char **argv) { ::program.run(argc, argv); return 0; }