1 /* Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License, version 2.0, for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
22 
23 #include <my_global.h>
24 #include "sql_priv.h"
25 #include "rpl_slave.h"
26 #include "rpl_info_factory.h"
27 
28 /*
29   Defines meta information on diferent repositories.
30 */
31 Rpl_info_factory::struct_table_data Rpl_info_factory::rli_table_data;
32 Rpl_info_factory::struct_file_data Rpl_info_factory::rli_file_data;
33 Rpl_info_factory::struct_table_data Rpl_info_factory::mi_table_data;
34 Rpl_info_factory::struct_file_data Rpl_info_factory::mi_file_data;
35 Rpl_info_factory::struct_file_data Rpl_info_factory::worker_file_data;
36 Rpl_info_factory::struct_table_data Rpl_info_factory::worker_table_data;
37 
38 /**
39   Creates both a Master info and a Relay log info repository whose types are
40   defined as parameters. Nothing is done for Workers here.
41 
42   @todo Make the repository a pluggable component.
43   @todo Use generic programming to make it easier and clearer to
44         add a new repositories' types and Rpl_info objects.
45 
46   @param[in]  mi_option  Type of the Master info repository.
47   @param[out] mi         Reference to the Master_info.
48   @param[in]  rli_option Type of the Relay log info repository.
49   @param[out] rli        Reference to the Relay_log_info.
50 
51   @retval FALSE No error
52   @retval TRUE  Failure
53 */
create_coordinators(uint mi_option,Master_info ** mi,uint rli_option,Relay_log_info ** rli)54 bool Rpl_info_factory::create_coordinators(uint mi_option, Master_info **mi,
55                                            uint rli_option, Relay_log_info **rli)
56 {
57   DBUG_ENTER("Rpl_info_factory::create_coordinators");
58 
59   Rpl_info_factory::init_repository_metadata();
60 
61   if (!((*mi)= Rpl_info_factory::create_mi(mi_option)))
62     DBUG_RETURN(TRUE);
63 
64   if (!((*rli)= Rpl_info_factory::create_rli(rli_option, relay_log_recovery)))
65   {
66     delete *mi;
67     *mi= NULL;
68     DBUG_RETURN(TRUE);
69   }
70 
71   /*
72     Setting the cross dependency used all over the code.
73   */
74   (*mi)->set_relay_log_info(*rli);
75   (*rli)->set_master_info(*mi);
76 
77   DBUG_RETURN(FALSE);
78 }
79 
80 /**
81   Creates a Master info repository whose type is defined as a parameter.
82 
83   @param[in]  mi_option Type of the repository, e.g. FILE TABLE.
84 
85   The execution fails if a user requests a type but a different type
86   already exists in the system. This is done to avoid that a user
87   accidentally accesses the wrong repository and makes the slave go out
88   of sync.
89 
90   @retval Pointer to Master_info Success
91   @retval NULL  Failure
92 */
create_mi(uint mi_option)93 Master_info *Rpl_info_factory::create_mi(uint mi_option)
94 {
95   Master_info* mi= NULL;
96   Rpl_info_handler*  handler_src= NULL;
97   Rpl_info_handler*  handler_dest= NULL;
98   uint instances= 1;
99   const char *msg= "Failed to allocate memory for the master info "
100                    "structure";
101 
102   DBUG_ENTER("Rpl_info_factory::create_mi");
103 
104   if (!(mi= new Master_info(
105 #ifdef HAVE_PSI_INTERFACE
106                             &key_master_info_run_lock,
107                             &key_master_info_data_lock,
108                             &key_master_info_sleep_lock,
109                             &key_master_info_data_cond,
110                             &key_master_info_start_cond,
111                             &key_master_info_stop_cond,
112                             &key_master_info_sleep_cond,
113 #endif
114                             instances
115                            )))
116     goto err;
117 
118   if(init_repositories(mi_table_data, mi_file_data, mi_option, instances,
119                        &handler_src, &handler_dest, &msg))
120     goto err;
121 
122   if (decide_repository(mi, mi_option, &handler_src, &handler_dest, &msg))
123     goto err;
124 
125   DBUG_RETURN(mi);
126 
127 err:
128   delete handler_src;
129   delete handler_dest;
130   if (mi)
131   {
132     /*
133       The handler was previously deleted so we need to remove
134       any reference to it.
135     */
136     mi->set_rpl_info_handler(NULL);
137     delete mi;
138   }
139   sql_print_error("Error creating master info: %s.", msg);
140   DBUG_RETURN(NULL);
141 }
142 
143 /**
144   Allows to change the master info repository after startup.
145 
146   @param[in]  mi        Reference to Master_info.
147   @param[in]  mi_option Type of the repository, e.g. FILE TABLE.
148   @param[out] msg       Error message if something goes wrong.
149 
150   @retval FALSE No error
151   @retval TRUE  Failure
152 */
change_mi_repository(Master_info * mi,uint mi_option,const char ** msg)153 bool Rpl_info_factory::change_mi_repository(Master_info *mi,
154                                             uint mi_option,
155                                             const char **msg)
156 {
157   Rpl_info_handler*  handler_src= mi->get_rpl_info_handler();
158   Rpl_info_handler*  handler_dest= NULL;
159   uint instances= 1;
160   DBUG_ENTER("Rpl_info_factory::change_mi_repository");
161 
162   DBUG_ASSERT(handler_src);
163   if (handler_src->get_rpl_info_type() == mi_option)
164     DBUG_RETURN(false);
165 
166   if (init_repositories(mi_table_data, mi_file_data, mi_option, instances,
167                         NULL, &handler_dest, msg))
168     goto err;
169 
170   if (decide_repository(mi, mi_option, &handler_src, &handler_dest, msg))
171     goto err;
172 
173   DBUG_RETURN(FALSE);
174 
175 err:
176   delete handler_dest;
177   handler_dest= NULL;
178 
179   sql_print_error("Error changing the type of master info's repository: %s.", *msg);
180   DBUG_RETURN(TRUE);
181 }
182 
183 /**
184   Creates a Relay log info repository whose type is defined as a parameter.
185 
186   @param[in]  rli_option        Type of the Relay log info repository
187   @param[in]  is_slave_recovery If the slave should try to start a recovery
188                                 process to get consistent relay log files
189 
190   The execution fails if a user requests a type but a different type
191   already exists in the system. This is done to avoid that a user
192   accidentally accesses the wrong repository and make the slave go out
193   of sync.
194 
195   @retval Pointer to Relay_log_info Success
196   @retval NULL  Failure
197 */
create_rli(uint rli_option,bool is_slave_recovery)198 Relay_log_info *Rpl_info_factory::create_rli(uint rli_option, bool is_slave_recovery)
199 {
200   Relay_log_info *rli= NULL;
201   Rpl_info_handler* handler_src= NULL;
202   Rpl_info_handler* handler_dest= NULL;
203   uint instances= 1;
204   uint worker_repository= INVALID_INFO_REPOSITORY;
205   uint worker_instances= 1;
206   const char *msg= NULL;
207   const char *msg_alloc= "Failed to allocate memory for the relay log info "
208     "structure";
209 
210   DBUG_ENTER("Rpl_info_factory::create_rli");
211 
212   /*
213     Returns how many occurrences of rli's repositories exist. For example,
214     if the repository is a table, this retrieves the number of rows in it.
215     Besides, it also returns the type of the repository where entries were
216     found.
217   */
218   if (rli_option != INFO_REPOSITORY_DUMMY &&
219       scan_repositories(&worker_instances, &worker_repository,
220                         worker_table_data, worker_file_data, &msg))
221     goto err;
222 
223   try
224   {
225     rli= new Relay_log_info(is_slave_recovery
226 #ifdef HAVE_PSI_INTERFACE
227                                 ,&key_relay_log_info_run_lock,
228                                 &key_relay_log_info_data_lock,
229                                 &key_relay_log_info_sleep_lock,
230                                 &key_relay_log_info_data_cond,
231                                 &key_relay_log_info_start_cond,
232                                 &key_relay_log_info_stop_cond,
233                                 &key_relay_log_info_sleep_cond
234 #endif
235                                 , instances
236                                 );
237   } catch (std::bad_alloc&)
238   {
239     msg= msg_alloc;
240     goto err;
241   }
242 
243   if(init_repositories(rli_table_data, rli_file_data, rli_option, instances,
244                        &handler_src, &handler_dest, &msg))
245     goto err;
246 
247   if (rli_option != INFO_REPOSITORY_DUMMY &&
248       worker_repository != INVALID_INFO_REPOSITORY &&
249       worker_repository != rli_option)
250   {
251     opt_rli_repository_id= rli_option= worker_repository;
252     sql_print_warning("It is not possible to change the type of the relay log "
253                       "repository because there are workers repositories with "
254                       "possible execution gaps. "
255                       "The value of --relay_log_info_repository is altered to "
256                       "one of the found Worker repositories. "
257                       "The gaps have to be sorted out before resuming with "
258                       "the type change.");
259     std::swap(handler_src, handler_dest);
260   }
261   if (decide_repository(rli, rli_option, &handler_src, &handler_dest, &msg))
262     goto err;
263 
264   DBUG_RETURN(rli);
265 
266 err:
267   delete handler_src;
268   delete handler_dest;
269   if (rli)
270   {
271     /*
272       The handler was previously deleted so we need to remove
273       any reference to it.
274     */
275     rli->set_rpl_info_handler(NULL);
276     delete rli;
277   }
278   sql_print_error("Error creating relay log info: %s.", msg);
279   DBUG_RETURN(NULL);
280 }
281 
282 /**
283   Allows to change the relay log info repository after startup.
284 
285   @param[in]  mi        Pointer to Relay_log_info.
286   @param[in]  mi_option Type of the repository, e.g. FILE TABLE.
287   @param[out] msg       Error message if something goes wrong.
288 
289   @retval FALSE No error
290   @retval TRUE  Failure
291 */
change_rli_repository(Relay_log_info * rli,uint rli_option,const char ** msg)292 bool Rpl_info_factory::change_rli_repository(Relay_log_info *rli,
293                                              uint rli_option,
294                                              const char **msg)
295 {
296   Rpl_info_handler*  handler_src= rli->get_rpl_info_handler();
297   Rpl_info_handler*  handler_dest= NULL;
298   uint instances= 1;
299   DBUG_ENTER("Rpl_info_factory::change_rli_repository");
300 
301   DBUG_ASSERT(handler_src != NULL);
302 
303   if (handler_src->get_rpl_info_type() == rli_option)
304     DBUG_RETURN(false);
305 
306   if (init_repositories(rli_table_data, rli_file_data, rli_option,
307                         instances, NULL, &handler_dest, msg))
308     goto err;
309 
310   if (decide_repository(rli, rli_option, &handler_src, &handler_dest,
311                         msg))
312     goto err;
313 
314   DBUG_RETURN(FALSE);
315 
316 err:
317   delete handler_dest;
318   handler_dest= NULL;
319 
320   sql_print_error("Error changing the type of relay log info's repository: %s.", *msg);
321   DBUG_RETURN(TRUE);
322 }
323 
324 /**
325    Delete all info from Worker info tables to render them useless in
326    future MTS recovery, and indicate that in Coordinator info table.
327 
328    @return false on success, true when a failure in deletion or writing
329            to Coordinator table fails.
330 */
reset_workers(Relay_log_info * rli)331 bool Rpl_info_factory::reset_workers(Relay_log_info *rli)
332 {
333   bool error= true;
334 
335   DBUG_ENTER("Rpl_info_factory::reset_workers");
336 
337   if (rli->recovery_parallel_workers == 0)
338     DBUG_RETURN(0);
339 
340   if (Rpl_info_file::do_reset_info(Slave_worker::get_number_worker_fields(),
341                                    worker_file_data.pattern,
342                                    worker_file_data.name_indexed))
343     goto err;
344 
345   if (Rpl_info_table::do_reset_info(Slave_worker::get_number_worker_fields(),
346                                     MYSQL_SCHEMA_NAME.str, WORKER_INFO_NAME.str))
347     goto err;
348 
349   error= false;
350 
351   DBUG_EXECUTE_IF("mts_debug_reset_workers_fails", error= true;);
352 
353 err:
354   if (error)
355     sql_print_error("Could not delete from Slave Workers info repository.");
356   rli->recovery_parallel_workers= 0;
357   rli->clear_mts_recovery_groups();
358   if (rli->flush_info(true))
359   {
360     error= true;
361     sql_print_error("Could not store the reset Slave Worker state into "
362                     "the slave info repository.");
363   }
364   DBUG_RETURN(error);
365 }
366 
367 /**
368   Creates a Slave worker repository whose type is defined as a parameter.
369 
370   @param[in]  rli_option Type of the repository, e.g. FILE TABLE.
371   @param[in]  rli        Pointer to Relay_log_info.
372 
373   The execution fails if a user requests a type but a different type
374   already exists in the system. This is done to avoid that a user
375   accidentally accesses the wrong repository and make the slave go out
376   of sync.
377 
378   @retval Pointer to Slave_worker Success
379   @retval NULL  Failure
380 */
create_worker(uint rli_option,uint worker_id,Relay_log_info * rli,bool is_gaps_collecting_phase)381 Slave_worker *Rpl_info_factory::create_worker(uint rli_option, uint worker_id,
382                                               Relay_log_info *rli,
383                                               bool is_gaps_collecting_phase)
384 {
385   Rpl_info_handler* handler_src= NULL;
386   Rpl_info_handler* handler_dest= NULL;
387   Slave_worker* worker= NULL;
388   const char *msg= "Failed to allocate memory for the worker info "
389                    "structure";
390 
391   DBUG_ENTER("Rpl_info_factory::create_worker");
392 
393   /*
394     Define the name of the worker and its repository.
395   */
396   char *pos= strmov(worker_file_data.name, worker_file_data.pattern);
397   sprintf(pos, "%u", worker_id + 1);
398 
399   try
400   {
401     worker= new Slave_worker(rli
402 #ifdef HAVE_PSI_INTERFACE
403                                  ,&key_relay_log_info_run_lock,
404                                  &key_relay_log_info_data_lock,
405                                  &key_relay_log_info_sleep_lock,
406                                  &key_relay_log_info_data_cond,
407                                  &key_relay_log_info_start_cond,
408                                  &key_relay_log_info_stop_cond,
409                                  &key_relay_log_info_sleep_cond
410 #endif
411                                  , worker_id
412                                 );
413   } catch (std::bad_alloc&)
414   {
415     goto err;
416   }
417 
418   if(init_repositories(worker_table_data, worker_file_data, rli_option,
419                        worker_id + 1, &handler_src, &handler_dest, &msg))
420     goto err;
421 
422   if (decide_repository(worker, rli_option, &handler_src, &handler_dest, &msg))
423     goto err;
424 
425   if (DBUG_EVALUATE_IF("mts_worker_thread_init_fails", 1, 0) ||
426       worker->rli_init_info(is_gaps_collecting_phase))
427   {
428     DBUG_EXECUTE_IF("enable_mts_worker_failure_init",
429                     {
430                       DBUG_SET("-d,mts_worker_thread_init_fails");
431                       DBUG_SET("-d,enable_mts_worker_failure_init");
432                     });
433     DBUG_EXECUTE_IF("enable_mts_wokrer_failure_in_recovery_finalize",
434                     {
435                       DBUG_SET("-d,mts_worker_thread_init_fails");
436                       DBUG_SET("-d,enable_mts_wokrer_failure_in_recovery_finalize");
437                     });
438     msg= "Failed to initialize the worker info structure";
439     goto err;
440   }
441 
442   if (rli->info_thd && rli->info_thd->is_error())
443   {
444     msg= "Failed to initialize worker info table";
445     goto err;
446   }
447 
448   DBUG_RETURN(worker);
449 
450 err:
451   delete handler_src;
452   delete handler_dest;
453   if (worker)
454   {
455     /*
456       The handler was previously deleted so we need to remove
457       any reference to it.
458     */
459     worker->set_rpl_info_handler(NULL);
460     delete worker;
461   }
462   sql_print_error("Error creating relay log info: %s.", msg);
463   DBUG_RETURN(NULL);
464 }
465 
build_worker_info_name(char * to,const char * path,const char * fname)466 static void build_worker_info_name(char* to,
467                                    const char* path,
468                                    const char* fname)
469 {
470   DBUG_ASSERT(to);
471   char* pos= to;
472   if (path[0])
473     pos= strmov(pos, path);
474   pos= strmov(pos, "worker-");
475   pos= strmov(pos, fname);
476   strmov(pos, ".");
477 }
478 
479 /**
480   Initializes startup information on diferent repositories.
481 */
init_repository_metadata()482 void Rpl_info_factory::init_repository_metadata()
483 {
484   /* Needed for the file names and paths for worker info files. */
485   size_t len;
486   char* relay_log_info_file_name;
487   char relay_log_info_file_dirpart[FN_REFLEN];
488 
489   /* Extract the directory name from relay_log_info_file */
490   dirname_part(relay_log_info_file_dirpart, relay_log_info_file, &len);
491   relay_log_info_file_name= relay_log_info_file + len;
492 
493   rli_table_data.n_fields= Relay_log_info::get_number_info_rli_fields();
494   rli_table_data.schema= MYSQL_SCHEMA_NAME.str;
495   rli_table_data.name= RLI_INFO_NAME.str;
496   rli_file_data.n_fields= Relay_log_info::get_number_info_rli_fields();
497   strmov(rli_file_data.name, relay_log_info_file);
498   strmov(rli_file_data.pattern, relay_log_info_file);
499   rli_file_data.name_indexed= false;
500 
501   mi_table_data.n_fields= Master_info::get_number_info_mi_fields();
502   mi_table_data.schema= MYSQL_SCHEMA_NAME.str;
503   mi_table_data.name= MI_INFO_NAME.str;
504   mi_file_data.n_fields= Master_info::get_number_info_mi_fields();
505   strmov(mi_file_data.name, master_info_file);
506   strmov(mi_file_data.pattern, master_info_file);
507   rli_file_data.name_indexed= false;
508 
509   worker_table_data.n_fields= Slave_worker::get_number_worker_fields();
510   worker_table_data.schema= MYSQL_SCHEMA_NAME.str;
511   worker_table_data.name= WORKER_INFO_NAME.str;
512   worker_file_data.n_fields= Slave_worker::get_number_worker_fields();
513   build_worker_info_name(worker_file_data.name,
514                          relay_log_info_file_dirpart,
515                          relay_log_info_file_name);
516   build_worker_info_name(worker_file_data.pattern,
517                          relay_log_info_file_dirpart,
518                          relay_log_info_file_name);
519   worker_file_data.name_indexed= true;
520 }
521 
522 
523 /**
524   Decides during startup what repository will be used based on the following
525   decision table:
526 
527   \code
528   |--------------+-----------------------+-----------------------|
529   | Exists \ Opt |         SOURCE        |      DESTINATION      |
530   |--------------+-----------------------+-----------------------|
531   | ~is_s, ~is_d |            -          | Create/Update D       |
532   | ~is_s,  is_d |            -          | Continue with D       |
533   |  is_s, ~is_d | Copy S into D         | Create/Update D       |
534   |  is_s,  is_d | Error                 | Error                 |
535   |--------------+-----------------------+-----------------------|
536   \endcode
537 
538   @param[in]  info         Either master info or relay log info.
539   @param[in]  option       Identifies the type of the repository that will
540                            be used, i.e., destination repository.
541   @param[out] handler_src  Source repository from where information is
542                            copied into the destination repository.
543   @param[out] handler_dest Destination repository to where informaiton is
544                            copied.
545   @param[out] msg          Error message if something goes wrong.
546 
547   @retval FALSE No error
548   @retval TRUE  Failure
549 */
decide_repository(Rpl_info * info,uint option,Rpl_info_handler ** handler_src,Rpl_info_handler ** handler_dest,const char ** msg)550 bool Rpl_info_factory::decide_repository(Rpl_info *info, uint option,
551                                          Rpl_info_handler **handler_src,
552                                          Rpl_info_handler **handler_dest,
553                                          const char **msg)
554 {
555   bool error= true;
556   enum_return_check return_check_src= ERROR_CHECKING_REPOSITORY;
557   enum_return_check return_check_dst= ERROR_CHECKING_REPOSITORY;
558   bool binlog_prot_acquired= false;
559   THD * const thd= current_thd;
560   DBUG_ENTER("Rpl_info_factory::decide_repository");
561 
562   if (option == INFO_REPOSITORY_DUMMY)
563   {
564     delete (*handler_src);
565     *handler_src= NULL;
566     info->set_rpl_info_handler(*handler_dest);
567     error = false;
568     goto err;
569   }
570 
571   DBUG_ASSERT((*handler_src) != NULL && (*handler_dest) != NULL &&
572               (*handler_src) != (*handler_dest));
573 
574   return_check_src= check_src_repository(info, option, handler_src);
575   return_check_dst= (*handler_dest)->do_check_info(info->get_internal_id());
576 
577   if (return_check_src == ERROR_CHECKING_REPOSITORY ||
578       return_check_dst == ERROR_CHECKING_REPOSITORY)
579   {
580     /*
581       If there is a problem with one of the repositories we print out
582       more information and exit.
583     */
584     DBUG_RETURN(check_error_repository(info, *handler_src, *handler_dest,
585                                        return_check_src,
586                                        return_check_dst, msg));
587   }
588   else
589   {
590     if ((return_check_src == REPOSITORY_EXISTS &&
591         return_check_dst == REPOSITORY_DOES_NOT_EXIST) ||
592         (return_check_src == REPOSITORY_EXISTS &&
593         return_check_dst == REPOSITORY_EXISTS))
594     {
595       /*
596         If there is no error, we can proceed with the normal operation.
597         However, if both repositories are set an error will be printed
598         out.
599       */
600       if (return_check_src == REPOSITORY_EXISTS &&
601         return_check_dst == REPOSITORY_EXISTS)
602       {
603         *msg= "Multiple replication metadata repository instances "
604               "found with data in them. Unable to decide which is "
605               "the correct one to choose";
606         goto err;
607       }
608 
609       /*
610         Do a low-level initialization to be able to do a state transfer.
611       */
612       if (init_repositories(info, handler_src, handler_dest, msg))
613         goto err;
614 
615       /*
616         Acquire a backup binlog protection lock, because Rpl_info::copy_info()
617         will modify master binary log coordinates.
618       */
619       if (thd && !thd->backup_binlog_lock.is_acquired())
620       {
621         const ulong timeout= thd->variables.lock_wait_timeout;
622 
623         DBUG_PRINT("debug", ("Acquiring binlog protection lock"));
624 
625         if (thd->backup_binlog_lock.acquire_protection(thd, MDL_EXPLICIT,
626                                                        timeout))
627           goto err;
628 
629         binlog_prot_acquired= true;
630       }
631 
632       /*
633         Transfer information from source to destination and delete the
634         source. Note this is not fault-tolerant and a crash before removing
635         source may cause the next restart to fail as is_src and is_dest may
636         be true. Moreover, any failure in removing the source may lead to
637         the same.
638         /Alfranio
639       */
640       if (info->copy_info(*handler_src, *handler_dest) ||
641         (*handler_dest)->flush_info(true))
642       {
643         *msg= "Error transfering information";
644         goto err;
645       }
646       (*handler_src)->end_info();
647       if ((*handler_src)->remove_info())
648       {
649         *msg= "Error removing old repository";
650         goto err;
651       }
652     }
653     else if (return_check_src == REPOSITORY_DOES_NOT_EXIST &&
654              return_check_dst == REPOSITORY_EXISTS)
655     {
656       DBUG_ASSERT(info->get_rpl_info_handler() == NULL);
657       if ((*handler_dest)->do_init_info(info->get_internal_id()))
658       {
659         *msg= "Error reading repository";
660         goto err;
661       }
662     }
663     else
664     {
665       DBUG_ASSERT(return_check_src == REPOSITORY_DOES_NOT_EXIST &&
666                   return_check_dst == REPOSITORY_DOES_NOT_EXIST);
667       info->inited= false;
668     }
669 
670     delete (*handler_src);
671     *handler_src= NULL;
672     info->set_rpl_info_handler(*handler_dest);
673     error= false;
674   }
675 
676 err:
677   if (binlog_prot_acquired)
678   {
679     DBUG_PRINT("debug", ("Releasing binlog protection lock"));
680     thd->backup_binlog_lock.release_protection(thd);
681   }
682   DBUG_RETURN(error);
683 }
684 
685 
686 /**
687   This method is called by the decide_repository() and is used to check if
688   the source repository exits.
689 
690   @param[in]  info         Either master info or relay log info.
691   @param[in]  option       Identifies the type of the repository that will
692                            be used, i.e., destination repository.
693   @param[out] handler_src  Source repository from where information is
694 
695   @return enum_return_check The repository's status.
696 */
697 enum_return_check
check_src_repository(Rpl_info * info,uint option,Rpl_info_handler ** handler_src)698 Rpl_info_factory::check_src_repository(Rpl_info *info,
699                                        uint option,
700                                        Rpl_info_handler **handler_src)
701 {
702   enum_return_check return_check_src= ERROR_CHECKING_REPOSITORY;
703   bool live_migration = info->get_rpl_info_handler() != NULL;
704 
705   if (!live_migration)
706   {
707     /*
708       This is not a live migration and we don't know whether the repository
709       exists or not.
710     */
711     return_check_src= (*handler_src)->do_check_info(info->get_internal_id());
712 
713     /*
714       Since this is not a live migration, if we are using file repository
715       and there is some error on table repository (for instance, engine
716       disabled) we can ignore it instead of stopping replication.
717       A warning saying that table is not ready to be used was logged.
718     */
719     if (ERROR_CHECKING_REPOSITORY == return_check_src &&
720         INFO_REPOSITORY_FILE == option &&
721         INFO_REPOSITORY_TABLE == (*handler_src)->do_get_rpl_info_type())
722     {
723       return_check_src= REPOSITORY_DOES_NOT_EXIST;
724       /*
725         If a already existent thread was used to access info tables,
726         current_thd will point to it and we must clear access error on
727         it. If a temporary thread was used, then there is nothing to
728         clean because the thread was already deleted.
729         See Rpl_info_table_access::create_thd().
730       */
731       if (current_thd)
732         current_thd->clear_error();
733     }
734   }
735   else
736   {
737     /*
738       This is a live migration as the repository is already associated to.
739       However, we cannot assume that it really exists, for instance, if a
740       file was really created.
741 
742       This situation may happen when we start a slave for the first time
743       but skips its initialization and tries to migrate it.
744     */
745     return_check_src= (*handler_src)->do_check_info();
746   }
747 
748   return return_check_src;
749 }
750 
751 
752 /**
753   This method is called by the decide_repository() and is used print out
754   information on errors.
755 
756   @param  info         Either master info or relay log info.
757   @param  handler_src  Source repository from where information is
758                        copied into the destination repository.
759   @param  handler_dest Destination repository to where informaiton is
760                        copied.
761   @param  err_src      Possible error status of the source repo check
762   @param  err_dst      Possible error status of the destination repo check
763   @param[out] msg      Error message if something goes wrong.
764 
765   @retval TRUE  Failure
766 */
check_error_repository(Rpl_info * info,Rpl_info_handler * handler_src,Rpl_info_handler * handler_dest,enum_return_check err_src,enum_return_check err_dst,const char ** msg)767 bool Rpl_info_factory::check_error_repository(Rpl_info *info,
768                                               Rpl_info_handler *handler_src,
769                                               Rpl_info_handler *handler_dest,
770                                               enum_return_check err_src,
771                                               enum_return_check err_dst,
772                                               const char **msg)
773 {
774   bool error = true;
775 
776   /*
777     If there is an error in any of the source or destination
778     repository checks, the normal operation can't be proceeded.
779     The runtime repository won't be initialized.
780   */
781   if (err_src == ERROR_CHECKING_REPOSITORY)
782     sql_print_error("Error in checking %s repository info type of %s.",
783                     handler_src->get_description_info(),
784                     handler_src->get_rpl_info_type_str());
785   if (err_dst == ERROR_CHECKING_REPOSITORY)
786     sql_print_error("Error in checking %s repository info type of %s.",
787                     handler_dest->get_description_info(),
788                     handler_dest->get_rpl_info_type_str());
789   *msg= "Error checking repositories";
790   return error;
791 }
792 
793 
794 /**
795   This method is called by the decide_repository() and is used to initialize
796   the repositories through a low-level interfacei, which means that if they
797   do not exist nothing will be created.
798 
799   @param[in]  info         Either master info or relay log info.
800   @param[out] handler_src  Source repository from where information is
801                            copied into the destination repository.
802   @param[out] handler_dest Destination repository to where informaiton is
803                            copied.
804   @param[out] msg          Error message if something goes wrong.
805 
806   @retval FALSE No error
807   @retval TRUE  Failure
808 */
init_repositories(Rpl_info * info,Rpl_info_handler ** handler_src,Rpl_info_handler ** handler_dest,const char ** msg)809 bool Rpl_info_factory::init_repositories(Rpl_info *info,
810                                          Rpl_info_handler **handler_src,
811                                          Rpl_info_handler **handler_dest,
812                                          const char **msg)
813 {
814   bool live_migration = info->get_rpl_info_handler() != NULL;
815 
816   if (!live_migration)
817   {
818     if ((*handler_src)->do_init_info(info->get_internal_id()) ||
819         (*handler_dest)->do_init_info(info->get_internal_id()))
820     {
821       *msg= "Error transfering information";
822       return true;
823     }
824   }
825   else
826   {
827     if ((*handler_dest)->do_init_info(info->get_internal_id()))
828     {
829       *msg= "Error transfering information";
830       return true;
831     }
832   }
833   return false;
834 }
835 
836 
837 /**
838   Creates repositories that will be associated to either the Master_info
839   or Relay_log_info.
840 
841   @param[in] table_data    Defines information to create a table repository.
842   @param[in] file_data     Defines information to create a file repository.
843   @param[in] rep_option    Identifies the type of the repository that will
844                            be used, i.e., destination repository.
845   @param[in] instance      Identifies the instance of the repository that
846                            will be used.
847   @param[out] handler_src  Source repository from where information is
848                            copied into the destination repository.
849   @param[out] handler_dest Destination repository to where informaiton is
850                            copied.
851   @param[out] msg          Error message if something goes wrong.
852 
853   @retval FALSE No error
854   @retval TRUE  Failure
855 */
init_repositories(const struct_table_data table_data,const struct_file_data file_data,uint rep_option,uint instance,Rpl_info_handler ** handler_src,Rpl_info_handler ** handler_dest,const char ** msg)856 bool Rpl_info_factory::init_repositories(const struct_table_data table_data,
857                                          const struct_file_data file_data,
858                                          uint rep_option,
859                                          uint instance,
860                                          Rpl_info_handler **handler_src,
861                                          Rpl_info_handler **handler_dest,
862                                          const char **msg)
863 {
864   bool error= TRUE;
865   *msg= "Failed to allocate memory for master info repositories";
866 
867   DBUG_ENTER("Rpl_info_factory::init_mi_repositories");
868 
869   DBUG_ASSERT(handler_dest != NULL);
870   switch (rep_option)
871   {
872     case INFO_REPOSITORY_FILE:
873       if (!(*handler_dest= new Rpl_info_file(file_data.n_fields,
874                                              file_data.pattern,
875                                              file_data.name,
876                                              file_data.name_indexed)))
877         goto err;
878       if (handler_src &&
879           !(*handler_src= new Rpl_info_table(table_data.n_fields,
880                                              table_data.schema,
881                                              table_data.name)))
882         goto err;
883     break;
884 
885     case INFO_REPOSITORY_TABLE:
886       if (!(*handler_dest= new Rpl_info_table(table_data.n_fields,
887                                               table_data.schema,
888                                               table_data.name)))
889         goto err;
890       if (handler_src &&
891           !(*handler_src= new Rpl_info_file(file_data.n_fields,
892                                             file_data.pattern,
893                                             file_data.name,
894                                             file_data.name_indexed)))
895         goto err;
896     break;
897 
898     case INFO_REPOSITORY_DUMMY:
899       if (!(*handler_dest= new Rpl_info_dummy(Master_info::get_number_info_mi_fields())))
900         goto err;
901     break;
902 
903     default:
904       DBUG_ASSERT(0);
905   }
906   error= FALSE;
907 
908 err:
909   DBUG_RETURN(error);
910 }
911 
scan_repositories(uint * found_instances,uint * found_rep_option,const struct_table_data table_data,const struct_file_data file_data,const char ** msg)912 bool Rpl_info_factory::scan_repositories(uint* found_instances,
913                                          uint* found_rep_option,
914                                          const struct_table_data table_data,
915                                          const struct_file_data file_data,
916                                          const char **msg)
917 {
918   bool error= false;
919   uint file_instances= 0;
920   uint table_instances= 0;
921   DBUG_ASSERT(found_rep_option != NULL);
922 
923   DBUG_ENTER("Rpl_info_factory::scan_repositories");
924 
925   if (Rpl_info_table::do_count_info(table_data.n_fields, table_data.schema,
926                                     table_data.name, &table_instances))
927   {
928     error= true;
929     goto err;
930   }
931 
932   if (Rpl_info_file::do_count_info(file_data.n_fields, file_data.pattern,
933                                    file_data.name_indexed, &file_instances))
934   {
935     error= true;
936     goto err;
937   }
938 
939   if (file_instances != 0 && table_instances != 0)
940   {
941     error= true;
942     *msg= "Multiple repository instances found with data in "
943       "them. Unable to decide which is the correct one to "
944       "choose";
945     goto err;
946   }
947 
948   if (table_instances != 0)
949   {
950     *found_instances= table_instances;
951     *found_rep_option= INFO_REPOSITORY_TABLE;
952   }
953   else if (file_instances != 0)
954   {
955     *found_instances= file_instances;
956     *found_rep_option= INFO_REPOSITORY_FILE;
957   }
958   else
959   {
960     *found_instances= 0;
961     *found_rep_option= INVALID_INFO_REPOSITORY;
962   }
963 
964 err:
965   DBUG_RETURN(error);
966 }
967