Drizzled Public API Documentation

drizzledump_mysql.cc
1 /* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
3  *
4  * Copyright (C) 2010 Andrew Hutchings
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; version 2 of the License.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 
20 #include "client/drizzledump_data.h"
21 #include "client/drizzledump_mysql.h"
22 #include "client/client_priv.h"
23 #include <string>
24 #include <iostream>
25 #include <boost/regex.hpp>
26 #include <boost/date_time/posix_time/posix_time.hpp>
27 #include <drizzled/gettext.h>
28 
29 extern bool verbose;
30 extern bool ignore_errors;
31 
32 bool DrizzleDumpDatabaseMySQL::populateTables()
33 {
34  drizzle_result_st *result;
35  drizzle_row_t row;
36  std::string query;
37 
38  if (not dcon->setDB(databaseName))
39  return false;
40 
41  if (verbose)
42  std::cerr << _("-- Retrieving table structures for ") << databaseName << "..." << std::endl;
43 
44  query="SELECT TABLE_NAME, TABLE_COLLATION, ENGINE, AUTO_INCREMENT, TABLE_COMMENT FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE != 'VIEW' AND TABLE_SCHEMA='";
45  query.append(databaseName);
46  query.append("' ORDER BY TABLE_NAME");
47 
48  result= dcon->query(query);
49 
50  if (result == NULL)
51  return false;
52 
53  while ((row= drizzle_row_next(result)))
54  {
55  size_t* row_sizes= drizzle_row_field_sizes(result);
56  std::string tableName(row[0]);
57  std::string displayName(tableName);
58  cleanTableName(displayName);
59  if (not ignoreTable(displayName))
60  continue;
61 
62  DrizzleDumpTableMySQL *table = new DrizzleDumpTableMySQL(tableName, dcon);
63  table->displayName= displayName;
64  table->setCollate(row[1]);
65  table->setEngine(row[2]);
66  if (row[3])
67  table->autoIncrement= boost::lexical_cast<uint64_t>(row[3]);
68  else
69  table->autoIncrement= 0;
70 
71  if ((row[4]) and (strstr(row[4], "InnoDB free") == NULL))
72  table->comment= DrizzleDumpData::escape(row[4], row_sizes[4]);
73  else
74  table->comment= "";
75 
76  table->database= this;
77  if ((not table->populateFields()) or (not table->populateIndexes()) or
78  (not table->populateFkeys()))
79  {
80  delete table;
81  if (not ignore_errors)
82  return false;
83  else
84  continue;
85  }
86  tables.push_back(table);
87  }
88 
89  dcon->freeResult(result);
90 
91  return true;
92 }
93 
94 bool DrizzleDumpDatabaseMySQL::populateTables(const std::vector<std::string> &table_names)
95 {
96  drizzle_result_st *result;
97  drizzle_row_t row;
98  std::string query;
99 
100  if (not dcon->setDB(databaseName))
101  return false;
102 
103  if (verbose)
104  std::cerr << _("-- Retrieving table structures for ") << databaseName << "..." << std::endl;
105  for (std::vector<std::string>::const_iterator it= table_names.begin(); it != table_names.end(); ++it)
106  {
107  std::string tableName= *it;
108  std::string displayName(tableName);
109  cleanTableName(displayName);
110  if (not ignoreTable(displayName))
111  continue;
112 
113  query="SELECT TABLE_NAME, TABLE_COLLATION, ENGINE, AUTO_INCREMENT FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='";
114  query.append(databaseName);
115  query.append("' AND TABLE_NAME = '");
116  query.append(tableName);
117  query.append("'");
118 
119  result= dcon->query(query);
120 
121  if (result == NULL)
122  return false;
123 
124  if ((row= drizzle_row_next(result)))
125  {
126  DrizzleDumpTableMySQL *table = new DrizzleDumpTableMySQL(tableName, dcon);
127  table->displayName= displayName;
128  table->setCollate(row[1]);
129  table->setEngine(row[2]);
130  if (row[3])
131  table->autoIncrement= boost::lexical_cast<uint64_t>(row[3]);
132  else
133  table->autoIncrement= 0;
134 
135  table->database= this;
136  if ((not table->populateFields()) or (not table->populateIndexes()))
137  {
138  delete table;
139  if (not ignore_errors)
140  return false;
141  else
142  continue;
143  }
144  tables.push_back(table);
145  dcon->freeResult(result);
146  }
147  else
148  {
149  dcon->freeResult(result);
150  if (not ignore_errors)
151  return false;
152  else
153  continue;
154  }
155  }
156 
157  return true;
158 
159 }
160 
161 bool DrizzleDumpTableMySQL::populateFields()
162 {
163  drizzle_result_st *result;
164  drizzle_row_t row;
165  std::string query;
166 
167  if (verbose)
168  std::cerr << _("-- Retrieving fields for ") << tableName << "..." << std::endl;
169 
170  query="SELECT COLUMN_NAME, COLUMN_TYPE, COLUMN_DEFAULT, IS_NULLABLE, CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION, NUMERIC_SCALE, COLLATION_NAME, EXTRA, COLUMN_COMMENT FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA='";
171  query.append(database->databaseName);
172  query.append("' AND TABLE_NAME='");
173  query.append(tableName);
174  query.append("' ORDER BY ORDINAL_POSITION");
175 
176  result= dcon->query(query);
177 
178  if (result == NULL)
179  return false;
180 
181  while ((row= drizzle_row_next(result)))
182  {
183  std::string fieldName(row[0]);
184  DrizzleDumpFieldMySQL *field = new DrizzleDumpFieldMySQL(fieldName, dcon);
185  /* Stop valgrind warning */
186  field->convertDateTime= false;
187  field->isNull= strcmp(row[3], "YES") == 0;
188  /* Also sets collation */
189  field->setType(row[1], row[8]);
190  if (field->type.compare("ENUM") == 0)
191  field->isNull= true;
192 
193  if ((row[2]) and (field->type.compare("TEXT") != 0))
194  {
195  field->defaultValue= row[2];
196  if (field->convertDateTime)
197  {
198  field->dateTimeConvert();
199  }
200  }
201  else
202  {
203  field->defaultValue= "";
204  }
205 
206  field->isAutoIncrement= strcmp(row[8], "auto_increment") == 0;
207  field->defaultIsNull= field->isNull;
208 
209  /* Seriously MySQL, why is BIT length in NUMERIC_PRECISION? */
210  if ((strncmp(row[1], "bit", 3) == 0) and (row[5] != NULL))
211  field->length= ((boost::lexical_cast<uint32_t>(row[5]) - 1) / 8) + 1;
212  else
213  field->length= (row[4]) ? boost::lexical_cast<uint32_t>(row[4]) : 0;
214 
215  /* Also, CHAR(0) is valid?? */
216  if (((field->type.compare("VARBINARY") == 0)
217  or (field->type.compare("VARCHAR") == 0))
218  and (field->length == 0))
219  {
220  field->length= 1;
221  }
222 
223  field->decimalPrecision= (row[5]) ? boost::lexical_cast<uint32_t>(row[5]) : 0;
224  field->decimalScale= (row[6]) ? boost::lexical_cast<uint32_t>(row[6]) : 0;
225  field->comment= (row[9]) ? row[9] : "";
226  fields.push_back(field);
227  }
228 
229  dcon->freeResult(result);
230  return true;
231 }
232 
233 
234 void DrizzleDumpFieldMySQL::dateTimeConvert(void)
235 {
236  boost::match_flag_type flags = boost::match_default;
237 
238  if (strcmp(defaultValue.c_str(), "CURRENT_TIMESTAMP") == 0)
239  return;
240 
241  if (type.compare("INT") == 0)
242  {
243  /* We were a TIME, now we are an INT */
244  std::string ts(defaultValue);
245  boost::posix_time::time_duration td(boost::posix_time::duration_from_string(ts));
246  defaultValue= boost::lexical_cast<std::string>(td.total_seconds());
247  return;
248  }
249 
250  boost::regex date_regex("(0000|-00)");
251 
252  if (regex_search(defaultValue, date_regex, flags))
253  {
254  defaultIsNull= true;
255  defaultValue="";
256  }
257 }
258 
259 
260 bool DrizzleDumpTableMySQL::populateIndexes()
261 {
262  drizzle_result_st *result;
263  drizzle_row_t row;
264  std::string query;
265  std::string lastKey;
266  bool firstIndex= true;
267  DrizzleDumpIndex *index;
268 
269  if (verbose)
270  std::cerr << _("-- Retrieving indexes for ") << tableName << "..." << std::endl;
271 
272  query="SHOW INDEXES FROM ";
273  query.append(tableName);
274 
275  result= dcon->query(query);
276 
277  if (result == NULL)
278  return false;
279 
280  while ((row= drizzle_row_next(result)))
281  {
282  std::string indexName(row[2]);
283  if (indexName.compare(lastKey) != 0)
284  {
285  if (strcmp(row[10], "FULLTEXT") == 0)
286  continue;
287 
288  if (!firstIndex)
289  indexes.push_back(index);
290  index = new DrizzleDumpIndexMySQL(indexName, dcon);
291  index->isPrimary= (strcmp(row[2], "PRIMARY") == 0);
292  index->isUnique= (strcmp(row[1], "0") == 0);
293  index->isHash= (strcmp(row[10], "HASH") == 0);
294  lastKey= row[2];
295  firstIndex= false;
296  }
297  uint32_t length= (row[7]) ? boost::lexical_cast<uint32_t>(row[7]) : 0;
298  index->columns.push_back(std::make_pair(row[4], length));
299  }
300  if (!firstIndex)
301  indexes.push_back(index);
302 
303  dcon->freeResult(result);
304  return true;
305 }
306 
307 bool DrizzleDumpTableMySQL::populateFkeys()
308 {
309  drizzle_result_st *result;
310  drizzle_row_t row;
311  std::string query;
312  DrizzleDumpForeignKey *fkey;
313 
314  if (verbose)
315  std::cerr << _("-- Retrieving foreign keys for ") << tableName << "..." << std::endl;
316 
317  query= "SHOW CREATE TABLE `";
318  query.append(database->databaseName);
319  query.append("`.`");
320  query.append(tableName);
321  query.append("`");
322  result= dcon->query(query);
323 
324  if (result == NULL)
325  return false;
326 
327  if ((row= drizzle_row_next(result)))
328  {
329  boost::match_flag_type flags = boost::match_default;
330  boost::regex constraint_regex("CONSTRAINT `(.*?)` FOREIGN KEY \\((.*?)\\) REFERENCES `(.*?)` \\((.*?)\\)( ON (UPDATE|DELETE) (CASCADE|RESTRICT|SET NULL))?( ON (UPDATE|DELETE) (CASCADE|RESTRICT|SET NULL))?");
331 
332  boost::match_results<std::string::const_iterator> constraint_results;
333 
334  std::string search_body(row[1]);
335  std::string::const_iterator start, end;
336  start= search_body.begin();
337  end= search_body.end();
338  while (regex_search(start, end, constraint_results, constraint_regex, flags))
339  {
340  fkey= new DrizzleDumpForeignKey(constraint_results[1], dcon);
341  fkey->parentColumns= constraint_results[2];
342  fkey->childTable= constraint_results[3];
343  fkey->childColumns= constraint_results[4];
344 
345  if (constraint_results[5].compare("") != 0)
346  {
347  if (constraint_results[6].compare("UPDATE") == 0)
348  fkey->updateRule= constraint_results[7];
349  else if (constraint_results[6].compare("DELETE") == 0)
350  fkey->deleteRule= constraint_results[7];
351  }
352  if (constraint_results[8].compare("") != 0)
353  {
354  if (constraint_results[9].compare("UPDATE") == 0)
355  fkey->updateRule= constraint_results[10];
356  else if (constraint_results[9].compare("DELETE") == 0)
357  fkey->deleteRule= constraint_results[10];
358  }
359  fkey->matchOption= "";
360 
361  fkeys.push_back(fkey);
362 
363  start= constraint_results[0].second;
364  flags |= boost::match_prev_avail;
365  flags |= boost::match_not_bob;
366  }
367  }
368  dcon->freeResult(result);
369  return true;
370 }
371 
372 void DrizzleDumpFieldMySQL::setType(const char* raw_type, const char* raw_collation)
373 {
374  std::string old_type(raw_type);
375  std::string extra;
376  size_t pos;
377 
378  if (((pos= old_type.find("(")) != std::string::npos) or
379  ((pos= old_type.find(" ")) != std::string::npos))
380  {
381  extra= old_type.substr(pos);
382  old_type.erase(pos, std::string::npos);
383  }
384 
385  boost::to_upper(old_type);
386  if ((old_type.find("CHAR") != std::string::npos) or
387  (old_type.find("TEXT") != std::string::npos))
388  setCollate(raw_collation);
389 
390  if ((old_type.compare("BIGINT") == 0) and
391  ((extra.find("unsigned") != std::string::npos)))
392  {
393  rangeCheck= true;
394  }
395 
396  if ((old_type.compare("INT") == 0) and
397  ((extra.find("unsigned") != std::string::npos)))
398  {
399  type= "BIGINT";
400  return;
401  }
402 
403  if ((old_type.compare("TINYINT") == 0) or
404  (old_type.compare("SMALLINT") == 0) or
405  (old_type.compare("MEDIUMINT") == 0))
406  {
407  type= "INT";
408  return;
409  }
410 
411  if ((old_type.compare("TINYBLOB") == 0) or
412  (old_type.compare("MEDIUMBLOB") == 0) or
413  (old_type.compare("LONGBLOB") == 0))
414  {
415  type= "BLOB";
416  return;
417  }
418 
419  if ((old_type.compare("TINYTEXT") == 0) or
420  (old_type.compare("MEDIUMTEXT") == 0) or
421  (old_type.compare("LONGTEXT") == 0) or
422  (old_type.compare("SET") == 0))
423  {
424  type= "TEXT";
425  return;
426  }
427 
428  if (old_type.compare("CHAR") == 0)
429  {
430  type= "VARCHAR";
431  return;
432  }
433 
434  if (old_type.compare("BINARY") == 0)
435  {
436  type= "VARBINARY";
437 
438  return;
439  }
440 
441  if (old_type.compare("ENUM") == 0)
442  {
443  type= old_type;
444  /* Strip out the braces, we add them again during output */
445  enumValues= extra.substr(1, extra.length()-2);
446  return;
447  }
448 
449  if ((old_type.find("TIME") != std::string::npos) or
450  (old_type.find("DATE") != std::string::npos))
451  {
452  /* Intended to catch TIME/DATE/TIMESTAMP/DATETIME
453  We may have a default TIME/DATE which needs converting */
454  convertDateTime= true;
455  isNull= true;
456  }
457 
458  if ((old_type.compare("TIME") == 0) or (old_type.compare("YEAR") == 0))
459  {
460  type= "INT";
461  return;
462  }
463 
464  if (old_type.compare("FLOAT") == 0)
465  {
466  type= "DOUBLE";
467  return;
468  }
469 
470  if (old_type.compare("BIT") == 0)
471  {
472  type= "VARBINARY";
473  return;
474  }
475 
476  type= old_type;
477  return;
478 }
479 
480 void DrizzleDumpTableMySQL::setEngine(const char* newEngine)
481 {
482  if ((strcmp(newEngine, "MyISAM") == 0) || (strcmp(newEngine, "MEMORY") == 0))
483  engineName= "InnoDB";
484  else
485  engineName= newEngine;
486 }
487 
488 DrizzleDumpData* DrizzleDumpTableMySQL::getData(void)
489 {
490  try
491  {
492  return new DrizzleDumpDataMySQL(this, dcon);
493  }
494  catch(...)
495  {
496  return NULL;
497  }
498 }
499 
500 void DrizzleDumpDatabaseMySQL::setCollate(const char* newCollate)
501 {
502  if (newCollate)
503  {
504  std::string tmpCollate(newCollate);
505  if (tmpCollate.find("utf8") != std::string::npos)
506  {
507  collate= tmpCollate;
508  return;
509  }
510  }
511  collate= "utf8_general_ci";
512 }
513 
514 void DrizzleDumpTableMySQL::setCollate(const char* newCollate)
515 {
516  if (newCollate)
517  {
518  std::string tmpCollate(newCollate);
519  if (tmpCollate.find("utf8") != std::string::npos)
520  {
521  collate= tmpCollate;
522  return;
523  }
524  }
525 
526  collate= "utf8_general_ci";
527 }
528 
529 void DrizzleDumpFieldMySQL::setCollate(const char* newCollate)
530 {
531  if (newCollate)
532  {
533  std::string tmpCollate(newCollate);
534  if (tmpCollate.find("utf8") != std::string::npos)
535  {
536  collation= tmpCollate;
537  return;
538  }
539  }
540  collation= "utf8_general_ci";
541 }
542 
543 DrizzleDumpDataMySQL::DrizzleDumpDataMySQL(DrizzleDumpTable *dataTable,
544  DrizzleDumpConnection *connection)
545  : DrizzleDumpData(dataTable, connection)
546 {
547  std::string query;
548  query= "SELECT * FROM `";
549  query.append(table->displayName);
550  query.append("`");
551 
552  result= dcon->query(query);
553  if (result == NULL)
554  throw std::exception();
555 }
556 
557 DrizzleDumpDataMySQL::~DrizzleDumpDataMySQL()
558 {
559  drizzle_result_free(result);
560  delete result;
561 }
562 
563 long DrizzleDumpDataMySQL::convertTime(const char* oldTime) const
564 {
565  std::string ts(oldTime);
566  boost::posix_time::time_duration td(boost::posix_time::duration_from_string(ts));
567  long seconds= td.total_seconds();
568  return seconds;
569 }
570 
571 std::string DrizzleDumpDataMySQL::convertDate(const char* oldDate) const
572 {
573  boost::match_flag_type flags = boost::match_default;
574  std::string output;
575  boost::regex date_regex("(0000|-00)");
576 
577  if (not regex_search(oldDate, date_regex, flags))
578  {
579  output.push_back('\'');
580  output.append(oldDate);
581  output.push_back('\'');
582  }
583  else
584  output= "NULL";
585 
586  return output;
587 }
588 
589 std::string DrizzleDumpDataMySQL::checkDateTime(const char* item, uint32_t field) const
590 {
591  std::string ret;
592 
593  if (table->fields[field]->convertDateTime)
594  {
595  if (table->fields[field]->type.compare("INT") == 0)
596  ret= boost::lexical_cast<std::string>(convertTime(item));
597  else
598  ret= convertDate(item);
599  }
600  return ret;
601 }
602