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/unireg.h>
00020 #include <drizzled/sql_table.h>
00021 #include <drizzled/global_charset_info.h>
00022 #include <drizzled/message/statement_transform.h>
00023 
00024 #include <drizzled/plugin/storage_engine.h>
00025 
00026 #include <drizzled/internal/my_sys.h>
00027 #include <drizzled/typelib.h>
00028 
00029 /* For proto */
00030 #include <string>
00031 #include <fstream>
00032 #include <fcntl.h>
00033 #include <drizzled/message/schema.h>
00034 #include <drizzled/message/table.h>
00035 #include <google/protobuf/io/zero_copy_stream.h>
00036 #include <google/protobuf/io/zero_copy_stream_impl.h>
00037 #include <google/protobuf/message.h>
00038 
00039 #include <drizzled/table_proto.h>
00040 #include <drizzled/charset.h>
00041 
00042 #include <drizzled/function/time/typecast.h>
00043 
00044 using namespace std;
00045 
00046 namespace drizzled {
00047 
00048 static
00049 bool fill_table_proto(identifier::Table::const_reference identifier,
00050                       message::Table &table_proto,
00051                       List<CreateField> &create_fields,
00052                       HA_CREATE_INFO *create_info,
00053                       uint32_t keys,
00054                       KeyInfo *key_info)
00055 {
00056   CreateField *field_arg;
00057   List<CreateField>::iterator it(create_fields.begin());
00058   message::Table::TableOptions *table_options= table_proto.mutable_options();
00059 
00060   if (create_fields.size() > MAX_FIELDS)
00061   {
00062     my_error(ER_TOO_MANY_FIELDS, MYF(0), ER(ER_TOO_MANY_FIELDS));
00063     return true;
00064   }
00065 
00066   assert(strcmp(table_proto.engine().name().c_str(),
00067     create_info->db_type->getName().c_str())==0);
00068 
00069   message::schema::shared_ptr schema_message= plugin::StorageEngine::getSchemaDefinition(identifier);
00070 
00071   if (schema_message and not message::is_replicated(*schema_message))
00072   {
00073     message::set_is_replicated(table_proto, false);
00074   }
00075 
00076   int field_number= 0;
00077   bool use_existing_fields= table_proto.field_size() > 0;
00078   while ((field_arg= it++))
00079   {
00080     message::Table::Field *attribute;
00081 
00082     /* some (one) code path for CREATE TABLE fills the proto
00083        out more than the others, so we already have partially
00084        filled out Field messages */
00085 
00086     if (use_existing_fields)
00087     {
00088       attribute= table_proto.mutable_field(field_number++);
00089     }
00090     else
00091     {
00092       /* Other code paths still have to fill out the proto */
00093       attribute= table_proto.add_field();
00094 
00095       if (field_arg->flags & NOT_NULL_FLAG)
00096       {
00097         attribute->mutable_constraints()->set_is_notnull(true);
00098       }
00099 
00100       if (field_arg->flags & UNSIGNED_FLAG and 
00101           (field_arg->sql_type == DRIZZLE_TYPE_LONGLONG or field_arg->sql_type == DRIZZLE_TYPE_LONG))
00102       {
00103         field_arg->sql_type= DRIZZLE_TYPE_LONGLONG;
00104         attribute->mutable_constraints()->set_is_unsigned(true);
00105       }
00106 
00107       attribute->set_name(field_arg->field_name);
00108     }
00109 
00110     assert(((field_arg->flags & NOT_NULL_FLAG)) == attribute->constraints().is_notnull());
00111     assert(strcmp(attribute->name().c_str(), field_arg->field_name)==0);
00112 
00113 
00114     message::Table::Field::FieldType parser_type= attribute->type();
00115 
00116     if (field_arg->sql_type == DRIZZLE_TYPE_NULL)
00117     {
00118       my_error(ER_CANT_CREATE_TABLE, MYF(ME_BELL+ME_WAITTANG), table_proto.name().c_str(), -1);
00119       return true;
00120     }
00121 
00122     if (field_arg->flags & UNSIGNED_FLAG and 
00123        (field_arg->sql_type == DRIZZLE_TYPE_LONGLONG or field_arg->sql_type == DRIZZLE_TYPE_LONG))
00124     {
00125       message::Table::Field::FieldConstraints *constraints= attribute->mutable_constraints();
00126 
00127       field_arg->sql_type= DRIZZLE_TYPE_LONGLONG;
00128       constraints->set_is_unsigned(true);
00129     }
00130 
00131     attribute->set_type(message::internalFieldTypeToFieldProtoType(field_arg->sql_type));
00132 
00133     switch (attribute->type()) {
00134     case message::Table::Field::BIGINT:
00135     case message::Table::Field::INTEGER:
00136     case message::Table::Field::DATE:
00137     case message::Table::Field::DATETIME:
00138     case message::Table::Field::UUID:
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 (field_arg->comment.length)
00235     {
00236       uint32_t tmp_len;
00237       tmp_len= system_charset_info->cset->charpos(system_charset_info,
00238               field_arg->comment.str,
00239               field_arg->comment.str +
00240               field_arg->comment.length,
00241               COLUMN_COMMENT_MAXLEN);
00242 
00243       if (tmp_len < field_arg->comment.length)
00244       {
00245   my_error(ER_WRONG_STRING_LENGTH, MYF(0),
00246      field_arg->comment.str,"COLUMN COMMENT",
00247      (uint32_t) COLUMN_COMMENT_MAXLEN);
00248   return true;
00249       }
00250 
00251       if (! use_existing_fields)
00252         attribute->set_comment(field_arg->comment.str);
00253 
00254       assert(strcmp(attribute->comment().c_str(), field_arg->comment.str)==0);
00255     }
00256 
00257     if (field_arg->unireg_check == Field::NEXT_NUMBER)
00258     {
00259       message::Table::Field::NumericFieldOptions *field_options;
00260       field_options= attribute->mutable_numeric_options();
00261       field_options->set_is_autoincrement(true);
00262     }
00263 
00264     if (field_arg->unireg_check == Field::TIMESTAMP_DN_FIELD
00265        || field_arg->unireg_check == Field::TIMESTAMP_DNUN_FIELD)
00266     {
00267       message::Table::Field::FieldOptions *field_options;
00268       field_options= attribute->mutable_options();
00269       field_options->set_default_expression("CURRENT_TIMESTAMP");
00270     }
00271 
00272     if (field_arg->unireg_check == Field::TIMESTAMP_UN_FIELD
00273        || field_arg->unireg_check == Field::TIMESTAMP_DNUN_FIELD)
00274     {
00275       message::Table::Field::FieldOptions *field_options;
00276       field_options= attribute->mutable_options();
00277       field_options->set_update_expression("CURRENT_TIMESTAMP");
00278     }
00279 
00280     if (field_arg->def == NULL  && not attribute->constraints().is_notnull())
00281     {
00282       message::Table::Field::FieldOptions *field_options;
00283       field_options= attribute->mutable_options();
00284 
00285       field_options->set_default_null(true);
00286     }
00287     if (field_arg->def)
00288     {
00289       message::Table::Field::FieldOptions *field_options;
00290       field_options= attribute->mutable_options();
00291  
00292       if (field_arg->def->is_null())
00293       {
00294   field_options->set_default_null(true);
00295       }
00296       else
00297       {
00298   String d;
00299   String *default_value= field_arg->def->val_str(&d);
00300 
00301   assert(default_value);
00302 
00303   if ((field_arg->sql_type==DRIZZLE_TYPE_VARCHAR
00304      || field_arg->sql_type==DRIZZLE_TYPE_BLOB)
00305      && ((field_arg->length / field_arg->charset->mbmaxlen)
00306      < default_value->length()))
00307   {
00308     my_error(ER_INVALID_DEFAULT, MYF(0), field_arg->field_name);
00309     return true;
00310   }
00311 
00312         if (field::isDateTime(field_arg->sql_type))
00313         {
00314           type::Time ltime;
00315 
00316           if (field_arg->def->get_date(ltime, TIME_FUZZY_DATE))
00317           {
00318             my_error(ER_INVALID_DATETIME_VALUE, MYF(ME_FATALERROR),
00319                      default_value->c_str());
00320             return true;
00321           }
00322 
00323           /* We now do the casting down to the appropriate type.
00324 
00325              Yes, this implicit casting is balls.
00326              It was previously done on reading the proto back in,
00327              but we really shouldn't store the bogus things in the proto,
00328              and instead do the casting behaviour here.
00329 
00330              the timestamp errors are taken care of elsewhere.
00331           */
00332 
00333           if (field_arg->sql_type == DRIZZLE_TYPE_DATETIME)
00334           {
00335             Item *typecast= new Item_datetime_typecast(field_arg->def);
00336             typecast->quick_fix_field();
00337             typecast->val_str(default_value);
00338           }
00339           else if (field_arg->sql_type == DRIZZLE_TYPE_DATE)
00340           {
00341             Item *typecast= new Item_date_typecast(field_arg->def);
00342             typecast->quick_fix_field();
00343             typecast->val_str(default_value);
00344           }
00345         }
00346 
00347   if ((field_arg->sql_type==DRIZZLE_TYPE_VARCHAR
00348       && field_arg->charset==&my_charset_bin)
00349      || (field_arg->sql_type==DRIZZLE_TYPE_BLOB
00350       && field_arg->charset==&my_charset_bin))
00351   {
00352     string bin_default;
00353     bin_default.assign(default_value->c_ptr(),
00354            default_value->length());
00355     field_options->set_default_bin_value(bin_default);
00356   }
00357   else
00358   {
00359     field_options->set_default_value(default_value->c_ptr());
00360   }
00361       }
00362     }
00363 
00364     assert(field_arg->unireg_check == Field::NONE
00365      || field_arg->unireg_check == Field::NEXT_NUMBER
00366      || field_arg->unireg_check == Field::TIMESTAMP_DN_FIELD
00367      || field_arg->unireg_check == Field::TIMESTAMP_UN_FIELD
00368      || field_arg->unireg_check == Field::TIMESTAMP_DNUN_FIELD);
00369 
00370   }
00371 
00372   assert(! use_existing_fields || (field_number == table_proto.field_size()));
00373 
00374   if (create_info->table_options & HA_OPTION_PACK_RECORD)
00375     table_options->set_pack_record(true);
00376 
00377   if (table_options->has_comment() && table_options->comment().length() == 0)
00378     table_options->clear_comment();
00379 
00380   if (table_options->has_comment())
00381   {
00382     uint32_t tmp_len;
00383     tmp_len= system_charset_info->cset->charpos(system_charset_info,
00384                                                 table_options->comment().c_str(),
00385                                                 table_options->comment().c_str() +
00386                                                 table_options->comment().length(),
00387                                                 TABLE_COMMENT_MAXLEN);
00388 
00389     if (tmp_len < table_options->comment().length())
00390     {
00391       my_error(ER_WRONG_STRING_LENGTH, MYF(0),
00392                table_options->comment().c_str(),"Table COMMENT",
00393                (uint32_t) TABLE_COMMENT_MAXLEN);
00394       return true;
00395     }
00396   }
00397 
00398   if (create_info->default_table_charset)
00399   {
00400     table_options->set_collation_id(create_info->default_table_charset->number);
00401     table_options->set_collation(create_info->default_table_charset->name);
00402   }
00403 
00404   if (create_info->used_fields & HA_CREATE_USED_AUTO)
00405     table_options->set_has_user_set_auto_increment_value(true);
00406   else
00407     table_options->set_has_user_set_auto_increment_value(false);
00408 
00409   if (create_info->auto_increment_value)
00410     table_options->set_auto_increment_value(create_info->auto_increment_value);
00411 
00412   for (uint32_t i= 0; i < keys; i++)
00413   {
00414     message::Table::Index *idx;
00415 
00416     idx= table_proto.add_indexes();
00417 
00418     assert(test(key_info[i].flags & HA_USES_COMMENT) ==
00419            (key_info[i].comment.length > 0));
00420 
00421     idx->set_name(key_info[i].name);
00422 
00423     idx->set_key_length(key_info[i].key_length);
00424 
00425     if (is_primary_key_name(key_info[i].name))
00426       idx->set_is_primary(true);
00427     else
00428       idx->set_is_primary(false);
00429 
00430     switch(key_info[i].algorithm)
00431     {
00432     case HA_KEY_ALG_HASH:
00433       idx->set_type(message::Table::Index::HASH);
00434       break;
00435 
00436     case HA_KEY_ALG_BTREE:
00437       idx->set_type(message::Table::Index::BTREE);
00438       break;
00439 
00440     case HA_KEY_ALG_UNDEF:
00441       idx->set_type(message::Table::Index::UNKNOWN_INDEX);
00442       break;
00443 
00444     default:
00445       abort(); /* Somebody's brain broke. haven't added index type to proto */
00446     }
00447 
00448     if (key_info[i].flags & HA_NOSAME)
00449       idx->set_is_unique(true);
00450     else
00451       idx->set_is_unique(false);
00452 
00453     message::Table::Index::Options *index_options= idx->mutable_options();
00454 
00455     if (key_info[i].flags & HA_USES_BLOCK_SIZE)
00456       index_options->set_key_block_size(key_info[i].block_size);
00457 
00458     if (key_info[i].flags & HA_PACK_KEY)
00459       index_options->set_pack_key(true);
00460 
00461     if (key_info[i].flags & HA_BINARY_PACK_KEY)
00462       index_options->set_binary_pack_key(true);
00463 
00464     if (key_info[i].flags & HA_VAR_LENGTH_PART)
00465       index_options->set_var_length_key(true);
00466 
00467     if (key_info[i].flags & HA_NULL_PART_KEY)
00468       index_options->set_null_part_key(true);
00469 
00470     if (key_info[i].flags & HA_KEY_HAS_PART_KEY_SEG)
00471       index_options->set_has_partial_segments(true);
00472 
00473     if (key_info[i].flags & HA_GENERATED_KEY)
00474       index_options->set_auto_generated_key(true);
00475 
00476     if (key_info[i].flags & HA_USES_COMMENT)
00477     {
00478       uint32_t tmp_len;
00479       tmp_len= system_charset_info->cset->charpos(system_charset_info,
00480               key_info[i].comment.str,
00481               key_info[i].comment.str +
00482               key_info[i].comment.length,
00483               TABLE_COMMENT_MAXLEN);
00484 
00485       if (tmp_len < key_info[i].comment.length)
00486       {
00487   my_error(ER_WRONG_STRING_LENGTH, MYF(0),
00488      key_info[i].comment.str,"Index COMMENT",
00489      (uint32_t) TABLE_COMMENT_MAXLEN);
00490   return true;
00491       }
00492 
00493       idx->set_comment(key_info[i].comment.str);
00494     }
00495     static const uint64_t unknown_index_flag= (HA_NOSAME | HA_PACK_KEY |
00496                                                HA_USES_BLOCK_SIZE | 
00497                                                HA_BINARY_PACK_KEY |
00498                                                HA_VAR_LENGTH_PART |
00499                                                HA_NULL_PART_KEY | 
00500                                                HA_KEY_HAS_PART_KEY_SEG |
00501                                                HA_GENERATED_KEY |
00502                                                HA_USES_COMMENT);
00503     if (key_info[i].flags & ~unknown_index_flag)
00504       abort(); // Invalid (unknown) index flag.
00505 
00506     for(unsigned int j=0; j< key_info[i].key_parts; j++)
00507     {
00508       message::Table::Index::IndexPart *idxpart;
00509       const int fieldnr= key_info[i].key_part[j].fieldnr;
00510       int mbmaxlen= 1;
00511 
00512       idxpart= idx->add_index_part();
00513 
00514       idxpart->set_fieldnr(fieldnr);
00515 
00516       if (table_proto.field(fieldnr).type() == message::Table::Field::VARCHAR
00517           || table_proto.field(fieldnr).type() == message::Table::Field::BLOB)
00518       {
00519         uint32_t collation_id;
00520 
00521         if (table_proto.field(fieldnr).string_options().has_collation_id())
00522           collation_id= table_proto.field(fieldnr).string_options().collation_id();
00523         else
00524           collation_id= table_proto.options().collation_id();
00525 
00526         const CHARSET_INFO *cs= get_charset(collation_id);
00527 
00528         mbmaxlen= cs->mbmaxlen;
00529       }
00530 
00531       idxpart->set_compare_length(key_info[i].key_part[j].length / mbmaxlen);
00532     }
00533   }
00534 
00535   if (not table_proto.IsInitialized())
00536   {
00537     my_error(ER_CORRUPT_TABLE_DEFINITION, MYF(0),
00538              table_proto.name().c_str(),
00539              table_proto.InitializationErrorString().c_str());
00540 
00541     return true;
00542   }
00543 
00544   /*
00545     Here we test to see if we can validate the Table Message before we continue. 
00546     We do this by serializing the protobuffer.
00547   */
00548   {
00549     string tmp_string;
00550 
00551     try {
00552       table_proto.SerializeToString(&tmp_string);
00553     }
00554 
00555     catch (...)
00556     {
00557       my_error(ER_CORRUPT_TABLE_DEFINITION, MYF(0),
00558                table_proto.name().c_str(),
00559                table_proto.InitializationErrorString().c_str());
00560 
00561       return true;
00562     }
00563   }
00564 
00565   return false;
00566 }
00567 
00568 /*
00569   Create a table definition proto file and the tables
00570 
00571   SYNOPSIS
00572     rea_create_table()
00573     session     Thread handler
00574     path    Name of file (including database, without .frm)
00575     db      Data base name
00576     table_name    Table name
00577     create_info   create info parameters
00578     create_fields Fields to create
00579     keys    number of keys to create
00580     key_info    Keys to create
00581 
00582   RETURN
00583     0  ok
00584     1  error
00585 */
00586 
00587 bool rea_create_table(Session *session,
00588                       const identifier::Table &identifier,
00589                       message::Table &table_proto,
00590                       HA_CREATE_INFO *create_info,
00591                       List<CreateField> &create_fields,
00592                       uint32_t keys, KeyInfo *key_info)
00593 {
00594   assert(table_proto.has_name());
00595 
00596   if (fill_table_proto(identifier,
00597                        table_proto, create_fields, create_info,
00598                        keys, key_info))
00599   {
00600     return false;
00601   }
00602 
00603   assert(table_proto.name() == identifier.getTableName());
00604 
00605   if (not plugin::StorageEngine::createTable(*session,
00606                                              identifier,
00607                                              table_proto))
00608   {
00609     return false;
00610   }
00611 
00612   return true;
00613 
00614 } /* rea_create_table */
00615 
00616 } /* namespace drizzled */
00617