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-1301 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
26Rpl_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
38Rpl_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
93bool
94Rpl_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
153bool
154Rpl_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
232bool
233Rpl_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
263bool
264Rpl_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
279int
280Rpl_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
324int
325Rpl_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
335int
336Rpl_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
346int
347Rpl_filter::set_do_table(const char* table_spec)
348{
349 int status;
350
351 if (do_table_inited)
352 my_hash_reset(&do_table);
353
354 status= parse_filter_rule(table_spec, &Rpl_filter::add_do_table);
355
356 if (!do_table.records)
357 {
358 my_hash_free(&do_table);
359 do_table_inited= 0;
360 }
361
362 return status;
363}
364
365
366int
367Rpl_filter::set_ignore_table(const char* table_spec)
368{
369 int status;
370
371 if (ignore_table_inited)
372 my_hash_reset(&ignore_table);
373
374 status= parse_filter_rule(table_spec, &Rpl_filter::add_ignore_table);
375
376 if (!ignore_table.records)
377 {
378 my_hash_free(&ignore_table);
379 ignore_table_inited= 0;
380 }
381
382 return status;
383}
384
385
386int
387Rpl_filter::add_wild_do_table(const char* table_spec)
388{
389 DBUG_ENTER("Rpl_filter::add_wild_do_table");
390 if (!wild_do_table_inited)
391 init_table_rule_array(&wild_do_table, &wild_do_table_inited);
392 table_rules_on= 1;
393 DBUG_RETURN(add_wild_table_rule(&wild_do_table, table_spec));
394}
395
396
397int
398Rpl_filter::add_wild_ignore_table(const char* table_spec)
399{
400 DBUG_ENTER("Rpl_filter::add_wild_ignore_table");
401 if (!wild_ignore_table_inited)
402 init_table_rule_array(&wild_ignore_table, &wild_ignore_table_inited);
403 table_rules_on= 1;
404 DBUG_RETURN(add_wild_table_rule(&wild_ignore_table, table_spec));
405}
406
407
408int
409Rpl_filter::set_wild_do_table(const char* table_spec)
410{
411 int status;
412
413 if (wild_do_table_inited)
414 free_string_array(&wild_do_table);
415
416 status= parse_filter_rule(table_spec, &Rpl_filter::add_wild_do_table);
417
418 if (!wild_do_table.elements)
419 {
420 delete_dynamic(&wild_do_table);
421 wild_do_table_inited= 0;
422 }
423
424 return status;
425}
426
427
428int
429Rpl_filter::set_wild_ignore_table(const char* table_spec)
430{
431 int status;
432
433 if (wild_ignore_table_inited)
434 free_string_array(&wild_ignore_table);
435
436 status= parse_filter_rule(table_spec, &Rpl_filter::add_wild_ignore_table);
437
438 if (!wild_ignore_table.elements)
439 {
440 delete_dynamic(&wild_ignore_table);
441 wild_ignore_table_inited= 0;
442 }
443
444 return status;
445}
446
447
448void
449Rpl_filter::add_db_rewrite(const char* from_db, const char* to_db)
450{
451 i_string_pair *db_pair = new i_string_pair(from_db, to_db);
452 rewrite_db.push_back(db_pair);
453}
454
455
456int
457Rpl_filter::add_table_rule(HASH* h, const char* table_spec)
458{
459 const char* dot = strchr(table_spec, '.');
460 if (!dot) return 1;
461 // len is always > 0 because we know the there exists a '.'
462 uint len = (uint)strlen(table_spec);
463 TABLE_RULE_ENT* e = (TABLE_RULE_ENT*)my_malloc(sizeof(TABLE_RULE_ENT)
464 + len, MYF(MY_WME));
465 if (!e) return 1;
466 e->db= (char*)e + sizeof(TABLE_RULE_ENT);
467 e->tbl_name= e->db + (dot - table_spec) + 1;
468 e->key_len= len;
469 memcpy(e->db, table_spec, len);
470
471 return my_hash_insert(h, (uchar*)e);
472}
473
474
475/*
476 Add table expression with wildcards to dynamic array
477*/
478
479int
480Rpl_filter::add_wild_table_rule(DYNAMIC_ARRAY* a, const char* table_spec)
481{
482 const char* dot = strchr(table_spec, '.');
483 if (!dot) return 1;
484 uint len = (uint)strlen(table_spec);
485 TABLE_RULE_ENT* e = (TABLE_RULE_ENT*)my_malloc(sizeof(TABLE_RULE_ENT)
486 + len, MYF(MY_WME));
487 if (!e) return 1;
488 e->db= (char*)e + sizeof(TABLE_RULE_ENT);
489 e->tbl_name= e->db + (dot - table_spec) + 1;
490 e->key_len= len;
491 memcpy(e->db, table_spec, len);
492 return insert_dynamic(a, (uchar*)&e);
493}
494
495
496int
497Rpl_filter::add_string_list(I_List<i_string> *list, const char* spec)
498{
499 char *str;
500 i_string *node;
501
502 if (! (str= my_strdup(spec, MYF(MY_WME))))
503 return true;
504
505 if (! (node= new i_string(str)))
506 {
507 my_free(str);
508 return true;
509 }
510
511 list->push_back(node);
512
513 return false;
514}
515
516
517int
518Rpl_filter::add_do_db(const char* table_spec)
519{
520 DBUG_ENTER("Rpl_filter::add_do_db");
521 DBUG_RETURN(add_string_list(&do_db, table_spec));
522}
523
524
525int
526Rpl_filter::add_ignore_db(const char* table_spec)
527{
528 DBUG_ENTER("Rpl_filter::add_ignore_db");
529 DBUG_RETURN(add_string_list(&ignore_db, table_spec));
530}
531
532
533int
534Rpl_filter::set_do_db(const char* db_spec)
535{
536 free_string_list(&do_db);
537 return parse_filter_rule(db_spec, &Rpl_filter::add_do_db);
538}
539
540
541int
542Rpl_filter::set_ignore_db(const char* db_spec)
543{
544 free_string_list(&ignore_db);
545 return parse_filter_rule(db_spec, &Rpl_filter::add_ignore_db);
546}
547
548
549extern "C" uchar *get_table_key(const uchar *, size_t *, my_bool);
550extern "C" void free_table_ent(void* a);
551
552uchar *get_table_key(const uchar* a, size_t *len,
553 my_bool __attribute__((unused)))
554{
555 TABLE_RULE_ENT *e= (TABLE_RULE_ENT *) a;
556
557 *len= e->key_len;
558 return (uchar*)e->db;
559}
560
561
562void free_table_ent(void* a)
563{
564 TABLE_RULE_ENT *e= (TABLE_RULE_ENT *) a;
565
566 my_free(e);
567}
568
569
570void
571Rpl_filter::init_table_rule_hash(HASH* h, bool* h_inited)
572{
573 my_hash_init(h, system_charset_info,TABLE_RULE_HASH_SIZE,0,0,
574 get_table_key, free_table_ent, 0);
575 *h_inited = 1;
576}
577
578
579void
580Rpl_filter::init_table_rule_array(DYNAMIC_ARRAY* a, bool* a_inited)
581{
582 my_init_dynamic_array(a, sizeof(TABLE_RULE_ENT*), TABLE_RULE_ARR_SIZE,
583 TABLE_RULE_ARR_SIZE, MYF(0));
584 *a_inited = 1;
585}
586
587
588TABLE_RULE_ENT*
589Rpl_filter::find_wild(DYNAMIC_ARRAY *a, const char* key, int len)
590{
591 uint i;
592 const char* key_end= key + len;
593
594 for (i= 0; i < a->elements; i++)
595 {
596 TABLE_RULE_ENT* e ;
597 get_dynamic(a, (uchar*)&e, i);
598 if (!my_wildcmp(system_charset_info, key, key_end,
599 (const char*)e->db,
600 (const char*)(e->db + e->key_len),
601 '\\',wild_one,wild_many))
602 return e;
603 }
604
605 return 0;
606}
607
608
609void
610Rpl_filter::free_string_array(DYNAMIC_ARRAY *a)
611{
612 uint i;
613 for (i= 0; i < a->elements; i++)
614 {
615 char* p;
616 get_dynamic(a, (uchar*) &p, i);
617 my_free(p);
618 }
619 delete_dynamic(a);
620}
621
622
623void
624Rpl_filter::free_string_list(I_List<i_string> *l)
625{
626 void *ptr;
627 i_string *tmp;
628
629 while ((tmp= l->get()))
630 {
631 ptr= (void *) tmp->ptr;
632 my_free(ptr);
633 delete tmp;
634 }
635
636 l->empty();
637}
638
639
640/*
641 Builds a String from a HASH of TABLE_RULE_ENT. Cannot be used for any other
642 hash, as it assumes that the hash entries are TABLE_RULE_ENT.
643
644 SYNOPSIS
645 table_rule_ent_hash_to_str()
646 s pointer to the String to fill
647 h pointer to the HASH to read
648
649 RETURN VALUES
650 none
651*/
652
653void
654Rpl_filter::table_rule_ent_hash_to_str(String* s, HASH* h, bool inited)
655{
656 s->length(0);
657 if (inited)
658 {
659 for (uint i= 0; i < h->records; i++)
660 {
661 TABLE_RULE_ENT* e= (TABLE_RULE_ENT*) my_hash_element(h, i);
662 if (s->length())
663 s->append(',');
664 s->append(e->db,e->key_len);
665 }
666 }
667}
668
669
670void
671Rpl_filter::table_rule_ent_dynamic_array_to_str(String* s, DYNAMIC_ARRAY* a,
672 bool inited)
673{
674 s->length(0);
675 if (inited)
676 {
677 for (uint i= 0; i < a->elements; i++)
678 {
679 TABLE_RULE_ENT* e;
680 get_dynamic(a, (uchar*)&e, i);
681 if (s->length())
682 s->append(',');
683 s->append(e->db,e->key_len);
684 }
685 }
686}
687
688
689void
690Rpl_filter::get_do_table(String* str)
691{
692 table_rule_ent_hash_to_str(str, &do_table, do_table_inited);
693}
694
695
696void
697Rpl_filter::get_ignore_table(String* str)
698{
699 table_rule_ent_hash_to_str(str, &ignore_table, ignore_table_inited);
700}
701
702
703void
704Rpl_filter::get_wild_do_table(String* str)
705{
706 table_rule_ent_dynamic_array_to_str(str, &wild_do_table, wild_do_table_inited);
707}
708
709
710void
711Rpl_filter::get_wild_ignore_table(String* str)
712{
713 table_rule_ent_dynamic_array_to_str(str, &wild_ignore_table, wild_ignore_table_inited);
714}
715
716
717bool
718Rpl_filter::rewrite_db_is_empty()
719{
720 return rewrite_db.is_empty();
721}
722
723
724const char*
725Rpl_filter::get_rewrite_db(const char* db, size_t *new_len)
726{
727 if (rewrite_db.is_empty() || !db)
728 return db;
729 I_List_iterator<i_string_pair> it(rewrite_db);
730 i_string_pair* tmp;
731
732 while ((tmp=it++))
733 {
734 if (!strcmp(tmp->key, db))
735 {
736 *new_len= strlen(tmp->val);
737 return tmp->val;
738 }
739 }
740 return db;
741}
742
743
744void
745Rpl_filter::copy_rewrite_db(Rpl_filter *from)
746{
747 I_List_iterator<i_string_pair> it(from->rewrite_db);
748 i_string_pair* tmp;
749 DBUG_ASSERT(rewrite_db.is_empty());
750
751 /* TODO: Add memory checking here and in all add_xxxx functions ! */
752 while ((tmp=it++))
753 add_db_rewrite(tmp->key, tmp->val);
754}
755
756I_List<i_string>*
757Rpl_filter::get_do_db()
758{
759 return &do_db;
760}
761
762
763I_List<i_string>*
764Rpl_filter::get_ignore_db()
765{
766 return &ignore_db;
767}
768
769
770void
771Rpl_filter::db_rule_ent_list_to_str(String* str, I_List<i_string>* list)
772{
773 I_List_iterator<i_string> it(*list);
774 i_string* s;
775
776 str->length(0);
777
778 while ((s= it++))
779 {
780 str->append(s->ptr);
781 str->append(',');
782 }
783
784 // Remove last ','
785 if (!str->is_empty())
786 str->chop();
787}
788
789
790void
791Rpl_filter::get_do_db(String* str)
792{
793 db_rule_ent_list_to_str(str, get_do_db());
794}
795
796
797void
798Rpl_filter::get_ignore_db(String* str)
799{
800 db_rule_ent_list_to_str(str, get_ignore_db());
801}
802