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