Drizzled Public API Documentation

statement_transform.cc
Go to the documentation of this file.
00001 /* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
00002  *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
00003  *
00004  *  Copyright (C) 2009 Sun Microsystems, Inc.
00005  *  Copyright (C) 2010 Jay Pipes
00006  *
00007  *  Authors:
00008  *
00009  *    Jay Pipes <jaypipes@gmail.com>
00010  *
00011  *  This program is free software; you can redistribute it and/or modify
00012  *  it under the terms of the GNU General Public License as published by
00013  *  the Free Software Foundation; version 2 of the License.
00014  *
00015  *  This program is distributed in the hope that it will be useful,
00016  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  *  GNU General Public License for more details.
00019  *
00020  *  You should have received a copy of the GNU General Public License
00021  *  along with this program; if not, write to the Free Software
00022  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00023  */
00024 
00032 #include <config.h>
00033 
00034 #include <boost/lexical_cast.hpp>
00035 
00036 #include <drizzled/charset.h>
00037 #include <drizzled/message.h>
00038 #include <drizzled/message/statement_transform.h>
00039 #include <drizzled/message/transaction.pb.h>
00040 #include <drizzled/message/access.h>
00041 
00042 #include <string>
00043 #include <vector>
00044 #include <sstream>
00045 #include <cstdio>
00046 
00047 using namespace std;
00048 
00049 namespace drizzled
00050 {
00051 
00052 namespace message
00053 {
00054 
00055 static void escapeEmbeddedQuotes(string &s, const char quote='\'')
00056 {
00057   string::iterator it;
00058 
00059   for (it= s.begin(); it != s.end(); ++it)
00060   {
00061     if (*it == quote)
00062     {
00063       it= s.insert(it, quote);
00064       ++it;  // advance back to the quote
00065     }
00066   }
00067 }
00068 
00069 /* Incredibly similar to append_unescaped() in table.cc, but for std::string */
00070 static void append_escaped_string(std::string *res, const std::string &input, const char quote='\'')
00071 {
00072   const char *pos= input.c_str();
00073   const char *end= input.c_str()+input.length();
00074   res->push_back(quote);
00075 
00076   for (; pos != end ; pos++)
00077   {
00078     uint32_t mblen;
00079     if (use_mb(default_charset_info) &&
00080         (mblen= my_ismbchar(default_charset_info, pos, end)))
00081     {
00082       res->append(pos, mblen);
00083       pos+= mblen - 1;
00084       if (pos >= end)
00085         break;
00086       continue;
00087     }
00088 
00089     switch (*pos) {
00090     case 0:       /* Must be escaped for 'mysql' */
00091       res->push_back('\\');
00092       res->push_back('0');
00093       break;
00094     case '\n':        /* Must be escaped for logs */
00095       res->push_back('\\');
00096       res->push_back('n');
00097       break;
00098     case '\r':
00099       res->push_back('\\');   /* This gives better readability */
00100       res->push_back('r');
00101       break;
00102     case '\\':
00103       res->push_back('\\');   /* Because of the sql syntax */
00104       res->push_back('\\');
00105       break;
00106     default:
00107       if (*pos == quote) /* SQL syntax for quoting a quote */
00108       {
00109         res->push_back(quote);
00110         res->push_back(quote);
00111       }
00112       else
00113         res->push_back(*pos);
00114       break;
00115     }
00116   }
00117   res->push_back(quote);
00118 }
00119 
00120 enum TransformSqlError
00121 transformStatementToSql(const Statement &source,
00122                         vector<string> &sql_strings,
00123                         enum TransformSqlVariant sql_variant,
00124                         bool already_in_transaction)
00125 {
00126   TransformSqlError error= NONE;
00127 
00128   switch (source.type())
00129   {
00130   case Statement::ROLLBACK_STATEMENT:
00131     {
00132       break;
00133     }
00134   case Statement::ROLLBACK:
00135     {
00136       sql_strings.push_back("ROLLBACK");
00137       break;
00138     }
00139   case Statement::INSERT:
00140     {
00141       if (! source.has_insert_header())
00142       {
00143         error= MISSING_HEADER;
00144         return error;
00145       }
00146       if (! source.has_insert_data())
00147       {
00148         error= MISSING_DATA;
00149         return error;
00150       }
00151 
00152       const InsertHeader &insert_header= source.insert_header();
00153       const InsertData &insert_data= source.insert_data();
00154       size_t num_keys= insert_data.record_size();
00155 
00156       if (num_keys > 1 && ! already_in_transaction)
00157         sql_strings.push_back("START TRANSACTION");
00158 
00159       for (size_t x= 0; x < num_keys; ++x)
00160       {
00161         string destination;
00162 
00163         error= transformInsertRecordToSql(insert_header,
00164                                           insert_data.record(x),
00165                                           destination,
00166                                           sql_variant);
00167         if (error != NONE)
00168           break;
00169 
00170         sql_strings.push_back(destination);
00171       }
00172 
00173       if (num_keys > 1 && ! already_in_transaction)
00174       {
00175         if (error == NONE)
00176           sql_strings.push_back("COMMIT");
00177         else
00178           sql_strings.push_back("ROLLBACK");
00179       }
00180     }
00181     break;
00182   case Statement::UPDATE:
00183     {
00184       if (! source.has_update_header())
00185       {
00186         error= MISSING_HEADER;
00187         return error;
00188       }
00189       if (! source.has_update_data())
00190       {
00191         error= MISSING_DATA;
00192         return error;
00193       }
00194 
00195       const UpdateHeader &update_header= source.update_header();
00196       const UpdateData &update_data= source.update_data();
00197       size_t num_keys= update_data.record_size();
00198       size_t x;
00199 
00200       if (num_keys > 1 && ! already_in_transaction)
00201         sql_strings.push_back("START TRANSACTION");
00202 
00203       for (x= 0; x < num_keys; ++x)
00204       {
00205         string destination;
00206 
00207         error= transformUpdateRecordToSql(update_header,
00208                                           update_data.record(x),
00209                                           destination,
00210                                           sql_variant);
00211         if (error != NONE)
00212           break;
00213 
00214         sql_strings.push_back(destination);
00215       }
00216 
00217       if (num_keys > 1 && ! already_in_transaction)
00218       {
00219         if (error == NONE)
00220           sql_strings.push_back("COMMIT");
00221         else
00222           sql_strings.push_back("ROLLBACK");
00223       }
00224     }
00225     break;
00226   case Statement::DELETE:
00227     {
00228       if (! source.has_delete_header())
00229       {
00230         error= MISSING_HEADER;
00231         return error;
00232       }
00233       if (! source.has_delete_data())
00234       {
00235         error= MISSING_DATA;
00236         return error;
00237       }
00238 
00239       const DeleteHeader &delete_header= source.delete_header();
00240       const DeleteData &delete_data= source.delete_data();
00241       size_t num_keys= delete_data.record_size();
00242       size_t x;
00243 
00244       if (num_keys > 1 && ! already_in_transaction)
00245         sql_strings.push_back("START TRANSACTION");
00246 
00247       for (x= 0; x < num_keys; ++x)
00248       {
00249         string destination;
00250 
00251         error= transformDeleteRecordToSql(delete_header,
00252                                           delete_data.record(x),
00253                                           destination,
00254                                           sql_variant);
00255         if (error != NONE)
00256           break;
00257 
00258         sql_strings.push_back(destination);
00259       }
00260 
00261       if (num_keys > 1 && ! already_in_transaction)
00262       {
00263         if (error == NONE)
00264           sql_strings.push_back("COMMIT");
00265         else
00266           sql_strings.push_back("ROLLBACK");
00267       }
00268     }
00269     break;
00270   case Statement::CREATE_TABLE:
00271     {
00272       assert(source.has_create_table_statement());
00273       string destination;
00274       error= transformCreateTableStatementToSql(source.create_table_statement(),
00275                                                 destination,
00276                                                 sql_variant);
00277       sql_strings.push_back(destination);
00278     }
00279     break;
00280   case Statement::TRUNCATE_TABLE:
00281     {
00282       assert(source.has_truncate_table_statement());
00283       string destination;
00284       error= transformTruncateTableStatementToSql(source.truncate_table_statement(),
00285                                                   destination,
00286                                                   sql_variant);
00287       sql_strings.push_back(destination);
00288     }
00289     break;
00290   case Statement::DROP_TABLE:
00291     {
00292       assert(source.has_drop_table_statement());
00293       string destination;
00294       error= transformDropTableStatementToSql(source.drop_table_statement(),
00295                                               destination,
00296                                               sql_variant);
00297       sql_strings.push_back(destination);
00298     }
00299     break;
00300   case Statement::CREATE_SCHEMA:
00301     {
00302       assert(source.has_create_schema_statement());
00303       string destination;
00304       error= transformCreateSchemaStatementToSql(source.create_schema_statement(),
00305                                                  destination,
00306                                                  sql_variant);
00307       sql_strings.push_back(destination);
00308     }
00309     break;
00310   case Statement::DROP_SCHEMA:
00311     {
00312       assert(source.has_drop_schema_statement());
00313       string destination;
00314       error= transformDropSchemaStatementToSql(source.drop_schema_statement(),
00315                                                destination,
00316                                                sql_variant);
00317       sql_strings.push_back(destination);
00318     }
00319     break;
00320   case Statement::ALTER_SCHEMA:
00321     {
00322       assert(source.has_alter_schema_statement());
00323       string destination;
00324       error= transformAlterSchemaStatementToSql(source.alter_schema_statement(),
00325                                                 destination,
00326                                                 sql_variant);
00327       sql_strings.push_back(destination);
00328     }
00329     break;
00330   case Statement::SET_VARIABLE:
00331     {
00332       assert(source.has_set_variable_statement());
00333       string destination;
00334       error= transformSetVariableStatementToSql(source.set_variable_statement(),
00335                                                 destination,
00336                                                 sql_variant);
00337       sql_strings.push_back(destination);
00338     }
00339     break;
00340   case Statement::RAW_SQL:
00341     {
00342       if (source.has_raw_sql_schema())
00343       {
00344         string destination("USE ");
00345         destination.append(source.raw_sql_schema());
00346         sql_strings.push_back(destination);
00347       }
00348       sql_strings.push_back(source.sql());
00349     }
00350     break;
00351   default:
00352     sql_strings.push_back(source.sql());
00353     break;
00354   }
00355   return error;
00356 }
00357 
00358 enum TransformSqlError
00359 transformInsertHeaderToSql(const InsertHeader &header,
00360                            string &destination,
00361                            enum TransformSqlVariant sql_variant)
00362 {
00363   char quoted_identifier= '`';
00364   if (sql_variant == ANSI)
00365     quoted_identifier= '"';
00366 
00367   destination.assign("INSERT INTO ", 12);
00368   destination.push_back(quoted_identifier);
00369   destination.append(header.table_metadata().schema_name());
00370   destination.push_back(quoted_identifier);
00371   destination.push_back('.');
00372   destination.push_back(quoted_identifier);
00373   destination.append(header.table_metadata().table_name());
00374   destination.push_back(quoted_identifier);
00375   destination.append(" (", 2);
00376 
00377   /* Add field list to SQL string... */
00378   size_t num_fields= header.field_metadata_size();
00379   size_t x;
00380 
00381   for (x= 0; x < num_fields; ++x)
00382   {
00383     const FieldMetadata &field_metadata= header.field_metadata(x);
00384     if (x != 0)
00385       destination.push_back(',');
00386     
00387     destination.push_back(quoted_identifier);
00388     destination.append(field_metadata.name());
00389     destination.push_back(quoted_identifier);
00390   }
00391 
00392   return NONE;
00393 }
00394 
00395 enum TransformSqlError
00396 transformInsertRecordToSql(const InsertHeader &header,
00397                            const InsertRecord &record,
00398                            string &destination,
00399                            enum TransformSqlVariant sql_variant)
00400 {
00401   enum TransformSqlError error= transformInsertHeaderToSql(header,
00402                                                            destination,
00403                                                            sql_variant);
00404 
00405   destination.append(") VALUES (");
00406 
00407   /* Add insert values */
00408   size_t num_fields= header.field_metadata_size();
00409   size_t x;
00410   bool should_quote_field_value= false;
00411   
00412   for (x= 0; x < num_fields; ++x)
00413   {
00414     if (x != 0)
00415       destination.push_back(',');
00416 
00417     const FieldMetadata &field_metadata= header.field_metadata(x);
00418 
00419     if (record.is_null(x))
00420     {
00421       should_quote_field_value= false;
00422     }
00423     else 
00424     {
00425       should_quote_field_value= shouldQuoteFieldValue(field_metadata.type());
00426     }
00427 
00428     if (should_quote_field_value)
00429       destination.push_back('\'');
00430 
00431     if (record.is_null(x))
00432     {
00433       destination.append("NULL");
00434     }
00435     else
00436     {
00437       if (field_metadata.type() == Table::Field::BLOB)
00438       {
00439         /*
00440          * We do this here because BLOB data is returned
00441          * in a string correctly, but calling append()
00442          * without a length will result in only the string
00443          * up to a \0 being output here.
00444          */
00445         string raw_data(record.insert_value(x));
00446         destination.append(raw_data.c_str(), raw_data.size());
00447       }
00448       else
00449       {
00450         string tmp(record.insert_value(x));
00451         escapeEmbeddedQuotes(tmp);
00452         destination.append(tmp);
00453       }
00454     }
00455 
00456     if (should_quote_field_value)
00457       destination.push_back('\'');
00458   }
00459   destination.push_back(')');
00460 
00461   return error;
00462 }
00463 
00464 enum TransformSqlError
00465 transformInsertStatementToSql(const InsertHeader &header,
00466                               const InsertData &data,
00467                               string &destination,
00468                               enum TransformSqlVariant sql_variant)
00469 {
00470   enum TransformSqlError error= transformInsertHeaderToSql(header,
00471                                                            destination,
00472                                                            sql_variant);
00473 
00474   destination.append(") VALUES (", 10);
00475 
00476   /* Add insert values */
00477   size_t num_records= data.record_size();
00478   size_t num_fields= header.field_metadata_size();
00479   size_t x, y;
00480   bool should_quote_field_value= false;
00481   
00482   for (x= 0; x < num_records; ++x)
00483   {
00484     if (x != 0)
00485       destination.append("),(", 3);
00486 
00487     for (y= 0; y < num_fields; ++y)
00488     {
00489       if (y != 0)
00490         destination.push_back(',');
00491 
00492       const FieldMetadata &field_metadata= header.field_metadata(y);
00493       
00494       should_quote_field_value= shouldQuoteFieldValue(field_metadata.type());
00495 
00496       if (should_quote_field_value)
00497         destination.push_back('\'');
00498 
00499       if (field_metadata.type() == Table::Field::BLOB)
00500       {
00501         /* 
00502          * We do this here because BLOB data is returned
00503          * in a string correctly, but calling append()
00504          * without a length will result in only the string
00505          * up to a \0 being output here.
00506          */
00507         string raw_data(data.record(x).insert_value(y));
00508         destination.append(raw_data.c_str(), raw_data.size());
00509       }
00510       else
00511       {
00512         string tmp(data.record(x).insert_value(y));
00513         escapeEmbeddedQuotes(tmp);
00514         destination.append(tmp);
00515       }
00516 
00517       if (should_quote_field_value)
00518         destination.push_back('\'');
00519     }
00520   }
00521   destination.push_back(')');
00522 
00523   return error;
00524 }
00525 
00526 enum TransformSqlError
00527 transformUpdateHeaderToSql(const UpdateHeader &header,
00528                            string &destination,
00529                            enum TransformSqlVariant sql_variant)
00530 {
00531   char quoted_identifier= '`';
00532   if (sql_variant == ANSI)
00533     quoted_identifier= '"';
00534 
00535   destination.assign("UPDATE ", 7);
00536   destination.push_back(quoted_identifier);
00537   destination.append(header.table_metadata().schema_name());
00538   destination.push_back(quoted_identifier);
00539   destination.push_back('.');
00540   destination.push_back(quoted_identifier);
00541   destination.append(header.table_metadata().table_name());
00542   destination.push_back(quoted_identifier);
00543   destination.append(" SET ", 5);
00544 
00545   return NONE;
00546 }
00547 
00548 enum TransformSqlError
00549 transformUpdateRecordToSql(const UpdateHeader &header,
00550                            const UpdateRecord &record,
00551                            string &destination,
00552                            enum TransformSqlVariant sql_variant)
00553 {
00554   enum TransformSqlError error= transformUpdateHeaderToSql(header,
00555                                                            destination,
00556                                                            sql_variant);
00557 
00558   char quoted_identifier= '`';
00559   if (sql_variant == ANSI)
00560     quoted_identifier= '"';
00561 
00562   /* Add field SET list to SQL string... */
00563   size_t num_set_fields= header.set_field_metadata_size();
00564   size_t x;
00565   bool should_quote_field_value= false;
00566 
00567   for (x= 0; x < num_set_fields; ++x)
00568   {
00569     const FieldMetadata &field_metadata= header.set_field_metadata(x);
00570     if (x != 0)
00571       destination.push_back(',');
00572     
00573     destination.push_back(quoted_identifier);
00574     destination.append(field_metadata.name());
00575     destination.push_back(quoted_identifier);
00576     destination.push_back('=');
00577 
00578     if (record.is_null(x))
00579     {
00580       should_quote_field_value= false;
00581     }
00582     else 
00583     {
00584       should_quote_field_value= shouldQuoteFieldValue(field_metadata.type());
00585     }    
00586 
00587     if (should_quote_field_value)
00588       destination.push_back('\'');
00589 
00590     if (record.is_null(x))
00591     {
00592       destination.append("NULL");
00593     }
00594     else 
00595     {
00596       if (field_metadata.type() == Table::Field::BLOB)
00597       {
00598         /*
00599          * We do this here because BLOB data is returned
00600          * in a string correctly, but calling append()
00601          * without a length will result in only the string
00602          * up to a \0 being output here.
00603          */
00604         string raw_data(record.after_value(x));
00605         destination.append(raw_data.c_str(), raw_data.size());
00606       }
00607       else 
00608       {
00609         string tmp(record.after_value(x));
00610         escapeEmbeddedQuotes(tmp);
00611         destination.append(tmp);
00612       }
00613     }
00614 
00615     if (should_quote_field_value)
00616       destination.push_back('\'');
00617   }
00618 
00619   size_t num_key_fields= header.key_field_metadata_size();
00620 
00621   destination.append(" WHERE ", 7);
00622   for (x= 0; x < num_key_fields; ++x) 
00623   {
00624     const FieldMetadata &field_metadata= header.key_field_metadata(x);
00625     
00626     if (x != 0)
00627       destination.append(" AND ", 5); /* Always AND condition with a multi-column PK */
00628 
00629     destination.push_back(quoted_identifier);
00630     destination.append(field_metadata.name());
00631     destination.push_back(quoted_identifier);
00632 
00633     destination.push_back('=');
00634 
00635     should_quote_field_value= shouldQuoteFieldValue(field_metadata.type());
00636 
00637     if (should_quote_field_value)
00638       destination.push_back('\'');
00639 
00640     if (field_metadata.type() == Table::Field::BLOB)
00641     {
00642       /* 
00643        * We do this here because BLOB data is returned
00644        * in a string correctly, but calling append()
00645        * without a length will result in only the string
00646        * up to a \0 being output here.
00647        */
00648       string raw_data(record.key_value(x));
00649       destination.append(raw_data.c_str(), raw_data.size());
00650     }
00651     else
00652     {
00653       destination.append(record.key_value(x));
00654     }
00655 
00656     if (should_quote_field_value)
00657       destination.push_back('\'');
00658   }
00659 
00660   return error;
00661 }
00662 
00663 enum TransformSqlError
00664 transformDeleteHeaderToSql(const DeleteHeader &header,
00665                            string &destination,
00666                            enum TransformSqlVariant sql_variant)
00667 {
00668   char quoted_identifier= '`';
00669   if (sql_variant == ANSI)
00670     quoted_identifier= '"';
00671 
00672   destination.assign("DELETE FROM ", 12);
00673   destination.push_back(quoted_identifier);
00674   destination.append(header.table_metadata().schema_name());
00675   destination.push_back(quoted_identifier);
00676   destination.push_back('.');
00677   destination.push_back(quoted_identifier);
00678   destination.append(header.table_metadata().table_name());
00679   destination.push_back(quoted_identifier);
00680 
00681   return NONE;
00682 }
00683 
00684 enum TransformSqlError
00685 transformDeleteRecordToSql(const DeleteHeader &header,
00686                            const DeleteRecord &record,
00687                            string &destination,
00688                            enum TransformSqlVariant sql_variant)
00689 {
00690   enum TransformSqlError error= transformDeleteHeaderToSql(header,
00691                                                            destination,
00692                                                            sql_variant);
00693   char quoted_identifier= '`';
00694   if (sql_variant == ANSI)
00695     quoted_identifier= '"';
00696 
00697   /* Add WHERE clause to SQL string... */
00698   uint32_t num_key_fields= header.key_field_metadata_size();
00699   uint32_t x;
00700   bool should_quote_field_value= false;
00701 
00702   destination.append(" WHERE ", 7);
00703   for (x= 0; x < num_key_fields; ++x) 
00704   {
00705     const FieldMetadata &field_metadata= header.key_field_metadata(x);
00706     
00707     if (x != 0)
00708       destination.append(" AND ", 5); /* Always AND condition with a multi-column PK */
00709 
00710     destination.push_back(quoted_identifier);
00711     destination.append(field_metadata.name());
00712     destination.push_back(quoted_identifier);
00713 
00714     destination.push_back('=');
00715 
00716     should_quote_field_value= shouldQuoteFieldValue(field_metadata.type());
00717 
00718     if (should_quote_field_value)
00719       destination.push_back('\'');
00720 
00721     if (field_metadata.type() == Table::Field::BLOB)
00722     {
00723       /* 
00724        * We do this here because BLOB data is returned
00725        * in a string correctly, but calling append()
00726        * without a length will result in only the string
00727        * up to a \0 being output here.
00728        */
00729       string raw_data(record.key_value(x));
00730       destination.append(raw_data.c_str(), raw_data.size());
00731     }
00732     else
00733     {
00734       string tmp(record.key_value(x));
00735       escapeEmbeddedQuotes(tmp);
00736       destination.append(tmp);
00737     }
00738 
00739     if (should_quote_field_value)
00740       destination.push_back('\'');
00741   }
00742 
00743   return error;
00744 }
00745 
00746 enum TransformSqlError
00747 transformDeleteStatementToSql(const DeleteHeader &header,
00748                               const DeleteData &data,
00749                               string &destination,
00750                               enum TransformSqlVariant sql_variant)
00751 {
00752   enum TransformSqlError error= transformDeleteHeaderToSql(header,
00753                                                            destination,
00754                                                            sql_variant);
00755   char quoted_identifier= '`';
00756   if (sql_variant == ANSI)
00757     quoted_identifier= '"';
00758 
00759   /* Add WHERE clause to SQL string... */
00760   uint32_t num_key_fields= header.key_field_metadata_size();
00761   uint32_t num_key_records= data.record_size();
00762   uint32_t x, y;
00763   bool should_quote_field_value= false;
00764 
00765   destination.append(" WHERE ", 7);
00766   for (x= 0; x < num_key_records; ++x)
00767   {
00768     if (x != 0)
00769       destination.append(" OR ", 4); /* Always OR condition for multiple key records */
00770 
00771     if (num_key_fields > 1)
00772       destination.push_back('(');
00773 
00774     for (y= 0; y < num_key_fields; ++y) 
00775     {
00776       const FieldMetadata &field_metadata= header.key_field_metadata(y);
00777       
00778       if (y != 0)
00779         destination.append(" AND ", 5); /* Always AND condition with a multi-column PK */
00780 
00781       destination.push_back(quoted_identifier);
00782       destination.append(field_metadata.name());
00783       destination.push_back(quoted_identifier);
00784 
00785       destination.push_back('=');
00786 
00787       should_quote_field_value= shouldQuoteFieldValue(field_metadata.type());
00788 
00789       if (should_quote_field_value)
00790         destination.push_back('\'');
00791 
00792       if (field_metadata.type() == Table::Field::BLOB)
00793       {
00794         /* 
00795          * We do this here because BLOB data is returned
00796          * in a string correctly, but calling append()
00797          * without a length will result in only the string
00798          * up to a \0 being output here.
00799          */
00800         string raw_data(data.record(x).key_value(y));
00801         destination.append(raw_data.c_str(), raw_data.size());
00802       }
00803       else
00804       {
00805         string tmp(data.record(x).key_value(y));
00806         escapeEmbeddedQuotes(tmp);
00807         destination.append(tmp);
00808       }
00809 
00810       if (should_quote_field_value)
00811         destination.push_back('\'');
00812     }
00813     if (num_key_fields > 1)
00814       destination.push_back(')');
00815   }
00816   return error;
00817 }
00818 
00819 enum TransformSqlError
00820 transformAlterSchemaStatementToSql(const AlterSchemaStatement &statement,
00821                                    string &destination,
00822                                    enum TransformSqlVariant sql_variant)
00823 {
00824   const Schema &before= statement.before();
00825   const Schema &after= statement.after();
00826 
00827   /* Make sure we are given the before and after for the same object */
00828   if (before.uuid() != after.uuid())
00829     return UUID_MISMATCH;
00830 
00831   char quoted_identifier= '`';
00832   if (sql_variant == ANSI)
00833     quoted_identifier= '"';
00834 
00835   destination.append("ALTER SCHEMA ");
00836   destination.push_back(quoted_identifier);
00837   destination.append(before.name());
00838   destination.push_back(quoted_identifier);
00839 
00840   /*
00841    * Diff our schemas. Currently, only collation can change so a
00842    * diff of the two structures is not really necessary.
00843    */
00844   destination.append(" COLLATE = ");
00845   destination.append(after.collation());
00846 
00847   return NONE;
00848 }
00849 
00850 enum TransformSqlError
00851 transformDropSchemaStatementToSql(const DropSchemaStatement &statement,
00852                                   string &destination,
00853                                   enum TransformSqlVariant sql_variant)
00854 {
00855   char quoted_identifier= '`';
00856   if (sql_variant == ANSI)
00857     quoted_identifier= '"';
00858 
00859   destination.append("DROP SCHEMA ", 12);
00860   destination.push_back(quoted_identifier);
00861   destination.append(statement.schema_name());
00862   destination.push_back(quoted_identifier);
00863 
00864   return NONE;
00865 }
00866 
00867 enum TransformSqlError
00868 transformCreateSchemaStatementToSql(const CreateSchemaStatement &statement,
00869                                     string &destination,
00870                                     enum TransformSqlVariant sql_variant)
00871 {
00872   char quoted_identifier= '`';
00873   if (sql_variant == ANSI)
00874     quoted_identifier= '"';
00875 
00876   const Schema &schema= statement.schema();
00877 
00878   destination.append("CREATE SCHEMA ");
00879   destination.push_back(quoted_identifier);
00880   destination.append(schema.name());
00881   destination.push_back(quoted_identifier);
00882 
00883   if (schema.has_collation())
00884   {
00885     destination.append(" COLLATE ");
00886     destination.append(schema.collation());
00887   }
00888 
00889   if (not message::is_replicated(schema))
00890   {
00891     destination.append(" REPLICATE = FALSE");
00892   }
00893 
00894   if (message::has_definer(schema))
00895   {
00896     destination.append(" DEFINER ");
00897     destination.push_back('\'');
00898     destination.append(message::definer(schema));
00899     destination.push_back('\'');
00900   }
00901 
00902   return NONE;
00903 }
00904 
00905 enum TransformSqlError
00906 transformDropTableStatementToSql(const DropTableStatement &statement,
00907                                  string &destination,
00908                                  enum TransformSqlVariant sql_variant)
00909 {
00910   char quoted_identifier= '`';
00911   if (sql_variant == ANSI)
00912     quoted_identifier= '"';
00913 
00914   const TableMetadata &table_metadata= statement.table_metadata();
00915 
00916   destination.append("DROP TABLE ");
00917 
00918   /* Add the IF EXISTS clause if necessary */
00919   if (statement.has_if_exists_clause() &&
00920       statement.if_exists_clause() == true)
00921   {
00922     destination.append("IF EXISTS ");
00923   }
00924 
00925   destination.push_back(quoted_identifier);
00926   destination.append(table_metadata.schema_name());
00927   destination.push_back(quoted_identifier);
00928   destination.push_back('.');
00929   destination.push_back(quoted_identifier);
00930   destination.append(table_metadata.table_name());
00931   destination.push_back(quoted_identifier);
00932 
00933   return NONE;
00934 }
00935 
00936 enum TransformSqlError
00937 transformTruncateTableStatementToSql(const TruncateTableStatement &statement,
00938                                      string &destination,
00939                                      enum TransformSqlVariant sql_variant)
00940 {
00941   char quoted_identifier= '`';
00942   if (sql_variant == ANSI)
00943     quoted_identifier= '"';
00944 
00945   const TableMetadata &table_metadata= statement.table_metadata();
00946 
00947   destination.append("TRUNCATE TABLE ");
00948   destination.push_back(quoted_identifier);
00949   destination.append(table_metadata.schema_name());
00950   destination.push_back(quoted_identifier);
00951   destination.push_back('.');
00952   destination.push_back(quoted_identifier);
00953   destination.append(table_metadata.table_name());
00954   destination.push_back(quoted_identifier);
00955 
00956   return NONE;
00957 }
00958 
00959 enum TransformSqlError
00960 transformSetVariableStatementToSql(const SetVariableStatement &statement,
00961                                    string &destination,
00962                                    enum TransformSqlVariant sql_variant)
00963 {
00964   (void) sql_variant;
00965   const FieldMetadata &variable_metadata= statement.variable_metadata();
00966   bool should_quote_field_value= shouldQuoteFieldValue(variable_metadata.type());
00967 
00968   destination.append("SET GLOBAL "); /* Only global variables are replicated */
00969   destination.append(variable_metadata.name());
00970   destination.push_back('=');
00971 
00972   if (should_quote_field_value)
00973     destination.push_back('\'');
00974   
00975   destination.append(statement.variable_value());
00976 
00977   if (should_quote_field_value)
00978     destination.push_back('\'');
00979 
00980   return NONE;
00981 }
00982 
00983 enum TransformSqlError
00984 transformCreateTableStatementToSql(const CreateTableStatement &statement,
00985                                    string &destination,
00986                                    enum TransformSqlVariant sql_variant)
00987 {
00988   return transformTableDefinitionToSql(statement.table(), destination, sql_variant);
00989 }
00990 
00991 enum TransformSqlError
00992 transformTableDefinitionToSql(const Table &table,
00993                               string &destination,
00994                               enum TransformSqlVariant sql_variant, bool with_schema)
00995 {
00996   char quoted_identifier= '`';
00997   if (sql_variant == ANSI)
00998     quoted_identifier= '"';
00999 
01000   destination.append("CREATE ");
01001 
01002   if (table.type() == Table::TEMPORARY)
01003     destination.append("TEMPORARY ");
01004   
01005   destination.append("TABLE ");
01006   if (with_schema)
01007   {
01008     append_escaped_string(&destination, table.schema(), quoted_identifier);
01009     destination.push_back('.');
01010   }
01011   append_escaped_string(&destination, table.name(), quoted_identifier);
01012   destination.append(" (\n");
01013 
01014   enum TransformSqlError result= NONE;
01015   size_t num_fields= table.field_size();
01016   for (size_t x= 0; x < num_fields; ++x)
01017   {
01018     const Table::Field &field= table.field(x);
01019 
01020     if (x != 0)
01021       destination.append(",\n");
01022 
01023     destination.append("  ");
01024 
01025     result= transformFieldDefinitionToSql(field, destination, sql_variant);
01026 
01027     if (result != NONE)
01028       return result;
01029   }
01030 
01031   size_t num_indexes= table.indexes_size();
01032   
01033   if (num_indexes > 0)
01034     destination.append(",\n");
01035 
01036   for (size_t x= 0; x < num_indexes; ++x)
01037   {
01038     const message::Table::Index &index= table.indexes(x);
01039 
01040     if (x != 0)
01041       destination.append(",\n");
01042 
01043     result= transformIndexDefinitionToSql(index, table, destination, sql_variant);
01044     
01045     if (result != NONE)
01046       return result;
01047   }
01048 
01049   size_t num_foreign_keys= table.fk_constraint_size();
01050 
01051   if (num_foreign_keys > 0)
01052     destination.append(",\n");
01053 
01054   for (size_t x= 0; x < num_foreign_keys; ++x)
01055   {
01056     const message::Table::ForeignKeyConstraint &fkey= table.fk_constraint(x);
01057 
01058     if (x != 0)
01059       destination.append(",\n");
01060 
01061     result= transformForeignKeyConstraintDefinitionToSql(fkey, table, destination, sql_variant);
01062 
01063     if (result != NONE)
01064       return result;
01065   }
01066 
01067   destination.append("\n)");
01068 
01069   /* Add ENGINE = " clause */
01070   if (table.has_engine())
01071   {
01072     destination.append(" ENGINE=");
01073     destination.append(table.engine().name());
01074 
01075     size_t num_engine_options= table.engine().options_size();
01076     if (num_engine_options > 0)
01077       destination.append(" ", 1);
01078     for (size_t x= 0; x < num_engine_options; ++x)
01079     {
01080       const Engine::Option &option= table.engine().options(x);
01081       destination.append(option.name());
01082       destination.append("='");
01083       destination.append(option.state());
01084       destination.append("'");
01085       if (x != num_engine_options-1)
01086       {
01087         destination.append(", ");
01088       }
01089     }
01090   }
01091 
01092   if (table.has_options())
01093     (void) transformTableOptionsToSql(table.options(), destination, sql_variant);
01094 
01095   if (not message::is_replicated(table))
01096   {
01097     destination.append(" REPLICATE = FALSE");
01098   }
01099 
01100   if (message::has_definer(table))
01101   {
01102     destination.append(" DEFINER ");
01103     destination.push_back('\'');
01104     destination.append(message::definer(table));
01105     destination.push_back('\'');
01106   }
01107 
01108   return NONE;
01109 }
01110 
01111 enum TransformSqlError
01112 transformTableOptionsToSql(const Table::TableOptions &options,
01113                            string &destination,
01114                            enum TransformSqlVariant sql_variant)
01115 {
01116   if (sql_variant == ANSI)
01117     return NONE; /* ANSI does not support table options... */
01118 
01119   if (options.has_comment())
01120   {
01121     destination.append(" COMMENT=");
01122     append_escaped_string(&destination, options.comment());
01123   }
01124 
01125   if (options.has_collation())
01126   {
01127     destination.append(" COLLATE = ");
01128     destination.append(options.collation());
01129   }
01130 
01131   if (options.has_data_file_name())
01132   {
01133     destination.append("\nDATA_FILE_NAME = '");
01134     destination.append(options.data_file_name());
01135     destination.push_back('\'');
01136   }
01137 
01138   if (options.has_index_file_name())
01139   {
01140     destination.append("\nINDEX_FILE_NAME = '");
01141     destination.append(options.index_file_name());
01142     destination.push_back('\'');
01143   }
01144 
01145   if (options.has_max_rows())
01146   {
01147     destination.append("\nMAX_ROWS = ");
01148     destination.append(boost::lexical_cast<string>(options.max_rows()));
01149   }
01150 
01151   if (options.has_min_rows())
01152   {
01153     destination.append("\nMIN_ROWS = ");
01154     destination.append(boost::lexical_cast<string>(options.min_rows()));
01155   }
01156 
01157   if (options.has_user_set_auto_increment_value()
01158       && options.has_auto_increment_value())
01159   {
01160     destination.append(" AUTO_INCREMENT=");
01161     destination.append(boost::lexical_cast<string>(options.auto_increment_value()));
01162   }
01163 
01164   if (options.has_avg_row_length())
01165   {
01166     destination.append("\nAVG_ROW_LENGTH = ");
01167     destination.append(boost::lexical_cast<string>(options.avg_row_length()));
01168   }
01169 
01170   if (options.has_checksum() && options.checksum())
01171     destination.append("\nCHECKSUM = TRUE");
01172 
01173   if (options.has_page_checksum() && options.page_checksum())
01174     destination.append("\nPAGE_CHECKSUM = TRUE");
01175 
01176   return NONE;
01177 }
01178 
01179 enum TransformSqlError
01180 transformIndexDefinitionToSql(const Table::Index &index,
01181                               const Table &table,
01182                               string &destination,
01183                               enum TransformSqlVariant sql_variant)
01184 {
01185   char quoted_identifier= '`';
01186   if (sql_variant == ANSI)
01187     quoted_identifier= '"';
01188 
01189   destination.append("  ", 2);
01190 
01191   if (index.is_primary())
01192     destination.append("PRIMARY ");
01193   else if (index.is_unique())
01194     destination.append("UNIQUE ");
01195 
01196   destination.append("KEY ", 4);
01197   if (! (index.is_primary() && index.name().compare("PRIMARY")==0))
01198   {
01199     destination.push_back(quoted_identifier);
01200     destination.append(index.name());
01201     destination.push_back(quoted_identifier);
01202     destination.append(" (", 2);
01203   }
01204   else
01205     destination.append("(", 1);
01206 
01207   size_t num_parts= index.index_part_size();
01208   for (size_t x= 0; x < num_parts; ++x)
01209   {
01210     const Table::Index::IndexPart &part= index.index_part(x);
01211     const Table::Field &field= table.field(part.fieldnr());
01212 
01213     if (x != 0)
01214       destination.push_back(',');
01215     
01216     destination.push_back(quoted_identifier);
01217     destination.append(field.name());
01218     destination.push_back(quoted_identifier);
01219 
01220     /* 
01221      * If the index part's field type is VARCHAR or TEXT
01222      * then check for a prefix length then is different
01223      * from the field's full length...
01224      */
01225     if (field.type() == Table::Field::VARCHAR ||
01226         field.type() == Table::Field::BLOB)
01227     {
01228       if (part.has_compare_length())
01229       {
01230         if (part.compare_length() != field.string_options().length())
01231         {
01232           destination.push_back('(');
01233           destination.append(boost::lexical_cast<string>(part.compare_length()));
01234           destination.push_back(')');
01235         }
01236       }
01237     }
01238   }
01239   destination.push_back(')');
01240 
01241   switch (index.type())
01242   {
01243   case Table::Index::UNKNOWN_INDEX:
01244     break;
01245   case Table::Index::BTREE:
01246     destination.append(" USING BTREE");
01247     break;
01248   case Table::Index::RTREE:
01249     destination.append(" USING RTREE");
01250     break;
01251   case Table::Index::HASH:
01252     destination.append(" USING HASH");
01253     break;
01254   case Table::Index::FULLTEXT:
01255     destination.append(" USING FULLTEXT");
01256     break;
01257   }
01258 
01259   if (index.has_comment())
01260   {
01261     destination.append(" COMMENT ");
01262     append_escaped_string(&destination, index.comment());
01263   }
01264 
01265   return NONE;
01266 }
01267 
01268 static void transformForeignKeyOptionToSql(Table::ForeignKeyConstraint::ForeignKeyOption opt, string &destination)
01269 {
01270   switch (opt)
01271   {
01272   case Table::ForeignKeyConstraint::OPTION_RESTRICT:
01273     destination.append("RESTRICT");
01274     break;
01275   case Table::ForeignKeyConstraint::OPTION_CASCADE:
01276     destination.append("CASCADE");
01277     break;
01278   case Table::ForeignKeyConstraint::OPTION_SET_NULL:
01279     destination.append("SET NULL");
01280     break;
01281   case Table::ForeignKeyConstraint::OPTION_UNDEF:
01282   case Table::ForeignKeyConstraint::OPTION_NO_ACTION:
01283     destination.append("NO ACTION");
01284     break;
01285   case Table::ForeignKeyConstraint::OPTION_SET_DEFAULT:
01286     destination.append("SET DEFAULT");
01287     break;
01288   }
01289 }
01290 
01291 enum TransformSqlError
01292 transformForeignKeyConstraintDefinitionToSql(const Table::ForeignKeyConstraint &fkey,
01293                                              const Table &,
01294                                              string &destination,
01295                                              enum TransformSqlVariant sql_variant)
01296 {
01297   char quoted_identifier= '`';
01298   if (sql_variant == ANSI)
01299     quoted_identifier= '"';
01300 
01301   destination.append("  ");
01302 
01303   if (fkey.has_name())
01304   {
01305     destination.append("CONSTRAINT ");
01306     append_escaped_string(&destination, fkey.name(), quoted_identifier);
01307     destination.append(" ", 1);
01308   }
01309 
01310   destination.append("FOREIGN KEY (");
01311 
01312   for (ssize_t x= 0; x < fkey.column_names_size(); ++x)
01313   {
01314     if (x != 0)
01315       destination.append(", ");
01316 
01317     append_escaped_string(&destination, fkey.column_names(x),
01318                           quoted_identifier);
01319   }
01320 
01321   destination.append(") REFERENCES ");
01322 
01323   append_escaped_string(&destination, fkey.references_table_name(),
01324                         quoted_identifier);
01325   destination.append(" (");
01326 
01327   for (ssize_t x= 0; x < fkey.references_columns_size(); ++x)
01328   {
01329     if (x != 0)
01330       destination.append(", ");
01331 
01332     append_escaped_string(&destination, fkey.references_columns(x),
01333                           quoted_identifier);
01334   }
01335 
01336   destination.push_back(')');
01337 
01338   if (fkey.has_update_option() and fkey.update_option() != Table::ForeignKeyConstraint::OPTION_UNDEF)
01339   {
01340     destination.append(" ON UPDATE ");
01341     transformForeignKeyOptionToSql(fkey.update_option(), destination);
01342   }
01343 
01344   if (fkey.has_delete_option() and fkey.delete_option() != Table::ForeignKeyConstraint::OPTION_UNDEF)
01345   {
01346     destination.append(" ON DELETE ");
01347     transformForeignKeyOptionToSql(fkey.delete_option(), destination);
01348   }
01349 
01350   return NONE;
01351 }
01352 
01353 enum TransformSqlError
01354 transformFieldDefinitionToSql(const Table::Field &field,
01355                               string &destination,
01356                               enum TransformSqlVariant sql_variant)
01357 {
01358   char quoted_identifier= '`';
01359   char quoted_default;
01360 
01361   if (sql_variant == ANSI)
01362     quoted_identifier= '"';
01363 
01364   if (sql_variant == DRIZZLE)
01365     quoted_default= '\'';
01366   else
01367     quoted_default= quoted_identifier;
01368 
01369   append_escaped_string(&destination, field.name(), quoted_identifier);
01370 
01371   Table::Field::FieldType field_type= field.type();
01372 
01373   switch (field_type)
01374   {
01375     case Table::Field::DOUBLE:
01376     destination.append(" DOUBLE");
01377     if (field.has_numeric_options()
01378         && field.numeric_options().has_precision())
01379     {
01380       stringstream ss;
01381       ss << "(" << field.numeric_options().precision() << ",";
01382       ss << field.numeric_options().scale() << ")";
01383       destination.append(ss.str());
01384     }
01385     break;
01386   case Table::Field::VARCHAR:
01387     {
01388       if (field.string_options().has_collation()
01389           && field.string_options().collation().compare("binary") == 0)
01390         destination.append(" VARBINARY(");
01391       else
01392         destination.append(" VARCHAR(");
01393 
01394       destination.append(boost::lexical_cast<string>(field.string_options().length()));
01395       destination.append(")");
01396     }
01397     break;
01398   case Table::Field::BLOB:
01399     {
01400       if (field.string_options().has_collation()
01401           && field.string_options().collation().compare("binary") == 0)
01402         destination.append(" BLOB");
01403       else
01404         destination.append(" TEXT");
01405     }
01406     break;
01407   case Table::Field::ENUM:
01408     {
01409       size_t num_field_values= field.enumeration_values().field_value_size();
01410       destination.append(" ENUM(");
01411       for (size_t x= 0; x < num_field_values; ++x)
01412       {
01413         const string &type= field.enumeration_values().field_value(x);
01414 
01415         if (x != 0)
01416           destination.push_back(',');
01417 
01418         destination.push_back('\'');
01419         destination.append(type);
01420         destination.push_back('\'');
01421       }
01422       destination.push_back(')');
01423       break;
01424     }
01425   case Table::Field::UUID:
01426     destination.append(" UUID");
01427     break;
01428   case Table::Field::IPV6:
01429     destination.append(" IPV6");
01430     break;
01431   case Table::Field::BOOLEAN:
01432     destination.append(" BOOLEAN");
01433     break;
01434   case Table::Field::INTEGER:
01435     destination.append(" INT");
01436     break;
01437   case Table::Field::BIGINT:
01438     if (field.has_constraints() and
01439         field.constraints().is_unsigned())
01440     {
01441       destination.append(" BIGINT UNSIGNED");
01442     }
01443     else
01444     {
01445       destination.append(" BIGINT");
01446     }
01447     break;
01448   case Table::Field::DECIMAL:
01449     {
01450       destination.append(" DECIMAL(");
01451       stringstream ss;
01452       ss << field.numeric_options().precision() << ",";
01453       ss << field.numeric_options().scale() << ")";
01454       destination.append(ss.str());
01455     }
01456     break;
01457   case Table::Field::DATE:
01458     destination.append(" DATE");
01459     break;
01460 
01461   case Table::Field::EPOCH:
01462     if (field.time_options().microseconds())
01463     {
01464       destination.append(" TIMESTAMP(6)");
01465     }
01466     else
01467     {
01468       destination.append(" TIMESTAMP");
01469     }
01470     break;
01471 
01472   case Table::Field::DATETIME:
01473     destination.append(" DATETIME");
01474     break;
01475   case Table::Field::TIME:
01476     destination.append(" TIME");
01477     break;
01478   }
01479 
01480   if (field.type() == Table::Field::BLOB ||
01481       field.type() == Table::Field::VARCHAR)
01482   {
01483     if (field.string_options().has_collation()
01484         && field.string_options().collation().compare("binary"))
01485     {
01486       destination.append(" COLLATE ");
01487       destination.append(field.string_options().collation());
01488     }
01489   }
01490 
01491   if (field.has_constraints() and field.constraints().is_unique())
01492   {
01493     destination.append(" UNIQUE");
01494   }
01495 
01496   if (field.has_constraints() && field.constraints().is_notnull())
01497   {
01498     destination.append(" NOT NULL");
01499   }
01500   else if (field.type() == Table::Field::EPOCH)
01501   {
01502     destination.append(" NULL");
01503   }
01504 
01505   if (field.type() == Table::Field::INTEGER || 
01506       field.type() == Table::Field::BIGINT)
01507   {
01508     /* AUTO_INCREMENT must be after NOT NULL */
01509     if (field.has_numeric_options() &&
01510         field.numeric_options().is_autoincrement())
01511     {
01512       destination.append(" AUTO_INCREMENT");
01513     }
01514   }
01515 
01516   if (field.options().has_default_value())
01517   {
01518     destination.append(" DEFAULT ");
01519     append_escaped_string(&destination, field.options().default_value());
01520   }
01521   else if (field.options().has_default_expression())
01522   {
01523     destination.append(" DEFAULT ");
01524     destination.append(field.options().default_expression());
01525   }
01526   else if (field.options().has_default_bin_value())
01527   {
01528     const string &v= field.options().default_bin_value();
01529     if (v.length() == 0)
01530     {
01531       destination.append(" DEFAULT ''");
01532     }
01533     else
01534     {
01535       destination.append(" DEFAULT 0x");
01536       for (size_t x= 0; x < v.length(); x++)
01537       {
01538         char hex[3];
01539         snprintf(hex, sizeof(hex), "%.2X", *(v.c_str() + x));
01540         destination.append(hex, 2);
01541       }
01542     }
01543   }
01544   else if (field.options().has_default_null()
01545            && field.options().default_null()
01546            && field.type() != Table::Field::BLOB)
01547   {
01548     destination.append(" DEFAULT NULL");
01549   }
01550 
01551   if (field.has_options() && field.options().has_update_expression())
01552   {
01553     destination.append(" ON UPDATE ");
01554     destination.append(field.options().update_expression());
01555   }
01556 
01557   if (field.has_comment())
01558   {
01559     destination.append(" COMMENT ");
01560     append_escaped_string(&destination, field.comment(), quoted_default);
01561   }
01562   return NONE;
01563 }
01564 
01565 bool shouldQuoteFieldValue(Table::Field::FieldType in_type)
01566 {
01567   switch (in_type)
01568   {
01569   case Table::Field::DOUBLE:
01570   case Table::Field::DECIMAL:
01571   case Table::Field::INTEGER:
01572   case Table::Field::BIGINT:
01573     return false;
01574   default:
01575     return true;
01576   } 
01577 }
01578 
01579 Table::Field::FieldType internalFieldTypeToFieldProtoType(enum enum_field_types type)
01580 {
01581   switch (type) {
01582   case DRIZZLE_TYPE_LONG:
01583     return Table::Field::INTEGER;
01584   case DRIZZLE_TYPE_DOUBLE:
01585     return Table::Field::DOUBLE;
01586   case DRIZZLE_TYPE_NULL:
01587     assert(false); /* Not a user definable type */
01588     return Table::Field::INTEGER; /* unreachable */
01589   case DRIZZLE_TYPE_MICROTIME:
01590   case DRIZZLE_TYPE_TIMESTAMP:
01591     return Table::Field::EPOCH;
01592   case DRIZZLE_TYPE_LONGLONG:
01593     return Table::Field::BIGINT;
01594   case DRIZZLE_TYPE_DATETIME:
01595     return Table::Field::DATETIME;
01596   case DRIZZLE_TYPE_TIME:
01597     return Table::Field::TIME;
01598   case DRIZZLE_TYPE_DATE:
01599     return Table::Field::DATE;
01600   case DRIZZLE_TYPE_VARCHAR:
01601     return Table::Field::VARCHAR;
01602   case DRIZZLE_TYPE_DECIMAL:
01603     return Table::Field::DECIMAL;
01604   case DRIZZLE_TYPE_ENUM:
01605     return Table::Field::ENUM;
01606   case DRIZZLE_TYPE_BLOB:
01607     return Table::Field::BLOB;
01608   case DRIZZLE_TYPE_UUID:
01609     return Table::Field::UUID;
01610   case DRIZZLE_TYPE_BOOLEAN:
01611     return Table::Field::BOOLEAN;
01612   case DRIZZLE_TYPE_IPV6:
01613     return Table::Field::IPV6;
01614   }
01615 
01616   assert(false);
01617   return Table::Field::INTEGER; /* unreachable */
01618 }
01619 
01620 bool transactionContainsBulkSegment(const Transaction &transaction)
01621 {
01622   size_t num_statements= transaction.statement_size();
01623   if (num_statements == 0)
01624     return false;
01625 
01626   /*
01627    * Only INSERT, UPDATE, and DELETE statements can possibly
01628    * have bulk segments.  So, we loop through the statements
01629    * checking for segment_id > 1 in those specific submessages.
01630    */
01631   size_t x;
01632   for (x= 0; x < num_statements; ++x)
01633   {
01634     const Statement &statement= transaction.statement(x);
01635     Statement::Type type= statement.type();
01636 
01637     switch (type)
01638     {
01639       case Statement::INSERT:
01640         if (statement.insert_data().segment_id() > 1)
01641           return true;
01642         break;
01643       case Statement::UPDATE:
01644         if (statement.update_data().segment_id() > 1)
01645           return true;
01646         break;
01647       case Statement::DELETE:
01648         if (statement.delete_data().segment_id() > 1)
01649           return true;
01650         break;
01651       default:
01652         break;
01653     }
01654   }
01655   return false;
01656 }
01657 
01658 } /* namespace message */
01659 } /* namespace drizzled */