MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
restore_main.cpp
1 /*
2  Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; version 2 of the License.
7 
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17 
18 #include <ndb_global.h>
19 #include <ndb_opts.h>
20 #include <Vector.hpp>
21 #include <Properties.hpp>
22 #include <ndb_limits.h>
23 #include <NdbTCP.h>
24 #include <NdbMem.h>
25 #include <NdbOut.hpp>
26 #include <OutputStream.hpp>
27 #include <NDBT_ReturnCodes.h>
28 
29 #include "consumer_restore.hpp"
30 #include "consumer_printer.hpp"
31 #include "../src/ndbapi/NdbDictionaryImpl.hpp"
32 
33 extern FilteredNdbOut err;
34 extern FilteredNdbOut info;
35 extern FilteredNdbOut debug;
36 
37 static Uint32 g_tableCompabilityMask = 0;
38 static int ga_nodeId = 0;
39 static int ga_nParallelism = 128;
40 static int ga_backupId = 0;
41 bool ga_dont_ignore_systab_0 = false;
42 static bool ga_no_upgrade = false;
43 static bool ga_promote_attributes = false;
44 static bool ga_demote_attributes = false;
45 static Vector<class BackupConsumer *> g_consumers;
46 static BackupPrinter* g_printer = NULL;
47 
48 static const char* default_backupPath = "." DIR_SEPARATOR;
49 static const char* ga_backupPath = default_backupPath;
50 
51 static const char *opt_nodegroup_map_str= 0;
52 static unsigned opt_nodegroup_map_len= 0;
53 static NODE_GROUP_MAP opt_nodegroup_map[MAX_NODE_GROUP_MAPS];
54 #define OPT_NDB_NODEGROUP_MAP 'z'
55 
56 const char *opt_ndb_database= NULL;
57 const char *opt_ndb_table= NULL;
58 unsigned int opt_verbose;
59 unsigned int opt_hex_format;
60 unsigned int opt_progress_frequency;
61 NDB_TICKS g_report_next;
62 Vector<BaseString> g_databases;
63 Vector<BaseString> g_tables;
64 Vector<BaseString> g_include_tables, g_exclude_tables;
65 Vector<BaseString> g_include_databases, g_exclude_databases;
66 Properties g_rewrite_databases;
67 NdbRecordPrintFormat g_ndbrecord_print_format;
68 unsigned int opt_no_binlog;
69 
71 {
72 public:
73  virtual ~RestoreOption() { }
74  int optid;
75  BaseString argument;
76 };
77 
78 Vector<class RestoreOption *> g_include_exclude;
79 static void save_include_exclude(int optid, char * argument);
80 
81 static inline void parse_rewrite_database(char * argument);
82 
86 static bool ga_restore_epoch = false;
87 static bool ga_restore = false;
88 static bool ga_print = false;
89 static bool ga_skip_table_check = false;
90 static bool ga_exclude_missing_columns = false;
91 static int _print = 0;
92 static int _print_meta = 0;
93 static int _print_data = 0;
94 static int _print_log = 0;
95 static int _restore_data = 0;
96 static int _restore_meta = 0;
97 static int _no_restore_disk = 0;
98 static bool _preserve_trailing_spaces = false;
99 static bool ga_disable_indexes = false;
100 static bool ga_rebuild_indexes = false;
101 bool ga_skip_unknown_objects = false;
102 bool ga_skip_broken_objects = false;
103 BaseString g_options("ndb_restore");
104 
105 const char *load_default_groups[]= { "mysql_cluster","ndb_restore",0 };
106 
107 enum ndb_restore_options {
108  OPT_VERBOSE = NDB_STD_OPTIONS_LAST,
109  OPT_INCLUDE_TABLES,
110  OPT_EXCLUDE_TABLES,
111  OPT_INCLUDE_DATABASES,
112  OPT_EXCLUDE_DATABASES,
113  OPT_REWRITE_DATABASE
114 };
115 static const char *opt_fields_enclosed_by= NULL;
116 static const char *opt_fields_terminated_by= NULL;
117 static const char *opt_fields_optionally_enclosed_by= NULL;
118 static const char *opt_lines_terminated_by= NULL;
119 
120 static const char *tab_path= NULL;
121 static int opt_append;
122 static const char *opt_exclude_tables= NULL;
123 static const char *opt_include_tables= NULL;
124 static const char *opt_exclude_databases= NULL;
125 static const char *opt_include_databases= NULL;
126 static const char *opt_rewrite_database= NULL;
127 static bool opt_restore_privilege_tables = false;
128 
129 static struct my_option my_long_options[] =
130 {
131  NDB_STD_OPTS("ndb_restore"),
132  { "connect", 'c', "same as --connect-string",
133  (uchar**) &opt_ndb_connectstring, (uchar**) &opt_ndb_connectstring, 0,
134  GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
135  { "nodeid", 'n', "Backup files from node with id",
136  (uchar**) &ga_nodeId, (uchar**) &ga_nodeId, 0,
137  GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
138  { "backupid", 'b', "Backup id",
139  (uchar**) &ga_backupId, (uchar**) &ga_backupId, 0,
140  GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
141  { "restore_data", 'r',
142  "Restore table data/logs into NDB Cluster using NDBAPI",
143  (uchar**) &_restore_data, (uchar**) &_restore_data, 0,
144  GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
145  { "restore_meta", 'm',
146  "Restore meta data into NDB Cluster using NDBAPI",
147  (uchar**) &_restore_meta, (uchar**) &_restore_meta, 0,
148  GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
149  { "no-upgrade", 'u',
150  "Don't upgrade array type for var attributes, which don't resize VAR data and don't change column attributes",
151  (uchar**) &ga_no_upgrade, (uchar**) &ga_no_upgrade, 0,
152  GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
153  { "promote-attributes", 'A',
154  "Allow attributes to be promoted when restoring data from backup",
155  (uchar**) &ga_promote_attributes, (uchar**) &ga_promote_attributes, 0,
156  GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
157  { "lossy-conversions", 'L',
158  "Allow lossy conversions for attributes (type demotions or integral"
159  " signed/unsigned type changes) when restoring data from backup",
160  (uchar**) &ga_demote_attributes, (uchar**) &ga_demote_attributes, 0,
161  GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
162  { "preserve-trailing-spaces", 'P',
163  "Allow to preserve the tailing spaces (including paddings) When char->varchar or binary->varbinary is promoted",
164  (uchar**) &_preserve_trailing_spaces, (uchar**)_preserve_trailing_spaces , 0,
165  GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
166  { "no-restore-disk-objects", 'd',
167  "Dont restore disk objects (tablespace/logfilegroups etc)",
168  (uchar**) &_no_restore_disk, (uchar**) &_no_restore_disk, 0,
169  GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
170  { "restore_epoch", 'e',
171  "Restore epoch info into the status table. Convenient on a MySQL Cluster "
172  "replication slave, for starting replication. The row in "
173  NDB_REP_DB "." NDB_APPLY_TABLE " with id 0 will be updated/inserted.",
174  (uchar**) &ga_restore_epoch, (uchar**) &ga_restore_epoch, 0,
175  GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
176  { "skip-table-check", 's', "Skip table structure check during restore of data",
177  (uchar**) &ga_skip_table_check, (uchar**) &ga_skip_table_check, 0,
178  GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
179  { "parallelism", 'p',
180  "No of parallel transactions during restore of data."
181  "(parallelism can be 1 to 1024)",
182  (uchar**) &ga_nParallelism, (uchar**) &ga_nParallelism, 0,
183  GET_INT, REQUIRED_ARG, 128, 1, 1024, 0, 1, 0 },
184  { "print", NDB_OPT_NOSHORT, "Print metadata, data and log to stdout",
185  (uchar**) &_print, (uchar**) &_print, 0,
186  GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
187  { "print_data", NDB_OPT_NOSHORT, "Print data to stdout",
188  (uchar**) &_print_data, (uchar**) &_print_data, 0,
189  GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
190  { "print_meta", NDB_OPT_NOSHORT, "Print meta data to stdout",
191  (uchar**) &_print_meta, (uchar**) &_print_meta, 0,
192  GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
193  { "print_log", NDB_OPT_NOSHORT, "Print log to stdout",
194  (uchar**) &_print_log, (uchar**) &_print_log, 0,
195  GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
196  { "backup_path", NDB_OPT_NOSHORT, "Path to backup files",
197  (uchar**) &ga_backupPath, (uchar**) &ga_backupPath, 0,
198  GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
199  { "dont_ignore_systab_0", 'f',
200  "Do not ignore system table during --print-data.",
201  (uchar**) &ga_dont_ignore_systab_0, (uchar**) &ga_dont_ignore_systab_0, 0,
202  GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
203  { "ndb-nodegroup-map", OPT_NDB_NODEGROUP_MAP,
204  "Nodegroup map for ndbcluster. Syntax: list of (source_ng, dest_ng)",
205  (uchar**) &opt_nodegroup_map_str,
206  (uchar**) &opt_nodegroup_map_str,
207  0,
208  GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
209  { "fields-enclosed-by", NDB_OPT_NOSHORT,
210  "Fields are enclosed by ...",
211  (uchar**) &opt_fields_enclosed_by, (uchar**) &opt_fields_enclosed_by, 0,
212  GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
213  { "fields-terminated-by", NDB_OPT_NOSHORT,
214  "Fields are terminated by ...",
215  (uchar**) &opt_fields_terminated_by,
216  (uchar**) &opt_fields_terminated_by, 0,
217  GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
218  { "fields-optionally-enclosed-by", NDB_OPT_NOSHORT,
219  "Fields are optionally enclosed by ...",
220  (uchar**) &opt_fields_optionally_enclosed_by,
221  (uchar**) &opt_fields_optionally_enclosed_by, 0,
222  GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
223  { "hex", NDB_OPT_NOSHORT, "print binary types in hex format",
224  (uchar**) &opt_hex_format, (uchar**) &opt_hex_format, 0,
225  GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
226  { "tab", 'T', "Creates tab separated textfile for each table to "
227  "given path. (creates .txt files)",
228  (uchar**) &tab_path, (uchar**) &tab_path, 0,
229  GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
230  { "append", NDB_OPT_NOSHORT, "for --tab append data to file",
231  (uchar**) &opt_append, (uchar**) &opt_append, 0,
232  GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
233  { "lines-terminated-by", NDB_OPT_NOSHORT, "",
234  (uchar**) &opt_lines_terminated_by, (uchar**) &opt_lines_terminated_by, 0,
235  GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
236  { "progress-frequency", NDB_OPT_NOSHORT,
237  "Print status uf restore periodically in given seconds",
238  (uchar**) &opt_progress_frequency, (uchar**) &opt_progress_frequency, 0,
239  GET_INT, REQUIRED_ARG, 0, 0, 65535, 0, 0, 0 },
240  { "no-binlog", NDB_OPT_NOSHORT,
241  "If a mysqld is connected and has binary log, do not log the restored data",
242  (uchar**) &opt_no_binlog, (uchar**) &opt_no_binlog, 0,
243  GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
244  { "verbose", OPT_VERBOSE,
245  "verbosity",
246  (uchar**) &opt_verbose, (uchar**) &opt_verbose, 0,
247  GET_INT, REQUIRED_ARG, 1, 0, 255, 0, 0, 0 },
248  { "include-databases", OPT_INCLUDE_DATABASES,
249  "Comma separated list of databases to restore. Example: db1,db3",
250  (uchar**) &opt_include_databases, (uchar**) &opt_include_databases, 0,
251  GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
252  { "exclude-databases", OPT_EXCLUDE_DATABASES,
253  "Comma separated list of databases to not restore. Example: db1,db3",
254  (uchar**) &opt_exclude_databases, (uchar**) &opt_exclude_databases, 0,
255  GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
256  { "rewrite-database", OPT_REWRITE_DATABASE,
257  "A pair 'source,dest' of database names from/into which to restore. "
258  "Example: --rewrite-database=oldDb,newDb",
259  (uchar**) &opt_rewrite_database, (uchar**) &opt_rewrite_database, 0,
260  GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
261  { "include-tables", OPT_INCLUDE_TABLES, "Comma separated list of tables to "
262  "restore. Table name should include database name. Example: db1.t1,db3.t1",
263  (uchar**) &opt_include_tables, (uchar**) &opt_include_tables, 0,
264  GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
265  { "exclude-tables", OPT_EXCLUDE_TABLES, "Comma separated list of tables to "
266  "not restore. Table name should include database name. "
267  "Example: db1.t1,db3.t1",
268  (uchar**) &opt_exclude_tables, (uchar**) &opt_exclude_tables, 0,
269  GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
270  { "restore-privilege-tables", NDB_OPT_NOSHORT,
271  "Restore privilege tables (after they have been moved to ndb)",
272  (uchar**) &opt_restore_privilege_tables,
273  (uchar**) &opt_restore_privilege_tables, 0,
274  GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
275  { "exclude-missing-columns", NDB_OPT_NOSHORT,
276  "Ignore columns present in backup but not in database",
277  (uchar**) &ga_exclude_missing_columns,
278  (uchar**) &ga_exclude_missing_columns, 0,
279  GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
280  { "disable-indexes", NDB_OPT_NOSHORT,
281  "Disable indexes",
282  (uchar**) &ga_disable_indexes,
283  (uchar**) &ga_disable_indexes, 0,
284  GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
285  { "rebuild-indexes", NDB_OPT_NOSHORT,
286  "Rebuild indexes",
287  (uchar**) &ga_rebuild_indexes,
288  (uchar**) &ga_rebuild_indexes, 0,
289  GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
290  { "skip-unknown-objects", 256, "Skip unknown object when parsing backup",
291  (uchar**) &ga_skip_unknown_objects, (uchar**) &ga_skip_unknown_objects, 0,
292  GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
293  { "skip-broken-objects", 256, "Skip broken object when parsing backup",
294  (uchar**) &ga_skip_broken_objects, (uchar**) &ga_skip_broken_objects, 0,
295  GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
296  { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
297 };
298 
299 
300 static char* analyse_one_map(char *map_str, uint16 *source, uint16 *dest)
301 {
302  char *end_ptr;
303  int number;
304  DBUG_ENTER("analyse_one_map");
305  /*
306  Search for pattern ( source_ng , dest_ng )
307  */
308 
309  while (isspace(*map_str)) map_str++;
310 
311  if (*map_str != '(')
312  {
313  DBUG_RETURN(NULL);
314  }
315  map_str++;
316 
317  while (isspace(*map_str)) map_str++;
318 
319  number= strtol(map_str, &end_ptr, 10);
320  if (!end_ptr || number < 0 || number >= MAX_NODE_GROUP_MAPS)
321  {
322  DBUG_RETURN(NULL);
323  }
324  *source= (uint16)number;
325  map_str= end_ptr;
326 
327  while (isspace(*map_str)) map_str++;
328 
329  if (*map_str != ',')
330  {
331  DBUG_RETURN(NULL);
332  }
333  map_str++;
334 
335  number= strtol(map_str, &end_ptr, 10);
336  if (!end_ptr || number < 0 || number >= NDB_UNDEF_NODEGROUP)
337  {
338  DBUG_RETURN(NULL);
339  }
340  *dest= (uint16)number;
341  map_str= end_ptr;
342 
343  if (*map_str != ')')
344  {
345  DBUG_RETURN(NULL);
346  }
347  map_str++;
348 
349  while (isspace(*map_str)) map_str++;
350  DBUG_RETURN(map_str);
351 }
352 
353 static bool insert_ng_map(NODE_GROUP_MAP *ng_map,
354  uint16 source_ng, uint16 dest_ng)
355 {
356  uint index= source_ng;
357  uint ng_index= ng_map[index].no_maps;
358 
359  opt_nodegroup_map_len++;
360  if (ng_index >= MAX_MAPS_PER_NODE_GROUP)
361  return true;
362  ng_map[index].no_maps++;
363  ng_map[index].map_array[ng_index]= dest_ng;
364  return false;
365 }
366 
367 static void init_nodegroup_map()
368 {
369  uint i,j;
370  NODE_GROUP_MAP *ng_map = &opt_nodegroup_map[0];
371 
372  for (i = 0; i < MAX_NODE_GROUP_MAPS; i++)
373  {
374  ng_map[i].no_maps= 0;
375  for (j= 0; j < MAX_MAPS_PER_NODE_GROUP; j++)
376  ng_map[i].map_array[j]= NDB_UNDEF_NODEGROUP;
377  }
378 }
379 
380 static bool analyse_nodegroup_map(const char *ng_map_str,
381  NODE_GROUP_MAP *ng_map)
382 {
383  uint16 source_ng, dest_ng;
384  char *local_str= (char*)ng_map_str;
385  DBUG_ENTER("analyse_nodegroup_map");
386 
387  do
388  {
389  if (!local_str)
390  {
391  DBUG_RETURN(TRUE);
392  }
393  local_str= analyse_one_map(local_str, &source_ng, &dest_ng);
394  if (!local_str)
395  {
396  DBUG_RETURN(TRUE);
397  }
398  if (insert_ng_map(ng_map, source_ng, dest_ng))
399  {
400  DBUG_RETURN(TRUE);
401  }
402  if (!(*local_str))
403  break;
404  } while (TRUE);
405  DBUG_RETURN(FALSE);
406 }
407 
408 static void short_usage_sub(void)
409 {
410  ndb_short_usage_sub("[<path to backup files>]");
411 }
412 static void usage()
413 {
414  ndb_usage(short_usage_sub, load_default_groups, my_long_options);
415 }
416 
417 static my_bool
418 get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
419  char *argument)
420 {
421 #ifndef DBUG_OFF
422  opt_debug= "d:t:O,/tmp/ndb_restore.trace";
423 #endif
424  ndb_std_get_one_option(optid, opt, argument);
425  switch (optid) {
426  case OPT_VERBOSE:
427  info.setThreshold(255-opt_verbose);
428  break;
429  case 'n':
430  if (ga_nodeId == 0)
431  {
432  err << "Error in --nodeid,-n setting, see --help";
433  exit(NDBT_ProgramExit(NDBT_WRONGARGS));
434  }
435  info.setLevel(254);
436  info << "Nodeid = " << ga_nodeId << endl;
437  break;
438  case 'b':
439  if (ga_backupId == 0)
440  {
441  err << "Error in --backupid,-b setting, see --help";
442  exit(NDBT_ProgramExit(NDBT_WRONGARGS));
443  }
444  info.setLevel(254);
445  info << "Backup Id = " << ga_backupId << endl;
446  break;
447  case OPT_NDB_NODEGROUP_MAP:
448  /*
449  This option is used to set a map from nodegroup in original cluster
450  to nodegroup in new cluster.
451  */
452  opt_nodegroup_map_len= 0;
453 
454  info.setLevel(254);
455  info << "Analyse node group map" << endl;
456  if (analyse_nodegroup_map(opt_nodegroup_map_str,
457  &opt_nodegroup_map[0]))
458  {
459  exit(NDBT_ProgramExit(NDBT_WRONGARGS));
460  }
461  break;
462  case OPT_INCLUDE_DATABASES:
463  case OPT_EXCLUDE_DATABASES:
464  case OPT_INCLUDE_TABLES:
465  case OPT_EXCLUDE_TABLES:
466  save_include_exclude(optid, argument);
467  break;
468  case OPT_REWRITE_DATABASE:
469  parse_rewrite_database(argument);
470  break;
471  }
472  return 0;
473 }
474 
475 static const char* SCHEMA_NAME="/def/";
476 static const int SCHEMA_NAME_SIZE= 5;
477 
478 int
479 makeInternalTableName(const BaseString &externalName,
480  BaseString& internalName)
481 {
482  // Make dbname.table1 into dbname/def/table1
483  Vector<BaseString> parts;
484 
485  // Must contain a dot
486  if (externalName.indexOf('.') == -1)
487  return -1;
488  externalName.split(parts,".");
489  // .. and only 1 dot
490  if (parts.size() != 2)
491  return -1;
492  internalName.clear();
493  internalName.append(parts[0]); // db name
494  internalName.append(SCHEMA_NAME); // /def/
495  internalName.append(parts[1]); // table name
496  return 0;
497 }
498 
499 void
500 processTableList(const char* str, Vector<BaseString> &lst)
501 {
502  // Process tables list like db1.t1,db2.t1 and exits when
503  // it finds problems.
504  Vector<BaseString> tmp;
505  unsigned int i;
506  /* Split passed string on comma into 2 BaseStrings in the vector */
507  BaseString(str).split(tmp,",");
508  for (i=0; i < tmp.size(); i++)
509  {
510  BaseString internalName;
511  if (makeInternalTableName(tmp[i], internalName))
512  {
513  info << "`" << tmp[i] << "` is not a valid tablename!" << endl;
514  exit(NDBT_ProgramExit(NDBT_WRONGARGS));
515  }
516  lst.push_back(internalName);
517  }
518 }
519 
521 makeExternalTableName(const BaseString &internalName)
522 {
523  // Make dbname/def/table1 into dbname.table1
524  BaseString externalName;
525 
526  ssize_t idx = internalName.indexOf('/');
527  externalName = internalName.substr(0,idx);
528  externalName.append(".");
529  externalName.append(internalName.substr(idx + SCHEMA_NAME_SIZE,
530  internalName.length()));
531  return externalName;
532 }
533 
534 #include "../../../../sql/ndb_dist_priv_util.h"
535 
536 // Exclude privilege tables unless explicitely included
537 void
538 exclude_privilege_tables()
539 {
540  const char* table_name;
541  Ndb_dist_priv_util dist_priv;
542  while((table_name= dist_priv.iter_next_table()))
543  {
544  BaseString priv_tab;
545  priv_tab.assfmt("%s.%s", dist_priv.database(), table_name);
546  g_exclude_tables.push_back(priv_tab);
547  save_include_exclude(OPT_EXCLUDE_TABLES, (char *)priv_tab.c_str());
548  }
549 }
550 
551 
552 bool
553 readArguments(int *pargc, char*** pargv)
554 {
555  Uint32 i;
556  BaseString tmp;
557  debug << "Load defaults" << endl;
558  const char *load_default_groups[]= { "mysql_cluster","ndb_restore",0 };
559 
560  init_nodegroup_map();
561  load_defaults("my",load_default_groups,pargc,pargv);
562  debug << "handle_options" << endl;
563 
564  ndb_opt_set_usage_funcs(short_usage_sub, usage);
565 
566  if (handle_options(pargc, pargv, my_long_options, get_one_option))
567  {
568  exit(NDBT_ProgramExit(NDBT_WRONGARGS));
569  }
570  for (i = 0; i < MAX_NODE_GROUP_MAPS; i++)
571  opt_nodegroup_map[i].curr_index = 0;
572 
573 #if 0
574  /*
575  Test code written t{
576 o verify nodegroup mapping
577  */
578  printf("Handled options successfully\n");
579  Uint16 map_ng[16];
580  Uint32 j;
581  for (j = 0; j < 4; j++)
582  {
583  for (i = 0; i < 4 ; i++)
584  map_ng[i] = i;
585  map_nodegroups(&map_ng[0], (Uint32)4);
586  for (i = 0; i < 4 ; i++)
587  printf("NG %u mapped to %u \n", i, map_ng[i]);
588  }
589  for (j = 0; j < 4; j++)
590  {
591  for (i = 0; i < 8 ; i++)
592  map_ng[i] = i >> 1;
593  map_nodegroups(&map_ng[0], (Uint32)8);
594  for (i = 0; i < 8 ; i++)
595  printf("NG %u mapped to %u \n", i >> 1, map_ng[i]);
596  }
597  exit(NDBT_ProgramExit(NDBT_WRONGARGS));
598 #endif
599 
600  g_printer = new BackupPrinter(opt_nodegroup_map,
601  opt_nodegroup_map_len);
602  if (g_printer == NULL)
603  return false;
604 
605  BackupRestore* restore = new BackupRestore(opt_ndb_connectstring,
606  opt_ndb_nodeid,
607  opt_nodegroup_map,
608  opt_nodegroup_map_len,
609  ga_nParallelism);
610  if (restore == NULL)
611  {
612  delete g_printer;
613  g_printer = NULL;
614  return false;
615  }
616 
617  if (_print)
618  {
619  ga_print = true;
620  ga_restore = true;
621  g_printer->m_print = true;
622  }
623  if (_print_meta)
624  {
625  ga_print = true;
626  g_printer->m_print_meta = true;
627  }
628  if (_print_data)
629  {
630  ga_print = true;
631  g_printer->m_print_data = true;
632  }
633  if (_print_log)
634  {
635  ga_print = true;
636  g_printer->m_print_log = true;
637  }
638 
639  if (_restore_data)
640  {
641  ga_restore = true;
642  restore->m_restore = true;
643  }
644 
645  if (_restore_meta)
646  {
647  // ga_restore = true;
648  restore->m_restore_meta = true;
649  }
650 
651  if (_no_restore_disk)
652  {
653  restore->m_no_restore_disk = true;
654  }
655 
656  if (ga_no_upgrade)
657  {
658  restore->m_no_upgrade = true;
659  }
660 
661  if (_preserve_trailing_spaces)
662  {
663  restore->m_preserve_trailing_spaces = true;
664  }
665 
666  if (ga_restore_epoch)
667  {
668  restore->m_restore_epoch = true;
669  }
670 
671  if (ga_disable_indexes)
672  {
673  restore->m_disable_indexes = true;
674  }
675 
676  if (ga_rebuild_indexes)
677  {
678  restore->m_rebuild_indexes = true;
679  }
680 
681  {
682  BackupConsumer * c = g_printer;
683  g_consumers.push_back(c);
684  }
685  {
686  BackupConsumer * c = restore;
687  g_consumers.push_back(c);
688  }
689  for (;;)
690  {
691  int i= 0;
692  if (ga_backupPath == default_backupPath)
693  {
694  // Set backup file path
695  if ((*pargv)[i] == NULL)
696  break;
697  ga_backupPath = (*pargv)[i++];
698  }
699  if ((*pargv)[i] == NULL)
700  break;
701  g_databases.push_back((*pargv)[i++]);
702  while ((*pargv)[i] != NULL)
703  {
704  g_tables.push_back((*pargv)[i++]);
705  }
706  break;
707  }
708  info.setLevel(254);
709  info << "backup path = " << ga_backupPath << endl;
710  if (g_databases.size() > 0)
711  {
712  info << "WARNING! Using deprecated syntax for selective object restoration." << endl;
713  info << "Please use --include-*/--exclude-* options in future." << endl;
714  info << "Restoring only from database " << g_databases[0].c_str() << endl;
715  if (g_tables.size() > 0)
716  {
717  info << "Restoring tables:";
718  }
719  for (unsigned i= 0; i < g_tables.size(); i++)
720  {
721  info << " " << g_tables[i].c_str();
722  }
723  if (g_tables.size() > 0)
724  info << endl;
725  }
726 
727  if (ga_restore)
728  {
729  // Exclude privilege tables unless explicitely included
730  if (!opt_restore_privilege_tables)
731  exclude_privilege_tables();
732 
733  // Move over old style arguments to include/exclude lists
734  if (g_databases.size() > 0)
735  {
736  BaseString tab_prefix, tab;
737  tab_prefix.append(g_databases[0].c_str());
738  tab_prefix.append(".");
739  if (g_tables.size() == 0)
740  {
741  g_include_databases.push_back(g_databases[0]);
742  save_include_exclude(OPT_INCLUDE_DATABASES,
743  (char *)g_databases[0].c_str());
744  }
745  for (unsigned i= 0; i < g_tables.size(); i++)
746  {
747  tab.assign(tab_prefix);
748  tab.append(g_tables[i]);
749  g_include_tables.push_back(tab);
750  save_include_exclude(OPT_INCLUDE_TABLES, (char *)tab.c_str());
751  }
752  }
753  }
754 
755  if (opt_include_databases)
756  {
757  tmp = BaseString(opt_include_databases);
758  tmp.split(g_include_databases,",");
759  info << "Including Databases: ";
760  for (i= 0; i < g_include_databases.size(); i++)
761  {
762  info << g_include_databases[i] << " ";
763  }
764  info << endl;
765  }
766 
767  if (opt_exclude_databases)
768  {
769  tmp = BaseString(opt_exclude_databases);
770  tmp.split(g_exclude_databases,",");
771  info << "Excluding databases: ";
772  for (i= 0; i < g_exclude_databases.size(); i++)
773  {
774  info << g_exclude_databases[i] << " ";
775  }
776  info << endl;
777  }
778 
779  if (opt_rewrite_database)
780  {
781  info << "Rewriting databases:";
782  Properties::Iterator it(&g_rewrite_databases);
783  const char * src;
784  for (src = it.first(); src != NULL; src = it.next()) {
785  const char * dst = NULL;
786  bool r = g_rewrite_databases.get(src, &dst);
787  assert(r && (dst != NULL));
788  info << " (" << src << "->" << dst << ")";
789  }
790  info << endl;
791  }
792 
793  if (opt_include_tables)
794  {
795  processTableList(opt_include_tables, g_include_tables);
796  info << "Including tables: ";
797  for (i= 0; i < g_include_tables.size(); i++)
798  {
799  info << makeExternalTableName(g_include_tables[i]).c_str() << " ";
800  }
801  info << endl;
802  }
803 
804  if (opt_exclude_tables)
805  {
806  processTableList(opt_exclude_tables, g_exclude_tables);
807  info << "Excluding tables: ";
808  for (i= 0; i < g_exclude_tables.size(); i++)
809  {
810  info << makeExternalTableName(g_exclude_tables[i]).c_str() << " ";
811  }
812  info << endl;
813  }
814 
815  /*
816  the below formatting follows the formatting from mysqldump
817  do not change unless to adopt to changes in mysqldump
818  */
819  g_ndbrecord_print_format.fields_enclosed_by=
820  opt_fields_enclosed_by ? opt_fields_enclosed_by : "";
821  g_ndbrecord_print_format.fields_terminated_by=
822  opt_fields_terminated_by ? opt_fields_terminated_by : "\t";
823  g_ndbrecord_print_format.fields_optionally_enclosed_by=
824  opt_fields_optionally_enclosed_by ? opt_fields_optionally_enclosed_by : "";
825  g_ndbrecord_print_format.lines_terminated_by=
826  opt_lines_terminated_by ? opt_lines_terminated_by : "\n";
827  if (g_ndbrecord_print_format.fields_optionally_enclosed_by[0] == '\0')
828  g_ndbrecord_print_format.null_string= "\\N";
829  else
830  g_ndbrecord_print_format.null_string= "";
831  g_ndbrecord_print_format.hex_prefix= "";
832  g_ndbrecord_print_format.hex_format= opt_hex_format;
833 
834  if (ga_skip_table_check)
835  {
836  g_tableCompabilityMask = ~(Uint32)0;
837  ga_skip_unknown_objects = true;
838  }
839 
840  if (ga_promote_attributes)
841  {
842  g_tableCompabilityMask |= TCM_ATTRIBUTE_PROMOTION;
843  }
844 
845  if (ga_demote_attributes)
846  {
847  g_tableCompabilityMask |= TCM_ATTRIBUTE_DEMOTION;
848  }
849 
850  if (ga_exclude_missing_columns)
851  {
852  g_tableCompabilityMask |= TCM_EXCLUDE_MISSING_COLUMNS;
853  }
854  return true;
855 }
856 
857 void
858 clearConsumers()
859 {
860  for(Uint32 i= 0; i<g_consumers.size(); i++)
861  delete g_consumers[i];
862  g_consumers.clear();
863 }
864 
865 static inline bool
866 checkSysTable(const TableS* table)
867 {
868  return ! table->getSysTable();
869 }
870 
871 static inline bool
872 checkSysTable(const RestoreMetaData& metaData, uint i)
873 {
874  assert(i < metaData.getNoOfTables());
875  return checkSysTable(metaData[i]);
876 }
877 
878 static inline bool
879 isBlobTable(const TableS* table)
880 {
881  return table->getMainTable() != NULL;
882 }
883 
884 static inline bool
885 isIndex(const TableS* table)
886 {
887  const NdbTableImpl & tmptab = NdbTableImpl::getImpl(* table->m_dictTable);
888  return (int) tmptab.m_indexType != (int) NdbDictionary::Index::Undefined;
889 }
890 
891 static inline bool
892 isSYSTAB_0(const TableS* table)
893 {
894  return table->isSYSTAB_0();
895 }
896 
897 static inline bool
898 isInList(BaseString &needle, Vector<BaseString> &lst){
899  unsigned int i= 0;
900  for (i= 0; i < lst.size(); i++)
901  {
902  if (strcmp(needle.c_str(), lst[i].c_str()) == 0)
903  return true;
904  }
905  return false;
906 }
907 
908 const char*
909 getTableName(const TableS* table)
910 {
911  const char *table_name;
912  if (isBlobTable(table))
913  table_name= table->getMainTable()->getTableName();
914  else if (isIndex(table))
915  table_name=
916  NdbTableImpl::getImpl(*table->m_dictTable).m_primaryTable.c_str();
917  else
918  table_name= table->getTableName();
919 
920  return table_name;
921 }
922 
923 static void parse_rewrite_database(char * argument)
924 {
925  const BaseString arg(argument);
926  Vector<BaseString> args;
927  unsigned int n = arg.split(args, ",");
928  if ((n == 2)
929  && (args[0].length() > 0)
930  && (args[1].length() > 0)) {
931  const BaseString src = args[0];
932  const BaseString dst = args[1];
933  const bool replace = true;
934  bool r = g_rewrite_databases.put(src.c_str(), dst.c_str(), replace);
935  assert(r);
936  return; // ok
937  }
938 
939  info << "argument `" << arg.c_str()
940  << "` is not a pair 'a,b' of non-empty names." << endl;
941  exit(NDBT_ProgramExit(NDBT_WRONGARGS));
942 }
943 
944 static void save_include_exclude(int optid, char * argument)
945 {
946  BaseString arg = argument;
947  Vector<BaseString> args;
948  arg.split(args, ",");
949  for (uint i = 0; i < args.size(); i++)
950  {
952  BaseString arg;
953 
954  option->optid = optid;
955  switch (optid) {
956  case OPT_INCLUDE_TABLES:
957  case OPT_EXCLUDE_TABLES:
958  if (makeInternalTableName(args[i], arg))
959  {
960  info << "`" << args[i] << "` is not a valid tablename!" << endl;
961  exit(NDBT_ProgramExit(NDBT_WRONGARGS));
962  }
963  break;
964  default:
965  arg = args[i];
966  break;
967  }
968  option->argument = arg;
969  g_include_exclude.push_back(option);
970  }
971 }
972 static bool check_include_exclude(BaseString database, BaseString table)
973 {
974  const char * db = database.c_str();
975  const char * tbl = table.c_str();
976  bool do_include = true;
977 
978  if (g_include_databases.size() != 0 ||
979  g_include_tables.size() != 0)
980  {
981  /*
982  User has explicitly specified what databases
983  and/or tables should be restored. If no match is
984  found then DON'T restore table.
985  */
986  do_include = false;
987  }
988  if (do_include &&
989  (g_exclude_databases.size() != 0 ||
990  g_exclude_tables.size() != 0))
991  {
992  /*
993  User has not explicitly specified what databases
994  and/or tables should be restored.
995  User has explicitly specified what databases
996  and/or tables should NOT be restored. If no match is
997  found then DO restore table.
998  */
999  do_include = true;
1000  }
1001 
1002  if (g_include_exclude.size() != 0)
1003  {
1004  /*
1005  Scan include exclude arguments in reverse.
1006  First matching include causes table to be restored.
1007  first matching exclude causes table NOT to be restored.
1008  */
1009  for(uint i = g_include_exclude.size(); i > 0; i--)
1010  {
1011  RestoreOption *option = g_include_exclude[i-1];
1012  switch (option->optid) {
1013  case OPT_INCLUDE_TABLES:
1014  if (strcmp(tbl, option->argument.c_str()) == 0)
1015  return true; // do include
1016  break;
1017  case OPT_EXCLUDE_TABLES:
1018  if (strcmp(tbl, option->argument.c_str()) == 0)
1019  return false; // don't include
1020  break;
1021  case OPT_INCLUDE_DATABASES:
1022  if (strcmp(db, option->argument.c_str()) == 0)
1023  return true; // do include
1024  break;
1025  case OPT_EXCLUDE_DATABASES:
1026  if (strcmp(db, option->argument.c_str()) == 0)
1027  return false; // don't include
1028  break;
1029  default:
1030  continue;
1031  }
1032  }
1033  }
1034 
1035  return do_include;
1036 }
1037 
1038 static inline bool
1039 checkDoRestore(const TableS* table)
1040 {
1041  bool ret = true;
1042  BaseString db, tbl;
1043 
1044  tbl.assign(getTableName(table));
1045  ssize_t idx = tbl.indexOf('/');
1046  db = tbl.substr(0, idx);
1047 
1048  /*
1049  Include/exclude flags are evaluated right
1050  to left, and first match overrides any other
1051  matches. Non-overlapping arguments are accumulative.
1052  If no include flags are specified this means all databases/tables
1053  except any excluded are restored.
1054  If include flags are specified than only those databases
1055  or tables specified are restored.
1056  */
1057  ret = check_include_exclude(db, tbl);
1058  return ret;
1059 }
1060 
1061 static inline bool
1062 checkDbAndTableName(const TableS* table)
1063 {
1064  if (table->isBroken())
1065  return false;
1066 
1067  // If new options are given, ignore the old format
1068  if (opt_include_tables || g_exclude_tables.size() > 0 ||
1069  opt_include_databases || opt_exclude_databases ) {
1070  return (checkDoRestore(table));
1071  }
1072 
1073  if (g_tables.size() == 0 && g_databases.size() == 0)
1074  return true;
1075 
1076  if (g_databases.size() == 0)
1077  g_databases.push_back("TEST_DB");
1078 
1079  // Filter on the main table name for indexes and blobs
1080  const char *table_name= getTableName(table);
1081 
1082  unsigned i;
1083  for (i= 0; i < g_databases.size(); i++)
1084  {
1085  if (strncmp(table_name, g_databases[i].c_str(),
1086  g_databases[i].length()) == 0 &&
1087  table_name[g_databases[i].length()] == '/')
1088  {
1089  // we have a match
1090  if (g_databases.size() > 1 || g_tables.size() == 0)
1091  return true;
1092  break;
1093  }
1094  }
1095  if (i == g_databases.size())
1096  return false; // no match found
1097 
1098  while (*table_name != '/') table_name++;
1099  table_name++;
1100  while (*table_name != '/') table_name++;
1101  table_name++;
1102 
1103  // Check if table should be restored
1104  for (i= 0; i < g_tables.size(); i++)
1105  {
1106  if (strcmp(table_name, g_tables[i].c_str()) == 0)
1107  return true;
1108  }
1109  return false;
1110 }
1111 
1112 static void
1113 free_data_callback()
1114 {
1115  for(Uint32 i= 0; i < g_consumers.size(); i++)
1116  g_consumers[i]->tuple_free();
1117 }
1118 
1119 static void exitHandler(int code)
1120 {
1121  NDBT_ProgramExit(code);
1122  if (opt_core)
1123  abort();
1124  else
1125  exit(code);
1126 }
1127 
1128 static void init_progress()
1129 {
1130  Uint64 now = NdbTick_CurrentMillisecond() / 1000;
1131  g_report_next = now + opt_progress_frequency;
1132 }
1133 
1134 static int check_progress()
1135 {
1136  if (!opt_progress_frequency)
1137  return 0;
1138 
1139  NDB_TICKS now = NdbTick_CurrentMillisecond() / 1000;
1140 
1141  if (now >= g_report_next)
1142  {
1143  g_report_next = now + opt_progress_frequency;
1144  return 1;
1145  }
1146  return 0;
1147 }
1148 
1149 static void report_progress(const char *prefix, const BackupFile &f)
1150 {
1151  info.setLevel(255);
1152  if (f.get_file_size())
1153  info << prefix << (f.get_file_pos() * 100 + f.get_file_size()-1) / f.get_file_size()
1154  << "%(" << f.get_file_pos() << " bytes)\n";
1155  else
1156  info << prefix << f.get_file_pos() << " bytes\n";
1157 }
1158 
1162 static void
1163 check_data_truncations(const TableS * table)
1164 {
1165  assert(table);
1166  const char * tname = table->getTableName();
1167  const int n = table->getNoOfAttributes();
1168  for (int i = 0; i < n; i++) {
1169  AttributeDesc * desc = table->getAttributeDesc(i);
1170  if (desc->truncation_detected) {
1171  const char * cname = desc->m_column->getName();
1172  info.setLevel(254);
1173  info << "Data truncation(s) detected for attribute: "
1174  << tname << "." << cname << endl;
1175  desc->truncation_detected = false;
1176  }
1177  }
1178 }
1179 
1180 int
1181 main(int argc, char** argv)
1182 {
1183  NDB_INIT(argv[0]);
1184 
1185  debug << "Start readArguments" << endl;
1186  if (!readArguments(&argc, &argv))
1187  {
1188  exitHandler(NDBT_FAILED);
1189  }
1190 
1191  g_options.appfmt(" -b %u", ga_backupId);
1192  g_options.appfmt(" -n %d", ga_nodeId);
1193  if (_restore_meta)
1194  g_options.appfmt(" -m");
1195  if (ga_no_upgrade)
1196  g_options.appfmt(" -u");
1197  if (ga_promote_attributes)
1198  g_options.appfmt(" -A");
1199  if (ga_demote_attributes)
1200  g_options.appfmt(" -L");
1201  if (_preserve_trailing_spaces)
1202  g_options.appfmt(" -P");
1203  if (ga_skip_table_check)
1204  g_options.appfmt(" -s");
1205  if (_restore_data)
1206  g_options.appfmt(" -r");
1207  if (ga_restore_epoch)
1208  g_options.appfmt(" -e");
1209  if (_no_restore_disk)
1210  g_options.appfmt(" -d");
1211  if (ga_exclude_missing_columns)
1212  g_options.append(" --exclude-missing-columns");
1213  if (ga_disable_indexes)
1214  g_options.append(" --disable-indexes");
1215  if (ga_rebuild_indexes)
1216  g_options.append(" --rebuild-indexes");
1217  g_options.appfmt(" -p %d", ga_nParallelism);
1218  if (ga_skip_unknown_objects)
1219  g_options.append(" --skip-unknown-objects");
1220  if (ga_skip_broken_objects)
1221  g_options.append(" --skip-broken-objects");
1222 
1223  init_progress();
1224 
1228  debug << "Start restoring meta data" << endl;
1229  RestoreMetaData metaData(ga_backupPath, ga_nodeId, ga_backupId);
1230  if (!metaData.readHeader())
1231  {
1232  err << "Failed to read " << metaData.getFilename() << endl << endl;
1233  exitHandler(NDBT_FAILED);
1234  }
1235 
1236  const BackupFormat::FileHeader & tmp = metaData.getFileHeader();
1237  const Uint32 version = tmp.BackupVersion;
1238 
1239  char buf[NDB_VERSION_STRING_BUF_SZ];
1240  info.setLevel(254);
1241  info << "Backup version in files: "
1242  << ndbGetVersionString(version, 0,
1243  isDrop6(version) ? "-drop6" : 0,
1244  buf, sizeof(buf));
1245  if (version >= NDBD_RAW_LCP)
1246  {
1247  info << " ndb version: "
1248  << ndbGetVersionString(tmp.NdbVersion, tmp.MySQLVersion, 0,
1249  buf, sizeof(buf));
1250  }
1251 
1252  info << endl;
1253 
1257  // in these versions there was an error in how replica info was
1258  // stored on disk
1259  if (version >= MAKE_VERSION(5,1,3) && version <= MAKE_VERSION(5,1,9))
1260  {
1261  err << "Restore program incompatible with backup versions between "
1262  << ndbGetVersionString(MAKE_VERSION(5,1,3), 0, 0, buf, sizeof(buf))
1263  << " and "
1264  << ndbGetVersionString(MAKE_VERSION(5,1,9), 0, 0, buf, sizeof(buf))
1265  << endl;
1266  exitHandler(NDBT_FAILED);
1267  }
1268 
1269  if (version > NDB_VERSION)
1270  {
1271  err << "Restore program older than backup version. Not supported. "
1272  << "Use new restore program" << endl;
1273  exitHandler(NDBT_FAILED);
1274  }
1275 
1276  debug << "Load content" << endl;
1277  int res = metaData.loadContent();
1278 
1279  info << "Stop GCP of Backup: " << metaData.getStopGCP() << endl;
1280 
1281  if (res == 0)
1282  {
1283  err << "Restore: Failed to load content" << endl;
1284  exitHandler(NDBT_FAILED);
1285  }
1286  debug << "Get no of Tables" << endl;
1287  if (metaData.getNoOfTables() == 0)
1288  {
1289  err << "The backup contains no tables" << endl;
1290  exitHandler(NDBT_FAILED);
1291  }
1292  debug << "Validate Footer" << endl;
1293 
1294  if (!metaData.validateFooter())
1295  {
1296  err << "Restore: Failed to validate footer." << endl;
1297  exitHandler(NDBT_FAILED);
1298  }
1299  debug << "Init Backup objects" << endl;
1300  Uint32 i;
1301  for(i= 0; i < g_consumers.size(); i++)
1302  {
1303  if (!g_consumers[i]->init(g_tableCompabilityMask))
1304  {
1305  clearConsumers();
1306  err << "Failed to initialize consumers" << endl;
1307  exitHandler(NDBT_FAILED);
1308  }
1309 
1310  }
1311 
1312  /* report to clusterlog if applicable */
1313  for (i = 0; i < g_consumers.size(); i++)
1314  g_consumers[i]->report_started(ga_backupId, ga_nodeId);
1315 
1316  debug << "Restore objects (tablespaces, ..)" << endl;
1317  for(i = 0; i<metaData.getNoOfObjects(); i++)
1318  {
1319  for(Uint32 j= 0; j < g_consumers.size(); j++)
1320  if (!g_consumers[j]->object(metaData.getObjType(i),
1321  metaData.getObjPtr(i)))
1322  {
1323  err << "Restore: Failed to restore table: ";
1324  err << metaData[i]->getTableName() << " ... Exiting " << endl;
1325  exitHandler(NDBT_FAILED);
1326  }
1327  if (check_progress())
1328  {
1329  info.setLevel(255);
1330  info << "Object create progress: "
1331  << i+1 << " objects out of "
1332  << metaData.getNoOfObjects() << endl;
1333  }
1334  }
1335 
1336  Vector<OutputStream *> table_output(metaData.getNoOfTables());
1337  debug << "Restoring tables" << endl;
1338  for(i = 0; i<metaData.getNoOfTables(); i++)
1339  {
1340  const TableS *table= metaData[i];
1341  table_output.push_back(NULL);
1342  if (!checkDbAndTableName(table))
1343  continue;
1344  if (isSYSTAB_0(table))
1345  {
1346  table_output[i]= ndbout.m_out;
1347  }
1348  if (checkSysTable(table))
1349  {
1350  if (!tab_path || isBlobTable(table) || isIndex(table))
1351  {
1352  table_output[i]= ndbout.m_out;
1353  }
1354  else
1355  {
1356  FILE* res;
1357  char filename[FN_REFLEN], tmp_path[FN_REFLEN];
1358  const char *table_name;
1359  table_name= table->getTableName();
1360  while (*table_name != '/') table_name++;
1361  table_name++;
1362  while (*table_name != '/') table_name++;
1363  table_name++;
1364  convert_dirname(tmp_path, tab_path, NullS);
1365  res= my_fopen(fn_format(filename, table_name, tmp_path, ".txt", 4),
1366  opt_append ?
1367  O_WRONLY|O_APPEND|O_CREAT :
1368  O_WRONLY|O_TRUNC|O_CREAT,
1369  MYF(MY_WME));
1370  if (res == 0)
1371  {
1372  exitHandler(NDBT_FAILED);
1373  }
1374  FileOutputStream *f= new FileOutputStream(res);
1375  table_output[i]= f;
1376  }
1377  for(Uint32 j= 0; j < g_consumers.size(); j++)
1378  if (!g_consumers[j]->table(* table))
1379  {
1380  err << "Restore: Failed to restore table: `";
1381  err << table->getTableName() << "` ... Exiting " << endl;
1382  exitHandler(NDBT_FAILED);
1383  }
1384  } else {
1385  for(Uint32 j= 0; j < g_consumers.size(); j++)
1386  if (!g_consumers[j]->createSystable(* table))
1387  {
1388  err << "Restore: Failed to restore system table: ";
1389  err << table->getTableName() << " ... Exiting " << endl;
1390  exitHandler(NDBT_FAILED);
1391  }
1392  }
1393  if (check_progress())
1394  {
1395  info.setLevel(255);
1396  info << "Table create progress: "
1397  << i+1 << " tables out of "
1398  << metaData.getNoOfTables() << endl;
1399  }
1400  }
1401  debug << "Close tables" << endl;
1402  for(i= 0; i < g_consumers.size(); i++)
1403  if (!g_consumers[i]->endOfTables())
1404  {
1405  err << "Restore: Failed while closing tables" << endl;
1406  exitHandler(NDBT_FAILED);
1407  }
1408  /* report to clusterlog if applicable */
1409  for(i= 0; i < g_consumers.size(); i++)
1410  {
1411  g_consumers[i]->report_meta_data(ga_backupId, ga_nodeId);
1412  }
1413  debug << "Iterate over data" << endl;
1414  if (ga_restore || ga_print)
1415  {
1416  if(_restore_data || _print_data)
1417  {
1418  // Check table compatibility
1419  for (i=0; i < metaData.getNoOfTables(); i++){
1420  if (checkSysTable(metaData, i) &&
1421  checkDbAndTableName(metaData[i]))
1422  {
1423  for(Uint32 j= 0; j < g_consumers.size(); j++)
1424  {
1425  if (!g_consumers[j]->table_compatible_check(*metaData[i]))
1426  {
1427  err << "Restore: Failed to restore data, ";
1428  err << metaData[i]->getTableName() << " table structure incompatible with backup's ... Exiting " << endl;
1429  exitHandler(NDBT_FAILED);
1430  }
1431  }
1432  }
1433  }
1434  RestoreDataIterator dataIter(metaData, &free_data_callback);
1435 
1436  // Read data file header
1437  if (!dataIter.readHeader())
1438  {
1439  err << "Failed to read header of data file. Exiting..." << endl;
1440  exitHandler(NDBT_FAILED);
1441  }
1442 
1443  Uint32 fragmentId;
1444  while (dataIter.readFragmentHeader(res= 0, &fragmentId))
1445  {
1446  const TupleS* tuple;
1447  while ((tuple = dataIter.getNextTuple(res= 1)) != 0)
1448  {
1449  const TableS* table = tuple->getTable();
1450  OutputStream *output = table_output[table->getLocalId()];
1451  if (!output)
1452  continue;
1453  OutputStream *tmp = ndbout.m_out;
1454  ndbout.m_out = output;
1455  for(Uint32 j= 0; j < g_consumers.size(); j++)
1456  g_consumers[j]->tuple(* tuple, fragmentId);
1457  ndbout.m_out = tmp;
1458  if (check_progress())
1459  report_progress("Data file progress: ", dataIter);
1460  } // while (tuple != NULL);
1461 
1462  if (res < 0)
1463  {
1464  err <<" Restore: An error occured while restoring data. Exiting...";
1465  err << endl;
1466  exitHandler(NDBT_FAILED);
1467  }
1468  if (!dataIter.validateFragmentFooter()) {
1469  err << "Restore: Error validating fragment footer. ";
1470  err << "Exiting..." << endl;
1471  exitHandler(NDBT_FAILED);
1472  }
1473  } // while (dataIter.readFragmentHeader(res))
1474 
1475  if (res < 0)
1476  {
1477  err << "Restore: An error occured while restoring data. Exiting... "
1478  << "res= " << res << endl;
1479  exitHandler(NDBT_FAILED);
1480  }
1481 
1482 
1483  dataIter.validateFooter(); //not implemented
1484 
1485  for (i= 0; i < g_consumers.size(); i++)
1486  g_consumers[i]->endOfTuples();
1487 
1488  /* report to clusterlog if applicable */
1489  for(i= 0; i < g_consumers.size(); i++)
1490  {
1491  g_consumers[i]->report_data(ga_backupId, ga_nodeId);
1492  }
1493  }
1494 
1495  if(_restore_data || _print_log)
1496  {
1497  RestoreLogIterator logIter(metaData);
1498  if (!logIter.readHeader())
1499  {
1500  err << "Failed to read header of data file. Exiting..." << endl;
1501  exitHandler(NDBT_FAILED);
1502  }
1503 
1504  const LogEntry * logEntry = 0;
1505  while ((logEntry = logIter.getNextLogEntry(res= 0)) != 0)
1506  {
1507  const TableS* table = logEntry->m_table;
1508  OutputStream *output = table_output[table->getLocalId()];
1509  if (!output)
1510  continue;
1511  for(Uint32 j= 0; j < g_consumers.size(); j++)
1512  g_consumers[j]->logEntry(* logEntry);
1513 
1514  if (check_progress())
1515  report_progress("Log file progress: ", logIter);
1516  }
1517  if (res < 0)
1518  {
1519  err << "Restore: An restoring the data log. Exiting... res="
1520  << res << endl;
1521  exitHandler(NDBT_FAILED);
1522  }
1523  logIter.validateFooter(); //not implemented
1524  for (i= 0; i < g_consumers.size(); i++)
1525  g_consumers[i]->endOfLogEntrys();
1526 
1527  /* report to clusterlog if applicable */
1528  for(i= 0; i < g_consumers.size(); i++)
1529  {
1530  g_consumers[i]->report_log(ga_backupId, ga_nodeId);
1531  }
1532  }
1533 
1534  if(_restore_data)
1535  {
1536  for(i = 0; i < metaData.getNoOfTables(); i++)
1537  {
1538  const TableS* table = metaData[i];
1539  check_data_truncations(table);
1540  OutputStream *output = table_output[table->getLocalId()];
1541  if (!output)
1542  continue;
1543  for(Uint32 j= 0; j < g_consumers.size(); j++)
1544  if (!g_consumers[j]->finalize_table(*table))
1545  {
1546  err << "Restore: Failed to finalize restore table: %s. ";
1547  err << "Exiting... " << metaData[i]->getTableName() << endl;
1548  exitHandler(NDBT_FAILED);
1549  }
1550  }
1551  }
1552  }
1553  if (ga_restore_epoch)
1554  {
1555  for (i= 0; i < g_consumers.size(); i++)
1556  if (!g_consumers[i]->update_apply_status(metaData))
1557  {
1558  err << "Restore: Failed to restore epoch" << endl;
1559  return -1;
1560  }
1561  }
1562 
1563  unsigned j;
1564  for(j= 0; j < g_consumers.size(); j++)
1565  {
1566  if (g_consumers[j]->has_temp_error())
1567  {
1568  clearConsumers();
1569  ndbout_c("\nRestore successful, but encountered temporary error, "
1570  "please look at configuration.");
1571  }
1572  }
1573 
1574  if (ga_rebuild_indexes)
1575  {
1576  debug << "Rebuilding indexes" << endl;
1577  for(i = 0; i<metaData.getNoOfTables(); i++)
1578  {
1579  const TableS *table= metaData[i];
1580  if (! (checkSysTable(table) && checkDbAndTableName(table)))
1581  continue;
1582  if (isBlobTable(table) || isIndex(table))
1583  continue;
1584  for(Uint32 j= 0; j < g_consumers.size(); j++)
1585  {
1586  if (!g_consumers[j]->rebuild_indexes(* table))
1587  return -1;
1588  }
1589  }
1590  }
1591 
1592  /* report to clusterlog if applicable */
1593  for (i = 0; i < g_consumers.size(); i++)
1594  g_consumers[i]->report_completed(ga_backupId, ga_nodeId);
1595 
1596  clearConsumers();
1597 
1598  for(i = 0; i < metaData.getNoOfTables(); i++)
1599  {
1600  if (table_output[i] &&
1601  table_output[i] != ndbout.m_out)
1602  {
1603  my_fclose(((FileOutputStream *)table_output[i])->getFile(), MYF(MY_WME));
1604  delete table_output[i];
1605  table_output[i] = NULL;
1606  }
1607  }
1608 
1609  if (opt_verbose)
1610  return NDBT_ProgramExit(NDBT_OK);
1611  else
1612  return 0;
1613 } // main
1614 
1615 template class Vector<BackupConsumer*>;
1616 template class Vector<OutputStream*>;
1617 template class Vector<RestoreOption *>;