diff options
author | unknown <sasha@mysql.sashanet.com> | 2001-05-28 19:18:23 -0600 |
---|---|---|
committer | unknown <sasha@mysql.sashanet.com> | 2001-05-28 19:18:23 -0600 |
commit | 704becf22801bfa00f866124eb7f4898e3003d03 (patch) | |
tree | 555928735ac48778eab810466107750e6e6131a1 /sql/sql_repl.cc | |
parent | 403b38ee5091e74df8adb6ee0c644903a82ffcad (diff) | |
download | mariadb-git-704becf22801bfa00f866124eb7f4898e3003d03.tar.gz |
updates for LOAD DATA FROM MASTER + some cleanup of replication code
include/mysqld_error.h:
new errors
mysql-test/r/rpl000009.result:
test load data from master
mysql-test/t/rpl000009.test:
test load data from master
sql/mini_client.cc:
extra functionality needed for load data from master and other things
sql/mini_client.h:
addition to API
sql/mysql_priv.h:
mysql_create_db()/mysql_rm_db() now return a value
sql/share/english/errmsg.txt:
more error messages
sql/slave.cc:
cleanup of fetch_nx_table()
sql/slave.h:
cleanup of fetch_nx_table()
sql/sql_base.cc:
remove unused code originally written for retrieving a non-existent table in slave thread
sql/sql_class.cc:
remove unused replication variables
sql/sql_class.h:
remove unused replication variabled
sql/sql_db.cc:
make mysql_create_db()/mysql_rm_db() work with thd == 0
(do not write messages to the net) and instead return success/error
sql/sql_lex.h:
added SQLCOM_LOAD_MASTER_DATA
sql/sql_parse.cc:
LOAD MASTER DATA, cleanup of LOAD TABLE FROM MASTER
sql/sql_repl.cc:
LOAD DATA FROM MASTER
sql/sql_repl.h:
LOAD DATA FROM MASTER
sql/sql_yacc.yy:
LOAD DATA FROM MASTER
Diffstat (limited to 'sql/sql_repl.cc')
-rw-r--r-- | sql/sql_repl.cc | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index e5039d118be..d36fa1a3534 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -21,6 +21,7 @@ #include "sql_repl.h" #include "sql_acl.h" #include "log_event.h" +#include "mini_client.h" #include <thr_alarm.h> #include <my_dir.h> @@ -845,5 +846,220 @@ err: return 1; } +int connect_to_master(THD *thd, MYSQL* mysql, MASTER_INFO* mi) +{ + if(!mc_mysql_connect(mysql, mi->host, mi->user, mi->password, 0, + mi->port, 0, 0)) + { + sql_print_error("Connection to master failed: %s", + mc_mysql_error(mysql)); + return 1; + } + return 0; +} + +static inline void cleanup_mysql_results(MYSQL_RES* db_res, + MYSQL_RES** cur, MYSQL_RES** start) +{ + for( ; cur >= start; --cur) + if(*cur) + mc_mysql_free_result(*cur); + mc_mysql_free_result(db_res); +} + +static inline int fetch_db_tables(THD* thd, MYSQL* mysql, const char* db, + MYSQL_RES* table_res) +{ + MYSQL_ROW row; + + for( row = mc_mysql_fetch_row(table_res); row; + row = mc_mysql_fetch_row(table_res)) + { + TABLE_LIST table; + const char* table_name = row[0]; + int error; + if(table_rules_on) + { + table.next = 0; + table.db = (char*)db; + table.real_name = (char*)table_name; + if(!tables_ok(thd, &table)) + continue; + } + + if((error = fetch_nx_table(thd, db, table_name, &glob_mi, mysql))) + return error; + } + + return 0; +} + +int load_master_data(THD* thd) +{ + MYSQL mysql; + MYSQL_RES* master_status_res = 0; + bool slave_was_running = 0; + int error = 0; + + mc_mysql_init(&mysql); + + pthread_mutex_lock(&LOCK_slave); + // we do not want anyone messing with the slave at all for the entire + // duration of the data load; + + // first, kill the slave + if((slave_was_running = slave_running)) + { + abort_slave = 1; + thr_alarm_kill(slave_real_id); + thd->proc_info = "waiting for slave to die"; + while(slave_running) + pthread_cond_wait(&COND_slave_stopped, &LOCK_slave); // wait until done + } + + + if(connect_to_master(thd, &mysql, &glob_mi)) + { + net_printf(&thd->net, error = ER_CONNECT_TO_MASTER, + mc_mysql_error(&mysql)); + goto err; + } + + // now that we are connected, get all database and tables in each + { + MYSQL_RES *db_res, **table_res, **table_res_end, **cur_table_res; + uint num_dbs; + MYSQL_ROW row; + + if(mc_mysql_query(&mysql, "show databases", 0) || + !(db_res = mc_mysql_store_result(&mysql))) + { + net_printf(&thd->net, error = ER_QUERY_ON_MASTER, + mc_mysql_error(&mysql)); + goto err; + } + + if(!(num_dbs = mc_mysql_num_rows(db_res))) + goto err; + // in theory, the master could have no databases at all + // and run with skip-grant + + if(!(table_res = (MYSQL_RES**)thd->alloc(num_dbs * sizeof(MYSQL_RES*)))) + { + net_printf(&thd->net, error = ER_OUTOFMEMORY); + goto err; + } + + // this is a temporary solution until we have online backup + // capabilities - to be replaced once online backup is working + // we wait to issue FLUSH TABLES WITH READ LOCK for as long as we + // can to minimize the lock time + if(mc_mysql_query(&mysql, "FLUSH TABLES WITH READ LOCK", 0) + || mc_mysql_query(&mysql, "SHOW MASTER STATUS",0) || + !(master_status_res = mc_mysql_store_result(&mysql))) + { + net_printf(&thd->net, error = ER_QUERY_ON_MASTER, + mc_mysql_error(&mysql)); + goto err; + } + + // go through every table in every database, and if the replication + // rules allow replicating it, get it + + table_res_end = table_res + num_dbs; + + for(cur_table_res = table_res; cur_table_res < table_res_end; + ++cur_table_res) + { + MYSQL_ROW row = mc_mysql_fetch_row(db_res); + // since we know how many rows we have, this can never be NULL + + char* db = row[0]; + int drop_error = 0; + + // do not replicate databases excluded by rules + // also skip mysql database - in most cases the user will + // mess up and not exclude mysql database with the rules when + // he actually means to - in this case, he is up for a surprise if + // his priv tables get dropped and downloaded from master + // TO DO - add special option, not enabled + // by default, to allow inclusion of mysql database into load + // data from master + if(!db_ok(db, replicate_do_db, replicate_ignore_db) || + !strcmp(db,"mysql")) + { + *cur_table_res = 0; + continue; + } + + if((drop_error = mysql_rm_db(0, db, 1)) || + mysql_create_db(0, db, 0)) + { + error = (drop_error) ? ER_DB_DROP_DELETE : ER_CANT_CREATE_DB; + net_printf(&thd->net, error, db, my_error); + cleanup_mysql_results(db_res, cur_table_res - 1, table_res); + goto err; + } + + if(mc_mysql_select_db(&mysql, db) || + mc_mysql_query(&mysql, "show tables", 0) || + !(*cur_table_res = mc_mysql_store_result(&mysql))) + { + net_printf(&thd->net, error = ER_QUERY_ON_MASTER, + mc_mysql_error(&mysql)); + cleanup_mysql_results(db_res, cur_table_res - 1, table_res); + goto err; + } + + if((error = fetch_db_tables(thd, &mysql, db, *cur_table_res))) + { + // we do not report the error - fetch_db_tables handles it + cleanup_mysql_results(db_res, cur_table_res, table_res); + goto err; + } + } + + cleanup_mysql_results(db_res, cur_table_res - 1, table_res); + + // adjust position in the master + if(master_status_res) + { + MYSQL_ROW row = mc_mysql_fetch_row(master_status_res); + + // we need this check because the master may not be running with + // log-bin, but it will still allow us to do all the steps + // of LOAD DATA FROM MASTER - no reason to forbid it, really, + // although it does not make much sense for the user to do it + if(row[0] && row[1]) + { + strmake(glob_mi.log_file_name, row[0], sizeof(glob_mi.log_file_name)); + glob_mi.pos = atoi(row[1]); // atoi() is ok, since offset is <= 1GB + if(glob_mi.pos < 4) + glob_mi.pos = 4; // don't hit the magic number + glob_mi.pending = 0; + flush_master_info(&glob_mi); + } + + mc_mysql_free_result(master_status_res); + } + + if(mc_mysql_query(&mysql, "UNLOCK TABLES", 0)) + { + net_printf(&thd->net, error = ER_QUERY_ON_MASTER, + mc_mysql_error(&mysql)); + goto err; + } + } +err: + pthread_mutex_unlock(&LOCK_slave); + if(slave_was_running) + start_slave(0, 0); + mc_mysql_close(&mysql); // safe to call since we always do mc_mysql_init() + if(!error) + send_ok(&thd->net); + + return error; +} + |