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 "mysql_crawler.h"
26 #include "mysql_function.h"
27 #include "stored_procedure.h"
28 #include "table_definition_dump_task.h"
29 #include "mysqldump_tool_chain_maker_options.h"
30 #include "table_rows_dump_task.h"
31 #include "table_deferred_indexes_dump_task.h"
32 #include "event_scheduler_event.h"
33 #include "privilege.h"
34 #include "trigger.h"
35 #include "view.h"
36 #include "base/mysql_query_runner.h"
37 #include <string>
38 #include <vector>
39 using std::string;
40 using std::vector;
41
42 using namespace Mysql::Tools::Dump;
43
Mysql_crawler(I_connection_provider * connection_provider,Mysql::I_callable<bool,const Mysql::Tools::Base::Message_data &> * message_handler,Simple_id_generator * object_id_generator,Mysql_chain_element_options * options,Mysqldump_tool_chain_maker_options * mysqldump_tool_cmaker_options,Mysql::Tools::Base::Abstract_program * program)44 Mysql_crawler::Mysql_crawler(I_connection_provider* connection_provider,
45 Mysql::I_callable<bool, const Mysql::Tools::Base::Message_data&>*
46 message_handler, Simple_id_generator* object_id_generator,
47 Mysql_chain_element_options* options,
48 Mysqldump_tool_chain_maker_options* mysqldump_tool_cmaker_options,
49 Mysql::Tools::Base::Abstract_program* program)
50 : Abstract_crawler(message_handler, object_id_generator, program),
51 Abstract_mysql_chain_element_extension(
52 connection_provider, message_handler, options),
53 m_mysqldump_tool_cmaker_options(mysqldump_tool_cmaker_options)
54 {}
55
enumerate_objects()56 void Mysql_crawler::enumerate_objects()
57 {
58 Mysql::Tools::Base::Mysql_query_runner* runner= this->get_runner();
59 std::vector<const Mysql::Tools::Base::Mysql_query_runner::Row*> gtid_mode;
60 std::string gtid_value("OFF");
61 /* Check if the server is GTID enabled */
62 runner->run_query_store("SELECT @@global.gtid_mode", >id_mode);
63 if (gtid_mode.size())
64 {
65 std::vector<const Mysql::Tools::Base::Mysql_query_runner::Row*>
66 ::iterator mode_it= gtid_mode.begin();
67 const Mysql::Tools::Base::Mysql_query_runner::Row& gtid_data= **mode_it;
68 gtid_value= gtid_data[0];
69 }
70 Mysql::Tools::Base::Mysql_query_runner::cleanup_result(>id_mode);
71
72 /* get the GTID_EXECUTED value */
73 std::vector<const Mysql::Tools::Base::Mysql_query_runner::Row*> gtid_executed;
74 runner->run_query_store("SELECT @@GLOBAL.GTID_EXECUTED", >id_executed);
75
76 std::string gtid_output_val;
77 if (gtid_executed.size())
78 {
79 std::vector<const Mysql::Tools::Base::Mysql_query_runner::Row*>
80 ::iterator gtid_executed_iter= gtid_executed.begin();
81 const Mysql::Tools::Base::Mysql_query_runner::Row& gtid_executed_val=
82 **gtid_executed_iter;
83 gtid_output_val= gtid_executed_val[0];
84 }
85 Mysql::Tools::Base::Mysql_query_runner::cleanup_result(>id_executed);
86
87 m_dump_start_task= new Dump_start_dump_task(gtid_value, gtid_output_val);
88 m_dump_end_task= new Dump_end_dump_task();
89 m_tables_definition_ready_dump_task=
90 new Tables_definition_ready_dump_task();
91
92 m_dump_end_task->add_dependency(m_dump_start_task);
93 this->process_dump_task(m_dump_start_task);
94
95 std::vector<const Mysql::Tools::Base::Mysql_query_runner::Row*> databases;
96 runner->run_query_store("SHOW DATABASES", &databases);
97
98 std::vector<Database* > db_list;
99 std::vector<Database_end_dump_task* > db_end_task_list;
100 for (std::vector<const Mysql::Tools::Base::Mysql_query_runner::Row*>::iterator
101 it= databases.begin(); it != databases.end(); ++it)
102 {
103 std::string db_name= (**it)[0];
104
105 Database* database= new Database(
106 this->generate_new_object_id(), db_name,
107 this->get_create_statement(runner, "", db_name,
108 "DATABASE IF NOT EXISTS").value());
109
110 m_current_database_start_dump_task=
111 new Database_start_dump_task(database);
112
113 Abstract_data_object* db_object = dynamic_cast<Abstract_data_object*>(
114 m_current_database_start_dump_task->get_related_db_object());
115
116 /*
117 This check is introduced so that only the database objects that are
118 included in the dump are validated.
119 */
120 if (!m_mysqldump_tool_cmaker_options->is_object_included_in_dump(db_object))
121 {
122 delete m_current_database_start_dump_task;
123 delete database;
124 continue;
125 }
126 db_list.push_back(database);
127 m_current_database_end_dump_task=
128 new Database_end_dump_task(database);
129 db_end_task_list.push_back(m_current_database_end_dump_task);
130
131 m_current_database_start_dump_task->add_dependency(m_dump_start_task);
132 m_dump_end_task->add_dependency(m_current_database_start_dump_task);
133 m_dump_end_task->add_dependency(m_current_database_end_dump_task);
134
135 this->process_dump_task(m_current_database_start_dump_task);
136 this->enumerate_database_objects(*database);
137 m_current_database_start_dump_task= NULL;
138 }
139
140 m_dump_end_task->add_dependency(m_tables_definition_ready_dump_task);
141 this->process_dump_task(m_tables_definition_ready_dump_task);
142
143 /* SHOW CREATE USER is introduced in 5.7.6 */
144 if (use_show_create_user)
145 this->enumerate_users();
146
147 std::vector<Database* >::iterator it;
148 std::vector<Database_end_dump_task* >::iterator it_end;
149 for (it= db_list.begin(),it_end= db_end_task_list.begin();
150 ((it != db_list.end()) && (it_end != db_end_task_list.end()));
151 ++it, ++it_end)
152 {
153 m_current_database_end_dump_task= *it_end;
154 this->enumerate_views(**it);
155 this->process_dump_task(m_current_database_end_dump_task);
156 m_current_database_end_dump_task= NULL;
157 }
158
159 Mysql::Tools::Base::Mysql_query_runner::cleanup_result(&databases);
160
161 this->process_dump_task(m_dump_end_task);
162
163 this->report_crawler_completed(this);
164
165 this->wait_for_tasks_completion();
166 delete runner;
167 }
168
enumerate_database_objects(const Database & db)169 void Mysql_crawler::enumerate_database_objects(const Database& db)
170 {
171 this->enumerate_tables(db);
172 this->enumerate_functions<Mysql_function>(db, "FUNCTION");
173 this->enumerate_functions<Stored_procedure>(db, "PROCEDURE");
174 this->enumerate_event_scheduler_events(db);
175 }
176
enumerate_tables(const Database & db)177 void Mysql_crawler::enumerate_tables(const Database& db)
178 {
179 Mysql::Tools::Base::Mysql_query_runner* runner= this->get_runner();
180 std::vector<const Mysql::Tools::Base::Mysql_query_runner::Row*> tables;
181
182 runner->run_query_store("SHOW TABLE STATUS FROM "
183 + this->quote_name(db.get_name()), &tables);
184
185 for (std::vector<const Mysql::Tools::Base::Mysql_query_runner::Row*>::iterator
186 it= tables.begin(); it != tables.end(); ++it)
187 {
188 const Mysql::Tools::Base::Mysql_query_runner::Row& table_data= **it;
189
190 std::string table_name= table_data[0]; // "Name"
191 std::vector<const Mysql::Tools::Base::Mysql_query_runner::Row*> fields_data;
192 runner->run_query_store("SHOW COLUMNS IN " + this->quote_name(table_name)
193 + " FROM " + this->quote_name(db.get_name()), &fields_data);
194 std::vector<Field> fields;
195 for (std::vector<const Mysql::Tools::Base::Mysql_query_runner::Row*>
196 ::iterator field_it=
197 fields_data.begin(); field_it != fields_data.end(); ++field_it)
198 {
199 fields.push_back(Field((**field_it)[0], (**field_it)[1]));
200 }
201 Mysql::Tools::Base::Mysql_query_runner::cleanup_result(&fields_data);
202 /*
203 For views create a dummy view so that dependent objects are
204 resolved when actually dumping views.
205 */
206
207 if (table_data.is_value_null(1))
208 {
209 std::string fake_view_ddl = "CREATE VIEW "
210 + this->get_quoted_object_full_name(db.get_name(), table_name)
211 + " AS SELECT\n";
212
213 for (std::vector<Field>::iterator field_iterator= fields.begin();
214 field_iterator != fields.end();
215 ++field_iterator)
216 {
217 fake_view_ddl+= " 1 AS " + this->quote_name(field_iterator->get_name());
218 if (field_iterator + 1 != fields.end())
219 fake_view_ddl += ",\n";
220 }
221
222 View* fake_view= new View(this->generate_new_object_id(),
223 table_name,
224 db.get_name(),
225 fake_view_ddl);
226
227 fake_view->add_dependency(m_current_database_start_dump_task);
228 m_current_database_end_dump_task->add_dependency(fake_view);
229 m_tables_definition_ready_dump_task->add_dependency(fake_view);
230 this->process_dump_task(fake_view);
231 continue;
232 }
233
234 uint64 rows= table_data.is_value_null(4)
235 ? 0 : atoll(table_data[4].c_str()); // "Rows"
236 bool isInnoDB= table_data[1] == "InnoDB"; // "Engine"
237 Table* table= new Table(this->generate_new_object_id(),
238 table_name,
239 db.get_name(),
240 this->get_create_statement(
241 runner, db.get_name(), table_name, "TABLE")
242 .value(),
243 fields, table_data[1], rows,
244 (uint64)(rows * (isInnoDB ? 1.5 : 1)),
245 atoll(table_data[6].c_str()) // "Data_length"
246 );
247
248 Table_definition_dump_task* ddl_task=
249 new Table_definition_dump_task(table);
250 Table_rows_dump_task* rows_task= new Table_rows_dump_task(table);
251 Table_deferred_indexes_dump_task* indexes_task=
252 new Table_deferred_indexes_dump_task(table);
253
254 ddl_task->add_dependency(m_current_database_start_dump_task);
255 rows_task->add_dependency(ddl_task);
256 indexes_task->add_dependency(rows_task);
257 m_current_database_end_dump_task->add_dependency(indexes_task);
258 m_tables_definition_ready_dump_task->add_dependency(ddl_task);
259
260 this->process_dump_task(ddl_task);
261 this->process_dump_task(rows_task);
262
263 this->enumerate_table_triggers(*table, rows_task);
264
265 this->process_dump_task(indexes_task);
266 }
267 Mysql::Tools::Base::Mysql_query_runner::cleanup_result(&tables);
268 delete runner;
269 }
270
enumerate_views(const Database & db)271 void Mysql_crawler::enumerate_views(const Database& db)
272 {
273 Mysql::Tools::Base::Mysql_query_runner* runner= this->get_runner();
274 std::vector<const Mysql::Tools::Base::Mysql_query_runner::Row*> tables;
275
276 runner->run_query_store("SHOW TABLES FROM "
277 + this->quote_name(db.get_name()), &tables);
278
279 for (std::vector<const Mysql::Tools::Base::Mysql_query_runner::Row*>::iterator
280 it= tables.begin(); it != tables.end(); ++it)
281 {
282 const Mysql::Tools::Base::Mysql_query_runner::Row& table_data= **it;
283
284 std::string table_name= table_data[0]; // "View Name"
285 std::vector<const Mysql::Tools::Base::Mysql_query_runner::Row*> check_view;
286 runner->run_query_store("SELECT COUNT(*) FROM "
287 + this->get_quoted_object_full_name("INFORMATION_SCHEMA", "VIEWS")
288 + " WHERE TABLE_NAME= '" + runner->escape_string(table_name)
289 + "' AND TABLE_SCHEMA= '" + runner->escape_string(db.get_name()) + "'",
290 &check_view);
291
292 for (std::vector<const Mysql::Tools::Base::Mysql_query_runner::Row*>::iterator
293 view_it= check_view.begin(); view_it != check_view.end(); ++view_it)
294 {
295 const Mysql::Tools::Base::Mysql_query_runner::Row& is_view= **view_it;
296 if (is_view[0] == "1")
297 {
298 /* Check if view dependent objects exists */
299 if (runner->run_query(std::string("LOCK TABLES ")
300 + this->get_quoted_object_full_name(db.get_name(), table_name)
301 + " READ") != 0)
302 {
303 Mysql::Tools::Base::Mysql_query_runner::cleanup_result(&check_view);
304 Mysql::Tools::Base::Mysql_query_runner::cleanup_result(&tables);
305 delete runner;
306 return;
307 }
308 else
309 runner->run_query(std::string("UNLOCK TABLES"));
310
311 View* view= new View(this->generate_new_object_id(),
312 table_name,
313 db.get_name(),
314 this->get_create_statement(runner,
315 db.get_name(),
316 table_name,
317 "TABLE").value()
318 );
319 m_current_database_end_dump_task->add_dependency(view);
320 view->add_dependency(m_tables_definition_ready_dump_task);
321 this->process_dump_task(view);
322 }
323 }
324 Mysql::Tools::Base::Mysql_query_runner::cleanup_result(&check_view);
325 }
326 Mysql::Tools::Base::Mysql_query_runner::cleanup_result(&tables);
327 delete runner;
328 }
329
330 template<typename TObject>
enumerate_functions(const Database & db,std::string type)331 void Mysql_crawler::enumerate_functions(const Database& db, std::string type)
332 {
333 Mysql::Tools::Base::Mysql_query_runner* runner= this->get_runner();
334
335 std::vector<const Mysql::Tools::Base::Mysql_query_runner::Row*> functions;
336 runner->run_query_store("SHOW " + type + " STATUS WHERE db = '"
337 + runner->escape_string(db.get_name()) + '\'', &functions);
338
339 for (std::vector<const Mysql::Tools::Base::Mysql_query_runner::Row*>::iterator
340 it= functions.begin(); it != functions.end(); ++it)
341 {
342 const Mysql::Tools::Base::Mysql_query_runner::Row& function_row= **it;
343
344 TObject* function= new TObject(this->generate_new_object_id(),
345 function_row[1], db.get_name(),
346 "DELIMITER //\n" + this->get_create_statement(
347 runner, db.get_name(), function_row[1], type, 2).value()
348 + "//\n" + "DELIMITER ;\n");
349
350 function->add_dependency(m_current_database_start_dump_task);
351 m_current_database_end_dump_task->add_dependency(function);
352
353 this->process_dump_task(function);
354 }
355 Mysql::Tools::Base::Mysql_query_runner::cleanup_result(&functions);
356 delete runner;
357 }
358
enumerate_event_scheduler_events(const Database & db)359 void Mysql_crawler::enumerate_event_scheduler_events(const Database& db)
360 {
361 Mysql::Tools::Base::Mysql_query_runner* runner= this->get_runner();
362
363 // Workaround for "access denied" error fixed in 5.7.6.
364 if (this->get_server_version() < 50706
365 && this->compare_no_case_latin_with_db_string(
366 "performance_schema", db.get_name()) == 0)
367 {
368 return;
369 }
370
371 std::vector<const Mysql::Tools::Base::Mysql_query_runner::Row*> events;
372 runner->run_query_store("SHOW EVENTS FROM "
373 + this->get_quoted_object_full_name(&db), &events);
374
375 for (std::vector<const Mysql::Tools::Base::Mysql_query_runner::Row*>::iterator
376 it= events.begin(); it != events.end(); ++it)
377 {
378 const Mysql::Tools::Base::Mysql_query_runner::Row& event_row= **it;
379
380 Event_scheduler_event* event=
381 new Event_scheduler_event(this->generate_new_object_id(),
382 event_row[1], db.get_name(),
383 "DELIMITER //\n" + this->get_create_statement(
384 runner, db.get_name(), event_row[1], "EVENT", 3).value()
385 + "//\n" + "DELIMITER ;\n");
386
387 event->add_dependency(m_current_database_start_dump_task);
388 m_current_database_end_dump_task->add_dependency(event);
389
390 this->process_dump_task(event);
391 }
392 Mysql::Tools::Base::Mysql_query_runner::cleanup_result(&events);
393 delete runner;
394 }
395
enumerate_users()396 void Mysql_crawler::enumerate_users()
397 {
398 Mysql::Tools::Base::Mysql_query_runner* runner= this->get_runner();
399
400 std::vector<const Mysql::Tools::Base::Mysql_query_runner::Row*> users;
401 runner->run_query_store(
402 "SELECT CONCAT(QUOTE(user),'@',QUOTE(host)) FROM mysql.user", &users);
403
404 for (std::vector<const Mysql::Tools::Base::Mysql_query_runner::Row*>::iterator
405 it= users.begin(); it != users.end(); ++it)
406 {
407 const Mysql::Tools::Base::Mysql_query_runner::Row& user_row= **it;
408
409 std::vector<const Mysql::Tools::Base::Mysql_query_runner::Row*> create_user;
410 std::vector<const Mysql::Tools::Base::Mysql_query_runner::Row*> user_grants;
411 if (runner->run_query_store(
412 "SHOW CREATE USER " + user_row[0], &create_user))
413 return;
414 if (runner->run_query_store(
415 "SHOW GRANTS FOR " + user_row[0], &user_grants))
416 return;
417
418 Abstract_dump_task* previous_grant= m_dump_start_task;
419
420 std::vector<const Mysql::Tools::Base::Mysql_query_runner::Row*> ::iterator
421 it1= create_user.begin();
422 const Mysql::Tools::Base::Mysql_query_runner::Row& create_row= **it1;
423
424 std::string user= create_row[0];
425 for (std::vector<const Mysql::Tools::Base::Mysql_query_runner::Row*>
426 ::iterator it2= user_grants.begin(); it2 != user_grants.end(); ++it2)
427 {
428 const Mysql::Tools::Base::Mysql_query_runner::Row& grant_row= **it2;
429 user+= std::string(";\n" + grant_row[0]);
430 }
431 Privilege* grant=
432 new Privilege(
433 this->generate_new_object_id(), user_row[0], user);
434
435 grant->add_dependency(previous_grant);
436 grant->add_dependency(m_tables_definition_ready_dump_task);
437 m_dump_end_task->add_dependency(grant);
438 this->process_dump_task(grant);
439 previous_grant= grant;
440
441 Mysql::Tools::Base::Mysql_query_runner::cleanup_result(&create_user);
442 Mysql::Tools::Base::Mysql_query_runner::cleanup_result(&user_grants);
443 }
444 Mysql::Tools::Base::Mysql_query_runner::cleanup_result(&users);
445 delete runner;
446 }
447
enumerate_table_triggers(const Table & table,Abstract_dump_task * dependency)448 void Mysql_crawler::enumerate_table_triggers(
449 const Table& table, Abstract_dump_task* dependency)
450 {
451 // Triggers were supported since 5.0.9
452 if (this->get_server_version() < 50009)
453 return;
454
455 Mysql::Tools::Base::Mysql_query_runner* runner= this->get_runner();
456
457 std::vector<const Mysql::Tools::Base::Mysql_query_runner::Row*> triggers;
458 runner->run_query_store("SHOW TRIGGERS FROM "
459 + this->quote_name(table.get_schema()) + " LIKE '"
460 + runner->escape_string(table.get_name()) + '\'', &triggers);
461
462 for (std::vector<const Mysql::Tools::Base::Mysql_query_runner::Row*>::iterator
463 it= triggers.begin(); it != triggers.end(); ++it)
464 {
465 const Mysql::Tools::Base::Mysql_query_runner::Row& trigger_row= **it;
466 Trigger* trigger= new Trigger(this->generate_new_object_id(),
467 trigger_row[0], table.get_schema(),
468 "DELIMITER //\n" + this->get_version_specific_statement(
469 this->get_create_statement(
470 runner, table.get_schema(), trigger_row[0], "TRIGGER", 2).value(),
471 "TRIGGER", "50017", "50003") + "\n//\n" + "DELIMITER ;\n",
472 &table);
473
474 trigger->add_dependency(dependency);
475 m_current_database_end_dump_task->add_dependency(trigger);
476
477 this->process_dump_task(trigger);
478 }
479 Mysql::Tools::Base::Mysql_query_runner::cleanup_result(&triggers);
480 delete runner;
481 }
482
483
get_version_specific_statement(std::string create_string,const std::string & keyword,std::string main_version,std::string definer_version)484 std::string Mysql_crawler::get_version_specific_statement(
485 std::string create_string, const std::string& keyword,
486 std::string main_version, std::string definer_version)
487 {
488 size_t keyword_pos= create_string.find(" " + keyword);
489 size_t definer_pos= create_string.find(" DEFINER");
490 if (keyword_pos != std::string::npos && definer_pos != std::string::npos
491 && definer_pos < keyword_pos)
492 {
493 create_string.insert(keyword_pos, "*/ /*!" + main_version);
494 create_string.insert(definer_pos, "*/ /*!" + definer_version);
495 }
496 return "/*!" + main_version + ' ' + create_string + " */";
497 }
498