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_info.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 = !my_strcasecmp(system_charset_info,
00520              key_info->name, "PRIMARY");
00521 
00522   /* If there is a UNIQUE INDEX consisting entirely of NOT NULL
00523   columns and if the index does not contain column prefix(es)
00524   (only prefix/part of the column is indexed), MySQL will treat the
00525   index as a PRIMARY KEY unless the table already has one. */
00526 
00527   if (!new_primary && (key_info->flags & HA_NOSAME)
00528       && (!(key_info->flags & HA_KEY_HAS_PART_KEY_SEG))
00529       && row_table_got_default_clust_index(table)) {
00530     uint    key_part = key_info->key_parts;
00531 
00532     new_primary = TRUE;
00533 
00534     while (key_part--) {
00535       if (key_info->key_part[key_part].null_bit == 0) {
00536         new_primary = FALSE;
00537         break;
00538       }
00539     }
00540   }
00541 
00542   if (new_primary) {
00543     const dict_index_t* index;
00544 
00545     /* Create the PRIMARY key index definition */
00546     innobase_create_index_def(&key_info[i++], TRUE, TRUE,
00547             indexdef++, heap);
00548 
00549     row_mysql_lock_data_dictionary(trx);
00550 
00551     index = dict_table_get_first_index(table);
00552 
00553     /* Copy the index definitions of the old table.  Skip
00554     the old clustered index if it is a generated clustered
00555     index or a PRIMARY KEY.  If the clustered index is a
00556     UNIQUE INDEX, it must be converted to a secondary index. */
00557 
00558     if (dict_index_get_nth_col(index, 0)->mtype == DATA_SYS
00559         || !my_strcasecmp(system_charset_info,
00560               index->name, "PRIMARY")) {
00561       index = dict_table_get_next_index(index);
00562     }
00563 
00564     while (index) {
00565       innobase_copy_index_def(index, indexdef++, heap);
00566       index = dict_table_get_next_index(index);
00567     }
00568 
00569     row_mysql_unlock_data_dictionary(trx);
00570   }
00571 
00572   /* Create definitions for added secondary indexes. */
00573 
00574   while (i < n_keys) {
00575     innobase_create_index_def(&key_info[i++], new_primary, FALSE,
00576             indexdef++, heap);
00577   }
00578 
00579   n_keys = indexdef - indexdefs;
00580 
00581   return(indexdefs);
00582 }
00583 
00584 /*******************************************************************/
00587 static
00588 char*
00589 innobase_create_temporary_tablename(
00590 /*================================*/
00591   mem_heap_t* heap,   
00592   char    id,   
00593   const char*     table_name) 
00594 {
00595   char*     name;
00596   ulint     len;
00597   static const char suffix[] = "@0023 "; /* "# " */
00598 
00599   len = strlen(table_name);
00600 
00601   name = (char*) mem_heap_alloc(heap, len + sizeof suffix);
00602   memcpy(name, table_name, len);
00603   memcpy(name + len, suffix, sizeof suffix);
00604   name[len + (sizeof suffix - 2)] = id;
00605 
00606   return(name);
00607 }
00608 
00609 
00610 /*******************************************************************/
00613 UNIV_INTERN
00614 int
00615 ha_innobase::add_index(
00616 /*===================*/
00617                        Session *session,
00618   Table*  i_table,  
00619   KeyInfo*  key_info, 
00620   uint  num_of_keys)  
00621 {
00622   dict_index_t**  index;    
00623   dict_table_t* innodb_table; 
00624   dict_table_t* indexed_table;  
00625   merge_index_def_t* index_defs;  
00626   mem_heap_t*     heap;   
00627   trx_t*    trx;    
00628   ulint   num_of_idx;
00629   ulint   num_created = 0;
00630   ibool   dict_locked = FALSE;
00631   ulint   new_primary;
00632   int   error;
00633 
00634   ut_a(i_table);
00635   ut_a(key_info);
00636   ut_a(num_of_keys);
00637 
00638   if (srv_created_new_raw || srv_force_recovery) {
00639     return(HA_ERR_WRONG_COMMAND);
00640   }
00641 
00642   update_session(session);
00643 
00644   heap = mem_heap_create(1024);
00645 
00646   /* In case MySQL calls this in the middle of a SELECT query, release
00647   possible adaptive hash latch to avoid deadlocks of threads. */
00648   trx_search_latch_release_if_reserved(prebuilt->trx);
00649   trx_start_if_not_started(prebuilt->trx);
00650 
00651   /* Create a background transaction for the operations on
00652   the data dictionary tables. */
00653   trx = innobase_trx_allocate(user_session);
00654   trx_start_if_not_started(trx);
00655 
00656   innodb_table = indexed_table
00657     = dict_table_get(prebuilt->table->name, FALSE);
00658 
00659   if (UNIV_UNLIKELY(!innodb_table)) {
00660     error = HA_ERR_NO_SUCH_TABLE;
00661     goto err_exit;
00662   }
00663 
00664   /* Check if the index name is reserved. */
00665   if (innobase_index_name_is_reserved(trx, key_info, num_of_keys)) {
00666     error = -1;
00667   } else {
00668     /* Check that index keys are sensible */
00669     error = innobase_check_index_keys(key_info, num_of_keys,
00670               innodb_table);
00671   }
00672 
00673   if (UNIV_UNLIKELY(error)) {
00674 err_exit:
00675     mem_heap_free(heap);
00676     trx_general_rollback_for_mysql(trx, NULL);
00677     trx_free_for_mysql(trx);
00678     trx_commit_for_mysql(prebuilt->trx);
00679     return(error);
00680   }
00681 
00682   /* Create table containing all indexes to be built in this
00683   alter table add index so that they are in the correct order
00684   in the table. */
00685 
00686   num_of_idx = num_of_keys;
00687 
00688   index_defs = innobase_create_key_def(
00689     trx, innodb_table, heap, key_info, num_of_idx);
00690 
00691   new_primary = DICT_CLUSTERED & index_defs[0].ind_type;
00692 
00693   /* Allocate memory for dictionary index definitions */
00694 
00695   index = (dict_index_t**) mem_heap_alloc(
00696     heap, num_of_idx * sizeof *index);
00697 
00698   /* Flag this transaction as a dictionary operation, so that
00699   the data dictionary will be locked in crash recovery. */
00700   trx_set_dict_operation(trx, TRX_DICT_OP_INDEX);
00701 
00702   /* Acquire a lock on the table before creating any indexes. */
00703   error = row_merge_lock_table(prebuilt->trx, innodb_table,
00704              new_primary ? LOCK_X : LOCK_S);
00705 
00706   if (UNIV_UNLIKELY(error != DB_SUCCESS)) {
00707 
00708     goto error_handling;
00709   }
00710 
00711   /* Latch the InnoDB data dictionary exclusively so that no deadlocks
00712   or lock waits can happen in it during an index create operation. */
00713 
00714   row_mysql_lock_data_dictionary(trx);
00715   dict_locked = TRUE;
00716 
00717   ut_d(dict_table_check_for_dup_indexes(innodb_table, FALSE));
00718 
00719   /* If a new primary key is defined for the table we need
00720   to drop the original table and rebuild all indexes. */
00721 
00722   if (UNIV_UNLIKELY(new_primary)) {
00723     /* This transaction should be the only one
00724     operating on the table. */
00725     ut_a(innodb_table->n_mysql_handles_opened == 1);
00726 
00727     char* new_table_name = innobase_create_temporary_tablename(
00728       heap, '1', innodb_table->name);
00729 
00730     /* Clone the table. */
00731     trx_set_dict_operation(trx, TRX_DICT_OP_TABLE);
00732     indexed_table = row_merge_create_temporary_table(
00733       new_table_name, index_defs, innodb_table, trx);
00734 
00735     if (!indexed_table) {
00736 
00737       switch (trx->error_state) {
00738       case DB_TABLESPACE_ALREADY_EXISTS:
00739       case DB_DUPLICATE_KEY:
00740         innobase_convert_tablename(new_table_name);
00741         my_error(HA_ERR_TABLE_EXIST, MYF(0),
00742            new_table_name);
00743         error = HA_ERR_TABLE_EXIST;
00744         break;
00745       default:
00746         error = convert_error_code_to_mysql(
00747           trx->error_state, innodb_table->flags,
00748           user_session);
00749       }
00750 
00751       ut_d(dict_table_check_for_dup_indexes(innodb_table,
00752                     FALSE));
00753       row_mysql_unlock_data_dictionary(trx);
00754       goto err_exit;
00755     }
00756 
00757     trx->table_id = indexed_table->id;
00758   }
00759 
00760   /* Create the indexes in SYS_INDEXES and load into dictionary. */
00761 
00762   for (ulint i = 0; i < num_of_idx; i++) {
00763 
00764     index[i] = row_merge_create_index(trx, indexed_table,
00765               &index_defs[i]);
00766 
00767     if (!index[i]) {
00768       error = trx->error_state;
00769       goto error_handling;
00770     }
00771 
00772     num_created++;
00773   }
00774 
00775   ut_ad(error == DB_SUCCESS);
00776 
00777   /* We will need to rebuild index translation table. Set
00778   valid index entry count in the translation table to zero */
00779   share->idx_trans_tbl.index_count = 0;
00780 
00781   /* Commit the data dictionary transaction in order to release
00782   the table locks on the system tables.  This means that if
00783   MySQL crashes while creating a new primary key inside
00784   row_merge_build_indexes(), indexed_table will not be dropped
00785   by trx_rollback_active().  It will have to be recovered or
00786   dropped by the database administrator. */
00787   trx_commit_for_mysql(trx);
00788 
00789   row_mysql_unlock_data_dictionary(trx);
00790   dict_locked = FALSE;
00791 
00792   ut_a(trx->n_active_thrs == 0);
00793   ut_a(UT_LIST_GET_LEN(trx->signals) == 0);
00794 
00795   if (UNIV_UNLIKELY(new_primary)) {
00796     /* A primary key is to be built.  Acquire an exclusive
00797     table lock also on the table that is being created. */
00798     ut_ad(indexed_table != innodb_table);
00799 
00800     error = row_merge_lock_table(prebuilt->trx, indexed_table,
00801                LOCK_X);
00802 
00803     if (UNIV_UNLIKELY(error != DB_SUCCESS)) {
00804 
00805       goto error_handling;
00806     }
00807   }
00808 
00809   /* Read the clustered index of the table and build indexes
00810   based on this information using temporary files and merge sort. */
00811   error = row_merge_build_indexes(prebuilt->trx,
00812           innodb_table, indexed_table,
00813           index, num_of_idx, i_table);
00814 
00815 error_handling:
00816 
00817   /* After an error, remove all those index definitions from the
00818   dictionary which were defined. */
00819 
00820   switch (error) {
00821     const char* old_name;
00822     char*   tmp_name;
00823   case DB_SUCCESS:
00824     ut_a(!dict_locked);
00825     row_mysql_lock_data_dictionary(trx);
00826     dict_locked = TRUE;
00827 
00828     ut_d(dict_table_check_for_dup_indexes(prebuilt->table, TRUE));
00829 
00830     if (!new_primary) {
00831       error = row_merge_rename_indexes(trx, indexed_table);
00832 
00833       if (error != DB_SUCCESS) {
00834         row_merge_drop_indexes(trx, indexed_table,
00835                    index, num_created);
00836       }
00837 
00838       goto convert_error;
00839     }
00840 
00841     /* If a new primary key was defined for the table and
00842     there was no error at this point, we can now rename
00843     the old table as a temporary table, rename the new
00844     temporary table as the old table and drop the old table. */
00845     old_name = innodb_table->name;
00846     tmp_name = innobase_create_temporary_tablename(heap, '2',
00847                      old_name);
00848 
00849     error = row_merge_rename_tables(innodb_table, indexed_table,
00850             tmp_name, trx);
00851 
00852     if (error != DB_SUCCESS) {
00853 
00854       row_merge_drop_table(trx, indexed_table);
00855 
00856       switch (error) {
00857       case DB_TABLESPACE_ALREADY_EXISTS:
00858       case DB_DUPLICATE_KEY:
00859         innobase_convert_tablename(tmp_name);
00860         my_error(HA_ERR_TABLE_EXIST, MYF(0), tmp_name);
00861         error = HA_ERR_TABLE_EXIST;
00862         break;
00863       default:
00864         goto convert_error;
00865       }
00866       break;
00867     }
00868 
00869     trx_commit_for_mysql(prebuilt->trx);
00870     row_prebuilt_free(prebuilt, TRUE);
00871     prebuilt = row_create_prebuilt(indexed_table);
00872 
00873     indexed_table->n_mysql_handles_opened++;
00874 
00875     error = row_merge_drop_table(trx, innodb_table);
00876     innodb_table = indexed_table;
00877     goto convert_error;
00878 
00879   case DB_TOO_BIG_RECORD:
00880     my_error(HA_ERR_TO_BIG_ROW, MYF(0));
00881     goto error;
00882   case DB_PRIMARY_KEY_IS_NULL:
00883     my_error(ER_PRIMARY_CANT_HAVE_NULL, MYF(0));
00884     /* fall through */
00885   case DB_DUPLICATE_KEY:
00886 error:
00887     prebuilt->trx->error_info = NULL;
00888     /* fall through */
00889   default:
00890     trx->error_state = DB_SUCCESS;
00891 
00892     if (new_primary) {
00893       if (indexed_table != innodb_table) {
00894         row_merge_drop_table(trx, indexed_table);
00895       }
00896     } else {
00897       if (!dict_locked) {
00898         row_mysql_lock_data_dictionary(trx);
00899         dict_locked = TRUE;
00900       }
00901 
00902       row_merge_drop_indexes(trx, indexed_table,
00903                  index, num_created);
00904     }
00905 
00906 convert_error:
00907     error = convert_error_code_to_mysql(error,
00908                 innodb_table->flags,
00909                 user_session);
00910   }
00911 
00912   mem_heap_free(heap);
00913   trx_commit_for_mysql(trx);
00914   if (prebuilt->trx) {
00915     trx_commit_for_mysql(prebuilt->trx);
00916   }
00917 
00918   if (dict_locked) {
00919     ut_d(dict_table_check_for_dup_indexes(innodb_table, FALSE));
00920     row_mysql_unlock_data_dictionary(trx);
00921   }
00922 
00923   trx_free_for_mysql(trx);
00924 
00925   /* There might be work for utility threads.*/
00926   srv_active_wake_master_thread();
00927 
00928   return(error);
00929 }
00930 
00931 /*******************************************************************/
00934 UNIV_INTERN
00935 int
00936 ha_innobase::prepare_drop_index(
00937 /*============================*/
00938                                 Session *session,
00939   Table*  i_table,  
00940   uint* key_num,  
00941   uint  num_of_keys)  
00942 {
00943   trx_t*    trx;
00944   int   err = 0;
00945   uint    n_key;
00946 
00947   ut_ad(i_table);
00948   ut_ad(key_num);
00949   ut_ad(num_of_keys);
00950   if (srv_created_new_raw || srv_force_recovery) {
00951     return(HA_ERR_WRONG_COMMAND);
00952   }
00953 
00954   update_session(session);
00955 
00956   trx_search_latch_release_if_reserved(prebuilt->trx);
00957   trx = prebuilt->trx;
00958 
00959   /* Test and mark all the indexes to be dropped */
00960 
00961   row_mysql_lock_data_dictionary(trx);
00962   ut_d(dict_table_check_for_dup_indexes(prebuilt->table, FALSE));
00963 
00964   /* Check that none of the indexes have previously been flagged
00965   for deletion. */
00966   {
00967     const dict_index_t* index
00968       = dict_table_get_first_index(prebuilt->table);
00969     do {
00970       ut_a(!index->to_be_dropped);
00971       index = dict_table_get_next_index(index);
00972     } while (index);
00973   }
00974 
00975   for (n_key = 0; n_key < num_of_keys; n_key++) {
00976     const KeyInfo*  key;
00977     dict_index_t* index;
00978 
00979     key = i_table->key_info + key_num[n_key];
00980     index = dict_table_get_index_on_name_and_min_id(
00981       prebuilt->table, key->name);
00982 
00983     if (!index) {
00984       errmsg_printf(ERRMSG_LVL_ERROR, "InnoDB could not find key n:o %u "
00985           "with name %s for table %s",
00986           key_num[n_key],
00987           key ? key->name : "NULL",
00988           prebuilt->table->name);
00989 
00990       err = HA_ERR_KEY_NOT_FOUND;
00991       goto func_exit;
00992     }
00993 
00994     /* Refuse to drop the clustered index.  It would be
00995     better to automatically generate a clustered index,
00996     but drizzled::alter_table() will call this method only
00997     after ha_innobase::add_index(). */
00998 
00999     if (dict_index_is_clust(index)) {
01000       my_error(ER_REQUIRES_PRIMARY_KEY, MYF(0));
01001       err = -1;
01002       goto func_exit;
01003     }
01004 
01005     index->to_be_dropped = TRUE;
01006   }
01007 
01008   /* If FOREIGN_KEY_CHECKS = 1 you may not drop an index defined
01009   for a foreign key constraint because InnoDB requires that both
01010   tables contain indexes for the constraint. Such index can
01011   be dropped only if FOREIGN_KEY_CHECKS is set to 0.
01012   Note that CREATE INDEX id ON table does a CREATE INDEX and
01013   DROP INDEX, and we can ignore here foreign keys because a
01014   new index for the foreign key has already been created.
01015 
01016   We check for the foreign key constraints after marking the
01017   candidate indexes for deletion, because when we check for an
01018   equivalent foreign index we don't want to select an index that
01019   is later deleted. */
01020 
01021   if (trx->check_foreigns
01022       && session_sql_command(user_session) != SQLCOM_CREATE_INDEX) {
01023     dict_index_t* index;
01024 
01025     for (index = dict_table_get_first_index(prebuilt->table);
01026          index;
01027          index = dict_table_get_next_index(index)) {
01028       dict_foreign_t* foreign;
01029 
01030       if (!index->to_be_dropped) {
01031 
01032         continue;
01033       }
01034 
01035       /* Check if the index is referenced. */
01036       foreign = dict_table_get_referenced_constraint(
01037         prebuilt->table, index);
01038 
01039       if (foreign) {
01040 index_needed:
01041         trx_set_detailed_error(
01042           trx,
01043           "Index needed in foreign key "
01044           "constraint");
01045 
01046         trx->error_info = index;
01047 
01048         err = HA_ERR_DROP_INDEX_FK;
01049         break;
01050       } else {
01051         /* Check if this index references some
01052         other table */
01053         foreign = dict_table_get_foreign_constraint(
01054           prebuilt->table, index);
01055 
01056         if (foreign) {
01057           ut_a(foreign->foreign_index == index);
01058 
01059           /* Search for an equivalent index that
01060           the foreign key constraint could use
01061           if this index were to be deleted. */
01062           if (!dict_foreign_find_equiv_index(
01063             foreign)) {
01064 
01065             goto index_needed;
01066           }
01067         }
01068       }
01069     }
01070   } else if (session_sql_command(user_session) == SQLCOM_CREATE_INDEX) {
01071     /* This is a drop of a foreign key constraint index that
01072     was created by MySQL when the constraint was added.  MySQL
01073     does this when the user creates an index explicitly which
01074     can be used in place of the automatically generated index. */
01075 
01076     dict_index_t* index;
01077 
01078     for (index = dict_table_get_first_index(prebuilt->table);
01079          index;
01080          index = dict_table_get_next_index(index)) {
01081       dict_foreign_t* foreign;
01082 
01083       if (!index->to_be_dropped) {
01084 
01085         continue;
01086       }
01087 
01088       /* Check if this index references some other table */
01089       foreign = dict_table_get_foreign_constraint(
01090         prebuilt->table, index);
01091 
01092       if (foreign == NULL) {
01093 
01094         continue;
01095       }
01096 
01097       ut_a(foreign->foreign_index == index);
01098 
01099       /* Search for an equivalent index that the
01100       foreign key constraint could use if this index
01101       were to be deleted. */
01102 
01103       if (!dict_foreign_find_equiv_index(foreign)) {
01104         trx_set_detailed_error(
01105           trx,
01106           "Index needed in foreign key "
01107           "constraint");
01108 
01109         trx->error_info = foreign->foreign_index;
01110 
01111         err = HA_ERR_DROP_INDEX_FK;
01112         break;
01113       }
01114     }
01115   }
01116 
01117 func_exit:
01118   if (err) {
01119     /* Undo our changes since there was some sort of error. */
01120     dict_index_t* index
01121       = dict_table_get_first_index(prebuilt->table);
01122 
01123     do {
01124       index->to_be_dropped = FALSE;
01125       index = dict_table_get_next_index(index);
01126     } while (index);
01127   }
01128 
01129   ut_d(dict_table_check_for_dup_indexes(prebuilt->table, FALSE));
01130   row_mysql_unlock_data_dictionary(trx);
01131 
01132   return(err);
01133 }
01134 
01135 /*******************************************************************/
01138 UNIV_INTERN
01139 int
01140 ha_innobase::final_drop_index(
01141 /*==========================*/
01142                               Session *session,
01143   Table*  )   
01144 {
01145   dict_index_t* index;    
01146   trx_t*    trx;    
01147   int   err;
01148 
01149   if (srv_created_new_raw || srv_force_recovery) {
01150     return(HA_ERR_WRONG_COMMAND);
01151   }
01152 
01153   update_session(session);
01154 
01155   trx_search_latch_release_if_reserved(prebuilt->trx);
01156   trx_start_if_not_started(prebuilt->trx);
01157 
01158   /* Create a background transaction for the operations on
01159   the data dictionary tables. */
01160   trx = innobase_trx_allocate(user_session);
01161   trx_start_if_not_started(trx);
01162 
01163   /* Flag this transaction as a dictionary operation, so that
01164   the data dictionary will be locked in crash recovery. */
01165   trx_set_dict_operation(trx, TRX_DICT_OP_INDEX);
01166 
01167   /* Lock the table exclusively, to ensure that no active
01168   transaction depends on an index that is being dropped. */
01169   err = convert_error_code_to_mysql(
01170     row_merge_lock_table(prebuilt->trx, prebuilt->table, LOCK_X),
01171     prebuilt->table->flags, user_session);
01172 
01173   row_mysql_lock_data_dictionary(trx);
01174   ut_d(dict_table_check_for_dup_indexes(prebuilt->table, FALSE));
01175 
01176   if (UNIV_UNLIKELY(err)) {
01177 
01178     /* Unmark the indexes to be dropped. */
01179     for (index = dict_table_get_first_index(prebuilt->table);
01180          index; index = dict_table_get_next_index(index)) {
01181 
01182       index->to_be_dropped = FALSE;
01183     }
01184 
01185     goto func_exit;
01186   }
01187 
01188   /* Drop indexes marked to be dropped */
01189 
01190   index = dict_table_get_first_index(prebuilt->table);
01191 
01192   while (index) {
01193     dict_index_t* next_index;
01194 
01195     next_index = dict_table_get_next_index(index);
01196 
01197     if (index->to_be_dropped) {
01198 
01199       row_merge_drop_index(index, prebuilt->table, trx);
01200     }
01201 
01202     index = next_index;
01203   }
01204 
01205   /* Check that all flagged indexes were dropped. */
01206   for (index = dict_table_get_first_index(prebuilt->table);
01207        index; index = dict_table_get_next_index(index)) {
01208     ut_a(!index->to_be_dropped);
01209   }
01210 
01211   /* We will need to rebuild index translation table. Set
01212   valid index entry count in the translation table to zero */
01213   share->idx_trans_tbl.index_count = 0;
01214 
01215 func_exit:
01216   ut_d(dict_table_check_for_dup_indexes(prebuilt->table, FALSE));
01217   trx_commit_for_mysql(trx);
01218   trx_commit_for_mysql(prebuilt->trx);
01219   row_mysql_unlock_data_dictionary(trx);
01220 
01221   /* Flush the log to reduce probability that the .frm files and
01222   the InnoDB data dictionary get out-of-sync if the user runs
01223   with innodb_flush_log_at_trx_commit = 0 */
01224 
01225   log_buffer_flush_to_disk();
01226 
01227   trx_free_for_mysql(trx);
01228 
01229   /* Tell the InnoDB server that there might be work for
01230   utility threads: */
01231 
01232   srv_active_wake_master_thread();
01233 
01234   return(err);
01235 }
01236 #endif