Merge lp:~sergei.glushchenko/percona-xtrabackup/transportabe-tablespaces into lp:percona-xtrabackup/2.0

Proposed by Sergei Glushchenko on 2013-04-26
Status: Merged
Approved by: Alexey Kopytov on 2013-04-27
Approved revision: 533
Merged at revision: 547
Proposed branch: lp:~sergei.glushchenko/percona-xtrabackup/transportabe-tablespaces
Merge into: lp:percona-xtrabackup/2.0
Diff against target: 459 lines (+403/-10)
2 files modified
src/xtrabackup.cc (+391/-4)
test/t/xb_export.sh (+12/-6)
To merge this branch: bzr merge lp:~sergei.glushchenko/percona-xtrabackup/transportabe-tablespaces
Reviewer Review Type Date Requested Status
Alexey Kopytov (community) 2013-04-26 Approve on 2013-04-27
Review via email: mp+161073@code.launchpad.net

Description of the change

  Blueprint: Support transportable tablespaces in xtrabackup --export
  https://blueprints.launchpad.net/percona-xtrabackup/+spec/basic-56-support
  MySQL 5.6 has a feature which allows user to export InnoDB tablespaces and
  import them later. This feature is very similar to XtraDB feature.
  Xtrabackup has an option to export single table tablespace from backup.
  It dump table metadata in format which InnoDB can recognize (.exp files).
  But MySQL has it's own format of metadata stored in .cfg files.
  This patch allows Xtrabackup to produce metadata in both formats when
  export is performed. The code to support cfg export is copied from
  MySQL sources with some modifications.
  Existing testcase xb_export.sh has been modified to test export
  on MySQL 5.6 as well. It however works even when .cfg is not generated
  because bug http://bugs.mysql.com/bug.php?id=66715 is fixed in
  MySQL 5.6.8. It fails however in case of incorrect or corrupted .cfg
  file.

Will post Jenkins link later

To post a comment you must log in.
Alexey Kopytov (akopytov) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/xtrabackup.cc'
2--- src/xtrabackup.cc 2013-04-24 14:09:42 +0000
3+++ src/xtrabackup.cc 2013-04-26 09:10:34 +0000
4@@ -292,6 +292,23 @@
5 #define SRV_PATH_SEPARATOR_STR "/"
6 #endif
7
8+/** The version number of the export meta-data text file. */
9+#define IB_EXPORT_CFG_VERSION_V1 0x1UL
10+
11+/* Maximum multi-byte character length in bytes, plus 1 */
12+#define DATA_MBMAX 5
13+
14+/* Pack mbminlen, mbmaxlen to mbminmaxlen. */
15+#define DATA_MBMINMAXLEN(mbminlen, mbmaxlen) \
16+ ((mbmaxlen) * DATA_MBMAX + (mbminlen))
17+/* Get mbminlen from mbminmaxlen. Cast the result of UNIV_EXPECT to ulint
18+because in GCC it returns a long. */
19+#define DATA_MBMINLEN(mbminmaxlen) ((ulint) \
20+ UNIV_EXPECT(((mbminmaxlen) % DATA_MBMAX), \
21+ 1))
22+/* Get mbmaxlen from mbminmaxlen. */
23+#define DATA_MBMAXLEN(mbminmaxlen) ((ulint) ((mbminmaxlen) / DATA_MBMAX))
24+
25 #ifndef UNIV_PAGE_SIZE_MAX
26 #define UNIV_PAGE_SIZE_MAX UNIV_PAGE_SIZE
27 #endif
28@@ -7336,6 +7353,370 @@
29 return(TRUE); /*ERROR*/
30 }
31
32+
33+/*********************************************************************//**
34+Write the meta data (index user fields) config file.
35+@return true in case of success otherwise false. */
36+static
37+bool
38+xb_export_cfg_write_index_fields(
39+/*===========================*/
40+ const dict_index_t* index, /*!< in: write the meta data for
41+ this index */
42+ FILE* file) /*!< in: file to write to */
43+{
44+ byte row[sizeof(ib_uint32_t) * 2];
45+
46+ for (ulint i = 0; i < index->n_fields; ++i) {
47+ byte* ptr = row;
48+ const dict_field_t* field = &index->fields[i];
49+
50+ mach_write_to_4(ptr, field->prefix_len);
51+ ptr += sizeof(ib_uint32_t);
52+
53+ mach_write_to_4(ptr, field->fixed_len);
54+
55+ if (fwrite(row, 1, sizeof(row), file) != sizeof(row)) {
56+
57+ msg("xtrabackup: Error: writing index fields.");
58+
59+ return(false);
60+ }
61+
62+ /* Include the NUL byte in the length. */
63+ ib_uint32_t len = strlen(field->name) + 1;
64+ ut_a(len > 1);
65+
66+ mach_write_to_4(row, len);
67+
68+ if (fwrite(row, 1, sizeof(len), file) != sizeof(len)
69+ || fwrite(field->name, 1, len, file) != len) {
70+
71+ msg("xtrabackup: Error: writing index column.");
72+
73+ return(false);
74+ }
75+ }
76+
77+ return(true);
78+}
79+
80+/*********************************************************************//**
81+Write the meta data config file index information.
82+@return true in case of success otherwise false. */
83+static __attribute__((nonnull, warn_unused_result))
84+bool
85+xb_export_cfg_write_indexes(
86+/*======================*/
87+ const dict_table_t* table, /*!< in: write the meta data for
88+ this table */
89+ FILE* file) /*!< in: file to write to */
90+{
91+ {
92+ byte row[sizeof(ib_uint32_t)];
93+
94+ /* Write the number of indexes in the table. */
95+ mach_write_to_4(row, UT_LIST_GET_LEN(table->indexes));
96+
97+ if (fwrite(row, 1, sizeof(row), file) != sizeof(row)) {
98+ msg("xtrabackup: Error: writing index count.");
99+
100+ return(false);
101+ }
102+ }
103+
104+ bool ret = true;
105+
106+ /* Write the index meta data. */
107+ for (const dict_index_t* index = UT_LIST_GET_FIRST(table->indexes);
108+ index != 0 && ret;
109+ index = UT_LIST_GET_NEXT(indexes, index)) {
110+
111+ byte* ptr;
112+ byte row[sizeof(IB_UINT64)
113+ + sizeof(ib_uint32_t) * 8];
114+
115+ ptr = row;
116+
117+ ut_ad(sizeof(IB_UINT64) == 8);
118+ mach_write_to_8(ptr, index->id);
119+ ptr += sizeof(IB_UINT64);
120+
121+ mach_write_to_4(ptr, index->space);
122+ ptr += sizeof(ib_uint32_t);
123+
124+ mach_write_to_4(ptr, index->page);
125+ ptr += sizeof(ib_uint32_t);
126+
127+ mach_write_to_4(ptr, index->type);
128+ ptr += sizeof(ib_uint32_t);
129+
130+ mach_write_to_4(ptr, index->trx_id_offset);
131+ ptr += sizeof(ib_uint32_t);
132+
133+ mach_write_to_4(ptr, index->n_user_defined_cols);
134+ ptr += sizeof(ib_uint32_t);
135+
136+ mach_write_to_4(ptr, index->n_uniq);
137+ ptr += sizeof(ib_uint32_t);
138+
139+ mach_write_to_4(ptr, index->n_nullable);
140+ ptr += sizeof(ib_uint32_t);
141+
142+ mach_write_to_4(ptr, index->n_fields);
143+
144+ if (fwrite(row, 1, sizeof(row), file) != sizeof(row)) {
145+
146+ msg("xtrabackup: Error: writing index meta-data.");
147+
148+ return(false);
149+ }
150+
151+ /* Write the length of the index name.
152+ NUL byte is included in the length. */
153+ ib_uint32_t len = strlen(index->name) + 1;
154+ ut_a(len > 1);
155+
156+ mach_write_to_4(row, len);
157+
158+ if (fwrite(row, 1, sizeof(len), file) != sizeof(len)
159+ || fwrite(index->name, 1, len, file) != len) {
160+
161+ msg("xtrabackup: Error: writing index name.");
162+
163+ return(false);
164+ }
165+
166+ ret = xb_export_cfg_write_index_fields(index, file);
167+ }
168+
169+ return(ret);
170+}
171+
172+/*********************************************************************//**
173+Write the meta data (table columns) config file. Serialise the contents of
174+dict_col_t structure, along with the column name. All fields are serialized
175+as ib_uint32_t.
176+@return true in case of success otherwise false. */
177+static __attribute__((nonnull, warn_unused_result))
178+bool
179+xb_export_cfg_write_table(
180+/*====================*/
181+ const dict_table_t* table, /*!< in: write the meta data for
182+ this table */
183+ FILE* file) /*!< in: file to write to */
184+{
185+ dict_col_t* col;
186+ byte row[sizeof(ib_uint32_t) * 7];
187+#if (MYSQL_VERSION_ID < 50600)
188+ ulint minlen;
189+ ulint maxlen;
190+#endif
191+
192+ col = table->cols;
193+
194+ for (ulint i = 0; i < table->n_cols; ++i, ++col) {
195+ byte* ptr = row;
196+
197+ mach_write_to_4(ptr, col->prtype);
198+ ptr += sizeof(ib_uint32_t);
199+
200+ mach_write_to_4(ptr, col->mtype);
201+ ptr += sizeof(ib_uint32_t);
202+
203+ mach_write_to_4(ptr, col->len);
204+ ptr += sizeof(ib_uint32_t);
205+
206+#if (MYSQL_VERSION_ID >= 50600)
207+ mach_write_to_4(ptr, col->mbminmaxlen);
208+#else
209+ innobase_get_cset_width(dtype_get_charset_coll(col->prtype),
210+ &minlen, &maxlen);
211+ mach_write_to_4(ptr, DATA_MBMINMAXLEN(minlen, maxlen));
212+#endif
213+ ptr += sizeof(ib_uint32_t);
214+
215+ mach_write_to_4(ptr, col->ind);
216+ ptr += sizeof(ib_uint32_t);
217+
218+ mach_write_to_4(ptr, col->ord_part);
219+ ptr += sizeof(ib_uint32_t);
220+
221+#if (MYSQL_VERSION_ID >= 50600)
222+ mach_write_to_4(ptr, col->max_prefix);
223+#else
224+ mach_write_to_4(ptr, 0);
225+#endif
226+
227+ if (fwrite(row, 1, sizeof(row), file) != sizeof(row)) {
228+ msg("xtrabackup: Error: writing table column data.");
229+
230+ return(false);
231+ }
232+
233+ /* Write out the column name as [len, byte array]. The len
234+ includes the NUL byte. */
235+ ib_uint32_t len;
236+ const char* col_name;
237+
238+ col_name = dict_table_get_col_name(table, dict_col_get_no(col));
239+
240+ /* Include the NUL byte in the length. */
241+ len = strlen(col_name) + 1;
242+ ut_a(len > 1);
243+
244+ mach_write_to_4(row, len);
245+
246+ if (fwrite(row, 1, sizeof(len), file) != sizeof(len)
247+ || fwrite(col_name, 1, len, file) != len) {
248+
249+ msg("xtrabackup: Error: writing column name.");
250+
251+ return(false);
252+ }
253+ }
254+
255+ return(true);
256+}
257+
258+/*********************************************************************//**
259+Write the meta data config file header.
260+@return true in case of success otherwise false. */
261+static __attribute__((nonnull, warn_unused_result))
262+bool
263+xb_export_cfg_write_header(
264+/*=====================*/
265+ const dict_table_t* table, /*!< in: write the meta data for
266+ this table */
267+ FILE* file) /*!< in: file to write to */
268+{
269+ byte value[sizeof(ib_uint32_t)];
270+
271+ /* Write the meta-data version number. */
272+ mach_write_to_4(value, IB_EXPORT_CFG_VERSION_V1);
273+
274+ if (fwrite(&value, 1, sizeof(value), file) != sizeof(value)) {
275+ msg("xtrabackup: Error: writing meta-data version number.");
276+
277+ return(false);
278+ }
279+
280+ /* Write the server hostname. */
281+ ib_uint32_t len;
282+ const char* hostname = "Hostname unknown";
283+
284+ /* The server hostname includes the NUL byte. */
285+ len = strlen(hostname) + 1;
286+ mach_write_to_4(value, len);
287+
288+ if (fwrite(&value, 1, sizeof(value), file) != sizeof(value)
289+ || fwrite(hostname, 1, len, file) != len) {
290+
291+ msg("xtrabackup: Error: writing hostname.");
292+
293+ return(false);
294+ }
295+
296+ /* The table name includes the NUL byte. */
297+ ut_a(table->name != 0);
298+ len = strlen(table->name) + 1;
299+
300+ /* Write the table name. */
301+ mach_write_to_4(value, len);
302+
303+ if (fwrite(&value, 1, sizeof(value), file) != sizeof(value)
304+ || fwrite(table->name, 1, len, file) != len) {
305+
306+ msg("xtrabackup: Error: writing table name.");
307+
308+ return(false);
309+ }
310+
311+ byte row[sizeof(ib_uint32_t) * 3];
312+
313+ /* Write the next autoinc value. */
314+#ifdef INNODB_VERSION_SHORT
315+ MACH_WRITE_64(row, table->autoinc);
316+#else
317+ MACH_WRITE_64(row,
318+ ut_dulint_create((table->autoinc >> 32) & 0xFFFFFFFFUL,
319+ table->autoinc & 0xFFFFFFFFUL));
320+#endif
321+
322+ if (fwrite(row, 1, sizeof(IB_UINT64), file) != sizeof(IB_UINT64)) {
323+ msg("xtrabackup: Error: writing table autoinc value.");
324+
325+ return(false);
326+ }
327+
328+ byte* ptr = row;
329+
330+ /* Write the system page size. */
331+ mach_write_to_4(ptr, UNIV_PAGE_SIZE);
332+ ptr += sizeof(ib_uint32_t);
333+
334+ /* Write the table->flags. */
335+ mach_write_to_4(ptr, table->flags);
336+ ptr += sizeof(ib_uint32_t);
337+
338+ /* Write the number of columns in the table. */
339+ mach_write_to_4(ptr, table->n_cols);
340+
341+ if (fwrite(row, 1, sizeof(row), file) != sizeof(row)) {
342+ msg("xtrabackup: Error: writing table meta-data.");
343+
344+ return(false);
345+ }
346+
347+ return(true);
348+}
349+
350+/*********************************************************************//**
351+Write MySQL 5.6-style meta data config file.
352+@return true in case of success otherwise false. */
353+static
354+bool
355+xb_export_cfg_write(
356+ const fil_node_t* node,
357+ const dict_table_t* table) /*!< in: write the meta data for
358+ this table */
359+{
360+ char file_path[FN_REFLEN];
361+ FILE* file;
362+ bool success;
363+
364+ strcpy(file_path, node->name);
365+ strcpy(file_path + strlen(file_path) - 4, ".cfg");
366+
367+ file = fopen(file_path, "w+b");
368+
369+ if (file == NULL) {
370+ msg("xtrabackup: Error: cannot close %s\n", node->name);
371+
372+ success = false;
373+ } else {
374+
375+ success = xb_export_cfg_write_header(table, file);
376+
377+ if (success) {
378+ success = xb_export_cfg_write_table(table, file);
379+ }
380+
381+ if (success) {
382+ success = xb_export_cfg_write_indexes(table, file);
383+ }
384+
385+ if (fclose(file) != 0) {
386+ msg("xtrabackup: Error: cannot close %s\n", node->name);
387+ success = false;
388+ }
389+
390+ }
391+
392+ return(success);
393+
394+}
395+
396 static void
397 xtrabackup_prepare_func(void)
398 {
399@@ -7581,11 +7962,12 @@
400 ulint n_index;
401
402 /* node exist == file exist, here */
403- strncpy(info_file_path, node->name, FN_REFLEN);
404+ strcpy(info_file_path, node->name);
405+ strcpy(info_file_path +
406+ strlen(info_file_path) -
407+ 4, ".exp");
408+
409 len = strlen(info_file_path);
410- info_file_path[len - 3] = 'e';
411- info_file_path[len - 2] = 'x';
412- info_file_path[len - 1] = 'p';
413
414 p = info_file_path;
415 prev = NULL;
416@@ -7619,6 +8001,11 @@
417 goto next_node;
418 }
419
420+ /* Write MySQL 5.6 .cfg file */
421+ if (!xb_export_cfg_write(node, table)) {
422+ goto next_node;
423+ }
424+
425 /* init exp file */
426 memset(page, 0, UNIV_PAGE_SIZE);
427 mach_write_to_4(page , 0x78706f72UL);
428
429=== modified file 'test/t/xb_export.sh'
430--- test/t/xb_export.sh 2013-03-11 08:22:38 +0000
431+++ test/t/xb_export.sh 2013-04-26 09:10:34 +0000
432@@ -1,15 +1,21 @@
433 . inc/common.sh
434
435-if [ -z "$XTRADB_VERSION" ]; then
436- echo "Requires XtraDB" > $SKIPPED_REASON
437+if [ -z "$XTRADB_VERSION" -a ${MYSQL_VERSION:0:3} != "5.6" ]; then
438+ echo "Requires XtraDB or MySQL 5.6" > $SKIPPED_REASON
439 exit $SKIPPED_EXIT_CODE
440 fi
441
442-if [ ${MYSQL_VERSION:0:3} = "5.5" ]
443+import_option=""
444+
445+if [ ! -z "$XTRADB_VERSION" ]
446 then
447- import_option="--innodb_import_table_from_xtrabackup=1"
448-else
449- import_option="--innodb_expand_import=1"
450+ # additional agrument should be passed to XtraDB
451+ if [ ${MYSQL_VERSION:0:3} = "5.5" ]
452+ then
453+ import_option="--innodb_import_table_from_xtrabackup=1"
454+ else
455+ import_option="--innodb_expand_import=1"
456+ fi
457 fi
458
459 mysql_extra_args="--innodb_file_per_table $import_option \

Subscribers

People subscribed via source and target branches