Merge lp:~sergei.glushchenko/percona-xtrabackup/xb20-databases into lp:percona-xtrabackup/2.0

Proposed by Sergei Glushchenko
Status: Superseded
Proposed branch: lp:~sergei.glushchenko/percona-xtrabackup/xb20-databases
Merge into: lp:percona-xtrabackup/2.0
Diff against target: 1012 lines (+706/-130)
8 files modified
doc/source/innobackupex/partial_backups_innobackupex.rst (+1/-4)
doc/source/xtrabackup_bin/partial_backups.rst (+6/-1)
doc/source/xtrabackup_bin/xbk_option_reference.rst (+8/-0)
innobackupex (+7/-0)
src/xtrabackup.c (+356/-125)
test/t/ib_databases.sh (+50/-0)
test/t/ib_databases_file.sh (+57/-0)
test/t/xb_databases_options.sh (+221/-0)
To merge this branch: bzr merge lp:~sergei.glushchenko/percona-xtrabackup/xb20-databases
Reviewer Review Type Date Requested Status
Laurynas Biveinis (community) Needs Fixing
Review via email: mp+143063@code.launchpad.net

Description of the change

Bug 569387
xtrabackup ignores --databases, i.e. when --databases
is specified it affects only innobackupex script,
InnoDB data will be copied fully.
--databases and --databases-file options been introduced
for xtrabackup binary, which cover two modes of --databases
option of innobackupex.

To post a comment you must log in.
Revision history for this message
Sergei Glushchenko (sergei.glushchenko) wrote :
Revision history for this message
Sergei Glushchenko (sergei.glushchenko) wrote :

have conflicts. looking

Revision history for this message
Sergei Glushchenko (sergei.glushchenko) wrote :

conflict resolved

Revision history for this message
Laurynas Biveinis (laurynas-biveinis) wrote :

   Code:

   - the commit message does not describe the changes done to
     xtrabackup.c.

   - please make static the variables and functions that are local to
     xtrabackup.c.

   - I don't think table_hash_entries_num is required. Its role can
     be handled by tables_hash != NULL check.

   - Likewise for database_hash_entries_num.

   - use my_malloc(.., MYF(MY_FAE)); instead of plain malloc and its
     result check in xb_new_filter_entry(). Then also the return
     value of xb_new_filter_entry() itself needs not to be checked.

   - please assert in xb_new_filter_entry() that the name does not
     violate MySQL name limits: 64 chars for db name, 64 chars for
     table name, and a dot.

   - Line 243: missing || xtrabackup_database_file? Shouldn't this
     have caused a crash for the new ib_databases_file.sh test? I'd
     replace this by lazy initialization of hash tables that actually
     get any insertions.

   - Lines 243--248: why both hashes are created unconditionally? I
     think the logic should be: init table hash, if any of table or db
     options given; init db hash, if any of db options given.

   - Lines 244 and 246: redundant comments.

   - Line 255: why strtok_r instead of strtok? I don't think the
     reentrancy is needed here?

   - The hash insert code is duplicated for the xtrabackup_databases
     and x_d_file hash insertion in xb_filters_init(), please make a
     function.

   - Please validate the provided names in xb_filter_init against
     length.

   - How are duplicate names handled in the hash tables?

   Testcases:
   - The existing testcases test the IB options and how IB works with
     XB fine. But I think that an XB-specific testcase is necessary
     as well. It should:
     - test the XB options;
     - test invalid input (too long table and database names, non
       existing tables and databases, illegal characters, duplicate
       filters, etc).

   Documentation:
   - diff lines 8 and 9: use "either ... or" instead of "whether", s/-
   or a/, or a.

   - diff line 43: cannot parse "names" in "Only names databases and tables will
     be backed up."

review: Needs Fixing
Revision history for this message
Sergei Glushchenko (sergei.glushchenko) wrote :

Code:

> - the commit message does not describe the changes done to
> xtrabackup.c.

Commit message has been changed.

> - please make static the variables and functions that are local to
> xtrabackup.c.

done.

> - I don't think table_hash_entries_num is required. Its role can
> be handled by tables_hash != NULL check.
>
> - Likewise for database_hash_entries_num.

filters init code has been refactored

> - use my_malloc(.., MYF(MY_FAE)); instead of plain malloc and its
> result check in xb_new_filter_entry(). Then also the return
> value of xb_new_filter_entry() itself needs not to be checked.

ut_malloc has been used.

> - please assert in xb_new_filter_entry() that the name does not
> violate MySQL name limits: 64 chars for db name, 64 chars for
> table name, and a dot.
>
> - Line 243: missing || xtrabackup_database_file? Shouldn't this
> have caused a crash for the new ib_databases_file.sh test? I'd
> replace this by lazy initialization of hash tables that actually
> get any insertions.
>
> - Lines 243--248: why both hashes are created unconditionally? I
> think the logic should be: init table hash, if any of table or db
> options given; init db hash, if any of db options given.
>
> - Lines 244 and 246: redundant comments.

filters init code has been refactored

> - Line 255: why strtok_r instead of strtok? I don't think the
> reentrancy is needed here?

I really like strtok_r more because of it design. Don't like functions, which
state is saved globally between invocations.

> - The hash insert code is duplicated for the xtrabackup_databases
> and x_d_file hash insertion in xb_filters_init(), please make a
> function.
>
> - Please validate the provided names in xb_filter_init against
> length.

filters init code has been refactored

> - How are duplicate names handled in the hash tables?

hash tables resolve collisions with linked list, so it will impact on
performance if we have *a lot* of duplicate entries in tables file, or
databases file. I don't think it is a case.

   Testcases:
   - The existing testcases test the IB options and how IB works with
     XB fine. But I think that an XB-specific testcase is necessary
     as well. It should:
     - test the XB options;
     - test invalid input (too long table and database names, non
       existing tables and databases, illegal characters, duplicate
       filters, etc).

> Documentation:
> - diff lines 8 and 9: use "either ... or" instead of "whether", s/-
> or a/, or a.
>
> - diff line 43: cannot parse "names" in "Only names databases and tables will
> be backed up."

fixed

Revision history for this message
Sergei Glushchenko (sergei.glushchenko) wrote :
Revision history for this message
Laurynas Biveinis (laurynas-biveinis) wrote :

   - Please rebase on the current trunk GCAs. The C++ conversion might
     require a tweak or two. Also that will require some testcase
     changes, described below.
   - Please make variable definitions that are local to xtrabackup.c
     static as well, not only functions.
   - Diff line 189: "There aren't tables specified ... " -> "There are
     no tables specified ... "
   - Diff line 240: s/sucessfull/sucessful
   - In xb_register_filter_entry(), please move db_entry declaration
     to the block start.
   - Diff line 412: s/etnries/entries
   - Diff line 434: likewise.
   - After the rebase, ib_databases.sh, ib_databases_file.sh will need to adjust to the
     current stop_server that kill -9s the server and thus encourages
     not a partial but full data dir overwrite to restore. Thus please
     backup mysql and performance_schema as well, and rm -rf the
     whole datadir.
   - In xb_database_options.sh at least some of $XB_BIN $XB_ARGS
     ... can be replaced by xtrabackup ...
   - Does "diff -u <(ls_dir $backup_dir)" fail the test on a diff
     difference found?

review: Needs Fixing
Revision history for this message
Sergei Glushchenko (sergei.glushchenko) wrote :

Laurynas,
Thanks for the review, I'd like to rebase this branch on the latest trunk when https://code.launchpad.net/~sergei.glushchenko/percona-xtrabackup/20-bug856400/+merge/152361 will be approved and merged to avoid conflicts and one more rebasing.

Thanks,
Sergei

Unmerged revisions

496. By Sergei Glushchenko

Bug 569387
xtrabackup ignores --databases, i.e. when --databases
is specified it affects only innobackupex script,
InnoDB data will be copied fully.
two options have been added for xtrabackup
  * --databases is a space separated list of entries database_name[.table_name]
  * --databases-file is a name of file which contains
          entries database_name[.table_name] one entry per line
  * check_if_skip_table has been modified to check whether database is
    enabled first and after check the table
  * databases and tables filters initialization has been refactored
    in order to avoid code duplication
innnobackupex's --databases option is mapped to one of these options,
depending on value.
Bug 1131084
Unneccessary/debug print in xtrabackup output.
There was msg("xtrabackup: tables regcomp(%s): %s\n", p, errbuf);
which has been removed in this branch.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'doc/source/innobackupex/partial_backups_innobackupex.rst'
2--- doc/source/innobackupex/partial_backups_innobackupex.rst 2012-09-19 11:35:43 +0000
3+++ doc/source/innobackupex/partial_backups_innobackupex.rst 2013-02-25 08:01:21 +0000
4@@ -42,7 +42,7 @@
5 Using the :option:`--databases` option
6 --------------------------------------
7
8-This option is specific to |innobackupex| and accepts whether a space-separated list of the databases and tables to backup - in the ``databasename[.tablename]`` form - or a file containing the list at one element per line.
9+This option accepts eigther a space-separated list of the databases and tables to backup - in the ``databasename[.tablename]`` form - or a file containing the list at one element per line.
10
11 For example, ::
12
13@@ -50,9 +50,6 @@
14
15 will create a timestamped directory with the usual files that |innobackupex| creates, but only containing the data-files related to ``mytable`` in the ``mydatabase`` directory and the ``mysql`` directory with the entire ``mysql`` database.
16
17-.. note::
18-
19- Currently in XtraBackup the --databases option has no effect for InnoDB files for both local and streaming backups, i.e. all InnoDB files are always backed up. Currently, only .frm and non-InnoDB tables are limited by that option.
20
21 Preparing Partial Backups
22 =========================
23
24=== modified file 'doc/source/xtrabackup_bin/partial_backups.rst'
25--- doc/source/xtrabackup_bin/partial_backups.rst 2011-07-07 05:32:50 +0000
26+++ doc/source/xtrabackup_bin/partial_backups.rst 2013-02-25 08:01:21 +0000
27@@ -2,7 +2,7 @@
28 Partial Backups
29 =================
30
31-|xtrabackup| supports taking partial backups when the :term:`innodb_file_per_table` option is enabled. There are two ways to create partial backups: matching the tables' names with a regular expression or providing a list of them in a file.
32+|xtrabackup| supports taking partial backups when the :term:`innodb_file_per_table` option is enabled. There are three ways to create partial backups: matching the tables' names with a regular expression, providing a list of them in a file or providing a list of databases.
33
34 .. warning:: If any of the matched or listed tables is deleted during the backup, |xtrabackup| will fail.
35
36@@ -30,6 +30,11 @@
37
38 |check| errors? outputs?
39
40+Using the :option:`--databases` and :option:`--databases-file` options
41+======================================================================
42+
43+The ``--databases`` option accepts a space-separated list of the databases and tables to backup - in the ``databasename[.tablename]`` form. The ``--databases-file`` option specifies a file that can contain multiple databases and tables in the ``databasename[.tablename]`` form, one element name per line in the file. Only named databases and tables will be backed up. Names are matched exactly, case-sensitive, with no pattern or regular expression matching.
44+
45 Preparing the Backup
46 ====================
47
48
49=== modified file 'doc/source/xtrabackup_bin/xbk_option_reference.rst'
50--- doc/source/xtrabackup_bin/xbk_option_reference.rst 2012-05-18 11:19:17 +0000
51+++ doc/source/xtrabackup_bin/xbk_option_reference.rst 2013-02-25 08:01:21 +0000
52@@ -17,6 +17,14 @@
53
54 Don't read default options from any option file. Must be given as the first option on the command-line.
55
56+.. option:: --databases=#
57+
58+ This option specifies the list of databases and tables that should be backed up. The option accepts the list of the form ``"databasename1[.table_name1] databasename2[.table_name2] . . ."``.
59+
60+.. option:: --databases-file=#
61+
62+ This option specifies the path to the file containing the list of databases and tables that should be backed up. The file can contain the list elements of the form ``databasename1[.table_name1]``, one element per line.
63+
64 .. option:: --defaults-file=#
65
66 Only read default options from the given file. Must be given as the first option on the command-line. Must be a real file; it cannot be a symbolic link.
67
68=== modified file 'innobackupex'
69--- innobackupex 2013-01-17 15:08:37 +0000
70+++ innobackupex 2013-02-25 08:01:21 +0000
71@@ -1067,6 +1067,13 @@
72 if ($option_stream) {
73 $options = $options . " --stream=$option_stream";
74 }
75+ if ($option_databases) {
76+ if ($option_databases =~ /^\//) {
77+ $options = $options . " --databases_file='$option_databases'";
78+ } else {
79+ $options = $options . " --databases='$option_databases'";
80+ }
81+ }
82 $cmdline = "$option_ibbackup_binary $options";
83
84 # run ibbackup as a child process
85
86=== modified file 'src/xtrabackup.c'
87--- src/xtrabackup.c 2013-01-13 22:34:01 +0000
88+++ src/xtrabackup.c 2013-02-25 08:01:21 +0000
89@@ -87,6 +87,7 @@
90 #include <log0recv.h>
91 #include <fcntl.h>
92 #include <buf0lru.h>
93+#include <string.h>
94
95 #ifdef INNODB_VERSION_SHORT
96 #include <ibuf0ibuf.h>
97@@ -660,7 +661,7 @@
98
99 #ifdef INNODB_VERSION_SHORT
100 #define XB_HASH_SEARCH(NAME, TABLE, FOLD, DATA, ASSERTION, TEST) \
101- HASH_SEARCH(NAME, TABLE, FOLD, xtrabackup_tables_t*, DATA, ASSERTION, \
102+ HASH_SEARCH(NAME, TABLE, FOLD, xtrabackup_filter_entry_t*, DATA, ASSERTION, \
103 TEST)
104 #else
105 #define XB_HASH_SEARCH(NAME, TABLE, FOLD, DATA, ASSERTION, TEST) \
106@@ -807,18 +808,30 @@
107 char *xtrabackup_incremental_dir = NULL; /* for --prepare */
108
109 char *xtrabackup_tables = NULL;
110-int tables_regex_num;
111-xb_regex_t *tables_regex;
112+
113+/* List of regular expressions for filtering */
114+typedef struct xb_regex_list_node_struct xb_regex_list_node_t;
115+struct xb_regex_list_node_struct {
116+ UT_LIST_NODE_T(xb_regex_list_node_t) regex_list;
117+ xb_regex_t regex;
118+};
119+UT_LIST_BASE_NODE_T(xb_regex_list_node_t) regex_list;
120+
121 xb_regmatch_t tables_regmatch[1];
122
123 char *xtrabackup_tables_file = NULL;
124-hash_table_t* tables_hash;
125-
126-struct xtrabackup_tables_struct{
127+hash_table_t* tables_hash = NULL;
128+
129+char *xtrabackup_databases = NULL;
130+char *xtrabackup_databases_file = NULL;
131+hash_table_t* databases_hash = NULL;
132+
133+struct xtrabackup_filter_entry_struct{
134 char* name;
135+ ibool has_tables_specified;
136 hash_node_t name_hash;
137 };
138-typedef struct xtrabackup_tables_struct xtrabackup_tables_t;
139+typedef struct xtrabackup_filter_entry_struct xtrabackup_filter_entry_t;
140
141 #ifdef XTRADB_BASED
142 static ulint thread_nr[SRV_MAX_N_IO_THREADS + 6 + 64];
143@@ -1059,6 +1072,8 @@
144 OPT_XTRA_INCREMENTAL_DIR,
145 OPT_XTRA_TABLES,
146 OPT_XTRA_TABLES_FILE,
147+ OPT_XTRA_DATABASES,
148+ OPT_XTRA_DATABASES_FILE,
149 OPT_XTRA_CREATE_IB_LOGFILE,
150 OPT_XTRA_PARALLEL,
151 OPT_XTRA_STREAM,
152@@ -1174,6 +1189,12 @@
153 {"tables_file", OPT_XTRA_TABLES_FILE, "filtering by list of the exact database.table name in the file.",
154 (G_PTR*) &xtrabackup_tables_file, (G_PTR*) &xtrabackup_tables_file,
155 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
156+ {"databases", OPT_XTRA_DATABASES, "filtering by list of databases.",
157+ (G_PTR*) &xtrabackup_databases, (G_PTR*) &xtrabackup_databases,
158+ 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
159+ {"databases_file", OPT_XTRA_TABLES_FILE, "filtering by list of databases in the file.",
160+ (G_PTR*) &xtrabackup_databases_file, (G_PTR*) &xtrabackup_databases_file,
161+ 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
162 {"create-ib-logfile", OPT_XTRA_CREATE_IB_LOGFILE, "** not work for now** creates ib_logfile* also after '--prepare'. ### If you want create ib_logfile*, only re-execute this command in same options. ###",
163 (G_PTR*) &xtrabackup_create_ib_logfile, (G_PTR*) &xtrabackup_create_ib_logfile,
164 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
165@@ -2900,7 +2921,9 @@
166 char *eptr;
167 int dbname_len;
168
169- if (xtrabackup_tables == NULL && xtrabackup_tables_file == NULL) {
170+ if (UT_LIST_GET_LEN(regex_list) == 0 &&
171+ tables_hash == NULL &&
172+ databases_hash == NULL) {
173 return(FALSE);
174 }
175
176@@ -2916,6 +2939,26 @@
177 }
178
179 strncpy(buf, dbname, FN_REFLEN);
180+ buf[tbname - 1 - dbname] = 0;
181+
182+ if (databases_hash) {
183+ /* There are some filters for databases, check them */
184+ xtrabackup_filter_entry_t* database;
185+
186+ XB_HASH_SEARCH(name_hash, databases_hash, ut_fold_string(buf),
187+ database, ut_ad(database->name),
188+ !strcmp(database->name, buf));
189+ /* Table's database isn't found, skip the table */
190+ if (!database) {
191+ return(TRUE);
192+ }
193+ /* There aren't tables specified for the database,
194+ it should be backed up entirely */
195+ if (!database->has_tables_specified) {
196+ return(FALSE);
197+ }
198+ }
199+
200 buf[FN_REFLEN - 1] = 0;
201 buf[tbname - 1 - dbname] = '.';
202
203@@ -2929,11 +2972,13 @@
204 *eptr = 0;
205 }
206
207- if (xtrabackup_tables) {
208- int regres = REG_NOMATCH;
209- int i;
210- for (i = 0; i < tables_regex_num; i++) {
211- regres = xb_regexec(&tables_regex[i], buf, 1,
212+ if (UT_LIST_GET_LEN(regex_list)) {
213+ /* Check against regular expressions list */
214+ int regres = REG_NOMATCH;
215+ xb_regex_list_node_t* node;
216+ for (node = UT_LIST_GET_FIRST(regex_list); node;
217+ node = UT_LIST_GET_NEXT(regex_list, node)) {
218+ regres = xb_regexec(&node->regex, buf, 1,
219 tables_regmatch, 0);
220 if (regres != REG_NOMATCH) {
221 break;
222@@ -2944,8 +2989,9 @@
223 }
224 }
225
226- if (xtrabackup_tables_file) {
227- xtrabackup_tables_t* table;
228+ if (tables_hash) {
229+ /* Finally, check against full qualified tables list */
230+ xtrabackup_filter_entry_t* table;
231
232 XB_HASH_SEARCH(name_hash, tables_hash, ut_fold_string(buf),
233 table, ut_ad(table->name),
234@@ -4258,6 +4304,242 @@
235 return(FALSE);
236 }
237
238+/***********************************************************************
239+Allocate and initialize the entry for databases and tables filtering
240+hash tables. If memory allocation is not successfull, terminate program.
241+@return pointer to the created entry. */
242+static
243+xtrabackup_filter_entry_t *
244+xb_new_filter_entry(
245+/*================*/
246+ const char* name) /*!< in: name of table/database */
247+{
248+ xtrabackup_filter_entry_t *entry;
249+ ulint namelen = strlen(name);
250+
251+ /* Length of name shouldn't be greater than 129 character,
252+ which isn't actually 129 bytes as some of UTF-8 characters
253+ for example can be encoded by 4 bytes. */
254+ ut_a(namelen <= NAME_LEN * 2 + 1);
255+
256+ entry = ut_malloc(sizeof(xtrabackup_filter_entry_t) + namelen + 1);
257+ memset(entry, '\0', sizeof(xtrabackup_filter_entry_t) + namelen + 1);
258+ entry->name = ((char*)entry) + sizeof(xtrabackup_filter_entry_t);
259+ strcpy(entry->name, name);
260+ entry->has_tables_specified = FALSE;
261+
262+ return entry;
263+}
264+
265+/***********************************************************************
266+Free entry filtering hash table entry. */
267+static
268+void
269+xb_free_filter_entry(
270+/*=================*/
271+ xtrabackup_filter_entry_t* entry) /*!< in: entry */
272+{
273+ ut_free(entry);
274+}
275+
276+/***********************************************************************
277+Add entry to hash table. If hash table is NULL, allocate and initialize
278+new hash table */
279+static
280+xtrabackup_filter_entry_t*
281+xb_add_filter_entry_to_hash(
282+/*========================*/
283+ const char* name, /*!< in: name of table/database */
284+ hash_table_t** hash) /*!< in/out: hash to insert into */
285+{
286+ xtrabackup_filter_entry_t* entry;
287+
288+ entry = xb_new_filter_entry(name);
289+
290+ if (UNIV_UNLIKELY(*hash == NULL)) {
291+ *hash = hash_create(1000);
292+ }
293+ HASH_INSERT(xtrabackup_filter_entry_t,
294+ name_hash, *hash,
295+ ut_fold_string(entry->name),
296+ entry);
297+
298+ return entry;
299+}
300+
301+/***********************************************************************
302+Validate name of table or database. If name is invalid, program will
303+be finished with error code */
304+static
305+void
306+xb_validate_name(
307+/*=============*/
308+ const char* name, /*!< in: name */
309+ size_t len) /*!< in: length of name */
310+{
311+ const char* p;
312+
313+ /* perform only basic validation. validate length and
314+ path symbols */
315+ if (len > NAME_LEN) {
316+ msg("xtrabackup: name `%s` is too long.\n", name);
317+ exit(EXIT_FAILURE);
318+ }
319+ p = strpbrk(name, "/\\~");
320+ if (p && p - name < NAME_LEN) {
321+ msg("xtrabackup: name `%s` is not valid.\n", name);
322+ exit(EXIT_FAILURE);
323+ }
324+}
325+
326+/***********************************************************************
327+Register new filter entry which can be either database
328+or table name. */
329+static
330+void
331+xb_register_filter_entry(
332+/*=====================*/
333+ const char* name) /*!< in: name */
334+{
335+ const char* p;
336+ size_t namelen;
337+
338+ namelen = strlen(name);
339+ if ((p = strchr(name, '.')) != NULL) {
340+ char dbname[NAME_LEN + 1];
341+
342+ xb_validate_name(name, p - name);
343+ xb_validate_name(p + 1, namelen - (p - name));
344+
345+ strncpy(dbname, name, p - name);
346+ dbname[p - name] = 0;
347+
348+ xtrabackup_filter_entry_t* db_entry = NULL;
349+
350+ if (databases_hash) {
351+ XB_HASH_SEARCH(name_hash, databases_hash,
352+ ut_fold_string(dbname),
353+ db_entry, ut_ad(db_entry->name),
354+ !strcmp(db_entry->name, dbname));
355+ }
356+ if (!db_entry) {
357+ db_entry = xb_add_filter_entry_to_hash(dbname,
358+ &databases_hash);
359+ }
360+ db_entry->has_tables_specified = TRUE;
361+ xb_add_filter_entry_to_hash(name, &tables_hash);
362+ } else {
363+ xb_validate_name(name, namelen);
364+
365+ xb_add_filter_entry_to_hash(name, &databases_hash);
366+ }
367+}
368+
369+/***********************************************************************
370+Register new table for the filter. */
371+static
372+void
373+xb_register_table(
374+/*==============*/
375+ const char* name) /*!< in: name of table */
376+{
377+ if (strchr(name, '.') == NULL) {
378+ msg("xtrabackup: `%s` is not fully qualified name.\n", name);
379+ exit(EXIT_FAILURE);
380+ }
381+
382+ xb_register_filter_entry(name);
383+}
384+
385+/***********************************************************************
386+Register new regex for the filter. */
387+static
388+void
389+xb_register_regex(
390+/*==============*/
391+ const char* regex) /*!< in: regex */
392+{
393+ xb_regex_list_node_t* node;
394+ char errbuf[100];
395+ int ret;
396+
397+ node = ut_malloc(sizeof(xb_regex_list_node_t));
398+
399+ ret = xb_regcomp(&node->regex, regex, REG_EXTENDED);
400+ if (ret != 0) {
401+ xb_regerror(ret, &node->regex, errbuf, sizeof(errbuf));
402+ msg("xtrabackup: error: tables regcomp(%s): %s\n", regex, errbuf);
403+ exit(EXIT_FAILURE);
404+ }
405+
406+ UT_LIST_ADD_LAST(regex_list, regex_list, node);
407+}
408+
409+typedef void (*insert_entry_func_t)(const char*);
410+
411+/***********************************************************************
412+Scan string and load filter etnries from it. */
413+static
414+void
415+xb_load_list_string(
416+/*================*/
417+ const char* list, /*!< in: string representing a list */
418+ const char* delimiters, /*!< in: delimiters of entries */
419+ insert_entry_func_t ins) /*!< in: callback to add entry */
420+{
421+ char* p;
422+ char* saveptr;
423+
424+ p = strtok_r(list, delimiters, &saveptr);
425+ while (p) {
426+
427+ ins(p);
428+
429+ p = strtok_r(NULL, delimiters, &saveptr);
430+ }
431+}
432+
433+/***********************************************************************
434+Scan file and load filter etnries from it. */
435+static
436+void
437+xb_load_list_file(
438+/*==============*/
439+ const char* filename, /*!< in: name of file */
440+ insert_entry_func_t ins) /*!< in: callback to add entry */
441+{
442+ char name_buf[NAME_LEN*2+2];
443+ FILE *fp;
444+
445+ /* read and store the filenames */
446+ fp = fopen(filename, "r");
447+ if (!fp) {
448+ msg("xtrabackup: cannot open %s\n",
449+ filename);
450+ exit(EXIT_FAILURE);
451+ }
452+ for (;;) {
453+ char* p = name_buf;
454+
455+ if (fgets(name_buf, sizeof(name_buf), fp) == 0) {
456+ break;
457+ }
458+
459+ p = strchr(name_buf, '\n');
460+ if (p) {
461+ *p = '\0';
462+ } else {
463+ msg("xtrabackup: `%s...` name is too long", name_buf);
464+ exit(EXIT_FAILURE);
465+ }
466+
467+ ins(name_buf);
468+ }
469+
470+ fclose(fp);
471+}
472+
473+
474 /************************************************************************
475 Inittialize table filters for partial backup. */
476 static
477@@ -4265,86 +4547,55 @@
478 xb_filters_init()
479 /*=============*/
480 {
481+ UT_LIST_INIT(regex_list);
482+
483+ if (xtrabackup_databases) {
484+ xb_load_list_string(xtrabackup_databases, " \t",
485+ xb_register_filter_entry);
486+ }
487+ if (xtrabackup_databases_file) {
488+ xb_load_list_file(xtrabackup_databases_file,
489+ xb_register_filter_entry);
490+ }
491 if (xtrabackup_tables) {
492- /* init regexp */
493- char *p, *next;
494- int i;
495- char errbuf[100];
496-
497- tables_regex_num = 1;
498-
499- p = xtrabackup_tables;
500- while ((p = strchr(p, ',')) != NULL) {
501- p++;
502- tables_regex_num++;
503- }
504-
505- tables_regex = ut_malloc(sizeof(xb_regex_t) * tables_regex_num);
506-
507- p = xtrabackup_tables;
508- for (i=0; i < tables_regex_num; i++) {
509- next = strchr(p, ',');
510- ut_a(next || i == tables_regex_num - 1);
511-
512- next++;
513- if (i != tables_regex_num - 1)
514- *(next - 1) = '\0';
515-
516- xb_regerror(xb_regcomp(&tables_regex[i], p,
517- REG_EXTENDED),
518- &tables_regex[i], errbuf, sizeof(errbuf));
519- msg("xtrabackup: tables regcomp(%s): %s\n", p, errbuf);
520-
521- if (i != tables_regex_num - 1)
522- *(next - 1) = ',';
523- p = next;
524- }
525+ xb_load_list_string(xtrabackup_tables, ",",
526+ xb_register_regex);
527 }
528-
529 if (xtrabackup_tables_file) {
530- char name_buf[NAME_LEN*2+2];
531- FILE *fp;
532-
533- name_buf[NAME_LEN*2+1] = '\0';
534-
535- /* init tables_hash */
536- tables_hash = hash_create(1000);
537-
538- /* read and store the filenames */
539- fp = fopen(xtrabackup_tables_file,"r");
540- if (!fp) {
541- msg("xtrabackup: cannot open %s\n",
542- xtrabackup_tables_file);
543- exit(EXIT_FAILURE);
544- }
545- for (;;) {
546- xtrabackup_tables_t* table;
547- char* p = name_buf;
548-
549- if ( fgets(name_buf, NAME_LEN*2+1, fp) == 0 ) {
550- break;
551- }
552-
553- p = strchr(name_buf, '\n');
554- if (p)
555- {
556- *p = '\0';
557- }
558-
559- table = malloc(sizeof(xtrabackup_tables_t) + strlen(name_buf) + 1);
560- memset(table, '\0', sizeof(xtrabackup_tables_t) + strlen(name_buf) + 1);
561- table->name = ((char*)table) + sizeof(xtrabackup_tables_t);
562- strcpy(table->name, name_buf);
563-
564- HASH_INSERT(xtrabackup_tables_t, name_hash, tables_hash,
565- ut_fold_string(table->name), table);
566-
567- msg("xtrabackup: table '%s' is registered to the "
568- "list.\n", table->name);
569- }
570- }
571-}
572-
573+ xb_load_list_file(xtrabackup_tables_file, xb_register_table);
574+ }
575+}
576+
577+
578+/************************************************************************
579+Release all the hash entries and hash itself. */
580+static
581+void
582+xb_filter_hash_clear(
583+ hash_table_t* hash)
584+{
585+ ulint i;
586+
587+ /* free the hash elements */
588+ for (i = 0; i < hash_get_n_cells(hash); i++) {
589+ xtrabackup_filter_entry_t* table;
590+
591+ table = HASH_GET_FIRST(hash, i);
592+
593+ while (table) {
594+ xtrabackup_filter_entry_t* prev_table = table;
595+
596+ table = HASH_GET_NEXT(name_hash, prev_table);
597+
598+ HASH_DELETE(xtrabackup_filter_entry_t, name_hash, hash,
599+ ut_fold_string(prev_table->name), prev_table);
600+ free(prev_table);
601+ }
602+ }
603+
604+ /* free hash */
605+ hash_table_free(hash);
606+}
607
608 /************************************************************************
609 Destroy table filters for partial backup. */
610@@ -4353,29 +4604,19 @@
611 xb_filters_free()
612 /*=============*/
613 {
614-
615- if (xtrabackup_tables_file) {
616- ulint i;
617-
618- /* free the hash elements */
619- for (i = 0; i < hash_get_n_cells(tables_hash); i++) {
620- xtrabackup_tables_t* table;
621-
622- table = HASH_GET_FIRST(tables_hash, i);
623-
624- while (table) {
625- xtrabackup_tables_t* prev_table = table;
626-
627- table = HASH_GET_NEXT(name_hash, prev_table);
628-
629- HASH_DELETE(xtrabackup_tables_t, name_hash, tables_hash,
630- ut_fold_string(prev_table->name), prev_table);
631- free(prev_table);
632- }
633- }
634-
635- /* free tables_hash */
636- hash_table_free(tables_hash);
637+ while (UT_LIST_GET_LEN(regex_list) > 0) {
638+ xb_regex_list_node_t* node = UT_LIST_GET_FIRST(regex_list);
639+ UT_LIST_REMOVE(regex_list, regex_list, node);
640+ xb_regfree(&node->regex);
641+ ut_free(node);
642+ }
643+
644+ if (tables_hash) {
645+ xb_filter_hash_clear(tables_hash);
646+ }
647+
648+ if (databases_hash) {
649+ xb_filter_hash_clear(databases_hash);
650 }
651
652 }
653@@ -7206,16 +7447,6 @@
654 if (xtrabackup_prepare)
655 xtrabackup_prepare_func();
656
657- if (xtrabackup_tables) {
658- /* free regexp */
659- int i;
660-
661- for (i = 0; i < tables_regex_num; i++) {
662- xb_regfree(&tables_regex[i]);
663- }
664- ut_free(tables_regex);
665- }
666-
667 xb_regex_end();
668
669 exit(EXIT_SUCCESS);
670
671=== added file 'test/t/ib_databases.sh'
672--- test/t/ib_databases.sh 1970-01-01 00:00:00 +0000
673+++ test/t/ib_databases.sh 2013-02-25 08:01:21 +0000
674@@ -0,0 +1,50 @@
675+########################################################################
676+# Bug #569387: innobackupex ignores --databases
677+# Testcase covers using --databases option with InnoDB
678+# database (list is specified in option itself)
679+########################################################################
680+
681+. inc/common.sh
682+
683+start_server --innodb-file-per-table
684+
685+cat <<EOF | run_cmd $MYSQL $MYSQL_ARGS
686+
687+ CREATE DATABASE test1;
688+
689+ CREATE TABLE test1.a (a INT PRIMARY KEY);
690+ CREATE TABLE test1.b (b INT PRIMARY KEY);
691+ CREATE TABLE test1.c (c INT PRIMARY KEY);
692+
693+ CREATE TABLE test.a (a INT PRIMARY KEY);
694+ CREATE TABLE test.b (b INT PRIMARY KEY);
695+ CREATE TABLE test.c (c INT PRIMARY KEY);
696+
697+EOF
698+
699+# This is a workaround to pass --databases='test test1.b test1.c'
700+$IB_BIN $IB_ARGS --no-timestamp --databases='test test1.b test1.c' $topdir/backup
701+innobackupex --apply-log $topdir/backup
702+vlog "Backup taken"
703+
704+stop_server
705+
706+# Restore partial backup
707+# Remove database
708+rm -rf $mysql_datadir/test/*
709+rm -rf $mysql_datadir/test1/*
710+vlog "Original database removed"
711+
712+# Restore database from backup
713+cp -rv $topdir/backup/test/* $mysql_datadir/test
714+cp -rv $topdir/backup/test1/* $mysql_datadir/test1
715+vlog "database restored from backup"
716+
717+start_server
718+
719+OUT=`run_cmd $MYSQL $MYSQL_ARGS -e "USE test; SHOW TABLES; USE test1; SHOW TABLES;" | tr -d '\n'`
720+
721+if [ $OUT != "Tables_in_testabcTables_in_test1bc" ] ; then
722+ vlog "Backed up tables set doesn't match filter" ;
723+ exit 1;
724+fi
725
726=== added file 'test/t/ib_databases_file.sh'
727--- test/t/ib_databases_file.sh 1970-01-01 00:00:00 +0000
728+++ test/t/ib_databases_file.sh 2013-02-25 08:01:21 +0000
729@@ -0,0 +1,57 @@
730+########################################################################
731+# Bug #569387: innobackupex ignores --databases
732+# Testcase covers using --databases option with InnoDB
733+# database (list is specified in file which option
734+# points to)
735+########################################################################
736+
737+. inc/common.sh
738+
739+start_server --innodb-file-per-table
740+
741+cat <<EOF | run_cmd $MYSQL $MYSQL_ARGS
742+
743+ CREATE DATABASE test1;
744+
745+ CREATE TABLE test1.a (a INT PRIMARY KEY) engine=InnoDB;
746+ CREATE TABLE test1.b (b INT PRIMARY KEY) engine=InnoDB;
747+ CREATE TABLE test1.c (c INT PRIMARY KEY) engine=InnoDB;
748+
749+ CREATE TABLE test.a (a INT PRIMARY KEY) engine=InnoDB;
750+ CREATE TABLE test.b (b INT PRIMARY KEY) engine=InnoDB;
751+ CREATE TABLE test.c (c INT PRIMARY KEY) engine=InnoDB;
752+
753+EOF
754+
755+# Take a backup
756+# Backup the whole test and b,c from test1
757+cat >$topdir/databases_file <<EOF
758+test
759+test1.b
760+test1.c
761+EOF
762+innobackupex --no-timestamp --databases=$topdir/databases_file $topdir/backup
763+innobackupex --apply-log $topdir/backup
764+vlog "Backup taken"
765+
766+stop_server
767+
768+# Restore partial backup
769+# Remove database
770+rm -rf $mysql_datadir/test/*
771+rm -rf $mysql_datadir/test1/*
772+vlog "Original database removed"
773+
774+# Restore database from backup
775+cp -rv $topdir/backup/test/* $mysql_datadir/test
776+cp -rv $topdir/backup/test1/* $mysql_datadir/test1
777+vlog "database restored from backup"
778+
779+start_server
780+
781+OUT=`run_cmd $MYSQL $MYSQL_ARGS -e "USE test; SHOW TABLES; USE test1; SHOW TABLES;" | tr -d '\n'`
782+
783+if [ $OUT != "Tables_in_testabcTables_in_test1bc" ] ; then
784+ vlog "Backed up tables set don't match filter" ;
785+ exit 1;
786+fi
787
788=== added file 'test/t/xb_databases_options.sh'
789--- test/t/xb_databases_options.sh 1970-01-01 00:00:00 +0000
790+++ test/t/xb_databases_options.sh 2013-02-25 08:01:21 +0000
791@@ -0,0 +1,221 @@
792+########################################################################
793+# Bug #569387: xtrabackup ignores --databases, i.e. when --databases
794+#
795+# Test following xtrabackup options
796+# --databases
797+# --databases-file
798+# --tables
799+# --tables-file
800+########################################################################
801+
802+. inc/common.sh
803+
804+start_server --innodb-file-per-table
805+
806+function setup_test {
807+
808+ cat <<EOF | run_cmd $MYSQL $MYSQL_ARGS
809+
810+ create database database1;
811+ create database database2;
812+ create database test1;
813+ create database test2;
814+ create database thisisadatabase;
815+ create database testdatabase;
816+
817+ create table database1.thisisatable (a int primary key) engine=InnoDB;
818+ create table database1.t (a int primary key) engine=InnoDB;
819+ create table database1.t1 (a int primary key) engine=InnoDB;
820+ create table database1.t2 (a int primary key) engine=InnoDB;
821+ create table database1.ancor (a int primary key) engine=InnoDB;
822+ create table database1.glow (a int primary key) engine=InnoDB;
823+
824+ create table database2.thisisatable (a int primary key) engine=InnoDB;
825+ create table database2.t (a int primary key) engine=InnoDB;
826+ create table database2.t1 (a int primary key) engine=InnoDB;
827+ create table database2.t2 (a int primary key) engine=InnoDB;
828+ create table database2.ancor (a int primary key) engine=InnoDB;
829+ create table database2.glow (a int primary key) engine=InnoDB;
830+
831+ create table test1.thisisatable (a int primary key) engine=InnoDB;
832+ create table test1.t (a int primary key) engine=InnoDB;
833+ create table test1.t1 (a int primary key) engine=InnoDB;
834+ create table test1.t2 (a int primary key) engine=InnoDB;
835+ create table test1.ancor (a int primary key) engine=InnoDB;
836+ create table test1.glow (a int primary key) engine=InnoDB;
837+
838+ create table test2.thisisatable (a int primary key) engine=InnoDB;
839+ create table test2.t (a int primary key) engine=InnoDB;
840+ create table test2.t1 (a int primary key) engine=InnoDB;
841+ create table test2.t2 (a int primary key) engine=InnoDB;
842+ create table test2.ancor (a int primary key) engine=InnoDB;
843+ create table test2.glow (a int primary key) engine=InnoDB;
844+
845+ create table thisisadatabase.thisisatable (a int primary key) engine=InnoDB;
846+ create table thisisadatabase.t (a int primary key) engine=InnoDB;
847+ create table thisisadatabase.t1 (a int primary key) engine=InnoDB;
848+ create table thisisadatabase.t2 (a int primary key) engine=InnoDB;
849+ create table thisisadatabase.ancor (a int primary key) engine=InnoDB;
850+ create table thisisadatabase.glow (a int primary key) engine=InnoDB;
851+
852+ create table testdatabase.thisisatable (a int primary key) engine=InnoDB;
853+ create table testdatabase.t (a int primary key) engine=InnoDB;
854+ create table testdatabase.t1 (a int primary key) engine=InnoDB;
855+ create table testdatabase.t2 (a int primary key) engine=InnoDB;
856+ create table testdatabase.ancor (a int primary key) engine=InnoDB;
857+ create table testdatabase.glow (a int primary key) engine=InnoDB;
858+
859+EOF
860+
861+}
862+
863+function ls_dir {
864+ d=`pwd`
865+ cd $1
866+ find . -name '*.ibd' | sort
867+ cd $d
868+}
869+
870+setup_test
871+
872+backup_dir=$topdir/backup_dir
873+mkdir -p $backup_dir
874+
875+# invalid characters
876+$XB_BIN $XB_ARGS --backup --databases=a/b/c --target-dir=$backup_dir --datadir=$mysql_datadir 2>&1 | grep 'is not valid'
877+
878+# too long name
879+$XB_BIN $XB_ARGS --backup --databases=verylonglonglongname111111skjhkdjhfkjdhgkjdfh1555555555555511stillnotlongenoughsowilladdmore1111111111111111111114848484848484fkjhdjfhkdjfhd8484848aaaaaaaaaancnvjvifmifjhfkmfkfnbfifnfkfik4848484841111111prettyenoughnow --target-dir=$backup_dir --datadir=$mysql_datadir 2>&1 | grep 'is too long'
880+
881+# too long name
882+$XB_BIN $XB_ARGS --backup --databases=test1.verylonglonglongname111111skjhkdjhfkjdhgkjdfh1555555555555511stillnotlongenoughsowilladdmore1111111111111111111114848484848484fkjhdjfhkdjfhd8484848aaaaaaaaaancnvjvifmifjhfkmfkfnbfifnfkfik4848484841111111prettyenoughnow --target-dir=$backup_dir --datadir=$mysql_datadir 2>&1 | grep 'is too long'
883+
884+# not fully qualified name
885+$XB_BIN $XB_ARGS --backup --tables-file=<(echo verylonglonglongname111111skjhkdjhfkjdhgkjdfh1555555555555511stillnotlongenoughsowilladdmore1111111111111111111114848484848484fkjhdjfhkdjfhd8484848aaaaaaaaaancnvjvifmifjhfkmfkfnbfifnfkfik4848484841111111prettyenoughnow) --target-dir=$backup_dir --datadir=$mysql_datadir 2>&1 | grep 'is not fully qualified'
886+
887+# again too long name
888+$XB_BIN $XB_ARGS --backup --tables-file=<(echo test1.verylonglonglongname111111skjhkdjhfkjdhgkjdfh1555555555555511stillnotlongenoughsowilladdmore1111111111111111111114848484848484fkjhdjfhkdjfhd8484848aaaaaaaaaancnvjvifmifjhfkmfkfnbfifnfkfik4848484841111111prettyenoughnow) --target-dir=$backup_dir --datadir=$mysql_datadir 2>&1 | grep 'is too long'
889+
890+# should go fine, we cannot validate regex against length
891+$XB_BIN $XB_ARGS --backup --tables=verylonglonglongname111111skjhkdjhfkjdhgkjdfh1555555555555511stillnotlongenoughsowilladdmore1111111111111111111114848484848484fkjhdjfhkdjfhd8484848aaaaaaaaaancnvjvifmifjhfkmfkfnbfifnfkfik4848484841111111prettyenoughnow --target-dir=$backup_dir --datadir=$mysql_datadir
892+rm -rf $backup_dir/*
893+
894+# should go fine, we cannot validate regex against length
895+$XB_BIN $XB_ARGS --backup --tables=test1.verylonglonglongname111111skjhkdjhfkjdhgkjdfh1555555555555511stillnotlongenoughsowilladdmore1111111111111111111114848484848484fkjhdjfhkdjfhd8484848aaaaaaaaaancnvjvifmifjhfkmfkfnbfifnfkfik4848484841111111prettyenoughnow --target-dir=$backup_dir --datadir=$mysql_datadir
896+rm -rf $backup_dir/*
897+
898+vlog "Testing with --databases=..."
899+$XB_BIN $XB_ARGS --backup --databases='database database2 test1.t test1.ancor thisisadatabase testdatabase.t testdatabase.t7' --target-dir=$backup_dir --datadir=$mysql_datadir
900+diff -u <(ls_dir $backup_dir) - <<EOF
901+./database2/ancor.ibd
902+./database2/glow.ibd
903+./database2/t.ibd
904+./database2/t1.ibd
905+./database2/t2.ibd
906+./database2/thisisatable.ibd
907+./test1/ancor.ibd
908+./test1/t.ibd
909+./testdatabase/t.ibd
910+./thisisadatabase/ancor.ibd
911+./thisisadatabase/glow.ibd
912+./thisisadatabase/t.ibd
913+./thisisadatabase/t1.ibd
914+./thisisadatabase/t2.ibd
915+./thisisadatabase/thisisatable.ibd
916+EOF
917+rm -rf $backup_dir/*
918+
919+vlog "Testing with --databases-file=..."
920+cat >$topdir/list <<EOF
921+database
922+database2
923+test1.t
924+test1.ancor
925+thisisadatabase
926+testdatabase.t
927+testdatabase.t7
928+EOF
929+$XB_BIN $XB_ARGS --backup --databases-file=$topdir/list --target-dir=$backup_dir --datadir=$mysql_datadir
930+diff -u <(ls_dir $backup_dir) - <<EOF
931+./database2/ancor.ibd
932+./database2/glow.ibd
933+./database2/t.ibd
934+./database2/t1.ibd
935+./database2/t2.ibd
936+./database2/thisisatable.ibd
937+./test1/ancor.ibd
938+./test1/t.ibd
939+./testdatabase/t.ibd
940+./thisisadatabase/ancor.ibd
941+./thisisadatabase/glow.ibd
942+./thisisadatabase/t.ibd
943+./thisisadatabase/t1.ibd
944+./thisisadatabase/t2.ibd
945+./thisisadatabase/thisisatable.ibd
946+EOF
947+rm -rf $backup_dir/*
948+
949+vlog "Testing failure with --tables-file=..."
950+cat >$topdir/list <<EOF
951+database
952+database2
953+test1.t
954+test1.ancor
955+thisisadatabase
956+testdatabase.t
957+testdatabase.t7
958+EOF
959+run_cmd_expect_failure $XB_BIN $XB_ARGS --backup --tables-file=$topdir/list --target-dir=$backup_dir --datadir=$mysql_datadir
960+diff -u <(ls_dir $backup_dir) - <<EOF
961+EOF
962+rm -rf $backup_dir/*
963+
964+vlog "Testing with --tables-file=..."
965+cat >$topdir/list <<EOF
966+test1.t
967+test1.ancor
968+testdatabase.t
969+testdatabase.t7
970+EOF
971+run_cmd $XB_BIN $XB_ARGS --backup --tables-file=$topdir/list --target-dir=$backup_dir --datadir=$mysql_datadir
972+diff -u <(ls_dir $backup_dir) - <<EOF
973+./test1/ancor.ibd
974+./test1/t.ibd
975+./testdatabase/t.ibd
976+EOF
977+rm -rf $backup_dir/*
978+
979+vlog "Testing with --tables=..."
980+$XB_BIN $XB_ARGS --backup --tables='dat.base,test1.t,test1.a' --target-dir=$backup_dir --datadir=$mysql_datadir
981+diff -u <(ls_dir $backup_dir) - <<EOF
982+./database1/ancor.ibd
983+./database1/glow.ibd
984+./database1/t.ibd
985+./database1/t1.ibd
986+./database1/t2.ibd
987+./database1/thisisatable.ibd
988+./database2/ancor.ibd
989+./database2/glow.ibd
990+./database2/t.ibd
991+./database2/t1.ibd
992+./database2/t2.ibd
993+./database2/thisisatable.ibd
994+./test1/ancor.ibd
995+./test1/t.ibd
996+./test1/t1.ibd
997+./test1/t2.ibd
998+./test1/thisisatable.ibd
999+./testdatabase/ancor.ibd
1000+./testdatabase/glow.ibd
1001+./testdatabase/t.ibd
1002+./testdatabase/t1.ibd
1003+./testdatabase/t2.ibd
1004+./testdatabase/thisisatable.ibd
1005+./thisisadatabase/ancor.ibd
1006+./thisisadatabase/glow.ibd
1007+./thisisadatabase/t.ibd
1008+./thisisadatabase/t1.ibd
1009+./thisisadatabase/t2.ibd
1010+./thisisadatabase/thisisatable.ibd
1011+EOF
1012+rm -rf $backup_dir/*

Subscribers

People subscribed via source and target branches