Drizzled Public API Documentation

drizzledump_mysql.cc

00001 /* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
00002  *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
00003  *
00004  *  Copyright (C) 2010 Andrew Hutchings
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; version 2 of the License.
00009  *
00010  *  This program is distributed in the hope that it will be useful,
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *  GNU General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU General Public License
00016  *  along with this program; if not, write to the Free Software
00017  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00018  */
00019 
00020 #include "drizzledump_data.h"
00021 #include "drizzledump_mysql.h"
00022 #include "client_priv.h"
00023 #include <string>
00024 #include <iostream>
00025 #include <boost/regex.hpp>
00026 #include <boost/date_time/posix_time/posix_time.hpp>
00027 #include <drizzled/gettext.h>
00028 
00029 extern bool verbose;
00030 extern bool ignore_errors;
00031 
00032 bool DrizzleDumpDatabaseMySQL::populateTables()
00033 {
00034   drizzle_result_st *result;
00035   drizzle_row_t row;
00036   std::string query;
00037 
00038   if (not dcon->setDB(databaseName))
00039     return false;
00040 
00041   if (verbose)
00042     std::cerr << _("-- Retrieving table structures for ") << databaseName << "..." << std::endl;
00043 
00044   query="SELECT TABLE_NAME, TABLE_COLLATION, ENGINE, AUTO_INCREMENT, TABLE_COMMENT FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE != 'VIEW' AND TABLE_SCHEMA='";
00045   query.append(databaseName);
00046   query.append("' ORDER BY TABLE_NAME");
00047 
00048   result= dcon->query(query);
00049 
00050   if (result == NULL)
00051     return false;
00052 
00053   while ((row= drizzle_row_next(result)))
00054   {
00055     size_t* row_sizes= drizzle_row_field_sizes(result);
00056     std::string tableName(row[0]);
00057     std::string displayName(tableName);
00058     cleanTableName(displayName);
00059     if (not ignoreTable(displayName))
00060       continue;
00061 
00062     DrizzleDumpTableMySQL *table = new DrizzleDumpTableMySQL(tableName, dcon);
00063     table->displayName= displayName;
00064     table->setCollate(row[1]);
00065     table->setEngine(row[2]);
00066     if (row[3])
00067       table->autoIncrement= boost::lexical_cast<uint64_t>(row[3]);
00068     else
00069       table->autoIncrement= 0;
00070 
00071     if ((row[4]) and (strstr(row[4], "InnoDB free") == NULL))
00072       table->comment= DrizzleDumpData::escape(row[4], row_sizes[4]);
00073     else
00074       table->comment= "";
00075 
00076     table->database= this;
00077     if ((not table->populateFields()) or (not table->populateIndexes()) or
00078      (not table->populateFkeys()))
00079     {
00080       delete table;
00081       if (not ignore_errors)
00082         return false;
00083       else
00084         continue;
00085     }
00086     tables.push_back(table);
00087   }
00088 
00089   dcon->freeResult(result);
00090 
00091   return true;
00092 }
00093 
00094 bool DrizzleDumpDatabaseMySQL::populateTables(const std::vector<std::string> &table_names)
00095 {
00096   drizzle_result_st *result;
00097   drizzle_row_t row;
00098   std::string query;
00099 
00100   if (not dcon->setDB(databaseName))
00101     return false;
00102 
00103   if (verbose)
00104     std::cerr << _("-- Retrieving table structures for ") << databaseName << "..." << std::endl;
00105   for (std::vector<std::string>::const_iterator it= table_names.begin(); it != table_names.end(); ++it)
00106   {
00107     std::string tableName= *it;
00108     std::string displayName(tableName);
00109     cleanTableName(displayName);
00110     if (not ignoreTable(displayName))
00111       continue;
00112 
00113     query="SELECT TABLE_NAME, TABLE_COLLATION, ENGINE, AUTO_INCREMENT FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='";
00114     query.append(databaseName);
00115     query.append("' AND TABLE_NAME = '");
00116     query.append(tableName);
00117     query.append("'");
00118 
00119     result= dcon->query(query);
00120 
00121     if (result == NULL)
00122       return false;
00123 
00124     if ((row= drizzle_row_next(result)))
00125     {
00126       DrizzleDumpTableMySQL *table = new DrizzleDumpTableMySQL(tableName, dcon);
00127       table->displayName= displayName;
00128       table->setCollate(row[1]);
00129       table->setEngine(row[2]);
00130       if (row[3])
00131         table->autoIncrement= boost::lexical_cast<uint64_t>(row[3]);
00132       else
00133         table->autoIncrement= 0;
00134 
00135       table->database= this;
00136       if ((not table->populateFields()) or (not table->populateIndexes()))
00137       {
00138         delete table;
00139         if (not ignore_errors)
00140           return false;
00141         else
00142           continue;
00143       }
00144       tables.push_back(table);
00145       dcon->freeResult(result);
00146     }
00147     else
00148     {
00149       dcon->freeResult(result);
00150       if (not ignore_errors)
00151         return false;
00152       else
00153         continue;
00154     }
00155   }
00156 
00157   return true;
00158 
00159 }
00160 
00161 bool DrizzleDumpTableMySQL::populateFields()
00162 {
00163   drizzle_result_st *result;
00164   drizzle_row_t row;
00165   std::string query;
00166 
00167   if (verbose)
00168     std::cerr << _("-- Retrieving fields for ") << tableName << "..." << std::endl;
00169 
00170   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='";
00171   query.append(database->databaseName);
00172   query.append("' AND TABLE_NAME='");
00173   query.append(tableName);
00174   query.append("' ORDER BY ORDINAL_POSITION");
00175 
00176   result= dcon->query(query);
00177 
00178   if (result == NULL)
00179     return false;
00180 
00181   while ((row= drizzle_row_next(result)))
00182   {
00183     std::string fieldName(row[0]);
00184     DrizzleDumpFieldMySQL *field = new DrizzleDumpFieldMySQL(fieldName, dcon);
00185     /* Stop valgrind warning */
00186     field->convertDateTime= false;
00187     field->isNull= (strcmp(row[3], "YES") == 0) ? true : false;
00188     /* Also sets collation */
00189     field->setType(row[1], row[8]);
00190     if (field->type.compare("ENUM") == 0)
00191       field->isNull= true;
00192 
00193     if ((row[2]) and (field->type.compare("TEXT") != 0))
00194     {
00195       field->defaultValue= row[2];
00196       if (field->convertDateTime)
00197       {
00198         field->dateTimeConvert();
00199       }
00200     }
00201     else
00202     {
00203       field->defaultValue= "";
00204     }
00205 
00206     field->isAutoIncrement= (strcmp(row[8], "auto_increment") == 0) ? true : false;
00207     field->defaultIsNull= field->isNull;
00208 
00209     /* Seriously MySQL, why is BIT length in NUMERIC_PRECISION? */
00210     if ((strncmp(row[1], "bit", 3) == 0) and (row[5] != NULL))
00211       field->length= ((boost::lexical_cast<uint32_t>(row[5]) - 1) / 8) + 1;
00212     else
00213       field->length= (row[4]) ? boost::lexical_cast<uint32_t>(row[4]) : 0;
00214 
00215     /* Also, CHAR(0) is valid?? */
00216     if (((field->type.compare("VARBINARY") == 0) 
00217       or (field->type.compare("VARCHAR") == 0))
00218       and (field->length == 0))
00219     {
00220       field->length= 1;
00221     }
00222 
00223     field->decimalPrecision= (row[5]) ? boost::lexical_cast<uint32_t>(row[5]) : 0;
00224     field->decimalScale= (row[6]) ? boost::lexical_cast<uint32_t>(row[6]) : 0;
00225     field->comment= (row[9]) ? row[9] : "";
00226     fields.push_back(field);
00227   }
00228 
00229   dcon->freeResult(result);
00230   return true;
00231 }
00232 
00233 
00234 void DrizzleDumpFieldMySQL::dateTimeConvert(void)
00235 {
00236   boost::match_flag_type flags = boost::match_default;
00237 
00238   if (strcmp(defaultValue.c_str(), "CURRENT_TIMESTAMP") == 0)
00239     return;
00240 
00241   if (type.compare("INT") == 0)
00242   {
00243     /* We were a TIME, now we are an INT */
00244     std::string ts(defaultValue);
00245     boost::posix_time::time_duration td(boost::posix_time::duration_from_string(ts));
00246     defaultValue= boost::lexical_cast<std::string>(td.total_seconds());
00247     return;
00248   }
00249 
00250   boost::regex date_regex("(0000|-00)");
00251 
00252   if (regex_search(defaultValue, date_regex, flags))
00253   {
00254     defaultIsNull= true;
00255     defaultValue="";
00256   }
00257 }
00258 
00259 
00260 bool DrizzleDumpTableMySQL::populateIndexes()
00261 {
00262   drizzle_result_st *result;
00263   drizzle_row_t row;
00264   std::string query;
00265   std::string lastKey;
00266   bool firstIndex= true;
00267   DrizzleDumpIndex *index;
00268 
00269   if (verbose)
00270     std::cerr << _("-- Retrieving indexes for ") << tableName << "..." << std::endl;
00271 
00272   query="SHOW INDEXES FROM ";
00273   query.append(tableName);
00274 
00275   result= dcon->query(query);
00276 
00277   if (result == NULL)
00278     return false;
00279 
00280   while ((row= drizzle_row_next(result)))
00281   {
00282     std::string indexName(row[2]);
00283     if (indexName.compare(lastKey) != 0)
00284     {
00285       if (strcmp(row[10], "FULLTEXT") == 0)
00286         continue;
00287 
00288       if (!firstIndex)
00289         indexes.push_back(index);
00290       index = new DrizzleDumpIndexMySQL(indexName, dcon);
00291       index->isPrimary= (strcmp(row[2], "PRIMARY") == 0);
00292       index->isUnique= (strcmp(row[1], "0") == 0);
00293       index->isHash= (strcmp(row[10], "HASH") == 0);
00294       lastKey= row[2];
00295       firstIndex= false;
00296     }
00297     uint32_t length= (row[7]) ? boost::lexical_cast<uint32_t>(row[7]) : 0;
00298     index->columns.push_back(std::make_pair(row[4], length));
00299   }
00300   if (!firstIndex)
00301     indexes.push_back(index);
00302 
00303   dcon->freeResult(result);
00304   return true;
00305 }
00306 
00307 bool DrizzleDumpTableMySQL::populateFkeys()
00308 {
00309   drizzle_result_st *result;
00310   drizzle_row_t row;
00311   std::string query;
00312   DrizzleDumpForeignKey *fkey;
00313 
00314   if (verbose)
00315     std::cerr << _("-- Retrieving foreign keys for ") << tableName << "..." << std::endl;
00316 
00317   query= "SHOW CREATE TABLE `";
00318   query.append(database->databaseName);
00319   query.append("`.`");
00320   query.append(tableName);
00321   query.append("`");
00322   result= dcon->query(query);
00323 
00324   if (result == NULL)
00325     return false;
00326 
00327   if ((row= drizzle_row_next(result)))
00328   {
00329     boost::match_flag_type flags = boost::match_default;
00330     boost::regex constraint_regex("CONSTRAINT `(.*?)` FOREIGN KEY \\((.*?)\\) REFERENCES `(.*?)` \\((.*?)\\)( ON (UPDATE|DELETE) (CASCADE|RESTRICT|SET NULL))?( ON (UPDATE|DELETE) (CASCADE|RESTRICT|SET NULL))?");
00331 
00332     boost::match_results<std::string::const_iterator> constraint_results;
00333 
00334     std::string search_body(row[1]);
00335     std::string::const_iterator start, end;
00336     start= search_body.begin();
00337     end= search_body.end();
00338     while (regex_search(start, end, constraint_results, constraint_regex, flags))
00339     {
00340       fkey= new DrizzleDumpForeignKey(constraint_results[1], dcon);
00341       fkey->parentColumns= constraint_results[2];
00342       fkey->childTable= constraint_results[3];
00343       fkey->childColumns= constraint_results[4];
00344         
00345       if (constraint_results[5].compare("") != 0)
00346       {
00347         if (constraint_results[6].compare("UPDATE") == 0)
00348           fkey->updateRule= constraint_results[7];
00349         else if (constraint_results[6].compare("DELETE") == 0)
00350           fkey->deleteRule= constraint_results[7];
00351       }
00352       if (constraint_results[8].compare("") != 0)
00353       {
00354         if (constraint_results[9].compare("UPDATE") == 0)
00355           fkey->updateRule= constraint_results[10];
00356         else if (constraint_results[9].compare("DELETE") == 0)
00357           fkey->deleteRule= constraint_results[10];
00358       }
00359       fkey->matchOption= "";
00360 
00361       fkeys.push_back(fkey);
00362 
00363       start= constraint_results[0].second;
00364       flags |= boost::match_prev_avail; 
00365       flags |= boost::match_not_bob;
00366     }
00367   }
00368   dcon->freeResult(result);
00369   return true;
00370 }
00371 
00372 void DrizzleDumpFieldMySQL::setType(const char* raw_type, const char* raw_collation)
00373 {
00374   std::string old_type(raw_type);
00375   std::string extra;
00376   size_t pos;
00377   
00378   if (((pos= old_type.find("(")) != std::string::npos) or
00379     ((pos= old_type.find(" ")) != std::string::npos))
00380   {
00381     extra= old_type.substr(pos);
00382     old_type.erase(pos, std::string::npos);
00383   }
00384 
00385   std::transform(old_type.begin(), old_type.end(), old_type.begin(), ::toupper);
00386   if ((old_type.find("CHAR") != std::string::npos) or 
00387     (old_type.find("TEXT") != std::string::npos))
00388     setCollate(raw_collation);
00389 
00390   if ((old_type.compare("BIGINT") == 0) and
00391     ((extra.find("unsigned") != std::string::npos)))
00392   {
00393     rangeCheck= true;
00394   }
00395 
00396   if ((old_type.compare("INT") == 0) and 
00397     ((extra.find("unsigned") != std::string::npos)))
00398   {
00399     type= "BIGINT";
00400     return;
00401   }
00402     
00403   if ((old_type.compare("TINYINT") == 0) or
00404     (old_type.compare("SMALLINT") == 0) or
00405     (old_type.compare("MEDIUMINT") == 0))
00406   {
00407     type= "INT";
00408     return;
00409   }
00410 
00411   if ((old_type.compare("TINYBLOB") == 0) or
00412     (old_type.compare("MEDIUMBLOB") == 0) or
00413     (old_type.compare("LONGBLOB") == 0))
00414   {
00415     type= "BLOB";
00416     return;
00417   }
00418 
00419   if ((old_type.compare("TINYTEXT") == 0) or
00420     (old_type.compare("MEDIUMTEXT") == 0) or
00421     (old_type.compare("LONGTEXT") == 0) or
00422     (old_type.compare("SET") == 0))
00423   {
00424     type= "TEXT";
00425     return;
00426   }
00427 
00428   if (old_type.compare("CHAR") == 0)
00429   {
00430     type= "VARCHAR";
00431     return;
00432   }
00433 
00434   if (old_type.compare("BINARY") == 0)
00435   {
00436     type= "VARBINARY";
00437     
00438     return;
00439   }
00440 
00441   if (old_type.compare("ENUM") == 0)
00442   {
00443     type= old_type;
00444     /* Strip out the braces, we add them again during output */
00445     enumValues= extra.substr(1, extra.length()-2);
00446     return;
00447   }
00448 
00449   if ((old_type.find("TIME") != std::string::npos) or
00450     (old_type.find("DATE") != std::string::npos))
00451   {
00452     /* Intended to catch TIME/DATE/TIMESTAMP/DATETIME 
00453        We may have a default TIME/DATE which needs converting */
00454     convertDateTime= true;
00455     isNull= true;
00456   }
00457 
00458   if ((old_type.compare("TIME") == 0) or (old_type.compare("YEAR") == 0))
00459   {
00460     type= "INT";
00461     return;
00462   }
00463 
00464   if (old_type.compare("FLOAT") == 0)
00465   {
00466     type= "DOUBLE";
00467     return;
00468   }
00469 
00470   if (old_type.compare("BIT") == 0)
00471   {
00472     type= "VARBINARY";
00473     return;
00474   }
00475 
00476   type= old_type;
00477   return;
00478 }
00479 
00480 void DrizzleDumpTableMySQL::setEngine(const char* newEngine)
00481 {
00482   if ((strcmp(newEngine, "MyISAM") == 0) || (strcmp(newEngine, "MEMORY") == 0))
00483     engineName= "InnoDB";
00484   else
00485     engineName= newEngine; 
00486 }
00487 
00488 DrizzleDumpData* DrizzleDumpTableMySQL::getData(void)
00489 {
00490   try
00491   {
00492     return new DrizzleDumpDataMySQL(this, dcon);
00493   }
00494   catch(...)
00495   {
00496     return NULL;
00497   }
00498 }
00499 
00500 void DrizzleDumpDatabaseMySQL::setCollate(const char* newCollate)
00501 {
00502   if (newCollate)
00503   {
00504     std::string tmpCollate(newCollate);
00505     if (tmpCollate.find("utf8") != std::string::npos)
00506     {
00507       collate= tmpCollate;
00508       return;
00509     }
00510   }
00511   collate= "utf8_general_ci";
00512 }
00513 
00514 void DrizzleDumpTableMySQL::setCollate(const char* newCollate)
00515 {
00516   if (newCollate)
00517   {
00518     std::string tmpCollate(newCollate);
00519     if (tmpCollate.find("utf8") != std::string::npos)
00520     {
00521       collate= tmpCollate;
00522       return;
00523     }
00524   }
00525 
00526   collate= "utf8_general_ci";
00527 }
00528 
00529 void DrizzleDumpFieldMySQL::setCollate(const char* newCollate)
00530 {
00531   if (newCollate)
00532   {
00533     std::string tmpCollate(newCollate);
00534     if (tmpCollate.find("utf8") != std::string::npos)
00535     {
00536       collation= tmpCollate;
00537       return;
00538     }
00539   }
00540   collation= "utf8_general_ci";
00541 }
00542 
00543 DrizzleDumpDataMySQL::DrizzleDumpDataMySQL(DrizzleDumpTable *dataTable,
00544   DrizzleDumpConnection *connection)
00545   : DrizzleDumpData(dataTable, connection)
00546 {
00547   std::string query;
00548   query= "SELECT * FROM `";
00549   query.append(table->displayName);
00550   query.append("`");
00551 
00552   result= dcon->query(query);
00553   if (result == NULL)
00554     throw std::exception();
00555 }
00556 
00557 DrizzleDumpDataMySQL::~DrizzleDumpDataMySQL()
00558 {
00559   drizzle_result_free(result);
00560   delete result;
00561 }
00562 
00563 long DrizzleDumpDataMySQL::convertTime(const char* oldTime) const
00564 {
00565   std::string ts(oldTime);
00566   boost::posix_time::time_duration td(boost::posix_time::duration_from_string(ts));
00567   long seconds= td.total_seconds();
00568   return seconds;
00569 }
00570 
00571 std::string DrizzleDumpDataMySQL::convertDate(const char* oldDate) const
00572 {
00573   boost::match_flag_type flags = boost::match_default;
00574   std::string output;
00575   boost::regex date_regex("(0000|-00)");
00576 
00577   if (not regex_search(oldDate, date_regex, flags))
00578   {
00579     output.push_back('\'');
00580     output.append(oldDate);
00581     output.push_back('\'');
00582   }
00583   else
00584     output= "NULL";
00585 
00586   return output;
00587 }
00588 
00589 std::string DrizzleDumpDataMySQL::checkDateTime(const char* item, uint32_t field) const
00590 {
00591   std::string ret;
00592 
00593   if (table->fields[field]->convertDateTime)
00594   {
00595     if (table->fields[field]->type.compare("INT") == 0)
00596       ret= boost::lexical_cast<std::string>(convertTime(item));
00597     else
00598       ret= convertDate(item);
00599   }
00600   return ret;
00601 }
00602