1/* Copyright (c) 2006, 2017, Oracle and/or its affiliates.
2 Copyright (c) 2010, 2017, MariaDB Corporation
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 as published by
6 the Free Software Foundation; version 2 of the License.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
16
17#include "mariadb.h" // For HAVE_REPLICATION
18#include "sql_priv.h"
19#include <my_dir.h>
20#include "rpl_mi.h"
21#include "slave.h"
22#include "strfunc.h"
23#include "sql_repl.h"
24
25#ifdef HAVE_REPLICATION
26
27#define DEFAULT_CONNECT_RETRY 60
28
29static void init_master_log_pos(Master_info* mi);
30
31Master_info::Master_info(LEX_CSTRING *connection_name_arg,
32 bool is_slave_recovery)
33 :Slave_reporting_capability("I/O"),
34 ssl(0), ssl_verify_server_cert(1), fd(-1), io_thd(0),
35 rli(is_slave_recovery), port(MYSQL_PORT),
36 checksum_alg_before_fd(BINLOG_CHECKSUM_ALG_UNDEF),
37 connect_retry(DEFAULT_CONNECT_RETRY), inited(0), abort_slave(0),
38 slave_running(MYSQL_SLAVE_NOT_RUN), slave_run_id(0),
39 clock_diff_with_master(0),
40 sync_counter(0), heartbeat_period(0), received_heartbeats(0),
41 master_id(0), prev_master_id(0),
42 using_gtid(USE_GTID_NO), events_queued_since_last_gtid(0),
43 gtid_reconnect_event_skip_count(0), gtid_event_seen(false),
44 in_start_all_slaves(0), in_stop_all_slaves(0), in_flush_all_relay_logs(0),
45 users(0), killed(0),
46 total_ddl_groups(0), total_non_trans_groups(0), total_trans_groups(0)
47{
48 char *tmp;
49 host[0] = 0; user[0] = 0; password[0] = 0;
50 ssl_ca[0]= 0; ssl_capath[0]= 0; ssl_cert[0]= 0;
51 ssl_cipher[0]= 0; ssl_key[0]= 0;
52 ssl_crl[0]= 0; ssl_crlpath[0]= 0;
53
54 /*
55 Store connection name and lower case connection name
56 It's safe to ignore any OMM errors as this is checked by error()
57 */
58 connection_name.length= cmp_connection_name.length=
59 connection_name_arg->length;
60 if ((connection_name.str= tmp= (char*)
61 my_malloc(connection_name_arg->length*2+2, MYF(MY_WME))))
62 {
63 strmake(tmp, connection_name_arg->str, connection_name.length);
64 tmp+= connection_name_arg->length+1;
65 cmp_connection_name.str= tmp;
66 memcpy(tmp, connection_name_arg->str, connection_name.length+1);
67 my_casedn_str(system_charset_info, tmp);
68 }
69 /*
70 When MySQL restarted, all Rpl_filter settings which aren't in the my.cnf
71 will be lost. If you want to lose a setting after restart, you
72 should add them into my.cnf
73 */
74 rpl_filter= get_or_create_rpl_filter(connection_name.str,
75 connection_name.length);
76 copy_filter_setting(rpl_filter, global_rpl_filter);
77
78 parallel_mode= rpl_filter->get_parallel_mode();
79
80 my_init_dynamic_array(&ignore_server_ids,
81 sizeof(global_system_variables.server_id), 16, 16,
82 MYF(0));
83 bzero((char*) &file, sizeof(file));
84 mysql_mutex_init(key_master_info_run_lock, &run_lock, MY_MUTEX_INIT_FAST);
85 mysql_mutex_init(key_master_info_data_lock, &data_lock, MY_MUTEX_INIT_FAST);
86 mysql_mutex_init(key_master_info_start_stop_lock, &start_stop_lock,
87 MY_MUTEX_INIT_SLOW);
88 mysql_mutex_setflags(&run_lock, MYF_NO_DEADLOCK_DETECTION);
89 mysql_mutex_setflags(&data_lock, MYF_NO_DEADLOCK_DETECTION);
90 mysql_mutex_init(key_master_info_sleep_lock, &sleep_lock, MY_MUTEX_INIT_FAST);
91 mysql_cond_init(key_master_info_data_cond, &data_cond, NULL);
92 mysql_cond_init(key_master_info_start_cond, &start_cond, NULL);
93 mysql_cond_init(key_master_info_stop_cond, &stop_cond, NULL);
94 mysql_cond_init(key_master_info_sleep_cond, &sleep_cond, NULL);
95}
96
97
98/**
99 Wait until no one is using Master_info
100*/
101
102void Master_info::wait_until_free()
103{
104 mysql_mutex_lock(&sleep_lock);
105 killed= 1;
106 while (users)
107 mysql_cond_wait(&sleep_cond, &sleep_lock);
108 mysql_mutex_unlock(&sleep_lock);
109}
110
111/**
112 Delete master_info
113*/
114
115Master_info::~Master_info()
116{
117 wait_until_free();
118#ifdef WITH_WSREP
119 /*
120 Do not free "wsrep" rpl_filter. It will eventually be freed by
121 free_all_rpl_filters() when server terminates.
122 */
123 if (strncmp(connection_name.str, STRING_WITH_LEN("wsrep")))
124#endif
125 rpl_filters.delete_element(connection_name.str, connection_name.length,
126 (void (*)(const char*, uchar*)) free_rpl_filter);
127 my_free(const_cast<char*>(connection_name.str));
128 delete_dynamic(&ignore_server_ids);
129 mysql_mutex_destroy(&run_lock);
130 mysql_mutex_destroy(&data_lock);
131 mysql_mutex_destroy(&sleep_lock);
132 mysql_mutex_destroy(&start_stop_lock);
133 mysql_cond_destroy(&data_cond);
134 mysql_cond_destroy(&start_cond);
135 mysql_cond_destroy(&stop_cond);
136 mysql_cond_destroy(&sleep_cond);
137}
138
139/**
140 A comparison function to be supplied as argument to @c sort_dynamic()
141 and @c bsearch()
142
143 @return -1 if first argument is less, 0 if it equal to, 1 if it is greater
144 than the second
145*/
146static int change_master_id_cmp(const void *id1, const void *id2)
147{
148 return (*(ulong *) id1 - *(ulong *) id2);
149}
150
151/**
152 Reports if the s_id server has been configured to ignore events
153 it generates with
154
155 CHANGE MASTER IGNORE_SERVER_IDS= ( list of server ids )
156
157 Method is called from the io thread event receiver filtering.
158
159 @param s_id the master server identifier
160
161 @retval TRUE if s_id is in the list of ignored master servers,
162 @retval FALSE otherwise.
163 */
164bool Master_info::shall_ignore_server_id(ulong s_id)
165{
166 if (likely(ignore_server_ids.elements == 1))
167 return (* (ulong*) dynamic_array_ptr(&ignore_server_ids, 0)) == s_id;
168 else
169 return bsearch((const ulong *) &s_id,
170 ignore_server_ids.buffer,
171 ignore_server_ids.elements, sizeof(ulong),
172 change_master_id_cmp) != NULL;
173}
174
175void Master_info::clear_in_memory_info(bool all)
176{
177 init_master_log_pos(this);
178 if (all)
179 {
180 port= MYSQL_PORT;
181 host[0] = 0; user[0] = 0; password[0] = 0;
182 }
183}
184
185
186const char *
187Master_info::using_gtid_astext(enum enum_using_gtid arg)
188{
189 switch (arg)
190 {
191 case USE_GTID_NO:
192 return "No";
193 case USE_GTID_SLAVE_POS:
194 return "Slave_Pos";
195 default:
196 DBUG_ASSERT(arg == USE_GTID_CURRENT_POS);
197 return "Current_Pos";
198 }
199}
200
201
202void init_master_log_pos(Master_info* mi)
203{
204 DBUG_ENTER("init_master_log_pos");
205
206 mi->master_log_name[0] = 0;
207 mi->master_log_pos = BIN_LOG_HEADER_SIZE; // skip magic number
208 mi->using_gtid= Master_info::USE_GTID_NO;
209 mi->gtid_current_pos.reset();
210 mi->events_queued_since_last_gtid= 0;
211 mi->gtid_reconnect_event_skip_count= 0;
212 mi->gtid_event_seen= false;
213
214 /* Intentionally init ssl_verify_server_cert to 0, no option available */
215 mi->ssl_verify_server_cert= 0;
216 /*
217 always request heartbeat unless master_heartbeat_period is set
218 explicitly zero. Here is the default value for heartbeat period
219 if CHANGE MASTER did not specify it. (no data loss in conversion
220 as hb period has a max)
221 */
222 mi->heartbeat_period= (float) MY_MIN(SLAVE_MAX_HEARTBEAT_PERIOD,
223 (slave_net_timeout/2.0));
224 DBUG_ASSERT(mi->heartbeat_period > (float) 0.001
225 || mi->heartbeat_period == 0);
226
227 DBUG_VOID_RETURN;
228}
229
230/**
231 Parses the IO_CACHE for "key=" and returns the "key".
232 If no '=' found, returns the whole line (for END_MARKER).
233
234 @param key [OUT] Key buffer
235 @param max_size [IN] Maximum buffer size
236 @param f [IN] IO_CACHE file
237 @param found_equal [OUT] Set true if a '=' was found.
238
239 @retval 0 Either "key=" or '\n' found
240 @retval 1 EOF
241*/
242static int
243read_mi_key_from_file(char *key, int max_size, IO_CACHE *f, bool *found_equal)
244{
245 int i= 0, c;
246
247 DBUG_ENTER("read_key_from_file");
248
249 *found_equal= false;
250 if (max_size <= 0)
251 DBUG_RETURN(1);
252 for (;;)
253 {
254 if (i >= max_size-1)
255 {
256 key[i] = '\0';
257 DBUG_RETURN(0);
258 }
259 c= my_b_get(f);
260 if (c == my_b_EOF)
261 {
262 DBUG_RETURN(1);
263 }
264 else if (c == '\n')
265 {
266 key[i]= '\0';
267 DBUG_RETURN(0);
268 }
269 else if (c == '=')
270 {
271 key[i]= '\0';
272 *found_equal= true;
273 DBUG_RETURN(0);
274 }
275 else
276 {
277 key[i]= c;
278 ++i;
279 }
280 }
281 /* NotReached */
282}
283
284enum {
285 LINES_IN_MASTER_INFO_WITH_SSL= 14,
286
287 /* 5.1.16 added value of master_ssl_verify_server_cert */
288 LINE_FOR_MASTER_SSL_VERIFY_SERVER_CERT= 15,
289
290 /* 5.5 added value of master_heartbeat_period */
291 LINE_FOR_MASTER_HEARTBEAT_PERIOD= 16,
292
293 /* MySQL Cluster 6.3 added master_bind */
294 LINE_FOR_MASTER_BIND = 17,
295
296 /* 6.0 added value of master_ignore_server_id */
297 LINE_FOR_REPLICATE_IGNORE_SERVER_IDS= 18,
298
299 /* 6.0 added value of master_uuid */
300 LINE_FOR_MASTER_UUID= 19,
301
302 /* line for master_retry_count */
303 LINE_FOR_MASTER_RETRY_COUNT= 20,
304
305 /* line for ssl_crl */
306 LINE_FOR_SSL_CRL= 21,
307
308 /* line for ssl_crl */
309 LINE_FOR_SSL_CRLPATH= 22,
310
311 /* MySQL 5.6 fixed-position lines. */
312 LINE_FOR_FIRST_MYSQL_5_6=23,
313 LINE_FOR_LAST_MYSQL_5_6=23,
314 /* Reserved lines for MySQL future versions. */
315 LINE_FOR_LAST_MYSQL_FUTURE=33,
316 /* Number of (fixed-position) lines used when saving master info file */
317 LINES_IN_MASTER_INFO= LINE_FOR_LAST_MYSQL_FUTURE
318};
319
320int init_master_info(Master_info* mi, const char* master_info_fname,
321 const char* slave_info_fname,
322 bool abort_if_no_master_info_file,
323 int thread_mask)
324{
325 int fd,error;
326 char fname[FN_REFLEN+128];
327 DBUG_ENTER("init_master_info");
328
329 if (mi->inited)
330 {
331 /*
332 We have to reset read position of relay-log-bin as we may have
333 already been reading from 'hotlog' when the slave was stopped
334 last time. If this case pos_in_file would be set and we would
335 get a crash when trying to read the signature for the binary
336 relay log.
337
338 We only rewind the read position if we are starting the SQL
339 thread. The handle_slave_sql thread assumes that the read
340 position is at the beginning of the file, and will read the
341 "signature" and then fast-forward to the last position read.
342 */
343 if (thread_mask & SLAVE_SQL)
344 {
345 bool hot_log= FALSE;
346 /*
347 my_b_seek does an implicit flush_io_cache, so we need to:
348
349 1. check if this log is active (hot)
350 2. if it is we keep log_lock until the seek ends, otherwise
351 release it right away.
352
353 If we did not take log_lock, SQL thread might race with IO
354 thread for the IO_CACHE mutex.
355
356 */
357 mysql_mutex_t *log_lock= mi->rli.relay_log.get_log_lock();
358 mysql_mutex_lock(log_lock);
359 hot_log= mi->rli.relay_log.is_active(mi->rli.linfo.log_file_name);
360
361 if (!hot_log)
362 mysql_mutex_unlock(log_lock);
363
364 my_b_seek(mi->rli.cur_log, (my_off_t) 0);
365
366 if (hot_log)
367 mysql_mutex_unlock(log_lock);
368 }
369 DBUG_RETURN(0);
370 }
371
372 mi->mysql=0;
373 mi->file_id=1;
374 fn_format(fname, master_info_fname, mysql_data_home, "", 4+32);
375
376 /*
377 We need a mutex while we are changing master info parameters to
378 keep other threads from reading bogus info
379 */
380
381 mysql_mutex_lock(&mi->data_lock);
382 fd = mi->fd;
383
384 /* does master.info exist ? */
385
386 if (access(fname,F_OK))
387 {
388 if (abort_if_no_master_info_file)
389 {
390 mysql_mutex_unlock(&mi->data_lock);
391 DBUG_RETURN(0);
392 }
393 /*
394 if someone removed the file from underneath our feet, just close
395 the old descriptor and re-create the old file
396 */
397 if (fd >= 0)
398 mysql_file_close(fd, MYF(MY_WME));
399 if ((fd= mysql_file_open(key_file_master_info,
400 fname, O_CREAT|O_RDWR|O_BINARY, MYF(MY_WME))) < 0 )
401 {
402 sql_print_error("Failed to create a new master info file (\
403file '%s', errno %d)", fname, my_errno);
404 goto err;
405 }
406 if (init_io_cache(&mi->file, fd, IO_SIZE*2, READ_CACHE, 0L,0,
407 MYF(MY_WME)))
408 {
409 sql_print_error("Failed to create a cache on master info file (\
410file '%s')", fname);
411 goto err;
412 }
413
414 mi->fd = fd;
415 mi->clear_in_memory_info(false);
416
417 }
418 else // file exists
419 {
420 if (fd >= 0)
421 reinit_io_cache(&mi->file, READ_CACHE, 0L,0,0);
422 else
423 {
424 if ((fd= mysql_file_open(key_file_master_info,
425 fname, O_RDWR|O_BINARY, MYF(MY_WME))) < 0 )
426 {
427 sql_print_error("Failed to open the existing master info file (\
428file '%s', errno %d)", fname, my_errno);
429 goto err;
430 }
431 if (init_io_cache(&mi->file, fd, IO_SIZE*2, READ_CACHE, 0L,
432 0, MYF(MY_WME)))
433 {
434 sql_print_error("Failed to create a cache on master info file (\
435file '%s')", fname);
436 goto err;
437 }
438 }
439
440 mi->fd = fd;
441 int port, connect_retry, master_log_pos, lines;
442 int ssl= 0, ssl_verify_server_cert= 0;
443 float master_heartbeat_period= 0.0;
444 char *first_non_digit;
445 char buf[HOSTNAME_LENGTH+1];
446
447 /*
448 Starting from 4.1.x master.info has new format. Now its
449 first line contains number of lines in file. By reading this
450 number we will be always distinguish to which version our
451 master.info corresponds to. We can't simply count lines in
452 file since versions before 4.1.x could generate files with more
453 lines than needed.
454 If first line doesn't contain a number or contain number less than
455 LINES_IN_MASTER_INFO_WITH_SSL then such file is treated like file
456 from pre 4.1.1 version.
457 There is no ambiguity when reading an old master.info, as before
458 4.1.1, the first line contained the binlog's name, which is either
459 empty or has an extension (contains a '.'), so can't be confused
460 with an integer.
461
462 So we're just reading first line and trying to figure which version
463 is this.
464 */
465
466 /*
467 The first row is temporarily stored in mi->master_log_name,
468 if it is line count and not binlog name (new format) it will be
469 overwritten by the second row later.
470 */
471 if (init_strvar_from_file(mi->master_log_name,
472 sizeof(mi->master_log_name), &mi->file,
473 ""))
474 goto errwithmsg;
475
476 lines= strtoul(mi->master_log_name, &first_non_digit, 10);
477
478 if (mi->master_log_name[0]!='\0' &&
479 *first_non_digit=='\0' && lines >= LINES_IN_MASTER_INFO_WITH_SSL)
480 {
481 /* Seems to be new format => read master log name from next line */
482 if (init_strvar_from_file(mi->master_log_name,
483 sizeof(mi->master_log_name), &mi->file, ""))
484 goto errwithmsg;
485 }
486 else
487 lines= 7;
488
489 if (init_intvar_from_file(&master_log_pos, &mi->file, 4) ||
490 init_strvar_from_file(mi->host, sizeof(mi->host), &mi->file, 0) ||
491 init_strvar_from_file(mi->user, sizeof(mi->user), &mi->file, "test") ||
492 init_strvar_from_file(mi->password, SCRAMBLED_PASSWORD_CHAR_LENGTH+1,
493 &mi->file, 0) ||
494 init_intvar_from_file(&port, &mi->file, MYSQL_PORT) ||
495 init_intvar_from_file(&connect_retry, &mi->file,
496 DEFAULT_CONNECT_RETRY))
497 goto errwithmsg;
498
499 /*
500 If file has ssl part use it even if we have server without
501 SSL support. But these options will be ignored later when
502 slave will try connect to master, so in this case warning
503 is printed.
504 */
505 if (lines >= LINES_IN_MASTER_INFO_WITH_SSL)
506 {
507 if (init_intvar_from_file(&ssl, &mi->file, 0) ||
508 init_strvar_from_file(mi->ssl_ca, sizeof(mi->ssl_ca),
509 &mi->file, 0) ||
510 init_strvar_from_file(mi->ssl_capath, sizeof(mi->ssl_capath),
511 &mi->file, 0) ||
512 init_strvar_from_file(mi->ssl_cert, sizeof(mi->ssl_cert),
513 &mi->file, 0) ||
514 init_strvar_from_file(mi->ssl_cipher, sizeof(mi->ssl_cipher),
515 &mi->file, 0) ||
516 init_strvar_from_file(mi->ssl_key, sizeof(mi->ssl_key),
517 &mi->file, 0))
518 goto errwithmsg;
519
520 /*
521 Starting from 5.1.16 ssl_verify_server_cert might be
522 in the file
523 */
524 if (lines >= LINE_FOR_MASTER_SSL_VERIFY_SERVER_CERT &&
525 init_intvar_from_file(&ssl_verify_server_cert, &mi->file, 0))
526 goto errwithmsg;
527 /*
528 Starting from 6.0 master_heartbeat_period might be
529 in the file
530 */
531 if (lines >= LINE_FOR_MASTER_HEARTBEAT_PERIOD &&
532 init_floatvar_from_file(&master_heartbeat_period, &mi->file, 0.0))
533 goto errwithmsg;
534 /*
535 Starting from MySQL Cluster 6.3 master_bind might be in the file
536 (this is just a reservation to avoid future upgrade problems)
537 */
538 if (lines >= LINE_FOR_MASTER_BIND &&
539 init_strvar_from_file(buf, sizeof(buf), &mi->file, ""))
540 goto errwithmsg;
541 /*
542 Starting from 6.0 list of server_id of ignorable servers might be
543 in the file
544 */
545 if (lines >= LINE_FOR_REPLICATE_IGNORE_SERVER_IDS &&
546 init_dynarray_intvar_from_file(&mi->ignore_server_ids, &mi->file))
547 {
548 sql_print_error("Failed to initialize master info ignore_server_ids");
549 goto errwithmsg;
550 }
551
552 /* reserved */
553 if (lines >= LINE_FOR_MASTER_UUID &&
554 init_strvar_from_file(buf, sizeof(buf), &mi->file, ""))
555 goto errwithmsg;
556
557 /* Starting from 5.5 the master_retry_count may be in the repository. */
558 if (lines >= LINE_FOR_MASTER_RETRY_COUNT &&
559 init_strvar_from_file(buf, sizeof(buf), &mi->file, ""))
560 goto errwithmsg;
561
562 if (lines >= LINE_FOR_SSL_CRLPATH &&
563 (init_strvar_from_file(mi->ssl_crl, sizeof(mi->ssl_crl),
564 &mi->file, "") ||
565 init_strvar_from_file(mi->ssl_crlpath, sizeof(mi->ssl_crlpath),
566 &mi->file, "")))
567 goto errwithmsg;
568
569 /*
570 Starting with MariaDB 10.0, we use a key=value syntax, which is nicer
571 in several ways. But we leave a bunch of empty lines to accomodate
572 any future old-style additions in MySQL (this will make it easier for
573 users moving from MariaDB to MySQL, to not have MySQL try to
574 interpret a MariaDB key=value line.)
575 */
576 if (lines >= LINE_FOR_LAST_MYSQL_FUTURE)
577 {
578 uint i;
579 bool got_eq;
580 bool seen_using_gtid= false;
581 bool seen_do_domain_ids=false, seen_ignore_domain_ids=false;
582
583 /* Skip lines used by / reserved for MySQL >= 5.6. */
584 for (i= LINE_FOR_FIRST_MYSQL_5_6; i <= LINE_FOR_LAST_MYSQL_FUTURE; ++i)
585 {
586 if (init_strvar_from_file(buf, sizeof(buf), &mi->file, ""))
587 goto errwithmsg;
588 }
589
590 /*
591 Parse any extra key=value lines. read_key_from_file() parses the file
592 for "key=" and returns the "key" if found. The "value" can then the
593 parsed on case by case basis. The "unknown" lines would be ignored to
594 facilitate downgrades.
595 10.0 does not have the END_MARKER before any left-overs at the end
596 of the file. So ignore any but the first occurrence of a key.
597 */
598 while (!read_mi_key_from_file(buf, sizeof(buf), &mi->file, &got_eq))
599 {
600 if (got_eq && !seen_using_gtid && !strcmp(buf, "using_gtid"))
601 {
602 int val;
603 if (!init_intvar_from_file(&val, &mi->file, 0))
604 {
605 if (val == Master_info::USE_GTID_CURRENT_POS)
606 mi->using_gtid= Master_info::USE_GTID_CURRENT_POS;
607 else if (val == Master_info::USE_GTID_SLAVE_POS)
608 mi->using_gtid= Master_info::USE_GTID_SLAVE_POS;
609 else
610 mi->using_gtid= Master_info::USE_GTID_NO;
611 seen_using_gtid= true;
612 } else {
613 sql_print_error("Failed to initialize master info using_gtid");
614 goto errwithmsg;
615 }
616 }
617 else if (got_eq && !seen_do_domain_ids && !strcmp(buf, "do_domain_ids"))
618 {
619 if (mi->domain_id_filter.init_ids(&mi->file,
620 Domain_id_filter::DO_DOMAIN_IDS))
621 {
622 sql_print_error("Failed to initialize master info do_domain_ids");
623 goto errwithmsg;
624 }
625 seen_do_domain_ids= true;
626 }
627 else if (got_eq && !seen_ignore_domain_ids &&
628 !strcmp(buf, "ignore_domain_ids"))
629 {
630 if (mi->domain_id_filter.init_ids(&mi->file,
631 Domain_id_filter::IGNORE_DOMAIN_IDS))
632 {
633 sql_print_error("Failed to initialize master info "
634 "ignore_domain_ids");
635 goto errwithmsg;
636 }
637 seen_ignore_domain_ids= true;
638 }
639 else if (!got_eq && !strcmp(buf, "END_MARKER"))
640 {
641 /*
642 Guard agaist extra left-overs at the end of file, in case a later
643 update causes the file to shrink compared to earlier contents.
644 */
645 break;
646 }
647 }
648 }
649 }
650
651#ifndef HAVE_OPENSSL
652 if (ssl)
653 sql_print_warning("SSL information in the master info file "
654 "('%s') are ignored because this MySQL slave was "
655 "compiled without SSL support.", fname);
656#endif /* HAVE_OPENSSL */
657
658 /*
659 This has to be handled here as init_intvar_from_file can't handle
660 my_off_t types
661 */
662 mi->master_log_pos= (my_off_t) master_log_pos;
663 mi->port= (uint) port;
664 mi->connect_retry= (uint) connect_retry;
665 mi->ssl= (my_bool) ssl;
666 mi->ssl_verify_server_cert= ssl_verify_server_cert;
667 mi->heartbeat_period= MY_MIN(SLAVE_MAX_HEARTBEAT_PERIOD, master_heartbeat_period);
668 }
669 DBUG_PRINT("master_info",("log_file_name: %s position: %ld",
670 mi->master_log_name,
671 (ulong) mi->master_log_pos));
672
673 mi->rli.mi= mi;
674 if (mi->rli.init(slave_info_fname))
675 goto err;
676
677 mi->inited = 1;
678 mi->rli.is_relay_log_recovery= FALSE;
679 // now change cache READ -> WRITE - must do this before flush_master_info
680 reinit_io_cache(&mi->file, WRITE_CACHE, 0L, 0, 1);
681 if (unlikely((error= MY_TEST(flush_master_info(mi, TRUE, TRUE)))))
682 sql_print_error("Failed to flush master info file");
683 mysql_mutex_unlock(&mi->data_lock);
684 DBUG_RETURN(error);
685
686errwithmsg:
687 sql_print_error("Error reading master configuration");
688
689err:
690 if (fd >= 0)
691 {
692 mysql_file_close(fd, MYF(0));
693 end_io_cache(&mi->file);
694 }
695 mi->fd= -1;
696 mysql_mutex_unlock(&mi->data_lock);
697 DBUG_RETURN(1);
698}
699
700
701/*
702 RETURN
703 2 - flush relay log failed
704 1 - flush master info failed
705 0 - all ok
706*/
707int flush_master_info(Master_info* mi,
708 bool flush_relay_log_cache,
709 bool need_lock_relay_log)
710{
711 IO_CACHE* file = &mi->file;
712 char lbuf[22];
713 int err= 0;
714
715 DBUG_ENTER("flush_master_info");
716 DBUG_PRINT("enter",("master_pos: %ld", (long) mi->master_log_pos));
717
718 /*
719 Flush the relay log to disk. If we don't do it, then the relay log while
720 have some part (its last kilobytes) in memory only, so if the slave server
721 dies now, with, say, from master's position 100 to 150 in memory only (not
722 on disk), and with position 150 in master.info, then when the slave
723 restarts, the I/O thread will fetch binlogs from 150, so in the relay log
724 we will have "[0, 100] U [150, infinity[" and nobody will notice it, so the
725 SQL thread will jump from 100 to 150, and replication will silently break.
726
727 When we come to this place in code, relay log may or not be initialized;
728 the caller is responsible for setting 'flush_relay_log_cache' accordingly.
729 */
730 if (flush_relay_log_cache)
731 {
732 mysql_mutex_t *log_lock= mi->rli.relay_log.get_log_lock();
733 IO_CACHE *log_file= mi->rli.relay_log.get_log_file();
734
735 if (need_lock_relay_log)
736 mysql_mutex_lock(log_lock);
737
738 mysql_mutex_assert_owner(log_lock);
739 err= flush_io_cache(log_file);
740
741 if (need_lock_relay_log)
742 mysql_mutex_unlock(log_lock);
743
744 if (err)
745 DBUG_RETURN(2);
746 }
747
748 /*
749 produce a line listing the total number and all the ignored server_id:s
750 */
751 char* ignore_server_ids_buf;
752 {
753 ignore_server_ids_buf=
754 (char *) my_malloc((sizeof(global_system_variables.server_id) * 3 + 1) *
755 (1 + mi->ignore_server_ids.elements), MYF(MY_WME));
756 if (!ignore_server_ids_buf)
757 DBUG_RETURN(1); /* error */
758 ulong cur_len= sprintf(ignore_server_ids_buf, "%u",
759 mi->ignore_server_ids.elements);
760 for (ulong i= 0; i < mi->ignore_server_ids.elements; i++)
761 {
762 ulong s_id;
763 get_dynamic(&mi->ignore_server_ids, (uchar*) &s_id, i);
764 cur_len+= sprintf(ignore_server_ids_buf + cur_len, " %lu", s_id);
765 }
766 }
767
768 char *do_domain_ids_buf= 0, *ignore_domain_ids_buf= 0;
769
770 do_domain_ids_buf=
771 mi->domain_id_filter.as_string(Domain_id_filter::DO_DOMAIN_IDS);
772 if (do_domain_ids_buf == NULL)
773 {
774 err= 1; /* error */
775 goto done;
776 }
777
778 ignore_domain_ids_buf=
779 mi->domain_id_filter.as_string(Domain_id_filter::IGNORE_DOMAIN_IDS);
780 if (ignore_domain_ids_buf == NULL)
781 {
782 err= 1; /* error */
783 goto done;
784 }
785
786 /*
787 We flushed the relay log BEFORE the master.info file, because if we crash
788 now, we will get a duplicate event in the relay log at restart. If we
789 flushed in the other order, we would get a hole in the relay log.
790 And duplicate is better than hole (with a duplicate, in later versions we
791 can add detection and scrap one event; with a hole there's nothing we can
792 do).
793 */
794
795 /*
796 In certain cases this code may create master.info files that seems
797 corrupted, because of extra lines filled with garbage in the end
798 file (this happens if new contents take less space than previous
799 contents of file). But because of number of lines in the first line
800 of file we don't care about this garbage.
801 */
802 char heartbeat_buf[FLOATING_POINT_BUFFER];
803 my_fcvt(mi->heartbeat_period, 3, heartbeat_buf, NULL);
804 my_b_seek(file, 0L);
805 my_b_printf(file,
806 "%u\n%s\n%s\n%s\n%s\n%s\n%d\n%d\n%d\n%s\n%s\n%s\n%s\n%s\n%d\n%s\n%s\n%s\n%s\n%d\n%s\n%s\n"
807 "\n\n\n\n\n\n\n\n\n\n\n"
808 "using_gtid=%d\n"
809 "do_domain_ids=%s\n"
810 "ignore_domain_ids=%s\n"
811 "END_MARKER\n",
812 LINES_IN_MASTER_INFO,
813 mi->master_log_name, llstr(mi->master_log_pos, lbuf),
814 mi->host, mi->user,
815 mi->password, mi->port, mi->connect_retry,
816 (int)(mi->ssl), mi->ssl_ca, mi->ssl_capath, mi->ssl_cert,
817 mi->ssl_cipher, mi->ssl_key, mi->ssl_verify_server_cert,
818 heartbeat_buf, "", ignore_server_ids_buf,
819 "", 0,
820 mi->ssl_crl, mi->ssl_crlpath, mi->using_gtid,
821 do_domain_ids_buf, ignore_domain_ids_buf);
822 err= flush_io_cache(file);
823 if (sync_masterinfo_period && !err &&
824 ++(mi->sync_counter) >= sync_masterinfo_period)
825 {
826 err= my_sync(mi->fd, MYF(MY_WME));
827 mi->sync_counter= 0;
828 }
829
830 /* Fix err; flush_io_cache()/my_sync() may return -1 */
831 err= (err != 0) ? 1 : 0;
832
833done:
834 my_free(ignore_server_ids_buf);
835 my_free(do_domain_ids_buf);
836 my_free(ignore_domain_ids_buf);
837 DBUG_RETURN(err);
838}
839
840
841void end_master_info(Master_info* mi)
842{
843 DBUG_ENTER("end_master_info");
844
845 if (!mi->inited)
846 DBUG_VOID_RETURN;
847 if (mi->fd >= 0)
848 {
849 end_io_cache(&mi->file);
850 mysql_file_close(mi->fd, MYF(MY_WME));
851 mi->fd = -1;
852 }
853 mi->inited = 0;
854
855 DBUG_VOID_RETURN;
856}
857
858/* Multi-Master By P.Linux */
859uchar *get_key_master_info(Master_info *mi, size_t *length,
860 my_bool not_used __attribute__((unused)))
861{
862 /* Return lower case name */
863 *length= mi->cmp_connection_name.length;
864 return (uchar*) mi->cmp_connection_name.str;
865}
866
867/*
868 Delete a master info
869
870 Called from my_hash_delete(&master_info_hash)
871 Stops associated slave threads and frees master_info
872*/
873
874void free_key_master_info(Master_info *mi)
875{
876 DBUG_ENTER("free_key_master_info");
877 mysql_mutex_unlock(&LOCK_active_mi);
878
879 /* Ensure that we are not in reset_slave while this is done */
880 mi->lock_slave_threads();
881 terminate_slave_threads(mi,SLAVE_FORCE_ALL);
882 /* We use 2 here instead of 1 just to make it easier when debugging */
883 mi->killed= 2;
884 end_master_info(mi);
885 end_relay_log_info(&mi->rli);
886 mi->unlock_slave_threads();
887 delete mi;
888
889 mysql_mutex_lock(&LOCK_active_mi);
890 DBUG_VOID_RETURN;
891}
892
893/**
894 Check if connection name for master_info is valid.
895
896 It's valid if it's a valid system name of length less than
897 MAX_CONNECTION_NAME.
898
899 @return
900 0 ok
901 1 error
902*/
903
904bool check_master_connection_name(LEX_CSTRING *name)
905{
906 if (name->length >= MAX_CONNECTION_NAME)
907 return 1;
908 return 0;
909}
910
911
912/**
913 Create a log file with a given suffix.
914
915 @param
916 res_file_name Store result here
917 length Length of res_file_name buffer
918 info_file Original file name (prefix)
919 append 1 if we should add suffix last (not before ext)
920 suffix Suffix
921
922 @note
923 The suffix is added before the extension of the file name prefixed with '-'.
924 The suffix is also converted to lower case and we transform
925 all not safe character, as we do with MySQL table names.
926
927 If suffix is an empty string, then we don't add any suffix.
928 This is to allow one to use this function also to generate old
929 file names without a prefix.
930*/
931
932void create_logfile_name_with_suffix(char *res_file_name, size_t length,
933 const char *info_file, bool append,
934 LEX_CSTRING *suffix)
935{
936 char buff[MAX_CONNECTION_NAME+1],
937 res[MAX_CONNECTION_NAME * MAX_FILENAME_MBWIDTH+1], *p;
938
939 p= strmake(res_file_name, info_file, length);
940 /* If not empty suffix and there is place left for some part of the suffix */
941 if (suffix->length != 0 && p <= res_file_name + length -1)
942 {
943 const char *info_file_end= info_file + (p - res_file_name);
944 const char *ext= append ? info_file_end : fn_ext2(info_file);
945 size_t res_length, ext_pos, from_length;
946 uint errors;
947
948 /* Create null terminated string */
949 from_length= strmake(buff, suffix->str, suffix->length) - buff;
950 /* Convert to characters usable in a file name */
951 res_length= strconvert(system_charset_info, buff, from_length,
952 &my_charset_filename, res, sizeof(res), &errors);
953
954 ext_pos= (size_t) (ext - info_file);
955 length-= (suffix->length - ext_pos); /* Leave place for extension */
956 p= res_file_name + ext_pos;
957 *p++= '-'; /* Add separator */
958 p= strmake(p, res, MY_MIN((size_t) (length - (p - res_file_name)),
959 res_length));
960 /* Add back extension. We have checked above that there is space for it */
961 strmov(p, ext);
962 }
963}
964
965void copy_filter_setting(Rpl_filter* dst_filter, Rpl_filter* src_filter)
966{
967 char buf[256];
968 String tmp(buf, sizeof(buf), &my_charset_bin);
969
970 dst_filter->get_do_db(&tmp);
971 if (tmp.is_empty())
972 {
973 src_filter->get_do_db(&tmp);
974 if (!tmp.is_empty())
975 dst_filter->set_do_db(tmp.ptr());
976 }
977
978 dst_filter->get_do_table(&tmp);
979 if (tmp.is_empty())
980 {
981 src_filter->get_do_table(&tmp);
982 if (!tmp.is_empty())
983 dst_filter->set_do_table(tmp.ptr());
984 }
985
986 dst_filter->get_ignore_db(&tmp);
987 if (tmp.is_empty())
988 {
989 src_filter->get_ignore_db(&tmp);
990 if (!tmp.is_empty())
991 dst_filter->set_ignore_db(tmp.ptr());
992 }
993
994 dst_filter->get_ignore_table(&tmp);
995 if (tmp.is_empty())
996 {
997 src_filter->get_ignore_table(&tmp);
998 if (!tmp.is_empty())
999 dst_filter->set_ignore_table(tmp.ptr());
1000 }
1001
1002 dst_filter->get_wild_do_table(&tmp);
1003 if (tmp.is_empty())
1004 {
1005 src_filter->get_wild_do_table(&tmp);
1006 if (!tmp.is_empty())
1007 dst_filter->set_wild_do_table(tmp.ptr());
1008 }
1009
1010 dst_filter->get_wild_ignore_table(&tmp);
1011 if (tmp.is_empty())
1012 {
1013 src_filter->get_wild_ignore_table(&tmp);
1014 if (!tmp.is_empty())
1015 dst_filter->set_wild_ignore_table(tmp.ptr());
1016 }
1017
1018 if (dst_filter->rewrite_db_is_empty())
1019 {
1020 if (!src_filter->rewrite_db_is_empty())
1021 dst_filter->copy_rewrite_db(src_filter);
1022 }
1023}
1024
1025Master_info_index::Master_info_index()
1026{
1027 size_t filename_length, dir_length;
1028 /*
1029 Create the Master_info index file by prepending 'multi-' before
1030 the master_info_file file name.
1031 */
1032 fn_format(index_file_name, master_info_file, mysql_data_home,
1033 "", MY_UNPACK_FILENAME);
1034 filename_length= strlen(index_file_name) + 1; /* Count 0 byte */
1035 dir_length= dirname_length(index_file_name);
1036 bmove_upp((uchar*) index_file_name + filename_length + 6,
1037 (uchar*) index_file_name + filename_length,
1038 filename_length - dir_length);
1039 memcpy(index_file_name + dir_length, "multi-", 6);
1040
1041 bzero((char*) &index_file, sizeof(index_file));
1042 index_file.file= -1;
1043}
1044
1045
1046/**
1047 Free all connection threads
1048
1049 This is done during early stages of shutdown
1050 to give connection threads and slave threads time
1051 to die before ~Master_info_index is called
1052*/
1053
1054void Master_info_index::free_connections()
1055{
1056 mysql_mutex_assert_owner(&LOCK_active_mi);
1057 my_hash_reset(&master_info_hash);
1058}
1059
1060
1061/**
1062 Free all connection threads and free structures
1063*/
1064
1065Master_info_index::~Master_info_index()
1066{
1067 my_hash_free(&master_info_hash);
1068 end_io_cache(&index_file);
1069 if (index_file.file >= 0)
1070 my_close(index_file.file, MYF(MY_WME));
1071}
1072
1073
1074/* Load All Master_info from master.info.index File
1075 * RETURN:
1076 * 0 - All Success
1077 * 1 - All Fail
1078 * 2 - Some Success, Some Fail
1079 */
1080
1081bool Master_info_index::init_all_master_info()
1082{
1083 int thread_mask;
1084 int err_num= 0, succ_num= 0; // The number of success read Master_info
1085 char sign[MAX_CONNECTION_NAME+1];
1086 File index_file_nr;
1087 THD *thd;
1088 DBUG_ENTER("init_all_master_info");
1089
1090 DBUG_ASSERT(master_info_index);
1091
1092 if ((index_file_nr= my_open(index_file_name,
1093 O_RDWR | O_CREAT | O_BINARY ,
1094 MYF(MY_WME | ME_NOREFRESH))) < 0 ||
1095 my_sync(index_file_nr, MYF(MY_WME)) ||
1096 init_io_cache(&index_file, index_file_nr,
1097 IO_SIZE, READ_CACHE,
1098 my_seek(index_file_nr,0L,MY_SEEK_END,MYF(0)),
1099 0, MYF(MY_WME | MY_WAIT_IF_FULL)))
1100 {
1101 if (index_file_nr >= 0)
1102 my_close(index_file_nr,MYF(0));
1103
1104 sql_print_error("Creation of Master_info index file '%s' failed",
1105 index_file_name);
1106 DBUG_RETURN(1);
1107 }
1108
1109 /* Initialize Master_info Hash Table */
1110 if (my_hash_init(&master_info_hash, system_charset_info,
1111 MAX_REPLICATION_THREAD, 0, 0,
1112 (my_hash_get_key) get_key_master_info,
1113 (my_hash_free_key)free_key_master_info, HASH_UNIQUE))
1114 {
1115 sql_print_error("Initializing Master_info hash table failed");
1116 DBUG_RETURN(1);
1117 }
1118
1119 thd= new THD(next_thread_id()); /* Needed by start_slave_threads */
1120 thd->thread_stack= (char*) &thd;
1121 thd->store_globals();
1122
1123 reinit_io_cache(&index_file, READ_CACHE, 0L,0,0);
1124 while (!init_strvar_from_file(sign, sizeof(sign),
1125 &index_file, NULL))
1126 {
1127 LEX_CSTRING connection_name;
1128 Master_info *mi;
1129 char buf_master_info_file[FN_REFLEN];
1130 char buf_relay_log_info_file[FN_REFLEN];
1131
1132 connection_name.str= sign;
1133 connection_name.length= strlen(sign);
1134 if (!(mi= new Master_info(&connection_name, relay_log_recovery)) ||
1135 mi->error())
1136 {
1137 delete mi;
1138 goto error;
1139 }
1140
1141 init_thread_mask(&thread_mask,mi,0 /*not inverse*/);
1142
1143 create_logfile_name_with_suffix(buf_master_info_file,
1144 sizeof(buf_master_info_file),
1145 master_info_file, 0,
1146 &mi->cmp_connection_name);
1147 create_logfile_name_with_suffix(buf_relay_log_info_file,
1148 sizeof(buf_relay_log_info_file),
1149 relay_log_info_file, 0,
1150 &mi->cmp_connection_name);
1151 if (global_system_variables.log_warnings > 1)
1152 sql_print_information("Reading Master_info: '%s' Relay_info:'%s'",
1153 buf_master_info_file, buf_relay_log_info_file);
1154
1155 mi->lock_slave_threads();
1156 if (init_master_info(mi, buf_master_info_file, buf_relay_log_info_file,
1157 0, thread_mask))
1158 {
1159 err_num++;
1160 sql_print_error("Initialized Master_info from '%s' failed",
1161 buf_master_info_file);
1162 if (!master_info_index->get_master_info(&connection_name,
1163 Sql_condition::WARN_LEVEL_NOTE))
1164 {
1165 /* Master_info is not in HASH; Add it */
1166 if (master_info_index->add_master_info(mi, FALSE))
1167 goto error;
1168 succ_num++;
1169 mi->unlock_slave_threads();
1170 }
1171 else
1172 {
1173 /* Master_info already in HASH */
1174 sql_print_error(ER_THD_OR_DEFAULT(current_thd,
1175 ER_CONNECTION_ALREADY_EXISTS),
1176 (int) connection_name.length, connection_name.str,
1177 (int) connection_name.length, connection_name.str);
1178 mi->unlock_slave_threads();
1179 delete mi;
1180 }
1181 continue;
1182 }
1183 else
1184 {
1185 /* Initialization of Master_info succeded. Add it to HASH */
1186 if (global_system_variables.log_warnings > 1)
1187 sql_print_information("Initialized Master_info from '%s'",
1188 buf_master_info_file);
1189 if (master_info_index->get_master_info(&connection_name,
1190 Sql_condition::WARN_LEVEL_NOTE))
1191 {
1192 /* Master_info was already registered */
1193 sql_print_error(ER_THD_OR_DEFAULT(current_thd,
1194 ER_CONNECTION_ALREADY_EXISTS),
1195 (int) connection_name.length, connection_name.str,
1196 (int) connection_name.length, connection_name.str);
1197 mi->unlock_slave_threads();
1198 delete mi;
1199 continue;
1200 }
1201
1202 /* Master_info was not registered; add it */
1203 if (master_info_index->add_master_info(mi, FALSE))
1204 goto error;
1205 succ_num++;
1206
1207 if (!opt_skip_slave_start)
1208 {
1209 if (start_slave_threads(current_thd,
1210 1 /* need mutex */,
1211 1 /* wait for start*/,
1212 mi,
1213 buf_master_info_file,
1214 buf_relay_log_info_file,
1215 SLAVE_IO | SLAVE_SQL))
1216 {
1217 sql_print_error("Failed to create slave threads for connection '%.*s'",
1218 (int) connection_name.length,
1219 connection_name.str);
1220 continue;
1221 }
1222 if (global_system_variables.log_warnings)
1223 sql_print_information("Started replication for '%.*s'",
1224 (int) connection_name.length,
1225 connection_name.str);
1226 }
1227 mi->unlock_slave_threads();
1228 }
1229 }
1230 thd->reset_globals();
1231 delete thd;
1232
1233 if (!err_num) // No Error on read Master_info
1234 {
1235 if (global_system_variables.log_warnings > 1)
1236 sql_print_information("Reading of all Master_info entries succeded");
1237 DBUG_RETURN(0);
1238 }
1239 if (succ_num) // Have some Error and some Success
1240 {
1241 sql_print_warning("Reading of some Master_info entries failed");
1242 DBUG_RETURN(1);
1243 }
1244
1245 sql_print_error("Reading of all Master_info entries failed!");
1246 DBUG_RETURN(1);
1247
1248error:
1249 thd->reset_globals();
1250 delete thd;
1251 DBUG_RETURN(1);
1252}
1253
1254
1255/* Write new master.info to master.info.index File */
1256bool Master_info_index::write_master_name_to_index_file(LEX_CSTRING *name,
1257 bool do_sync)
1258{
1259 DBUG_ASSERT(my_b_inited(&index_file) != 0);
1260 DBUG_ENTER("write_master_name_to_index_file");
1261
1262 /* Don't write default slave to master_info.index */
1263 if (name->length == 0)
1264 DBUG_RETURN(0);
1265
1266 reinit_io_cache(&index_file, WRITE_CACHE,
1267 my_b_filelength(&index_file), 0, 0);
1268
1269 if (my_b_write(&index_file, (uchar*) name->str, name->length) ||
1270 my_b_write(&index_file, (uchar*) "\n", 1) ||
1271 flush_io_cache(&index_file) ||
1272 (do_sync && my_sync(index_file.file, MYF(MY_WME))))
1273 {
1274 sql_print_error("Write of new Master_info for '%.*s' to index file failed",
1275 (int) name->length, name->str);
1276 DBUG_RETURN(1);
1277 }
1278
1279 DBUG_RETURN(0);
1280}
1281
1282
1283/**
1284 Get Master_info for a connection and lock the object from deletion
1285
1286 @param
1287 connection_name Connection name
1288 warning WARN_LEVEL_NOTE -> Don't print anything
1289 WARN_LEVEL_WARN -> Issue warning if not exists
1290 WARN_LEVEL_ERROR-> Issue error if not exists
1291*/
1292
1293Master_info *get_master_info(const LEX_CSTRING *connection_name,
1294 Sql_condition::enum_warning_level warning)
1295{
1296 Master_info *mi;
1297 DBUG_ENTER("get_master_info");
1298
1299 /* Protect against inserts into hash */
1300 mysql_mutex_lock(&LOCK_active_mi);
1301 /*
1302 The following can only be true during shutdown when slave has been killed
1303 but some other threads are still trying to access slave statistics.
1304 */
1305 if (unlikely(!master_info_index))
1306 {
1307 if (warning != Sql_condition::WARN_LEVEL_NOTE)
1308 my_error(WARN_NO_MASTER_INFO,
1309 MYF(warning == Sql_condition::WARN_LEVEL_WARN ?
1310 ME_JUST_WARNING : 0),
1311 (int) connection_name->length, connection_name->str);
1312 mysql_mutex_unlock(&LOCK_active_mi);
1313 DBUG_RETURN(0);
1314 }
1315 if ((mi= master_info_index->get_master_info(connection_name, warning)))
1316 {
1317 /*
1318 We have to use sleep_lock here. If we would use LOCK_active_mi
1319 then we would take locks in wrong order in Master_info::release()
1320 */
1321 mysql_mutex_lock(&mi->sleep_lock);
1322 mi->users++;
1323 DBUG_PRINT("info",("users: %d", mi->users));
1324 mysql_mutex_unlock(&mi->sleep_lock);
1325 }
1326 mysql_mutex_unlock(&LOCK_active_mi);
1327 DBUG_RETURN(mi);
1328}
1329
1330
1331/**
1332 Release master info.
1333 Signals ~Master_info that it's now safe to delete it
1334*/
1335
1336void Master_info::release()
1337{
1338 mysql_mutex_lock(&sleep_lock);
1339 if (!--users && killed)
1340 {
1341 /* Signal ~Master_info that it's ok to now free it */
1342 mysql_cond_signal(&sleep_cond);
1343 }
1344 mysql_mutex_unlock(&sleep_lock);
1345}
1346
1347
1348/**
1349 Get Master_info for a connection
1350
1351 @param
1352 connection_name Connection name
1353 warning WARN_LEVEL_NOTE -> Don't print anything
1354 WARN_LEVEL_WARN -> Issue warning if not exists
1355 WARN_LEVEL_ERROR-> Issue error if not exists
1356*/
1357
1358Master_info *
1359Master_info_index::get_master_info(const LEX_CSTRING *connection_name,
1360 Sql_condition::enum_warning_level warning)
1361{
1362 Master_info *mi;
1363 char buff[MAX_CONNECTION_NAME+1], *res;
1364 size_t buff_length;
1365 DBUG_ENTER("get_master_info");
1366 DBUG_PRINT("enter",
1367 ("connection_name: '%.*s'", (int) connection_name->length,
1368 connection_name->str));
1369
1370 /* Make name lower case for comparison */
1371 res= strmake(buff, connection_name->str, connection_name->length);
1372 my_casedn_str(system_charset_info, buff);
1373 buff_length= (size_t) (res-buff);
1374
1375 mi= (Master_info*) my_hash_search(&master_info_hash,
1376 (uchar*) buff, buff_length);
1377 if (!mi && warning != Sql_condition::WARN_LEVEL_NOTE)
1378 {
1379 my_error(WARN_NO_MASTER_INFO,
1380 MYF(warning == Sql_condition::WARN_LEVEL_WARN ? ME_JUST_WARNING :
1381 0),
1382 (int) connection_name->length,
1383 connection_name->str);
1384 }
1385 DBUG_RETURN(mi);
1386}
1387
1388
1389/* Check Master_host & Master_port is duplicated or not */
1390bool Master_info_index::check_duplicate_master_info(LEX_CSTRING *name_arg,
1391 const char *host,
1392 uint port)
1393{
1394 Master_info *mi;
1395 DBUG_ENTER("check_duplicate_master_info");
1396
1397 mysql_mutex_assert_owner(&LOCK_active_mi);
1398 DBUG_ASSERT(master_info_index);
1399
1400 /* Get full host and port name */
1401 if ((mi= master_info_index->get_master_info(name_arg,
1402 Sql_condition::WARN_LEVEL_NOTE)))
1403 {
1404 if (!host)
1405 host= mi->host;
1406 if (!port)
1407 port= mi->port;
1408 }
1409 if (!host || !port)
1410 DBUG_RETURN(FALSE); // Not comparable yet
1411
1412 for (uint i= 0; i < master_info_hash.records; ++i)
1413 {
1414 Master_info *tmp_mi;
1415 tmp_mi= (Master_info *) my_hash_element(&master_info_hash, i);
1416 if (tmp_mi == mi)
1417 continue; // Current connection
1418 if (!strcasecmp(host, tmp_mi->host) && port == tmp_mi->port)
1419 {
1420 my_error(ER_CONNECTION_ALREADY_EXISTS, MYF(0),
1421 (int) name_arg->length,
1422 name_arg->str,
1423 (int) tmp_mi->connection_name.length,
1424 tmp_mi->connection_name.str);
1425 DBUG_RETURN(TRUE);
1426 }
1427 }
1428 DBUG_RETURN(FALSE);
1429}
1430
1431
1432/* Add a Master_info class to Hash Table */
1433bool Master_info_index::add_master_info(Master_info *mi, bool write_to_file)
1434{
1435 /*
1436 We have to protect against shutdown to ensure we are not calling
1437 my_hash_insert() while my_hash_free() is in progress
1438 */
1439 if (unlikely(shutdown_in_progress) ||
1440 !my_hash_insert(&master_info_hash, (uchar*) mi))
1441 {
1442 if (global_system_variables.log_warnings > 1)
1443 sql_print_information("Added new Master_info '%.*s' to hash table",
1444 (int) mi->connection_name.length,
1445 mi->connection_name.str);
1446 if (write_to_file)
1447 return write_master_name_to_index_file(&mi->connection_name, 1);
1448 return FALSE;
1449 }
1450
1451 /* Impossible error (EOM) ? */
1452 sql_print_error("Adding new entry '%.*s' to master_info failed",
1453 (int) mi->connection_name.length,
1454 mi->connection_name.str);
1455 return TRUE;
1456}
1457
1458
1459/**
1460 Remove a Master_info class From Hash Table
1461
1462 TODO: Change this to use my_rename() to make the file name creation
1463 atomic
1464*/
1465
1466bool Master_info_index::remove_master_info(Master_info *mi)
1467{
1468 DBUG_ENTER("remove_master_info");
1469 mysql_mutex_assert_owner(&LOCK_active_mi);
1470
1471 // Delete Master_info and rewrite others to file
1472 if (!my_hash_delete(&master_info_hash, (uchar*) mi))
1473 {
1474 File index_file_nr;
1475
1476 // Close IO_CACHE and FILE handler fisrt
1477 end_io_cache(&index_file);
1478 my_close(index_file.file, MYF(MY_WME));
1479
1480 // Reopen File and truncate it
1481 if ((index_file_nr= my_open(index_file_name,
1482 O_RDWR | O_CREAT | O_TRUNC | O_BINARY ,
1483 MYF(MY_WME))) < 0 ||
1484 init_io_cache(&index_file, index_file_nr,
1485 IO_SIZE, WRITE_CACHE,
1486 my_seek(index_file_nr,0L,MY_SEEK_END,MYF(0)),
1487 0, MYF(MY_WME | MY_WAIT_IF_FULL)))
1488 {
1489 int error= my_errno;
1490 if (index_file_nr >= 0)
1491 my_close(index_file_nr,MYF(0));
1492
1493 sql_print_error("Create of Master Info Index file '%s' failed with "
1494 "error: %M",
1495 index_file_name, error);
1496 DBUG_RETURN(TRUE);
1497 }
1498
1499 // Rewrite Master_info.index
1500 for (uint i= 0; i< master_info_hash.records; ++i)
1501 {
1502 Master_info *tmp_mi;
1503 tmp_mi= (Master_info *) my_hash_element(&master_info_hash, i);
1504 write_master_name_to_index_file(&tmp_mi->connection_name, 0);
1505 }
1506 if (my_sync(index_file_nr, MYF(MY_WME)))
1507 DBUG_RETURN(TRUE);
1508 }
1509 DBUG_RETURN(FALSE);
1510}
1511
1512
1513/**
1514 give_error_if_slave_running()
1515
1516 @param
1517 already_locked 0 if we need to lock, 1 if we have LOCK_active_mi_locked
1518
1519 @return
1520 TRUE If some slave is running. An error is printed
1521 FALSE No slave is running
1522*/
1523
1524bool give_error_if_slave_running(bool already_locked)
1525{
1526 bool ret= 0;
1527 DBUG_ENTER("give_error_if_slave_running");
1528
1529 if (!already_locked)
1530 mysql_mutex_lock(&LOCK_active_mi);
1531 if (!master_info_index)
1532 {
1533 my_error(ER_SERVER_SHUTDOWN, MYF(0));
1534 ret= 1;
1535 }
1536 else
1537 {
1538 HASH *hash= &master_info_index->master_info_hash;
1539 for (uint i= 0; i< hash->records; ++i)
1540 {
1541 Master_info *mi;
1542 mi= (Master_info *) my_hash_element(hash, i);
1543 if (mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN)
1544 {
1545 my_error(ER_SLAVE_MUST_STOP, MYF(0), (int) mi->connection_name.length,
1546 mi->connection_name.str);
1547 ret= 1;
1548 break;
1549 }
1550 }
1551 }
1552 if (!already_locked)
1553 mysql_mutex_unlock(&LOCK_active_mi);
1554 DBUG_RETURN(ret);
1555}
1556
1557
1558/**
1559 any_slave_sql_running()
1560
1561 @param
1562 already_locked 0 if we need to lock, 1 if we have LOCK_active_mi_locked
1563
1564 @return
1565 0 No Slave SQL thread is running
1566 # Number of slave SQL thread running
1567
1568 Note that during shutdown we return 1. This is needed to ensure we
1569 don't try to resize thread pool during shutdown as during shutdown
1570 master_info_hash may be freeing the hash and during that time
1571 hash entries can't be accessed.
1572*/
1573
1574uint any_slave_sql_running(bool already_locked)
1575{
1576 uint count= 0;
1577 HASH *hash;
1578 DBUG_ENTER("any_slave_sql_running");
1579
1580 if (!already_locked)
1581 mysql_mutex_lock(&LOCK_active_mi);
1582 if (unlikely(shutdown_in_progress || !master_info_index))
1583 count= 1;
1584 else
1585 {
1586 hash= &master_info_index->master_info_hash;
1587 for (uint i= 0; i< hash->records; ++i)
1588 {
1589 Master_info *mi= (Master_info *)my_hash_element(hash, i);
1590 if (mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN)
1591 count++;
1592 }
1593 }
1594 if (!already_locked)
1595 mysql_mutex_unlock(&LOCK_active_mi);
1596 DBUG_RETURN(count);
1597}
1598
1599
1600/**
1601 Master_info_index::start_all_slaves()
1602
1603 Start all slaves that was not running.
1604
1605 @return
1606 TRUE Error
1607 FALSE Everything ok.
1608
1609 This code is written so that we don't keep LOCK_active_mi active
1610 while we are starting a slave.
1611*/
1612
1613bool Master_info_index::start_all_slaves(THD *thd)
1614{
1615 bool result= FALSE;
1616 DBUG_ENTER("start_all_slaves");
1617 mysql_mutex_assert_owner(&LOCK_active_mi);
1618
1619 for (uint i= 0; i< master_info_hash.records; i++)
1620 {
1621 Master_info *mi;
1622 mi= (Master_info *) my_hash_element(&master_info_hash, i);
1623 mi->in_start_all_slaves= 0;
1624 }
1625
1626 for (uint i= 0; i< master_info_hash.records; )
1627 {
1628 int error;
1629 Master_info *mi;
1630 mi= (Master_info *) my_hash_element(&master_info_hash, i);
1631
1632 /*
1633 Try to start all slaves that are configured (host is defined)
1634 and are not already running
1635 */
1636 if (!((mi->slave_running == MYSQL_SLAVE_NOT_RUN ||
1637 !mi->rli.slave_running) && *mi->host) ||
1638 mi->in_start_all_slaves)
1639 {
1640 i++;
1641 continue;
1642 }
1643 mi->in_start_all_slaves= 1;
1644
1645 mysql_mutex_lock(&mi->sleep_lock);
1646 mi->users++; // Mark used
1647 mysql_mutex_unlock(&mi->sleep_lock);
1648 mysql_mutex_unlock(&LOCK_active_mi);
1649 error= start_slave(thd, mi, 1);
1650 mi->release();
1651 mysql_mutex_lock(&LOCK_active_mi);
1652 if (unlikely(error))
1653 {
1654 my_error(ER_CANT_START_STOP_SLAVE, MYF(0),
1655 "START",
1656 (int) mi->connection_name.length,
1657 mi->connection_name.str);
1658 result= 1;
1659 if (error < 0) // fatal error
1660 break;
1661 }
1662 else if (thd)
1663 push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
1664 ER_SLAVE_STARTED, ER_THD(thd, ER_SLAVE_STARTED),
1665 (int) mi->connection_name.length,
1666 mi->connection_name.str);
1667 /* Restart from first element as master_info_hash may have changed */
1668 i= 0;
1669 continue;
1670 }
1671 DBUG_RETURN(result);
1672}
1673
1674
1675/**
1676 Master_info_index::stop_all_slaves()
1677
1678 Start all slaves that was not running.
1679
1680 @param thread id from user
1681
1682 @return
1683 TRUE Error
1684 FALSE Everything ok.
1685
1686 This code is written so that we don't keep LOCK_active_mi active
1687 while we are stopping a slave.
1688*/
1689
1690bool Master_info_index::stop_all_slaves(THD *thd)
1691{
1692 bool result= FALSE;
1693 DBUG_ENTER("stop_all_slaves");
1694 mysql_mutex_assert_owner(&LOCK_active_mi);
1695 DBUG_ASSERT(thd);
1696
1697 for (uint i= 0; i< master_info_hash.records; i++)
1698 {
1699 Master_info *mi;
1700 mi= (Master_info *) my_hash_element(&master_info_hash, i);
1701 mi->in_stop_all_slaves= 0;
1702 }
1703
1704 for (uint i= 0; i< master_info_hash.records ;)
1705 {
1706 int error;
1707 Master_info *mi;
1708 mi= (Master_info *) my_hash_element(&master_info_hash, i);
1709 if (!(mi->slave_running != MYSQL_SLAVE_NOT_RUN ||
1710 mi->rli.slave_running) ||
1711 mi->in_stop_all_slaves)
1712 {
1713 i++;
1714 continue;
1715 }
1716 mi->in_stop_all_slaves= 1; // Protection for loops
1717
1718 mysql_mutex_lock(&mi->sleep_lock);
1719 mi->users++; // Mark used
1720 mysql_mutex_unlock(&mi->sleep_lock);
1721 mysql_mutex_unlock(&LOCK_active_mi);
1722 error= stop_slave(thd, mi, 1);
1723 mi->release();
1724 mysql_mutex_lock(&LOCK_active_mi);
1725 if (unlikely(error))
1726 {
1727 my_error(ER_CANT_START_STOP_SLAVE, MYF(0),
1728 "STOP",
1729 (int) mi->connection_name.length,
1730 mi->connection_name.str);
1731 result= 1;
1732 if (error < 0) // Fatal error
1733 break;
1734 }
1735 else
1736 push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
1737 ER_SLAVE_STOPPED, ER_THD(thd, ER_SLAVE_STOPPED),
1738 (int) mi->connection_name.length,
1739 mi->connection_name.str);
1740 /* Restart from first element as master_info_hash may have changed */
1741 i= 0;
1742 continue;
1743 }
1744 DBUG_RETURN(result);
1745}
1746
1747Domain_id_filter::Domain_id_filter() : m_filter(false)
1748{
1749 for (int i= DO_DOMAIN_IDS; i <= IGNORE_DOMAIN_IDS; i ++)
1750 {
1751 my_init_dynamic_array(&m_domain_ids[i], sizeof(ulong), 16, 16, MYF(0));
1752 }
1753}
1754
1755Domain_id_filter::~Domain_id_filter()
1756{
1757 for (int i= DO_DOMAIN_IDS; i <= IGNORE_DOMAIN_IDS; i ++)
1758 {
1759 delete_dynamic(&m_domain_ids[i]);
1760 }
1761}
1762
1763/**
1764 Update m_filter flag for the current group by looking up its domain id in the
1765 domain ids list. DO_DOMAIN_IDS list is only looked-up is both (do & ignore)
1766 list are non-empty.
1767*/
1768void Domain_id_filter::do_filter(ulong domain_id)
1769{
1770 DYNAMIC_ARRAY *do_domain_ids= &m_domain_ids[DO_DOMAIN_IDS];
1771 DYNAMIC_ARRAY *ignore_domain_ids= &m_domain_ids[IGNORE_DOMAIN_IDS];
1772
1773 if (do_domain_ids->elements > 0)
1774 {
1775 if (likely(do_domain_ids->elements == 1))
1776 m_filter= ((* (ulong *) dynamic_array_ptr(do_domain_ids, 0))
1777 != domain_id);
1778 else
1779 m_filter= (bsearch((const ulong *) &domain_id, do_domain_ids->buffer,
1780 do_domain_ids->elements, sizeof(ulong),
1781 change_master_id_cmp) == NULL);
1782 }
1783 else if (ignore_domain_ids->elements > 0)
1784 {
1785 if (likely(ignore_domain_ids->elements == 1))
1786 m_filter= ((* (ulong *) dynamic_array_ptr(ignore_domain_ids, 0)) ==
1787 domain_id);
1788 else
1789 m_filter= (bsearch((const ulong *) &domain_id, ignore_domain_ids->buffer,
1790 ignore_domain_ids->elements, sizeof(ulong),
1791 change_master_id_cmp) != NULL);
1792 }
1793 return;
1794}
1795
1796/**
1797 Reset m_filter. It should be called when IO thread receives COMMIT_EVENT or
1798 XID_EVENT.
1799*/
1800void Domain_id_filter::reset_filter()
1801{
1802 m_filter= false;
1803}
1804
1805/**
1806 Update the do/ignore domain id filter lists.
1807
1808 @param do_ids [IN] domain ids to be kept
1809 @param ignore_ids [IN] domain ids to be filtered out
1810 @param using_gtid [IN] use GTID?
1811
1812 @retval false Success
1813 true Error
1814*/
1815bool Domain_id_filter::update_ids(DYNAMIC_ARRAY *do_ids,
1816 DYNAMIC_ARRAY *ignore_ids,
1817 bool using_gtid)
1818{
1819 bool do_list_empty, ignore_list_empty;
1820
1821 if (do_ids)
1822 {
1823 do_list_empty= (do_ids->elements > 0) ? false : true;
1824 } else {
1825 do_list_empty= (m_domain_ids[DO_DOMAIN_IDS].elements > 0) ? false : true;
1826 }
1827
1828 if (ignore_ids)
1829 {
1830 ignore_list_empty= (ignore_ids->elements > 0) ? false : true;
1831 } else {
1832 ignore_list_empty= (m_domain_ids[IGNORE_DOMAIN_IDS].elements > 0) ? false :
1833 true;
1834 }
1835
1836 if (!do_list_empty && !ignore_list_empty)
1837 {
1838 sql_print_error("Both DO_DOMAIN_IDS & IGNORE_DOMAIN_IDS lists can't be "
1839 "non-empty at the same time");
1840 return true;
1841 }
1842
1843 if (using_gtid == Master_info::USE_GTID_NO &&
1844 (!do_list_empty || !ignore_list_empty))
1845 {
1846 sql_print_error("DO_DOMAIN_IDS or IGNORE_DOMAIN_IDS lists can't be "
1847 "non-empty in non-GTID mode (MASTER_USE_GTID=no)");
1848 return true;
1849 }
1850
1851 if (do_ids)
1852 update_change_master_ids(do_ids, &m_domain_ids[DO_DOMAIN_IDS]);
1853
1854 if (ignore_ids)
1855 update_change_master_ids(ignore_ids, &m_domain_ids[IGNORE_DOMAIN_IDS]);
1856
1857 m_filter= false;
1858
1859 return false;
1860}
1861
1862/**
1863 Serialize and store the ids from domain id lists into the thd's protocol
1864 buffer.
1865
1866 @param thd [IN] thread handler
1867
1868 @retval void
1869*/
1870void Domain_id_filter::store_ids(THD *thd)
1871{
1872 for (int i= DO_DOMAIN_IDS; i <= IGNORE_DOMAIN_IDS; i ++)
1873 {
1874 prot_store_ids(thd, &m_domain_ids[i]);
1875 }
1876}
1877
1878/**
1879 Initialize the given domain_id list (DYNAMIC_ARRAY) with the
1880 space-separated list of numbers from the specified IO_CACHE where
1881 the first number represents the total number of entries to follows.
1882
1883 @param f [IN] IO_CACHE file
1884 @param type [IN] domain id list type
1885
1886 @retval false Success
1887 true Error
1888*/
1889bool Domain_id_filter::init_ids(IO_CACHE *f, enum_list_type type)
1890{
1891 return init_dynarray_intvar_from_file(&m_domain_ids[type], f);
1892}
1893
1894/**
1895 Return the elements of the give domain id list type as string.
1896
1897 @param type [IN] domain id list type
1898
1899 @retval a string buffer storing the total number
1900 of elements followed by the individual
1901 elements (space-separated) in the
1902 specified list.
1903
1904 Note: Its caller's responsibility to free the returned string buffer.
1905*/
1906char *Domain_id_filter::as_string(enum_list_type type)
1907{
1908 char *buf;
1909 size_t sz;
1910 DYNAMIC_ARRAY *ids= &m_domain_ids[type];
1911
1912 sz= (sizeof(ulong) * 3 + 1) * (1 + ids->elements);
1913
1914 if (!(buf= (char *) my_malloc(sz, MYF(MY_WME))))
1915 return NULL;
1916
1917 // Store the total number of elements followed by the individual elements.
1918 size_t cur_len= sprintf(buf, "%u", ids->elements);
1919 sz-= cur_len;
1920
1921 for (uint i= 0; i < ids->elements; i++)
1922 {
1923 ulong domain_id;
1924 get_dynamic(ids, (void *) &domain_id, i);
1925 cur_len+= my_snprintf(buf + cur_len, sz, " %lu", domain_id);
1926 sz-= cur_len;
1927 }
1928 return buf;
1929}
1930
1931void update_change_master_ids(DYNAMIC_ARRAY *new_ids, DYNAMIC_ARRAY *old_ids)
1932{
1933 reset_dynamic(old_ids);
1934
1935 /* bsearch requires an ordered list. */
1936 sort_dynamic(new_ids, change_master_id_cmp);
1937
1938 for (uint i= 0; i < new_ids->elements; i++)
1939 {
1940 ulong id;
1941 get_dynamic(new_ids, (void *) &id, i);
1942
1943 if (bsearch((const ulong *) &id, old_ids->buffer, old_ids->elements,
1944 sizeof(ulong), change_master_id_cmp) == NULL)
1945 {
1946 insert_dynamic(old_ids, (ulong *) &id);
1947 }
1948 }
1949 return;
1950}
1951
1952/**
1953 Serialize and store the ids from the given ids DYNAMIC_ARRAY into the thd's
1954 protocol buffer.
1955
1956 @param thd [IN] thread handler
1957 @param ids [IN] ids list
1958
1959 @retval void
1960*/
1961
1962void prot_store_ids(THD *thd, DYNAMIC_ARRAY *ids)
1963{
1964 char buff[FN_REFLEN];
1965 uint i, cur_len;
1966
1967 for (i= 0, buff[0]= 0, cur_len= 0; i < ids->elements; i++)
1968 {
1969 ulong id, len;
1970 char dbuff[FN_REFLEN];
1971 get_dynamic(ids, (void *) &id, i);
1972 len= sprintf(dbuff, (i == 0 ? "%lu" : ", %lu"), id);
1973 if (cur_len + len + 4 > FN_REFLEN)
1974 {
1975 /*
1976 break the loop whenever remained space could not fit
1977 ellipses on the next cycle
1978 */
1979 sprintf(dbuff + cur_len, "...");
1980 break;
1981 }
1982 cur_len += sprintf(buff + cur_len, "%s", dbuff);
1983 }
1984 thd->protocol->store(buff, &my_charset_bin);
1985 return;
1986}
1987
1988bool Master_info_index::flush_all_relay_logs()
1989{
1990 DBUG_ENTER("flush_all_relay_logs");
1991 bool result= false;
1992 int error= 0;
1993 mysql_mutex_lock(&LOCK_active_mi);
1994 for (uint i= 0; i< master_info_hash.records; i++)
1995 {
1996 Master_info *mi;
1997 mi= (Master_info *) my_hash_element(&master_info_hash, i);
1998 mi->in_flush_all_relay_logs= 0;
1999 }
2000 for (uint i=0; i < master_info_hash.records;)
2001 {
2002 Master_info *mi;
2003 mi= (Master_info *)my_hash_element(&master_info_hash, i);
2004 DBUG_ASSERT(mi);
2005
2006 if (mi->in_flush_all_relay_logs)
2007 {
2008 i++;
2009 continue;
2010 }
2011 mi->in_flush_all_relay_logs= 1;
2012
2013 mysql_mutex_lock(&mi->sleep_lock);
2014 mi->users++; // Mark used
2015 mysql_mutex_unlock(&mi->sleep_lock);
2016 mysql_mutex_unlock(&LOCK_active_mi);
2017
2018 mysql_mutex_lock(&mi->data_lock);
2019 error= rotate_relay_log(mi);
2020 mysql_mutex_unlock(&mi->data_lock);
2021 mi->release();
2022 mysql_mutex_lock(&LOCK_active_mi);
2023
2024 if (unlikely(error))
2025 {
2026 result= true;
2027 break;
2028 }
2029 /* Restart from first element as master_info_hash may have changed */
2030 i= 0;
2031 continue;
2032 }
2033 mysql_mutex_unlock(&LOCK_active_mi);
2034 DBUG_RETURN(result);
2035}
2036
2037#endif /* HAVE_REPLICATION */
2038