1 /*
2   Copyright (c) 2015, 2021, Oracle and/or its affiliates.
3 
4   This program is free software; you can redistribute it and/or modify
5   it under the terms of the GNU General Public License, version 2.0,
6   as published by the Free Software Foundation.
7 
8   This program is also distributed with certain software (including
9   but not limited to OpenSSL) that is licensed under separate terms,
10   as designated in a particular file or component or in included license
11   documentation.  The authors of MySQL hereby grant you an additional
12   permission to link the program and your derivative works with the
13   separately licensed software that they have included with MySQL.
14 
15   This program is distributed in the hope that it will be useful,
16   but WITHOUT ANY WARRANTY; without even the implied warranty of
17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18   GNU General Public License, version 2.0, for more details.
19 
20   You should have received a copy of the GNU General Public License
21   along with this program; if not, write to the Free Software
22   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
23 */
24 
25 #include "program.h"
26 #include "i_connection_provider.h"
27 #include "thread_specific_connection_provider.h"
28 #include "single_transaction_connection_provider.h"
29 #include "simple_id_generator.h"
30 #include "i_progress_watcher.h"
31 #include "standard_progress_watcher.h"
32 #include "i_crawler.h"
33 #include "mysql_crawler.h"
34 #include "i_chain_maker.h"
35 #include "mysqldump_tool_chain_maker.h"
36 #include <boost/chrono.hpp>
37 
38 using namespace Mysql::Tools::Dump;
39 
close_redirected_stderr()40 void Program::close_redirected_stderr()
41 {
42   if (m_stderr != NULL)
43     fclose(m_stderr);
44 }
45 
error_log_file_callback(char *)46 void Program::error_log_file_callback(char*)
47 {
48   if (!m_error_log_file.has_value())
49     return;
50   this->close_redirected_stderr();
51   m_stderr= freopen(m_error_log_file.value().c_str(), "a", stderr);
52   if (m_stderr == NULL)
53   {
54     this->error(Mysql::Tools::Base::Message_data(errno,
55       "Cannot append error log to specified file: \""
56       + m_error_log_file.value() + "\"",
57       Mysql::Tools::Base::Message_type_error));
58   }
59 }
60 
message_handler(const Mysql::Tools::Base::Message_data & message)61 bool Program::message_handler(const Mysql::Tools::Base::Message_data& message)
62 {
63   this->error(message);
64   return false;
65 }
66 
error(const Mysql::Tools::Base::Message_data & message)67 void Program::error(const Mysql::Tools::Base::Message_data& message)
68 {
69   std::cerr << this->get_name() << ": [" << message.get_message_type_string()
70     << "] (" << message.get_code() << ") " << message.get_message()
71     << std::endl;
72 
73   if (message.get_message_type() == Mysql::Tools::Base::Message_type_error)
74   {
75     std::cerr << "Dump process encountered error and will not continue."
76       << std::endl;
77     m_error_code.store((int)message.get_code());
78   }
79 }
80 
create_options()81 void Program::create_options()
82 {
83   this->create_new_option(&m_error_log_file, "log-error-file",
84     "Append warnings and errors to specified file.")
85     ->add_callback(new Mysql::Instance_callback<void, char*, Program>(
86     this, &Program::error_log_file_callback));
87   this->create_new_option(&m_watch_progress, "watch-progress",
88     "Shows periodically dump process progress information on error output. "
89     "Progress information include both completed and total number of "
90     "tables, rows and other objects collected.")
91     ->set_value(true);
92   this->create_new_option(&m_single_transaction, "single-transaction",
93     "Creates a consistent snapshot by dumping all tables in a single "
94     "transaction. Works ONLY for tables stored in storage engines which "
95     "support multiversioning (currently only InnoDB does); the dump is NOT "
96     "guaranteed to be consistent for other storage engines. "
97     "While a --single-transaction dump is in process, to ensure a valid "
98     "dump file (correct table contents and binary log position), no other "
99     "connection should use the following statements: ALTER TABLE, DROP "
100     "TABLE, RENAME TABLE, TRUNCATE TABLE, as consistent snapshot is not "
101     "isolated from them. This option is mutually exclusive with "
102     "--add-locks option.");
103 }
104 
check_mutually_exclusive_options()105 void  Program::check_mutually_exclusive_options()
106 {
107   /*
108     In case of --add-locks we dont allow parallelism
109   */
110   if (m_mysqldump_tool_chain_maker_options->m_default_parallelism ||
111      m_mysqldump_tool_chain_maker_options->get_parallel_schemas_thread_count())
112   {
113     if (m_mysqldump_tool_chain_maker_options->m_formatter_options->m_add_locks)
114       m_mysql_chain_element_options->get_program()->error(
115         Mysql::Tools::Base::Message_data(1, "Usage of --add-locks "
116         "is mutually exclusive with parallelism.",
117         Mysql::Tools::Base::Message_type_error));
118   }
119 }
120 
get_total_connections()121 int Program::get_total_connections()
122 {
123   /*
124     total thread count for mysqlpump would be as below:
125      1 main thread +
126      default queues thread (specified by default parallelism) +
127      total parallel-schemas without threads specified * dp +
128      total threads mentioned in parallel-schemas
129   */
130 
131    int dp= m_mysqldump_tool_chain_maker_options->m_default_parallelism;
132   return (1 + dp +
133     m_mysqldump_tool_chain_maker_options->get_parallel_schemas_thread_count() +
134     (m_mysqldump_tool_chain_maker_options->
135      get_parallel_schemas_with_default_thread_count() * dp));
136 }
137 
get_error_code()138 int Program::get_error_code()
139 {
140   return m_error_code.load();
141 }
142 
execute(std::vector<std::string> positional_options)143 int Program::execute(std::vector<std::string> positional_options)
144 {
145   I_connection_provider* connection_provider= NULL;
146   int num_connections= get_total_connections();
147 
148   Mysql::I_callable<bool, const Mysql::Tools::Base::Message_data&>*
149     message_handler= new Mysql::Instance_callback
150     <bool, const Mysql::Tools::Base::Message_data&, Program>(
151     this, &Program::message_handler);
152 
153   try
154   {
155     connection_provider=
156       m_single_transaction ?
157       new Single_transaction_connection_provider(this, num_connections, message_handler)
158       : new Thread_specific_connection_provider(this);
159   }
160   catch (const std::exception &e)
161   {
162     this->error(Mysql::Tools::Base::Message_data(
163       0, "Error during creating connection.",
164       Mysql::Tools::Base::Message_type_error));
165   }
166 
167   Mysql::Tools::Base::Mysql_query_runner* runner= connection_provider
168          ->get_runner(message_handler);
169   ulong server_version =
170     mysql_get_server_version(runner->get_low_level_connection());
171   if (server_version < 50646)
172   {
173     std::cerr << "Server version is not compatible. Server version should "
174                  "be 5.6.46 or above.";
175     delete runner;
176     delete message_handler;
177     delete connection_provider;
178     return 0;
179   }
180   use_show_create_user = (server_version > 50705);
181   Simple_id_generator* id_generator= new Simple_id_generator();
182 
183   boost::chrono::high_resolution_clock::time_point start_time=
184     boost::chrono::high_resolution_clock::now();
185 
186   I_progress_watcher* progress_watcher= NULL;
187 
188   if (m_watch_progress)
189   {
190     progress_watcher= new Standard_progress_watcher(
191       message_handler, id_generator);
192   }
193   I_crawler* crawler= new Mysql_crawler(
194     connection_provider, message_handler, id_generator,
195     m_mysql_chain_element_options, m_mysqldump_tool_chain_maker_options, this);
196   m_mysqldump_tool_chain_maker_options->process_positional_options(
197     positional_options);
198   check_mutually_exclusive_options();
199   I_chain_maker* chain_maker= new Mysqldump_tool_chain_maker(
200     connection_provider, message_handler, id_generator,
201     m_mysqldump_tool_chain_maker_options, this);
202 
203   crawler->register_chain_maker(chain_maker);
204   if (progress_watcher != NULL)
205   {
206     crawler->register_progress_watcher(progress_watcher);
207     chain_maker->register_progress_watcher(progress_watcher);
208   }
209 
210   crawler->enumerate_objects();
211 
212   delete runner;
213   delete crawler;
214   if (progress_watcher != NULL)
215     delete progress_watcher;
216   delete id_generator;
217   delete connection_provider;
218   delete message_handler;
219   delete chain_maker;
220 
221   if (!get_error_code())
222   {
223     std::cerr << "Dump completed in " <<
224       boost::chrono::duration_cast<boost::chrono::milliseconds>(
225       boost::chrono::high_resolution_clock::now() - start_time) << std::endl;
226   }
227   return get_error_code();
228 }
229 
get_description()230 std::string Program::get_description()
231 {
232   return "MySQL utility for dumping data from databases to external file.";
233 }
234 
get_first_release_year()235 int Program::get_first_release_year()
236 {
237   return 2014;
238 }
239 
get_version()240 std::string Program::get_version()
241 {
242   return "1.0.0";
243 }
244 
~Program()245 Program::~Program()
246 {
247   delete m_mysql_chain_element_options;
248   delete m_mysqldump_tool_chain_maker_options;
249   this->close_redirected_stderr();
250 }
251 
short_usage()252 void Program::short_usage()
253 {
254   std::cout << "Usage: " << get_name() <<" [OPTIONS] [--all-databases]"
255             << std::endl;
256   std::cout << "OR     " << get_name() <<" [OPTIONS] --databases DB1 [DB2 DB3...]"
257             << std::endl;
258   std::cout << "OR     " << get_name() <<" [OPTIONS] database [tables]"
259             << std::endl;
260 }
261 
Program()262 Program::Program()
263   : Abstract_connection_program(),
264   m_stderr(NULL),
265   m_error_code(0)
266 {
267   m_mysql_chain_element_options= new Mysql_chain_element_options(this);
268   m_mysqldump_tool_chain_maker_options=
269     new Mysqldump_tool_chain_maker_options(m_mysql_chain_element_options);
270 
271   this->add_provider(m_mysql_chain_element_options);
272   this->add_provider(m_mysqldump_tool_chain_maker_options);
273 }
274 
275 const char *load_default_groups[]=
276 {
277   "client", /* Read settings how to connect to server. */
278   /*
279     Read special settings for mysql_dump.
280     This section will be deprecated.
281   */
282   "mysql_dump",
283   /* Read config options from mysqlpump section. */
284   "mysqlpump",
285   0
286 };
287 
288 static Program program;
289 
main(int argc,char ** argv)290 int main(int argc, char **argv)
291 {
292   ::program.run(argc, argv);
293   return 0;
294 }
295