Drizzled Public API Documentation

table_proto.cc
00001 /* Copyright (C) 2000-2006 MySQL AB
00002 
00003    This program is free software; you can redistribute it and/or modify
00004    it under the terms of the GNU General Public License as published by
00005    the Free Software Foundation; version 2 of the License.
00006 
00007    This program is distributed in the hope that it will be useful,
00008    but WITHOUT ANY WARRANTY; without even the implied warranty of
00009    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00010    GNU General Public License for more details.
00011 
00012    You should have received a copy of the GNU General Public License
00013    along with this program; if not, write to the Free Software
00014    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
00015 
00016 #include <config.h>
00017 #include <drizzled/error.h>
00018 #include <drizzled/session.h>
00019 #include <drizzled/sql_table.h>
00020 #include <drizzled/message/statement_transform.h>
00021 
00022 #include <drizzled/plugin/storage_engine.h>
00023 
00024 #include <drizzled/internal/my_sys.h>
00025 #include <drizzled/typelib.h>
00026 #include <drizzled/util/test.h>
00027 
00028 /* For proto */
00029 #include <string>
00030 #include <fstream>
00031 #include <fcntl.h>
00032 #include <drizzled/message/schema.h>
00033 #include <drizzled/message/table.h>
00034 #include <google/protobuf/io/zero_copy_stream.h>
00035 #include <google/protobuf/io/zero_copy_stream_impl.h>
00036 #include <google/protobuf/message.h>
00037 
00038 #include <drizzled/table_proto.h>
00039 #include <drizzled/charset.h>
00040 #include <drizzled/create_field.h>
00041 #include <drizzled/function/time/typecast.h>
00042 
00043 using namespace std;
00044 
00045 namespace drizzled {
00046 
00047 static
00048 bool fill_table_proto(const identifier::Table& identifier,
00049                       message::Table &table_proto,
00050                       List<CreateField> &create_fields,
00051                       HA_CREATE_INFO *create_info,
00052                       uint32_t keys,
00053                       KeyInfo *key_info)
00054 {
00055   CreateField *field_arg;
00056   List<CreateField>::iterator it(create_fields.begin());
00057   message::Table::TableOptions *table_options= table_proto.mutable_options();
00058 
00059   if (create_fields.size() > MAX_FIELDS)
00060   {
00061     my_error(ER_TOO_MANY_FIELDS, MYF(0), ER(ER_TOO_MANY_FIELDS));
00062     return true;
00063   }
00064 
00065   assert(strcmp(table_proto.engine().name().c_str(),
00066     create_info->db_type->getName().c_str())==0);
00067 
00068   message::schema::shared_ptr schema_message= plugin::StorageEngine::getSchemaDefinition(identifier);
00069 
00070   if (schema_message and not message::is_replicated(*schema_message))
00071   {
00072     message::set_is_replicated(table_proto, false);
00073   }
00074 
00075   int field_number= 0;
00076   bool use_existing_fields= table_proto.field_size() > 0;
00077   while ((field_arg= it++))
00078   {
00079     message::Table::Field *attribute;
00080 
00081     /* some (one) code path for CREATE TABLE fills the proto
00082        out more than the others, so we already have partially
00083        filled out Field messages */
00084 
00085     if (use_existing_fields)
00086     {
00087       attribute= table_proto.mutable_field(field_number++);
00088     }
00089     else
00090     {
00091       /* Other code paths still have to fill out the proto */
00092       attribute= table_proto.add_field();
00093 
00094       if (field_arg->flags & NOT_NULL_FLAG)
00095       {
00096         attribute->mutable_constraints()->set_is_notnull(true);
00097       }
00098 
00099       if (field_arg->flags & UNSIGNED_FLAG and 
00100           (field_arg->sql_type == DRIZZLE_TYPE_LONGLONG or field_arg->sql_type == DRIZZLE_TYPE_LONG))
00101       {
00102         field_arg->sql_type= DRIZZLE_TYPE_LONGLONG;
00103         attribute->mutable_constraints()->set_is_unsigned(true);
00104       }
00105 
00106       attribute->set_name(field_arg->field_name);
00107     }
00108 
00109     assert(((field_arg->flags & NOT_NULL_FLAG)) == attribute->constraints().is_notnull());
00110     assert(strcmp(attribute->name().c_str(), field_arg->field_name)==0);
00111 
00112 
00113     message::Table::Field::FieldType parser_type= attribute->type();
00114 
00115     if (field_arg->sql_type == DRIZZLE_TYPE_NULL)
00116     {
00117       my_error(ER_CANT_CREATE_TABLE, MYF(ME_BELL+ME_WAITTANG), table_proto.name().c_str(), -1);
00118       return true;
00119     }
00120 
00121     if (field_arg->flags & UNSIGNED_FLAG and 
00122        (field_arg->sql_type == DRIZZLE_TYPE_LONGLONG or field_arg->sql_type == DRIZZLE_TYPE_LONG))
00123     {
00124       message::Table::Field::FieldConstraints *constraints= attribute->mutable_constraints();
00125 
00126       field_arg->sql_type= DRIZZLE_TYPE_LONGLONG;
00127       constraints->set_is_unsigned(true);
00128     }
00129 
00130     attribute->set_type(message::internalFieldTypeToFieldProtoType(field_arg->sql_type));
00131 
00132     switch (attribute->type()) {
00133     case message::Table::Field::BIGINT:
00134     case message::Table::Field::INTEGER:
00135     case message::Table::Field::DATE:
00136     case message::Table::Field::DATETIME:
00137     case message::Table::Field::UUID:
00138     case message::Table::Field::IPV6:
00139     case message::Table::Field::TIME:
00140     case message::Table::Field::BOOLEAN:
00141       break;
00142     case message::Table::Field::DOUBLE:
00143       {
00144         /*
00145          * For DOUBLE, we only add a specific scale and precision iff
00146          * the fixed decimal point has been specified...
00147          */
00148         if (field_arg->decimals != NOT_FIXED_DEC)
00149         {
00150           message::Table::Field::NumericFieldOptions *numeric_field_options;
00151 
00152           numeric_field_options= attribute->mutable_numeric_options();
00153 
00154           numeric_field_options->set_precision(field_arg->length);
00155           numeric_field_options->set_scale(field_arg->decimals);
00156         }
00157       }
00158       break;
00159     case message::Table::Field::VARCHAR:
00160       {
00161         message::Table::Field::StringFieldOptions *string_field_options;
00162 
00163         string_field_options= attribute->mutable_string_options();
00164 
00165         if (! use_existing_fields || string_field_options->length()==0)
00166           string_field_options->set_length(field_arg->length
00167                                            / field_arg->charset->mbmaxlen);
00168         else
00169           assert((uint32_t)string_field_options->length() == (uint32_t)(field_arg->length / field_arg->charset->mbmaxlen));
00170 
00171         if (! string_field_options->has_collation())
00172         {
00173           string_field_options->set_collation_id(field_arg->charset->number);
00174           string_field_options->set_collation(field_arg->charset->name);
00175         }
00176         break;
00177       }
00178     case message::Table::Field::DECIMAL:
00179       {
00180         message::Table::Field::NumericFieldOptions *numeric_field_options;
00181 
00182         numeric_field_options= attribute->mutable_numeric_options();
00183         /* This is magic, I hate magic numbers -Brian */
00184         numeric_field_options->set_precision(field_arg->length + ( field_arg->decimals ? -2 : -1));
00185         numeric_field_options->set_scale(field_arg->decimals);
00186         break;
00187       }
00188     case message::Table::Field::ENUM:
00189       {
00190         message::Table::Field::EnumerationValues *enumeration_options;
00191 
00192         assert(field_arg->interval);
00193 
00194         enumeration_options= attribute->mutable_enumeration_values();
00195 
00196         for (uint32_t pos= 0; pos < field_arg->interval->count; pos++)
00197         {
00198           const char *src= field_arg->interval->type_names[pos];
00199 
00200           enumeration_options->add_field_value(src);
00201         }
00202   enumeration_options->set_collation_id(field_arg->charset->number);
00203         enumeration_options->set_collation(field_arg->charset->name);
00204         break;
00205       }
00206 
00207     case message::Table::Field::BLOB:
00208       {
00209         message::Table::Field::StringFieldOptions *string_field_options;
00210 
00211         string_field_options= attribute->mutable_string_options();
00212         string_field_options->set_collation_id(field_arg->charset->number);
00213         string_field_options->set_collation(field_arg->charset->name);
00214       }
00215 
00216       break;
00217 
00218     case message::Table::Field::EPOCH:
00219       {
00220         if (field_arg->sql_type == DRIZZLE_TYPE_MICROTIME)
00221           attribute->mutable_time_options()->set_microseconds(true);
00222       }
00223 
00224       break;
00225     }
00226 
00227     assert (!use_existing_fields || parser_type == attribute->type());
00228 
00229 #ifdef NOTDONE
00230     field_constraints= attribute->mutable_constraints();
00231     constraints->set_is_nullable(field_arg->def->null_value);
00232 #endif
00233 
00234     if (not field_arg->comment.empty())
00235     {
00236       uint32_t tmp_len= system_charset_info->cset->charpos(system_charset_info,
00237               field_arg->comment.begin(),
00238               field_arg->comment.end(),
00239               COLUMN_COMMENT_MAXLEN);
00240 
00241       if (tmp_len < field_arg->comment.size())
00242       {
00243         my_error(ER_WRONG_STRING_LENGTH, MYF(0), field_arg->comment.data(), "COLUMN COMMENT", (uint32_t) COLUMN_COMMENT_MAXLEN);
00244         return true;
00245       }
00246 
00247       if (not use_existing_fields)
00248         attribute->set_comment(field_arg->comment.data());
00249 
00250       assert(strcmp(attribute->comment().c_str(), field_arg->comment.data())==0);
00251     }
00252 
00253     if (field_arg->unireg_check == Field::NEXT_NUMBER)
00254     {
00255       message::Table::Field::NumericFieldOptions *field_options;
00256       field_options= attribute->mutable_numeric_options();
00257       field_options->set_is_autoincrement(true);
00258     }
00259 
00260     if (field_arg->unireg_check == Field::TIMESTAMP_DN_FIELD
00261        || field_arg->unireg_check == Field::TIMESTAMP_DNUN_FIELD)
00262     {
00263       message::Table::Field::FieldOptions *field_options;
00264       field_options= attribute->mutable_options();
00265       field_options->set_default_expression("CURRENT_TIMESTAMP");
00266     }
00267 
00268     if (field_arg->unireg_check == Field::TIMESTAMP_UN_FIELD
00269        || field_arg->unireg_check == Field::TIMESTAMP_DNUN_FIELD)
00270     {
00271       message::Table::Field::FieldOptions *field_options;
00272       field_options= attribute->mutable_options();
00273       field_options->set_update_expression("CURRENT_TIMESTAMP");
00274     }
00275 
00276     if (field_arg->def == NULL  && not attribute->constraints().is_notnull())
00277     {
00278       message::Table::Field::FieldOptions *field_options;
00279       field_options= attribute->mutable_options();
00280 
00281       field_options->set_default_null(true);
00282     }
00283     if (field_arg->def)
00284     {
00285       message::Table::Field::FieldOptions *field_options;
00286       field_options= attribute->mutable_options();
00287  
00288       if (field_arg->def->is_null())
00289       {
00290   field_options->set_default_null(true);
00291       }
00292       else
00293       {
00294   String d;
00295   String *default_value= field_arg->def->val_str(&d);
00296 
00297   assert(default_value);
00298 
00299   if ((field_arg->sql_type==DRIZZLE_TYPE_VARCHAR
00300      || field_arg->sql_type==DRIZZLE_TYPE_BLOB)
00301      && ((field_arg->length / field_arg->charset->mbmaxlen)
00302      < default_value->length()))
00303   {
00304     my_error(ER_INVALID_DEFAULT, MYF(0), field_arg->field_name);
00305     return true;
00306   }
00307 
00308         if (field::isDateTime(field_arg->sql_type))
00309         {
00310           type::Time ltime;
00311 
00312           if (field_arg->def->get_date(ltime, TIME_FUZZY_DATE))
00313           {
00314             my_error(ER_INVALID_DATETIME_VALUE, MYF(ME_FATALERROR),
00315                      default_value->c_str());
00316             return true;
00317           }
00318 
00319           /* We now do the casting down to the appropriate type.
00320 
00321              Yes, this implicit casting is balls.
00322              It was previously done on reading the proto back in,
00323              but we really shouldn't store the bogus things in the proto,
00324              and instead do the casting behaviour here.
00325 
00326              the timestamp errors are taken care of elsewhere.
00327           */
00328 
00329           if (field_arg->sql_type == DRIZZLE_TYPE_DATETIME)
00330           {
00331             Item *typecast= new Item_datetime_typecast(field_arg->def);
00332             typecast->quick_fix_field();
00333             typecast->val_str(default_value);
00334           }
00335           else if (field_arg->sql_type == DRIZZLE_TYPE_DATE)
00336           {
00337             Item *typecast= new Item_date_typecast(field_arg->def);
00338             typecast->quick_fix_field();
00339             typecast->val_str(default_value);
00340           }
00341         }
00342 
00343   if ((field_arg->sql_type == DRIZZLE_TYPE_VARCHAR && field_arg->charset == &my_charset_bin)
00344      || (field_arg->sql_type == DRIZZLE_TYPE_BLOB && field_arg->charset == &my_charset_bin))
00345   {
00346     field_options->set_default_bin_value(string(default_value->c_ptr(), default_value->length()));
00347   }
00348   else
00349   {
00350     field_options->set_default_value(default_value->c_ptr());
00351   }
00352       }
00353     }
00354 
00355     assert(field_arg->unireg_check == Field::NONE
00356      || field_arg->unireg_check == Field::NEXT_NUMBER
00357      || field_arg->unireg_check == Field::TIMESTAMP_DN_FIELD
00358      || field_arg->unireg_check == Field::TIMESTAMP_UN_FIELD
00359      || field_arg->unireg_check == Field::TIMESTAMP_DNUN_FIELD);
00360 
00361   }
00362 
00363   assert(! use_existing_fields || (field_number == table_proto.field_size()));
00364 
00365   if (create_info->table_options & HA_OPTION_PACK_RECORD)
00366     table_options->set_pack_record(true);
00367 
00368   if (table_options->has_comment() && table_options->comment().length() == 0)
00369     table_options->clear_comment();
00370 
00371   if (table_options->has_comment())
00372   {
00373     uint32_t tmp_len;
00374     tmp_len= system_charset_info->cset->charpos(system_charset_info,
00375                                                 table_options->comment().c_str(),
00376                                                 table_options->comment().c_str() +
00377                                                 table_options->comment().length(),
00378                                                 TABLE_COMMENT_MAXLEN);
00379 
00380     if (tmp_len < table_options->comment().length())
00381     {
00382       my_error(ER_WRONG_STRING_LENGTH, MYF(0),
00383                table_options->comment().c_str(),"Table COMMENT",
00384                (uint32_t) TABLE_COMMENT_MAXLEN);
00385       return true;
00386     }
00387   }
00388 
00389   if (create_info->default_table_charset)
00390   {
00391     table_options->set_collation_id(create_info->default_table_charset->number);
00392     table_options->set_collation(create_info->default_table_charset->name);
00393   }
00394 
00395   if (create_info->used_fields & HA_CREATE_USED_AUTO)
00396     table_options->set_has_user_set_auto_increment_value(true);
00397   else
00398     table_options->set_has_user_set_auto_increment_value(false);
00399 
00400   if (create_info->auto_increment_value)
00401     table_options->set_auto_increment_value(create_info->auto_increment_value);
00402 
00403   for (uint32_t i= 0; i < keys; i++)
00404   {
00405     message::Table::Index *idx= table_proto.add_indexes();
00406 
00407     assert(test(key_info[i].flags & HA_USES_COMMENT) == (key_info[i].comment.size() > 0));
00408 
00409     idx->set_name(key_info[i].name);
00410     idx->set_key_length(key_info[i].key_length);
00411     idx->set_is_primary(is_primary_key(key_info[i].name));
00412 
00413     switch(key_info[i].algorithm)
00414     {
00415     case HA_KEY_ALG_HASH:
00416       idx->set_type(message::Table::Index::HASH);
00417       break;
00418 
00419     case HA_KEY_ALG_BTREE:
00420       idx->set_type(message::Table::Index::BTREE);
00421       break;
00422 
00423     case HA_KEY_ALG_UNDEF:
00424       {
00425         idx->set_type(create_info->db_type->default_index_type());
00426         break;
00427       }
00428     }
00429 
00430     if (key_info[i].flags & HA_NOSAME)
00431       idx->set_is_unique(true);
00432     else
00433       idx->set_is_unique(false);
00434 
00435     message::Table::Index::Options *index_options= idx->mutable_options();
00436 
00437     if (key_info[i].flags & HA_USES_BLOCK_SIZE)
00438       index_options->set_key_block_size(key_info[i].block_size);
00439 
00440     if (key_info[i].flags & HA_PACK_KEY)
00441       index_options->set_pack_key(true);
00442 
00443     if (key_info[i].flags & HA_BINARY_PACK_KEY)
00444       index_options->set_binary_pack_key(true);
00445 
00446     if (key_info[i].flags & HA_VAR_LENGTH_PART)
00447       index_options->set_var_length_key(true);
00448 
00449     if (key_info[i].flags & HA_NULL_PART_KEY)
00450       index_options->set_null_part_key(true);
00451 
00452     if (key_info[i].flags & HA_KEY_HAS_PART_KEY_SEG)
00453       index_options->set_has_partial_segments(true);
00454 
00455     if (key_info[i].flags & HA_GENERATED_KEY)
00456       index_options->set_auto_generated_key(true);
00457 
00458     if (key_info[i].flags & HA_USES_COMMENT)
00459     {
00460       uint32_t tmp_len;
00461       tmp_len= system_charset_info->cset->charpos(system_charset_info,
00462               key_info[i].comment.begin(),
00463               key_info[i].comment.end(),
00464               TABLE_COMMENT_MAXLEN);
00465 
00466       if (tmp_len < key_info[i].comment.size())
00467       {
00468   my_error(ER_WRONG_STRING_LENGTH, MYF(0),
00469      key_info[i].comment.data(), "Index COMMENT",
00470      (uint32_t) TABLE_COMMENT_MAXLEN);
00471   return true;
00472       }
00473 
00474       idx->set_comment(key_info[i].comment.data());
00475     }
00476     static const uint64_t unknown_index_flag= (HA_NOSAME | HA_PACK_KEY |
00477                                                HA_USES_BLOCK_SIZE | 
00478                                                HA_BINARY_PACK_KEY |
00479                                                HA_VAR_LENGTH_PART |
00480                                                HA_NULL_PART_KEY | 
00481                                                HA_KEY_HAS_PART_KEY_SEG |
00482                                                HA_GENERATED_KEY |
00483                                                HA_USES_COMMENT);
00484     if (key_info[i].flags & ~unknown_index_flag)
00485       abort(); // Invalid (unknown) index flag.
00486 
00487     for(unsigned int j=0; j< key_info[i].key_parts; j++)
00488     {
00489       message::Table::Index::IndexPart *idxpart;
00490       const int fieldnr= key_info[i].key_part[j].fieldnr;
00491       int mbmaxlen= 1;
00492 
00493       idxpart= idx->add_index_part();
00494 
00495       idxpart->set_fieldnr(fieldnr);
00496 
00497       if (table_proto.field(fieldnr).type() == message::Table::Field::VARCHAR
00498           || table_proto.field(fieldnr).type() == message::Table::Field::BLOB)
00499       {
00500         uint32_t collation_id;
00501 
00502         if (table_proto.field(fieldnr).string_options().has_collation_id())
00503           collation_id= table_proto.field(fieldnr).string_options().collation_id();
00504         else
00505           collation_id= table_proto.options().collation_id();
00506 
00507         const charset_info_st *cs= get_charset(collation_id);
00508 
00509         mbmaxlen= cs->mbmaxlen;
00510       }
00511 
00512       idxpart->set_compare_length(key_info[i].key_part[j].length / mbmaxlen);
00513     }
00514   }
00515 
00516   if (not table_proto.IsInitialized())
00517   {
00518     my_error(ER_CORRUPT_TABLE_DEFINITION, MYF(0),
00519              table_proto.name().c_str(),
00520              table_proto.InitializationErrorString().c_str());
00521 
00522     return true;
00523   }
00524 
00525   /*
00526     Here we test to see if we can validate the Table Message before we continue. 
00527     We do this by serializing the protobuffer.
00528   */
00529   {
00530     string tmp_string;
00531 
00532     try {
00533       table_proto.SerializeToString(&tmp_string);
00534     }
00535 
00536     catch (...)
00537     {
00538       my_error(ER_CORRUPT_TABLE_DEFINITION, MYF(0),
00539                table_proto.name().c_str(),
00540                table_proto.InitializationErrorString().c_str());
00541 
00542       return true;
00543     }
00544   }
00545 
00546   return false;
00547 }
00548 
00549 /*
00550   Create a table definition proto file and the tables
00551 
00552   SYNOPSIS
00553     rea_create_table()
00554     session     Thread handler
00555     path    Name of file (including database, without .frm)
00556     db      Data base name
00557     table_name    Table name
00558     create_info   create info parameters
00559     create_fields Fields to create
00560     keys    number of keys to create
00561     key_info    Keys to create
00562 
00563   RETURN
00564     0  ok
00565     1  error
00566 */
00567 
00568 bool rea_create_table(Session *session,
00569                       const identifier::Table &identifier,
00570                       message::Table &table_proto,
00571                       HA_CREATE_INFO *create_info,
00572                       List<CreateField> &create_fields,
00573                       uint32_t keys, KeyInfo *key_info)
00574 {
00575   assert(table_proto.has_name());
00576 
00577   if (fill_table_proto(identifier,
00578                        table_proto, create_fields, create_info,
00579                        keys, key_info))
00580   {
00581     return false;
00582   }
00583 
00584   assert(table_proto.name() == identifier.getTableName());
00585 
00586   if (not plugin::StorageEngine::createTable(*session,
00587                                              identifier,
00588                                              table_proto))
00589   {
00590     return false;
00591   }
00592 
00593   return true;
00594 
00595 } /* rea_create_table */
00596 
00597 } /* namespace drizzled */
00598