1 /* Copyright (c) 2000, 2013, 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 as published by
5    the Free Software Foundation; version 2 of the License.
6 
7    This program is distributed in the hope that it will be useful,
8    but WITHOUT ANY WARRANTY; without even the implied warranty of
9    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10    GNU General Public License for more details.
11 
12    You should have received a copy of the GNU General Public License
13    along with this program; if not, write to the Free Software
14    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335  USA */
15 
16 #include "mariadb.h"
17 #include "sql_priv.h"
18 #include "mysqld.h"                             // system_charset_info
19 #include "rpl_filter.h"
20 #include "hash.h"                               // my_hash_free
21 #include "table.h"                              // TABLE_LIST
22 
23 #define TABLE_RULE_HASH_SIZE   16
24 #define TABLE_RULE_ARR_SIZE   16
25 
Rpl_filter()26 Rpl_filter::Rpl_filter() :
27   parallel_mode(SLAVE_PARALLEL_CONSERVATIVE),
28   table_rules_on(0),
29   do_table_inited(0), ignore_table_inited(0),
30   wild_do_table_inited(0), wild_ignore_table_inited(0)
31 {
32   do_db.empty();
33   ignore_db.empty();
34   rewrite_db.empty();
35 }
36 
37 
~Rpl_filter()38 Rpl_filter::~Rpl_filter()
39 {
40   if (do_table_inited)
41     my_hash_free(&do_table);
42   if (ignore_table_inited)
43     my_hash_free(&ignore_table);
44   if (wild_do_table_inited)
45     free_string_array(&wild_do_table);
46   if (wild_ignore_table_inited)
47     free_string_array(&wild_ignore_table);
48   free_string_list(&do_db);
49   free_string_list(&ignore_db);
50   free_list(&rewrite_db);
51 }
52 
53 
54 #ifndef MYSQL_CLIENT
55 /*
56   Returns true if table should be logged/replicated
57 
58   SYNOPSIS
59     tables_ok()
60     db              db to use if db in TABLE_LIST is undefined for a table
61     tables          list of tables to check
62 
63   NOTES
64     Changing table order in the list can lead to different results.
65 
66     Note also order of precedence of do/ignore rules (see code).  For
67     that reason, users should not set conflicting rules because they
68     may get unpredicted results (precedence order is explained in the
69     manual).
70 
71     If no table in the list is marked "updating", then we always
72     return 0, because there is no reason to execute this statement on
73     slave if it updates nothing.  (Currently, this can only happen if
74     statement is a multi-delete (SQLCOM_DELETE_MULTI) and "tables" are
75     the tables in the FROM):
76 
77     In the case of SQLCOM_DELETE_MULTI, there will be a second call to
78     tables_ok(), with tables having "updating==TRUE" (those after the
79     DELETE), so this second call will make the decision (because
80     all_tables_not_ok() = !tables_ok(1st_list) &&
81     !tables_ok(2nd_list)).
82 
83   TODO
84     "Include all tables like "abc.%" except "%.EFG"". (Can't be done now.)
85     If we supported Perl regexps, we could do it with pattern: /^abc\.(?!EFG)/
86     (I could not find an equivalent in the regex library MySQL uses).
87 
88   RETURN VALUES
89     0           should not be logged/replicated
90     1           should be logged/replicated
91 */
92 
93 bool
tables_ok(const char * db,TABLE_LIST * tables)94 Rpl_filter::tables_ok(const char* db, TABLE_LIST* tables)
95 {
96   bool some_tables_updating= 0;
97   DBUG_ENTER("Rpl_filter::tables_ok");
98 
99   for (; tables; tables= tables->next_global)
100   {
101     char hash_key[SAFE_NAME_LEN*2+2];
102     char *end;
103     uint len;
104 
105     if (!tables->updating)
106       continue;
107     some_tables_updating= 1;
108     end= strmov(hash_key, tables->db.str ? tables->db.str : db);
109     *end++= '.';
110     len= (uint) (strmov(end, tables->table_name.str) - hash_key);
111     if (do_table_inited) // if there are any do's
112     {
113       if (my_hash_search(&do_table, (uchar*) hash_key, len))
114 	DBUG_RETURN(1);
115     }
116     if (ignore_table_inited) // if there are any ignores
117     {
118       if (my_hash_search(&ignore_table, (uchar*) hash_key, len))
119 	DBUG_RETURN(0);
120     }
121     if (wild_do_table_inited &&
122 	find_wild(&wild_do_table, hash_key, len))
123       DBUG_RETURN(1);
124     if (wild_ignore_table_inited &&
125 	find_wild(&wild_ignore_table, hash_key, len))
126       DBUG_RETURN(0);
127   }
128 
129   /*
130     If no table was to be updated, ignore statement (no reason we play it on
131     slave, slave is supposed to replicate _changes_ only).
132     If no explicit rule found and there was a do list, do not replicate.
133     If there was no do list, go ahead
134   */
135   DBUG_RETURN(some_tables_updating &&
136               !do_table_inited && !wild_do_table_inited);
137 }
138 
139 #endif
140 
141 /*
142   Checks whether a db matches some do_db and ignore_db rules
143 
144   SYNOPSIS
145     db_ok()
146     db              name of the db to check
147 
148   RETURN VALUES
149     0           should not be logged/replicated
150     1           should be logged/replicated
151 */
152 
153 bool
db_ok(const char * db)154 Rpl_filter::db_ok(const char* db)
155 {
156   DBUG_ENTER("Rpl_filter::db_ok");
157 
158   if (do_db.is_empty() && ignore_db.is_empty())
159     DBUG_RETURN(1); // Ok to replicate if the user puts no constraints
160 
161   /*
162     Previous behaviour "if the user has specified restrictions on which
163     databases to replicate and db was not selected, do not replicate" has
164     been replaced with "do replicate".
165     Since the filtering criteria is not equal to "NULL" the statement should
166     be logged into binlog.
167   */
168   if (!db)
169     DBUG_RETURN(1);
170 
171   if (!do_db.is_empty()) // if the do's are not empty
172   {
173     I_List_iterator<i_string> it(do_db);
174     i_string* tmp;
175 
176     while ((tmp=it++))
177     {
178       if (!strcmp(tmp->ptr, db))
179 	DBUG_RETURN(1); // match
180     }
181     DBUG_PRINT("exit", ("Don't replicate"));
182     DBUG_RETURN(0);
183   }
184   else // there are some elements in the don't, otherwise we cannot get here
185   {
186     I_List_iterator<i_string> it(ignore_db);
187     i_string* tmp;
188 
189     while ((tmp=it++))
190     {
191       if (!strcmp(tmp->ptr, db))
192       {
193         DBUG_PRINT("exit", ("Don't replicate"));
194 	DBUG_RETURN(0); // match
195       }
196     }
197     DBUG_RETURN(1);
198   }
199 }
200 
201 
202 /*
203   Checks whether a db matches wild_do_table and wild_ignore_table
204   rules (for replication)
205 
206   SYNOPSIS
207     db_ok_with_wild_table()
208     db		name of the db to check.
209 		Is tested with check_db_name() before calling this function.
210 
211   NOTES
212     Here is the reason for this function.
213     We advise users who want to exclude a database 'db1' safely to do it
214     with replicate_wild_ignore_table='db1.%' instead of binlog_ignore_db or
215     replicate_ignore_db because the two lasts only check for the selected db,
216     which won't work in that case:
217     USE db2;
218     UPDATE db1.t SET ... #this will be replicated and should not
219     whereas replicate_wild_ignore_table will work in all cases.
220     With replicate_wild_ignore_table, we only check tables. When
221     one does 'DROP DATABASE db1', tables are not involved and the
222     statement will be replicated, while users could expect it would not (as it
223     rougly means 'DROP db1.first_table, DROP db1.second_table...').
224     In other words, we want to interpret 'db1.%' as "everything touching db1".
225     That is why we want to match 'db1' against 'db1.%' wild table rules.
226 
227   RETURN VALUES
228     0           should not be logged/replicated
229     1           should be logged/replicated
230 */
231 
232 bool
db_ok_with_wild_table(const char * db)233 Rpl_filter::db_ok_with_wild_table(const char *db)
234 {
235   DBUG_ENTER("Rpl_filter::db_ok_with_wild_table");
236 
237   char hash_key[SAFE_NAME_LEN+2];
238   char *end;
239   int len;
240   end= strmov(hash_key, db);
241   *end++= '.';
242   len= (int)(end - hash_key);
243   if (wild_do_table_inited && find_wild(&wild_do_table, hash_key, len))
244   {
245     DBUG_PRINT("return",("1"));
246     DBUG_RETURN(1);
247   }
248   if (wild_ignore_table_inited && find_wild(&wild_ignore_table, hash_key, len))
249   {
250     DBUG_PRINT("return",("0"));
251     DBUG_RETURN(0);
252   }
253 
254   /*
255     If no explicit rule found and there was a do list, do not replicate.
256     If there was no do list, go ahead
257   */
258   DBUG_PRINT("return",("db=%s,retval=%d", db, !wild_do_table_inited));
259   DBUG_RETURN(!wild_do_table_inited);
260 }
261 
262 
263 bool
is_on()264 Rpl_filter::is_on()
265 {
266   return table_rules_on;
267 }
268 
269 
270 /**
271   Parse and add the given comma-separated sequence of filter rules.
272 
273   @param  spec  Comma-separated sequence of filter rules.
274   @param  add   Callback member function to add a filter rule.
275 
276   @return true if error, false otherwise.
277 */
278 
279 int
parse_filter_rule(const char * spec,Add_filter add)280 Rpl_filter::parse_filter_rule(const char* spec, Add_filter add)
281 {
282   int status= 0;
283   char *arg, *ptr, *pstr;
284 
285   if (!spec)
286     return false;
287 
288   if (! (ptr= my_strdup(spec, MYF(MY_WME))))
289     return true;
290 
291   pstr= ptr;
292 
293   while (pstr)
294   {
295     arg= pstr;
296 
297     /* Parse token string. */
298     pstr= strpbrk(arg, ",");
299 
300     /* NUL terminate the token string. */
301     if (pstr)
302       *pstr++= '\0';
303 
304     /* Skip an empty token string. */
305     if (arg[0] == '\0')
306       continue;
307 
308     /* Skip leading spaces.  */
309     while (my_isspace(system_charset_info, *arg))
310       arg++;
311 
312     status= (this->*add)(arg);
313 
314     if (status)
315       break;
316   }
317 
318   my_free(ptr);
319 
320   return status;
321 }
322 
323 
324 int
add_do_table(const char * table_spec)325 Rpl_filter::add_do_table(const char* table_spec)
326 {
327   DBUG_ENTER("Rpl_filter::add_do_table");
328   if (!do_table_inited)
329     init_table_rule_hash(&do_table, &do_table_inited);
330   table_rules_on= 1;
331   DBUG_RETURN(add_table_rule(&do_table, table_spec));
332 }
333 
334 
335 int
add_ignore_table(const char * table_spec)336 Rpl_filter::add_ignore_table(const char* table_spec)
337 {
338   DBUG_ENTER("Rpl_filter::add_ignore_table");
339   if (!ignore_table_inited)
340     init_table_rule_hash(&ignore_table, &ignore_table_inited);
341   table_rules_on= 1;
342   DBUG_RETURN(add_table_rule(&ignore_table, table_spec));
343 }
344 
345 
346 int
set_do_table(const char * table_spec)347 Rpl_filter::set_do_table(const char* table_spec)
348 {
349   int status;
350 
351   if (do_table_inited)
352   {
353     my_hash_free(&do_table);
354     do_table_inited= 0;
355   }
356 
357   status= parse_filter_rule(table_spec, &Rpl_filter::add_do_table);
358 
359   if (do_table_inited && status)
360   {
361     if (!do_table.records)
362     {
363       my_hash_free(&do_table);
364       do_table_inited= 0;
365     }
366   }
367 
368   return status;
369 }
370 
371 
372 int
set_ignore_table(const char * table_spec)373 Rpl_filter::set_ignore_table(const char* table_spec)
374 {
375   int status;
376 
377   if (ignore_table_inited)
378   {
379     my_hash_free(&ignore_table);
380     ignore_table_inited= 0;
381   }
382 
383   status= parse_filter_rule(table_spec, &Rpl_filter::add_ignore_table);
384 
385   if (ignore_table_inited && status)
386   {
387     if (!ignore_table.records)
388     {
389       my_hash_free(&ignore_table);
390       ignore_table_inited= 0;
391     }
392   }
393 
394   return status;
395 }
396 
397 
398 int
add_wild_do_table(const char * table_spec)399 Rpl_filter::add_wild_do_table(const char* table_spec)
400 {
401   DBUG_ENTER("Rpl_filter::add_wild_do_table");
402   if (!wild_do_table_inited)
403     init_table_rule_array(&wild_do_table, &wild_do_table_inited);
404   table_rules_on= 1;
405   DBUG_RETURN(add_wild_table_rule(&wild_do_table, table_spec));
406 }
407 
408 
409 int
add_wild_ignore_table(const char * table_spec)410 Rpl_filter::add_wild_ignore_table(const char* table_spec)
411 {
412   DBUG_ENTER("Rpl_filter::add_wild_ignore_table");
413   if (!wild_ignore_table_inited)
414     init_table_rule_array(&wild_ignore_table, &wild_ignore_table_inited);
415   table_rules_on= 1;
416   DBUG_RETURN(add_wild_table_rule(&wild_ignore_table, table_spec));
417 }
418 
419 
420 int
set_wild_do_table(const char * table_spec)421 Rpl_filter::set_wild_do_table(const char* table_spec)
422 {
423   int status;
424 
425   if (wild_do_table_inited)
426   {
427     free_string_array(&wild_do_table);
428     wild_do_table_inited= 0;
429   }
430 
431   status= parse_filter_rule(table_spec, &Rpl_filter::add_wild_do_table);
432 
433   if (wild_do_table_inited && status)
434   {
435     if (!wild_do_table.elements)
436     {
437       delete_dynamic(&wild_do_table);
438       wild_do_table_inited= 0;
439     }
440   }
441 
442   return status;
443 }
444 
445 
446 int
set_wild_ignore_table(const char * table_spec)447 Rpl_filter::set_wild_ignore_table(const char* table_spec)
448 {
449   int status;
450 
451   if (wild_ignore_table_inited)
452   {
453     free_string_array(&wild_ignore_table);
454     wild_ignore_table_inited= 0;
455   }
456 
457   status= parse_filter_rule(table_spec, &Rpl_filter::add_wild_ignore_table);
458 
459   if (wild_ignore_table_inited && status)
460   {
461     if (!wild_ignore_table.elements)
462     {
463       delete_dynamic(&wild_ignore_table);
464       wild_ignore_table_inited= 0;
465     }
466   }
467 
468   return status;
469 }
470 
471 
472 void
add_db_rewrite(const char * from_db,const char * to_db)473 Rpl_filter::add_db_rewrite(const char* from_db, const char* to_db)
474 {
475   i_string_pair *db_pair = new i_string_pair(from_db, to_db);
476   rewrite_db.push_back(db_pair);
477 }
478 
479 
480 int
add_table_rule(HASH * h,const char * table_spec)481 Rpl_filter::add_table_rule(HASH* h, const char* table_spec)
482 {
483   const char* dot = strchr(table_spec, '.');
484   if (!dot) return 1;
485   // len is always > 0 because we know the there exists a '.'
486   uint len = (uint)strlen(table_spec);
487   TABLE_RULE_ENT* e = (TABLE_RULE_ENT*)my_malloc(sizeof(TABLE_RULE_ENT)
488 						 + len, MYF(MY_WME));
489   if (!e) return 1;
490   e->db= (char*)e + sizeof(TABLE_RULE_ENT);
491   e->tbl_name= e->db + (dot - table_spec) + 1;
492   e->key_len= len;
493   memcpy(e->db, table_spec, len);
494 
495   return my_hash_insert(h, (uchar*)e);
496 }
497 
498 
499 /*
500   Add table expression with wildcards to dynamic array
501 */
502 
503 int
add_wild_table_rule(DYNAMIC_ARRAY * a,const char * table_spec)504 Rpl_filter::add_wild_table_rule(DYNAMIC_ARRAY* a, const char* table_spec)
505 {
506   const char* dot = strchr(table_spec, '.');
507   if (!dot) return 1;
508   uint len = (uint)strlen(table_spec);
509   TABLE_RULE_ENT* e = (TABLE_RULE_ENT*)my_malloc(sizeof(TABLE_RULE_ENT)
510 						 + len, MYF(MY_WME));
511   if (!e) return 1;
512   e->db= (char*)e + sizeof(TABLE_RULE_ENT);
513   e->tbl_name= e->db + (dot - table_spec) + 1;
514   e->key_len= len;
515   memcpy(e->db, table_spec, len);
516   return insert_dynamic(a, (uchar*)&e);
517 }
518 
519 
520 int
add_string_list(I_List<i_string> * list,const char * spec)521 Rpl_filter::add_string_list(I_List<i_string> *list, const char* spec)
522 {
523   char *str;
524   i_string *node;
525 
526   if (! (str= my_strdup(spec, MYF(MY_WME))))
527     return true;
528 
529   if (! (node= new i_string(str)))
530   {
531     my_free(str);
532     return true;
533   }
534 
535   list->push_back(node);
536 
537   return false;
538 }
539 
540 
541 int
add_do_db(const char * table_spec)542 Rpl_filter::add_do_db(const char* table_spec)
543 {
544   DBUG_ENTER("Rpl_filter::add_do_db");
545   DBUG_RETURN(add_string_list(&do_db, table_spec));
546 }
547 
548 
549 int
add_ignore_db(const char * table_spec)550 Rpl_filter::add_ignore_db(const char* table_spec)
551 {
552   DBUG_ENTER("Rpl_filter::add_ignore_db");
553   DBUG_RETURN(add_string_list(&ignore_db, table_spec));
554 }
555 
556 
557 int
set_do_db(const char * db_spec)558 Rpl_filter::set_do_db(const char* db_spec)
559 {
560   free_string_list(&do_db);
561   return parse_filter_rule(db_spec, &Rpl_filter::add_do_db);
562 }
563 
564 
565 int
set_ignore_db(const char * db_spec)566 Rpl_filter::set_ignore_db(const char* db_spec)
567 {
568   free_string_list(&ignore_db);
569   return parse_filter_rule(db_spec, &Rpl_filter::add_ignore_db);
570 }
571 
572 
573 extern "C" uchar *get_table_key(const uchar *, size_t *, my_bool);
574 extern "C" void free_table_ent(void* a);
575 
get_table_key(const uchar * a,size_t * len,my_bool)576 uchar *get_table_key(const uchar* a, size_t *len,
577                      my_bool __attribute__((unused)))
578 {
579   TABLE_RULE_ENT *e= (TABLE_RULE_ENT *) a;
580 
581   *len= e->key_len;
582   return (uchar*)e->db;
583 }
584 
585 
free_table_ent(void * a)586 void free_table_ent(void* a)
587 {
588   TABLE_RULE_ENT *e= (TABLE_RULE_ENT *) a;
589 
590   my_free(e);
591 }
592 
593 
594 void
init_table_rule_hash(HASH * h,bool * h_inited)595 Rpl_filter::init_table_rule_hash(HASH* h, bool* h_inited)
596 {
597   my_hash_init(h, system_charset_info,TABLE_RULE_HASH_SIZE,0,0,
598 	    get_table_key, free_table_ent, 0);
599   *h_inited = 1;
600 }
601 
602 
603 void
init_table_rule_array(DYNAMIC_ARRAY * a,bool * a_inited)604 Rpl_filter::init_table_rule_array(DYNAMIC_ARRAY* a, bool* a_inited)
605 {
606   my_init_dynamic_array(a, sizeof(TABLE_RULE_ENT*), TABLE_RULE_ARR_SIZE,
607 			TABLE_RULE_ARR_SIZE, MYF(0));
608   *a_inited = 1;
609 }
610 
611 
612 TABLE_RULE_ENT*
find_wild(DYNAMIC_ARRAY * a,const char * key,int len)613 Rpl_filter::find_wild(DYNAMIC_ARRAY *a, const char* key, int len)
614 {
615   uint i;
616   const char* key_end= key + len;
617 
618   for (i= 0; i < a->elements; i++)
619   {
620     TABLE_RULE_ENT* e ;
621     get_dynamic(a, (uchar*)&e, i);
622     if (!my_wildcmp(system_charset_info, key, key_end,
623 		    (const char*)e->db,
624 		    (const char*)(e->db + e->key_len),
625 		    '\\',wild_one,wild_many))
626       return e;
627   }
628 
629   return 0;
630 }
631 
632 
633 void
free_string_array(DYNAMIC_ARRAY * a)634 Rpl_filter::free_string_array(DYNAMIC_ARRAY *a)
635 {
636   uint i;
637   for (i= 0; i < a->elements; i++)
638   {
639     char* p;
640     get_dynamic(a, (uchar*) &p, i);
641     my_free(p);
642   }
643   delete_dynamic(a);
644 }
645 
646 
647 void
free_string_list(I_List<i_string> * l)648 Rpl_filter::free_string_list(I_List<i_string> *l)
649 {
650   void *ptr;
651   i_string *tmp;
652 
653   while ((tmp= l->get()))
654   {
655     ptr= (void *) tmp->ptr;
656     my_free(ptr);
657     delete tmp;
658   }
659 
660   l->empty();
661 }
662 
663 
664 /*
665   Builds a String from a HASH of TABLE_RULE_ENT. Cannot be used for any other
666   hash, as it assumes that the hash entries are TABLE_RULE_ENT.
667 
668   SYNOPSIS
669     table_rule_ent_hash_to_str()
670     s               pointer to the String to fill
671     h               pointer to the HASH to read
672 
673   RETURN VALUES
674     none
675 */
676 
677 void
table_rule_ent_hash_to_str(String * s,HASH * h,bool inited)678 Rpl_filter::table_rule_ent_hash_to_str(String* s, HASH* h, bool inited)
679 {
680   s->length(0);
681   if (inited)
682   {
683     for (uint i= 0; i < h->records; i++)
684     {
685       TABLE_RULE_ENT* e= (TABLE_RULE_ENT*) my_hash_element(h, i);
686       if (s->length())
687         s->append(',');
688       s->append(e->db,e->key_len);
689     }
690   }
691 }
692 
693 
694 void
table_rule_ent_dynamic_array_to_str(String * s,DYNAMIC_ARRAY * a,bool inited)695 Rpl_filter::table_rule_ent_dynamic_array_to_str(String* s, DYNAMIC_ARRAY* a,
696                                                 bool inited)
697 {
698   s->length(0);
699   if (inited)
700   {
701     for (uint i= 0; i < a->elements; i++)
702     {
703       TABLE_RULE_ENT* e;
704       get_dynamic(a, (uchar*)&e, i);
705       if (s->length())
706         s->append(',');
707       s->append(e->db,e->key_len);
708     }
709   }
710 }
711 
712 
713 void
get_do_table(String * str)714 Rpl_filter::get_do_table(String* str)
715 {
716   table_rule_ent_hash_to_str(str, &do_table, do_table_inited);
717 }
718 
719 
720 void
get_ignore_table(String * str)721 Rpl_filter::get_ignore_table(String* str)
722 {
723   table_rule_ent_hash_to_str(str, &ignore_table, ignore_table_inited);
724 }
725 
726 
727 void
get_wild_do_table(String * str)728 Rpl_filter::get_wild_do_table(String* str)
729 {
730   table_rule_ent_dynamic_array_to_str(str, &wild_do_table, wild_do_table_inited);
731 }
732 
733 
734 void
get_wild_ignore_table(String * str)735 Rpl_filter::get_wild_ignore_table(String* str)
736 {
737   table_rule_ent_dynamic_array_to_str(str, &wild_ignore_table, wild_ignore_table_inited);
738 }
739 
740 
741 bool
rewrite_db_is_empty()742 Rpl_filter::rewrite_db_is_empty()
743 {
744   return rewrite_db.is_empty();
745 }
746 
747 
748 const char*
get_rewrite_db(const char * db,size_t * new_len)749 Rpl_filter::get_rewrite_db(const char* db, size_t *new_len)
750 {
751   if (rewrite_db.is_empty() || !db)
752     return db;
753   I_List_iterator<i_string_pair> it(rewrite_db);
754   i_string_pair* tmp;
755 
756   while ((tmp=it++))
757   {
758     if (!strcmp(tmp->key, db))
759     {
760       *new_len= strlen(tmp->val);
761       return tmp->val;
762     }
763   }
764   return db;
765 }
766 
767 
768 void
copy_rewrite_db(Rpl_filter * from)769 Rpl_filter::copy_rewrite_db(Rpl_filter *from)
770 {
771   I_List_iterator<i_string_pair> it(from->rewrite_db);
772   i_string_pair* tmp;
773   DBUG_ASSERT(rewrite_db.is_empty());
774 
775   /* TODO: Add memory checking here and in all add_xxxx functions ! */
776   while ((tmp=it++))
777     add_db_rewrite(tmp->key, tmp->val);
778 }
779 
780 I_List<i_string>*
get_do_db()781 Rpl_filter::get_do_db()
782 {
783   return &do_db;
784 }
785 
786 
787 I_List<i_string>*
get_ignore_db()788 Rpl_filter::get_ignore_db()
789 {
790   return &ignore_db;
791 }
792 
793 
794 void
db_rule_ent_list_to_str(String * str,I_List<i_string> * list)795 Rpl_filter::db_rule_ent_list_to_str(String* str, I_List<i_string>* list)
796 {
797   I_List_iterator<i_string> it(*list);
798   i_string* s;
799 
800   str->length(0);
801 
802   while ((s= it++))
803   {
804     str->append(s->ptr);
805     str->append(',');
806   }
807 
808   // Remove last ','
809   if (!str->is_empty())
810     str->chop();
811 }
812 
813 
814 void
get_do_db(String * str)815 Rpl_filter::get_do_db(String* str)
816 {
817   db_rule_ent_list_to_str(str, get_do_db());
818 }
819 
820 
821 void
get_ignore_db(String * str)822 Rpl_filter::get_ignore_db(String* str)
823 {
824   db_rule_ent_list_to_str(str, get_ignore_db());
825 }
826