Drizzled Public API Documentation

handler0alter.cc
Go to the documentation of this file.
00001 /*****************************************************************************
00002 
00003 Copyright (C) 2005, 2010, Innobase Oy. All Rights Reserved.
00004 
00005 This program is free software; you can redistribute it and/or modify it under
00006 the terms of the GNU General Public License as published by the Free Software
00007 Foundation; version 2 of the License.
00008 
00009 This program is distributed in the hope that it will be useful, but WITHOUT
00010 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00011 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
00012 
00013 You should have received a copy of the GNU General Public License along with
00014 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
00015 St, Fifth Floor, Boston, MA 02110-1301 USA
00016 
00017 *****************************************************************************/
00018 
00019 /**************************************************/
00024 #include <config.h>
00025 #include <drizzled/error.h>
00026 #include <drizzled/charset.h>
00027 #include <drizzled/field.h>
00028 #include <drizzled/table.h>
00029 #include <drizzled/field/varstring.h>
00030 #include <drizzled/internal/my_sys.h>
00031 
00032 #include "log0log.h"
00033 #include "row0merge.h"
00034 #include "srv0srv.h"
00035 #include "trx0trx.h"
00036 #include "trx0roll.h"
00037 #include "ha_prototypes.h"
00038 #include "handler0alter.h"
00039 
00040 #include "ha_innodb.h"
00041 #include "handler0vars.h"
00042 
00043 /*************************************************************/
00046 static
00047 void
00048 innobase_col_to_mysql(
00049 /*==================*/
00050   const dict_col_t* col,  
00051   const unsigned char*  data, 
00052   ulint     len,  
00053   Field*      field)  
00054 {
00055   unsigned char*  ptr;
00056   unsigned char*  dest  = field->ptr;
00057   ulint flen  = field->pack_length();
00058 
00059   switch (col->mtype) {
00060   case DATA_INT:
00061     ut_ad(len == flen);
00062 
00063     /* Convert integer data from Innobase to little-endian
00064     format, sign bit restored to normal */
00065 
00066     for (ptr = dest + len; ptr != dest; ) {
00067       *--ptr = *data++;
00068     }
00069 
00070     if (!(field->flags & UNSIGNED_FLAG)) {
00071       ((byte*) dest)[len - 1] ^= 0x80;
00072     }
00073 
00074     break;
00075 
00076   case DATA_VARCHAR:
00077   case DATA_VARMYSQL:
00078   case DATA_BINARY:
00079     field->reset();
00080 
00081     if (field->type() == DRIZZLE_TYPE_VARCHAR) {
00082       /* This is a >= 5.0.3 type true VARCHAR. Store the
00083       length of the data to the first byte or the first
00084       two bytes of dest. */
00085 
00086       dest = row_mysql_store_true_var_len(
00087         dest, len, flen - field->key_length());
00088     }
00089 
00090     /* Copy the actual data */
00091     memcpy(dest, data, len);
00092     break;
00093 
00094   case DATA_BLOB:
00095     /* Store a pointer to the BLOB buffer to dest: the BLOB was
00096     already copied to the buffer in row_sel_store_mysql_rec */
00097 
00098     row_mysql_store_blob_ref(dest, flen, data, len);
00099     break;
00100 
00101 #ifdef UNIV_DEBUG
00102   case DATA_MYSQL:
00103     ut_ad(flen >= len);
00104     ut_ad(DATA_MBMAXLEN(col->mbminmaxlen)
00105           >= DATA_MBMINLEN(col->mbminmaxlen));
00106     ut_ad(DATA_MBMAXLEN(col->mbminmaxlen)
00107           > DATA_MBMINLEN(col->mbminmaxlen) || flen == len);
00108     memcpy(dest, data, len);
00109     break;
00110 
00111   default:
00112   case DATA_SYS_CHILD:
00113   case DATA_SYS:
00114     /* These column types should never be shipped to MySQL. */
00115     ut_ad(0);
00116 
00117   case DATA_CHAR:
00118   case DATA_FIXBINARY:
00119   case DATA_FLOAT:
00120   case DATA_DOUBLE:
00121   case DATA_DECIMAL:
00122     /* Above are the valid column types for MySQL data. */
00123     ut_ad(flen == len);
00124 #else /* UNIV_DEBUG */
00125   default:
00126 #endif /* UNIV_DEBUG */
00127     memcpy(dest, data, len);
00128   }
00129 }
00130 
00131 /*************************************************************/
00133 UNIV_INTERN
00134 void
00135 innobase_rec_to_mysql(
00136 /*==================*/
00137   Table*      table,    
00138   const rec_t*    rec,    
00139   const dict_index_t* index,    
00140   const ulint*    offsets)  
00142 {
00143   uint  n_fields  = table->getShare()->sizeFields();
00144   uint  i;
00145 
00146   ut_ad(n_fields == dict_table_get_n_user_cols(index->table));
00147 
00148   for (i = 0; i < n_fields; i++) {
00149     Field*    field = table->getField(i);
00150     ulint   ipos;
00151     ulint   ilen;
00152     const unsigned char*  ifield;
00153 
00154     field->reset();
00155 
00156     ipos = dict_index_get_nth_col_pos(index, i);
00157 
00158     if (UNIV_UNLIKELY(ipos == ULINT_UNDEFINED)) {
00159 null_field:
00160       field->set_null();
00161       continue;
00162     }
00163 
00164     ifield = rec_get_nth_field(rec, offsets, ipos, &ilen);
00165 
00166     /* Assign the NULL flag */
00167     if (ilen == UNIV_SQL_NULL) {
00168       ut_ad(field->real_maybe_null());
00169       goto null_field;
00170     }
00171 
00172     field->set_notnull();
00173 
00174     innobase_col_to_mysql(
00175       dict_field_get_col(
00176         dict_index_get_nth_field(index, ipos)),
00177       ifield, ilen, field);
00178   }
00179 }
00180 
00181 /*************************************************************/
00183 UNIV_INTERN
00184 void
00185 innobase_rec_reset(
00186 /*===============*/
00187   Table*      table)    
00188 {
00189   uint  n_fields  = table->getShare()->sizeFields();
00190   uint  i;
00191 
00192   for (i = 0; i < n_fields; i++) {
00193     table->getField(i)->set_default();
00194   }
00195 }
00196 
00197 #if 0 // This is a part of the fast index code.
00198 /******************************************************************/
00200 static
00201 void
00202 innobase_convert_tablename(
00203 /*=======================*/
00204   char* s)  
00205 {
00206 
00207   char* slash = strchr(s, '/');
00208 
00209   if (slash) {
00210     char* t;
00211     /* Temporarily replace the '/' with NUL. */
00212     *slash = 0;
00213     strncpy(s, s, slash - s + 1);
00214 
00215     t = s + strlen(s);
00216     ut_ad(slash >= t);
00217     /* Append a  '.' after the database name. */
00218     *t++ = '.';
00219     slash++;
00220     /* Convert the table name. */
00221     strncpy(t, slash, slash - t + strlen(slash));
00222   }
00223 }
00224 
00225 
00226 /*******************************************************************/
00229 static
00230 int
00231 innobase_check_index_keys(
00232 /*======================*/
00233   const KeyInfo*  key_info, 
00234   ulint   num_of_keys,  
00236   const dict_table_t* table)  
00237 {
00238   ulint   key_num;
00239 
00240   ut_ad(key_info);
00241   ut_ad(num_of_keys);
00242 
00243   for (key_num = 0; key_num < num_of_keys; key_num++) {
00244     const KeyInfo&  key = key_info[key_num];
00245 
00246     /* Check that the same index name does not appear
00247     twice in indexes to be created. */
00248 
00249     for (ulint i = 0; i < key_num; i++) {
00250       const KeyInfo&  key2 = key_info[i];
00251 
00252       if (0 == strcmp(key.name, key2.name)) {
00253         my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0),
00254            key.name);
00255 
00256         return(ER_WRONG_NAME_FOR_INDEX);
00257       }
00258     }
00259 
00260     /* Check that the same index name does not already exist. */
00261 
00262     for (const dict_index_t* index
00263            = dict_table_get_first_index(table);
00264          index; index = dict_table_get_next_index(index)) {
00265 
00266       if (0 == strcmp(key.name, index->name)) {
00267         my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0),
00268            key.name);
00269 
00270         return(ER_WRONG_NAME_FOR_INDEX);
00271       }
00272     }
00273 
00274     /* Check that MySQL does not try to create a column
00275     prefix index field on an inappropriate data type and
00276     that the same column does not appear twice in the index. */
00277 
00278     for (ulint i = 0; i < key.key_parts; i++) {
00279       const KeyPartInfo&  key_part1
00280         = key.key_part[i];
00281       const Field*    field
00282         = key_part1.field;
00283       ibool     is_unsigned;
00284 
00285       switch (get_innobase_type_from_mysql_type(
00286           &is_unsigned, field)) {
00287       default:
00288         break;
00289       case DATA_INT:
00290       case DATA_FLOAT:
00291       case DATA_DOUBLE:
00292       case DATA_DECIMAL:
00293         if (field->type() == DRIZZLE_TYPE_VARCHAR) {
00294           if (key_part1.length
00295               >= field->pack_length()
00296               - ((Field_varstring*) field)
00297               ->length_bytes) {
00298             break;
00299           }
00300         } else {
00301           if (key_part1.length
00302               >= field->pack_length()) {
00303             break;
00304           }
00305         }
00306 
00307         my_error(ER_WRONG_KEY_COLUMN, MYF(0),
00308            field->field_name);
00309         return(ER_WRONG_KEY_COLUMN);
00310       }
00311 
00312       for (ulint j = 0; j < i; j++) {
00313         const KeyPartInfo&  key_part2
00314           = key.key_part[j];
00315 
00316         if (strcmp(key_part1.field->field_name,
00317              key_part2.field->field_name)) {
00318           continue;
00319         }
00320 
00321         my_error(ER_WRONG_KEY_COLUMN, MYF(0),
00322            key_part1.field->field_name);
00323         return(ER_WRONG_KEY_COLUMN);
00324       }
00325     }
00326   }
00327 
00328   return(0);
00329 }
00330 
00331 /*******************************************************************/
00333 static
00334 void
00335 innobase_create_index_field_def(
00336 /*============================*/
00337   KeyPartInfo*    key_part, 
00338   mem_heap_t*   heap,   
00339   merge_index_field_t*  index_field)  
00341 {
00342   Field*    field;
00343   ibool   is_unsigned;
00344   ulint   col_type;
00345 
00346   ut_ad(key_part);
00347   ut_ad(index_field);
00348 
00349   field = key_part->field;
00350   ut_a(field);
00351 
00352   col_type = get_innobase_type_from_mysql_type(&is_unsigned, field);
00353 
00354   if (DATA_BLOB == col_type
00355       || (key_part->length < field->pack_length()
00356     && field->type() != DRIZZLE_TYPE_VARCHAR)
00357       || (field->type() == DRIZZLE_TYPE_VARCHAR
00358     && key_part->length < field->pack_length()
00359       - ((Field_varstring*)field)->length_bytes)) {
00360 
00361     index_field->prefix_len = key_part->length;
00362   } else {
00363     index_field->prefix_len = 0;
00364   }
00365 
00366   index_field->field_name = mem_heap_strdup(heap, field->field_name);
00367 
00368   return;
00369 }
00370 
00371 /*******************************************************************/
00373 static
00374 void
00375 innobase_create_index_def(
00376 /*======================*/
00377   KeyInfo*      key,    
00378   bool      new_primary,  
00381   bool      key_primary,  
00383   merge_index_def_t*  index,    
00384   mem_heap_t*   heap)   
00386 {
00387   ulint i;
00388   ulint len;
00389   ulint n_fields = key->key_parts;
00390   char* index_name;
00391 
00392   index->fields = (merge_index_field_t*) mem_heap_alloc(
00393     heap, n_fields * sizeof *index->fields);
00394 
00395   index->ind_type = 0;
00396   index->n_fields = n_fields;
00397   len = strlen(key->name) + 1;
00398   index->name = index_name = (char*) mem_heap_alloc(heap,
00399                 len + !new_primary);
00400 
00401   if (UNIV_LIKELY(!new_primary)) {
00402     *index_name++ = TEMP_INDEX_PREFIX;
00403   }
00404 
00405   memcpy(index_name, key->name, len);
00406 
00407   if (key->flags & HA_NOSAME) {
00408     index->ind_type |= DICT_UNIQUE;
00409   }
00410 
00411   if (key_primary) {
00412     index->ind_type |= DICT_CLUSTERED;
00413   }
00414 
00415   for (i = 0; i < n_fields; i++) {
00416     innobase_create_index_field_def(&key->key_part[i], heap,
00417             &index->fields[i]);
00418   }
00419 
00420   return;
00421 }
00422 
00423 /*******************************************************************/
00425 static
00426 void
00427 innobase_copy_index_field_def(
00428 /*==========================*/
00429   const dict_field_t* field,    
00430   merge_index_field_t*  index_field)  
00431 {
00432   assert(field != NULL);
00433   assert(index_field != NULL);
00434 
00435   index_field->field_name = field->name;
00436   index_field->prefix_len = field->prefix_len;
00437 
00438   return;
00439 }
00440 
00441 /*******************************************************************/
00443 static
00444 void
00445 innobase_copy_index_def(
00446 /*====================*/
00447   const dict_index_t* index,  
00448   merge_index_def_t*  new_index,
00449   mem_heap_t*   heap) 
00450 {
00451   ulint n_fields;
00452   ulint i;
00453 
00454   /* Note that we take only those fields that user defined to be
00455   in the index.  In the internal representation more colums were
00456   added and those colums are not copied .*/
00457 
00458   n_fields = index->n_user_defined_cols;
00459 
00460   new_index->fields = (merge_index_field_t*) mem_heap_alloc(
00461     heap, n_fields * sizeof *new_index->fields);
00462 
00463   /* When adding a PRIMARY KEY, we may convert a previous
00464   clustered index to a secondary index (UNIQUE NOT NULL). */
00465   new_index->ind_type = index->type & ~DICT_CLUSTERED;
00466   new_index->n_fields = n_fields;
00467   new_index->name = index->name;
00468 
00469   for (i = 0; i < n_fields; i++) {
00470     innobase_copy_index_field_def(&index->fields[i],
00471                 &new_index->fields[i]);
00472   }
00473 
00474   return;
00475 }
00476 
00477 /*******************************************************************/
00495 static
00496 merge_index_def_t*
00497 innobase_create_key_def(
00498 /*====================*/
00499   trx_t*    trx,    
00500   const dict_table_t*table,   
00501   mem_heap_t* heap,   
00503   KeyInfo*    key_info, 
00504   ulint&    n_keys)   
00506 {
00507   ulint     i = 0;
00508   merge_index_def_t*  indexdef;
00509   merge_index_def_t*  indexdefs;
00510   bool      new_primary;
00511 
00512   indexdef = indexdefs = (merge_index_def_t*)
00513     mem_heap_alloc(heap, sizeof *indexdef
00514              * (n_keys + UT_LIST_GET_LEN(table->indexes)));
00515 
00516   /* If there is a primary key, it is always the first index
00517   defined for the table. */
00518 
00519   new_primary = !system_charset_info->strcasecmp(key_info->name, "PRIMARY");
00520 
00521   /* If there is a UNIQUE INDEX consisting entirely of NOT NULL
00522   columns and if the index does not contain column prefix(es)
00523   (only prefix/part of the column is indexed), MySQL will treat the
00524   index as a PRIMARY KEY unless the table already has one. */
00525 
00526   if (!new_primary && (key_info->flags & HA_NOSAME)
00527       && (!(key_info->flags & HA_KEY_HAS_PART_KEY_SEG))
00528       && row_table_got_default_clust_index(table)) {
00529     uint    key_part = key_info->key_parts;
00530 
00531     new_primary = TRUE;
00532 
00533     while (key_part--) {
00534       if (key_info->key_part[key_part].null_bit == 0) {
00535         new_primary = FALSE;
00536         break;
00537       }
00538     }
00539   }
00540 
00541   if (new_primary) {
00542     const dict_index_t* index;
00543 
00544     /* Create the PRIMARY key index definition */
00545     innobase_create_index_def(&key_info[i++], TRUE, TRUE,
00546             indexdef++, heap);
00547 
00548     row_mysql_lock_data_dictionary(trx);
00549 
00550     index = dict_table_get_first_index(table);
00551 
00552     /* Copy the index definitions of the old table.  Skip
00553     the old clustered index if it is a generated clustered
00554     index or a PRIMARY KEY.  If the clustered index is a
00555     UNIQUE INDEX, it must be converted to a secondary index. */
00556 
00557     if (dict_index_get_nth_col(index, 0)->mtype == DATA_SYS
00558         || !system_charset_info->strcasecmp(index->name, "PRIMARY")) 
00559     {
00560       index = dict_table_get_next_index(index);
00561     }
00562 
00563     while (index) {
00564       innobase_copy_index_def(index, indexdef++, heap);
00565       index = dict_table_get_next_index(index);
00566     }
00567 
00568     row_mysql_unlock_data_dictionary(trx);
00569   }
00570 
00571   /* Create definitions for added secondary indexes. */
00572 
00573   while (i < n_keys) {
00574     innobase_create_index_def(&key_info[i++], new_primary, FALSE,
00575             indexdef++, heap);
00576   }
00577 
00578   n_keys = indexdef - indexdefs;
00579 
00580   return(indexdefs);
00581 }
00582 
00583 /*******************************************************************/
00586 static
00587 char*
00588 innobase_create_temporary_tablename(
00589 /*================================*/
00590   mem_heap_t* heap,   
00591   char    id,   
00592   const char*     table_name) 
00593 {
00594   char*     name;
00595   ulint     len;
00596   static const char suffix[] = "@0023 "; /* "# " */
00597 
00598   len = strlen(table_name);
00599 
00600   name = (char*) mem_heap_alloc(heap, len + sizeof suffix);
00601   memcpy(name, table_name, len);
00602   memcpy(name + len, suffix, sizeof suffix);
00603   name[len + (sizeof suffix - 2)] = id;
00604 
00605   return(name);
00606 }
00607 
00608 
00609 /*******************************************************************/
00612 UNIV_INTERN
00613 int
00614 ha_innobase::add_index(
00615 /*===================*/
00616                        Session *session,
00617   Table*  i_table,  
00618   KeyInfo*  key_info, 
00619   uint  num_of_keys)  
00620 {
00621   dict_index_t**  index;    
00622   dict_table_t* innodb_table; 
00623   dict_table_t* indexed_table;  
00624   merge_index_def_t* index_defs;  
00625   mem_heap_t*     heap;   
00626   trx_t*    trx;    
00627   ulint   num_of_idx;
00628   ulint   num_created = 0;
00629   ibool   dict_locked = FALSE;
00630   ulint   new_primary;
00631   int   error;
00632 
00633   ut_a(i_table);
00634   ut_a(key_info);
00635   ut_a(num_of_keys);
00636 
00637   if (srv_created_new_raw || srv_force_recovery) {
00638     return(HA_ERR_WRONG_COMMAND);
00639   }
00640 
00641   update_session(session);
00642 
00643   heap = mem_heap_create(1024);
00644 
00645   /* In case MySQL calls this in the middle of a SELECT query, release
00646   possible adaptive hash latch to avoid deadlocks of threads. */
00647   trx_search_latch_release_if_reserved(prebuilt->trx);
00648   trx_start_if_not_started(prebuilt->trx);
00649 
00650   /* Create a background transaction for the operations on
00651   the data dictionary tables. */
00652   trx = innobase_trx_allocate(user_session);
00653   trx_start_if_not_started(trx);
00654 
00655   innodb_table = indexed_table
00656     = dict_table_get(prebuilt->table->name, FALSE);
00657 
00658   if (UNIV_UNLIKELY(!innodb_table)) {
00659     error = HA_ERR_NO_SUCH_TABLE;
00660     goto err_exit;
00661   }
00662 
00663   /* Check if the index name is reserved. */
00664   if (innobase_index_name_is_reserved(trx, key_info, num_of_keys)) {
00665     error = -1;
00666   } else {
00667     /* Check that index keys are sensible */
00668     error = innobase_check_index_keys(key_info, num_of_keys,
00669               innodb_table);
00670   }
00671 
00672   if (UNIV_UNLIKELY(error)) {
00673 err_exit:
00674     mem_heap_free(heap);
00675     trx_general_rollback_for_mysql(trx, NULL);
00676     trx_free_for_mysql(trx);
00677     trx_commit_for_mysql(prebuilt->trx);
00678     return(error);
00679   }
00680 
00681   /* Create table containing all indexes to be built in this
00682   alter table add index so that they are in the correct order
00683   in the table. */
00684 
00685   num_of_idx = num_of_keys;
00686 
00687   index_defs = innobase_create_key_def(
00688     trx, innodb_table, heap, key_info, num_of_idx);
00689 
00690   new_primary = DICT_CLUSTERED & index_defs[0].ind_type;
00691 
00692   /* Allocate memory for dictionary index definitions */
00693 
00694   index = (dict_index_t**) mem_heap_alloc(
00695     heap, num_of_idx * sizeof *index);
00696 
00697   /* Flag this transaction as a dictionary operation, so that
00698   the data dictionary will be locked in crash recovery. */
00699   trx_set_dict_operation(trx, TRX_DICT_OP_INDEX);
00700 
00701   /* Acquire a lock on the table before creating any indexes. */
00702   error = row_merge_lock_table(prebuilt->trx, innodb_table,
00703              new_primary ? LOCK_X : LOCK_S);
00704 
00705   if (UNIV_UNLIKELY(error != DB_SUCCESS)) {
00706 
00707     goto error_handling;
00708   }
00709 
00710   /* Latch the InnoDB data dictionary exclusively so that no deadlocks
00711   or lock waits can happen in it during an index create operation. */
00712 
00713   row_mysql_lock_data_dictionary(trx);
00714   dict_locked = TRUE;
00715 
00716   ut_d(dict_table_check_for_dup_indexes(innodb_table, FALSE));
00717 
00718   /* If a new primary key is defined for the table we need
00719   to drop the original table and rebuild all indexes. */
00720 
00721   if (UNIV_UNLIKELY(new_primary)) {
00722     /* This transaction should be the only one
00723     operating on the table. */
00724     ut_a(innodb_table->n_mysql_handles_opened == 1);
00725 
00726     char* new_table_name = innobase_create_temporary_tablename(
00727       heap, '1', innodb_table->name);
00728 
00729     /* Clone the table. */
00730     trx_set_dict_operation(trx, TRX_DICT_OP_TABLE);
00731     indexed_table = row_merge_create_temporary_table(
00732       new_table_name, index_defs, innodb_table, trx);
00733 
00734     if (!indexed_table) {
00735 
00736       switch (trx->error_state) {
00737       case DB_TABLESPACE_ALREADY_EXISTS:
00738       case DB_DUPLICATE_KEY:
00739         innobase_convert_tablename(new_table_name);
00740         my_error(HA_ERR_TABLE_EXIST, MYF(0),
00741            new_table_name);
00742         error = HA_ERR_TABLE_EXIST;
00743         break;
00744       default:
00745         error = convert_error_code_to_mysql(
00746           trx->error_state, innodb_table->flags,
00747           user_session);
00748       }
00749 
00750       ut_d(dict_table_check_for_dup_indexes(innodb_table,
00751                     FALSE));
00752       row_mysql_unlock_data_dictionary(trx);
00753       goto err_exit;
00754     }
00755 
00756     trx->table_id = indexed_table->id;
00757   }
00758 
00759   /* Create the indexes in SYS_INDEXES and load into dictionary. */
00760 
00761   for (ulint i = 0; i < num_of_idx; i++) {
00762 
00763     index[i] = row_merge_create_index(trx, indexed_table,
00764               &index_defs[i]);
00765 
00766     if (!index[i]) {
00767       error = trx->error_state;
00768       goto error_handling;
00769     }
00770 
00771     num_created++;
00772   }
00773 
00774   ut_ad(error == DB_SUCCESS);
00775 
00776   /* We will need to rebuild index translation table. Set
00777   valid index entry count in the translation table to zero */
00778   share->idx_trans_tbl.index_count = 0;
00779 
00780   /* Commit the data dictionary transaction in order to release
00781   the table locks on the system tables.  This means that if
00782   MySQL crashes while creating a new primary key inside
00783   row_merge_build_indexes(), indexed_table will not be dropped
00784   by trx_rollback_active().  It will have to be recovered or
00785   dropped by the database administrator. */
00786   trx_commit_for_mysql(trx);
00787 
00788   row_mysql_unlock_data_dictionary(trx);
00789   dict_locked = FALSE;
00790 
00791   ut_a(trx->n_active_thrs == 0);
00792   ut_a(UT_LIST_GET_LEN(trx->signals) == 0);
00793 
00794   if (UNIV_UNLIKELY(new_primary)) {
00795     /* A primary key is to be built.  Acquire an exclusive
00796     table lock also on the table that is being created. */
00797     ut_ad(indexed_table != innodb_table);
00798 
00799     error = row_merge_lock_table(prebuilt->trx, indexed_table,
00800                LOCK_X);
00801 
00802     if (UNIV_UNLIKELY(error != DB_SUCCESS)) {
00803 
00804       goto error_handling;
00805     }
00806   }
00807 
00808   /* Read the clustered index of the table and build indexes
00809   based on this information using temporary files and merge sort. */
00810   error = row_merge_build_indexes(prebuilt->trx,
00811           innodb_table, indexed_table,
00812           index, num_of_idx, i_table);
00813 
00814 error_handling:
00815 
00816   /* After an error, remove all those index definitions from the
00817   dictionary which were defined. */
00818 
00819   switch (error) {
00820     const char* old_name;
00821     char*   tmp_name;
00822   case DB_SUCCESS:
00823     ut_a(!dict_locked);
00824     row_mysql_lock_data_dictionary(trx);
00825     dict_locked = TRUE;
00826 
00827     ut_d(dict_table_check_for_dup_indexes(prebuilt->table, TRUE));
00828 
00829     if (!new_primary) {
00830       error = row_merge_rename_indexes(trx, indexed_table);
00831 
00832       if (error != DB_SUCCESS) {
00833         row_merge_drop_indexes(trx, indexed_table,
00834                    index, num_created);
00835       }
00836 
00837       goto convert_error;
00838     }
00839 
00840     /* If a new primary key was defined for the table and
00841     there was no error at this point, we can now rename
00842     the old table as a temporary table, rename the new
00843     temporary table as the old table and drop the old table. */
00844     old_name = innodb_table->name;
00845     tmp_name = innobase_create_temporary_tablename(heap, '2',
00846                      old_name);
00847 
00848     error = row_merge_rename_tables(innodb_table, indexed_table,
00849             tmp_name, trx);
00850 
00851     if (error != DB_SUCCESS) {
00852 
00853       row_merge_drop_table(trx, indexed_table);
00854 
00855       switch (error) {
00856       case DB_TABLESPACE_ALREADY_EXISTS:
00857       case DB_DUPLICATE_KEY:
00858         innobase_convert_tablename(tmp_name);
00859         my_error(HA_ERR_TABLE_EXIST, MYF(0), tmp_name);
00860         error = HA_ERR_TABLE_EXIST;
00861         break;
00862       default:
00863         goto convert_error;
00864       }
00865       break;
00866     }
00867 
00868     trx_commit_for_mysql(prebuilt->trx);
00869     row_prebuilt_free(prebuilt, TRUE);
00870     prebuilt = row_create_prebuilt(indexed_table);
00871 
00872     indexed_table->n_mysql_handles_opened++;
00873 
00874     error = row_merge_drop_table(trx, innodb_table);
00875     innodb_table = indexed_table;
00876     goto convert_error;
00877 
00878   case DB_TOO_BIG_RECORD:
00879     my_error(HA_ERR_TO_BIG_ROW, MYF(0));
00880     goto error;
00881   case DB_PRIMARY_KEY_IS_NULL:
00882     my_error(ER_PRIMARY_CANT_HAVE_NULL, MYF(0));
00883     /* fall through */
00884   case DB_DUPLICATE_KEY:
00885 error:
00886     prebuilt->trx->error_info = NULL;
00887     /* fall through */
00888   default:
00889     trx->error_state = DB_SUCCESS;
00890 
00891     if (new_primary) {
00892       if (indexed_table != innodb_table) {
00893         row_merge_drop_table(trx, indexed_table);
00894       }
00895     } else {
00896       if (!dict_locked) {
00897         row_mysql_lock_data_dictionary(trx);
00898         dict_locked = TRUE;
00899       }
00900 
00901       row_merge_drop_indexes(trx, indexed_table,
00902                  index, num_created);
00903     }
00904 
00905 convert_error:
00906     error = convert_error_code_to_mysql(error,
00907                 innodb_table->flags,
00908                 user_session);
00909   }
00910 
00911   mem_heap_free(heap);
00912   trx_commit_for_mysql(trx);
00913   if (prebuilt->trx) {
00914     trx_commit_for_mysql(prebuilt->trx);
00915   }
00916 
00917   if (dict_locked) {
00918     ut_d(dict_table_check_for_dup_indexes(innodb_table, FALSE));
00919     row_mysql_unlock_data_dictionary(trx);
00920   }
00921 
00922   trx_free_for_mysql(trx);
00923 
00924   /* There might be work for utility threads.*/
00925   srv_active_wake_master_thread();
00926 
00927   return(error);
00928 }
00929 
00930 /*******************************************************************/
00933 UNIV_INTERN
00934 int
00935 ha_innobase::prepare_drop_index(
00936 /*============================*/
00937                                 Session *session,
00938   Table*  i_table,  
00939   uint* key_num,  
00940   uint  num_of_keys)  
00941 {
00942   trx_t*    trx;
00943   int   err = 0;
00944   uint    n_key;
00945 
00946   ut_ad(i_table);
00947   ut_ad(key_num);
00948   ut_ad(num_of_keys);
00949   if (srv_created_new_raw || srv_force_recovery) {
00950     return(HA_ERR_WRONG_COMMAND);
00951   }
00952 
00953   update_session(session);
00954 
00955   trx_search_latch_release_if_reserved(prebuilt->trx);
00956   trx = prebuilt->trx;
00957 
00958   /* Test and mark all the indexes to be dropped */
00959 
00960   row_mysql_lock_data_dictionary(trx);
00961   ut_d(dict_table_check_for_dup_indexes(prebuilt->table, FALSE));
00962 
00963   /* Check that none of the indexes have previously been flagged
00964   for deletion. */
00965   {
00966     const dict_index_t* index
00967       = dict_table_get_first_index(prebuilt->table);
00968     do {
00969       ut_a(!index->to_be_dropped);
00970       index = dict_table_get_next_index(index);
00971     } while (index);
00972   }
00973 
00974   for (n_key = 0; n_key < num_of_keys; n_key++) {
00975     const KeyInfo*  key;
00976     dict_index_t* index;
00977 
00978     key = i_table->key_info + key_num[n_key];
00979     index = dict_table_get_index_on_name_and_min_id(
00980       prebuilt->table, key->name);
00981 
00982     if (!index) {
00983       errmsg_printf(ERRMSG_LVL_ERROR, "InnoDB could not find key n:o %u "
00984           "with name %s for table %s",
00985           key_num[n_key],
00986           key ? key->name : "NULL",
00987           prebuilt->table->name);
00988 
00989       err = HA_ERR_KEY_NOT_FOUND;
00990       goto func_exit;
00991     }
00992 
00993     /* Refuse to drop the clustered index.  It would be
00994     better to automatically generate a clustered index,
00995     but drizzled::alter_table() will call this method only
00996     after ha_innobase::add_index(). */
00997 
00998     if (dict_index_is_clust(index)) {
00999       my_error(ER_REQUIRES_PRIMARY_KEY, MYF(0));
01000       err = -1;
01001       goto func_exit;
01002     }
01003 
01004     index->to_be_dropped = TRUE;
01005   }
01006 
01007   /* If FOREIGN_KEY_CHECKS = 1 you may not drop an index defined
01008   for a foreign key constraint because InnoDB requires that both
01009   tables contain indexes for the constraint. Such index can
01010   be dropped only if FOREIGN_KEY_CHECKS is set to 0.
01011   Note that CREATE INDEX id ON table does a CREATE INDEX and
01012   DROP INDEX, and we can ignore here foreign keys because a
01013   new index for the foreign key has already been created.
01014 
01015   We check for the foreign key constraints after marking the
01016   candidate indexes for deletion, because when we check for an
01017   equivalent foreign index we don't want to select an index that
01018   is later deleted. */
01019 
01020   if (trx->check_foreigns
01021       && session_sql_command(user_session) != SQLCOM_CREATE_INDEX) {
01022     dict_index_t* index;
01023 
01024     for (index = dict_table_get_first_index(prebuilt->table);
01025          index;
01026          index = dict_table_get_next_index(index)) {
01027       dict_foreign_t* foreign;
01028 
01029       if (!index->to_be_dropped) {
01030 
01031         continue;
01032       }
01033 
01034       /* Check if the index is referenced. */
01035       foreign = dict_table_get_referenced_constraint(
01036         prebuilt->table, index);
01037 
01038       if (foreign) {
01039 index_needed:
01040         trx_set_detailed_error(
01041           trx,
01042           "Index needed in foreign key "
01043           "constraint");
01044 
01045         trx->error_info = index;
01046 
01047         err = HA_ERR_DROP_INDEX_FK;
01048         break;
01049       } else {
01050         /* Check if this index references some
01051         other table */
01052         foreign = dict_table_get_foreign_constraint(
01053           prebuilt->table, index);
01054 
01055         if (foreign) {
01056           ut_a(foreign->foreign_index == index);
01057 
01058           /* Search for an equivalent index that
01059           the foreign key constraint could use
01060           if this index were to be deleted. */
01061           if (!dict_foreign_find_equiv_index(
01062             foreign)) {
01063 
01064             goto index_needed;
01065           }
01066         }
01067       }
01068     }
01069   } else if (session_sql_command(user_session) == SQLCOM_CREATE_INDEX) {
01070     /* This is a drop of a foreign key constraint index that
01071     was created by MySQL when the constraint was added.  MySQL
01072     does this when the user creates an index explicitly which
01073     can be used in place of the automatically generated index. */
01074 
01075     dict_index_t* index;
01076 
01077     for (index = dict_table_get_first_index(prebuilt->table);
01078          index;
01079          index = dict_table_get_next_index(index)) {
01080       dict_foreign_t* foreign;
01081 
01082       if (!index->to_be_dropped) {
01083 
01084         continue;
01085       }
01086 
01087       /* Check if this index references some other table */
01088       foreign = dict_table_get_foreign_constraint(
01089         prebuilt->table, index);
01090 
01091       if (foreign == NULL) {
01092 
01093         continue;
01094       }
01095 
01096       ut_a(foreign->foreign_index == index);
01097 
01098       /* Search for an equivalent index that the
01099       foreign key constraint could use if this index
01100       were to be deleted. */
01101 
01102       if (!dict_foreign_find_equiv_index(foreign)) {
01103         trx_set_detailed_error(
01104           trx,
01105           "Index needed in foreign key "
01106           "constraint");
01107 
01108         trx->error_info = foreign->foreign_index;
01109 
01110         err = HA_ERR_DROP_INDEX_FK;
01111         break;
01112       }
01113     }
01114   }
01115 
01116 func_exit:
01117   if (err) {
01118     /* Undo our changes since there was some sort of error. */
01119     dict_index_t* index
01120       = dict_table_get_first_index(prebuilt->table);
01121 
01122     do {
01123       index->to_be_dropped = FALSE;
01124       index = dict_table_get_next_index(index);
01125     } while (index);
01126   }
01127 
01128   ut_d(dict_table_check_for_dup_indexes(prebuilt->table, FALSE));
01129   row_mysql_unlock_data_dictionary(trx);
01130 
01131   return(err);
01132 }
01133 
01134 /*******************************************************************/
01137 UNIV_INTERN
01138 int
01139 ha_innobase::final_drop_index(
01140 /*==========================*/
01141                               Session *session,
01142   Table*  )   
01143 {
01144   dict_index_t* index;    
01145   trx_t*    trx;    
01146   int   err;
01147 
01148   if (srv_created_new_raw || srv_force_recovery) {
01149     return(HA_ERR_WRONG_COMMAND);
01150   }
01151 
01152   update_session(session);
01153 
01154   trx_search_latch_release_if_reserved(prebuilt->trx);
01155   trx_start_if_not_started(prebuilt->trx);
01156 
01157   /* Create a background transaction for the operations on
01158   the data dictionary tables. */
01159   trx = innobase_trx_allocate(user_session);
01160   trx_start_if_not_started(trx);
01161 
01162   /* Flag this transaction as a dictionary operation, so that
01163   the data dictionary will be locked in crash recovery. */
01164   trx_set_dict_operation(trx, TRX_DICT_OP_INDEX);
01165 
01166   /* Lock the table exclusively, to ensure that no active
01167   transaction depends on an index that is being dropped. */
01168   err = convert_error_code_to_mysql(
01169     row_merge_lock_table(prebuilt->trx, prebuilt->table, LOCK_X),
01170     prebuilt->table->flags, user_session);
01171 
01172   row_mysql_lock_data_dictionary(trx);
01173   ut_d(dict_table_check_for_dup_indexes(prebuilt->table, FALSE));
01174 
01175   if (UNIV_UNLIKELY(err)) {
01176 
01177     /* Unmark the indexes to be dropped. */
01178     for (index = dict_table_get_first_index(prebuilt->table);
01179          index; index = dict_table_get_next_index(index)) {
01180 
01181       index->to_be_dropped = FALSE;
01182     }
01183 
01184     goto func_exit;
01185   }
01186 
01187   /* Drop indexes marked to be dropped */
01188 
01189   index = dict_table_get_first_index(prebuilt->table);
01190 
01191   while (index) {
01192     dict_index_t* next_index;
01193 
01194     next_index = dict_table_get_next_index(index);
01195 
01196     if (index->to_be_dropped) {
01197 
01198       row_merge_drop_index(index, prebuilt->table, trx);
01199     }
01200 
01201     index = next_index;
01202   }
01203 
01204   /* Check that all flagged indexes were dropped. */
01205   for (index = dict_table_get_first_index(prebuilt->table);
01206        index; index = dict_table_get_next_index(index)) {
01207     ut_a(!index->to_be_dropped);
01208   }
01209 
01210   /* We will need to rebuild index translation table. Set
01211   valid index entry count in the translation table to zero */
01212   share->idx_trans_tbl.index_count = 0;
01213 
01214 func_exit:
01215   ut_d(dict_table_check_for_dup_indexes(prebuilt->table, FALSE));
01216   trx_commit_for_mysql(trx);
01217   trx_commit_for_mysql(prebuilt->trx);
01218   row_mysql_unlock_data_dictionary(trx);
01219 
01220   /* Flush the log to reduce probability that the .frm files and
01221   the InnoDB data dictionary get out-of-sync if the user runs
01222   with innodb_flush_log_at_trx_commit = 0 */
01223 
01224   log_buffer_flush_to_disk();
01225 
01226   trx_free_for_mysql(trx);
01227 
01228   /* Tell the InnoDB server that there might be work for
01229   utility threads: */
01230 
01231   srv_active_wake_master_thread();
01232 
01233   return(err);
01234 }
01235 #endif