Drizzled Public API Documentation

handler0alter.cc
Go to the documentation of this file.
1 /*****************************************************************************
2 
3 Copyright (C) 2005, 2010, Innobase Oy. All Rights Reserved.
4 
5 This program is free software; you can redistribute it and/or modify it under
6 the terms of the GNU General Public License as published by the Free Software
7 Foundation; version 2 of the License.
8 
9 This program is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 
13 You should have received a copy of the GNU General Public License along with
14 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
15 St, Fifth Floor, Boston, MA 02110-1301 USA
16 
17 *****************************************************************************/
18 
19 /**************************************************/
24 #include <config.h>
25 #include <drizzled/error.h>
26 #include <drizzled/charset.h>
27 #include <drizzled/field.h>
28 #include <drizzled/table.h>
29 #include <drizzled/field/varstring.h>
30 #include <drizzled/internal/my_sys.h>
31 
32 #include "log0log.h"
33 #include "row0merge.h"
34 #include "srv0srv.h"
35 #include "trx0trx.h"
36 #include "trx0roll.h"
37 #include "ha_prototypes.h"
38 #include "handler0alter.h"
39 
40 #include "ha_innodb.h"
41 #include "handler0vars.h"
42 
43 /*************************************************************/
46 static
47 void
49 /*==================*/
50  const dict_col_t* col,
51  const unsigned char* data,
52  ulint len,
53  Field* field)
54 {
55  unsigned char* ptr;
56  unsigned char* dest = field->ptr;
57  ulint flen = field->pack_length();
58 
59  switch (col->mtype) {
60  case DATA_INT:
61  ut_ad(len == flen);
62 
63  /* Convert integer data from Innobase to little-endian
64  format, sign bit restored to normal */
65 
66  for (ptr = dest + len; ptr != dest; ) {
67  *--ptr = *data++;
68  }
69 
70  if (!(field->flags & UNSIGNED_FLAG)) {
71  ((byte*) dest)[len - 1] ^= 0x80;
72  }
73 
74  break;
75 
76  case DATA_VARCHAR:
77  case DATA_VARMYSQL:
78  case DATA_BINARY:
79  field->reset();
80 
81  if (field->type() == DRIZZLE_TYPE_VARCHAR) {
82  /* This is a >= 5.0.3 type true VARCHAR. Store the
83  length of the data to the first byte or the first
84  two bytes of dest. */
85 
87  dest, len, flen - field->key_length());
88  }
89 
90  /* Copy the actual data */
91  memcpy(dest, data, len);
92  break;
93 
94  case DATA_BLOB:
95  /* Store a pointer to the BLOB buffer to dest: the BLOB was
96  already copied to the buffer in row_sel_store_mysql_rec */
97 
98  row_mysql_store_blob_ref(dest, flen, data, len);
99  break;
100 
101 #ifdef UNIV_DEBUG
102  case DATA_MYSQL:
103  ut_ad(flen >= len);
104  ut_ad(DATA_MBMAXLEN(col->mbminmaxlen)
105  >= DATA_MBMINLEN(col->mbminmaxlen));
106  ut_ad(DATA_MBMAXLEN(col->mbminmaxlen)
107  > DATA_MBMINLEN(col->mbminmaxlen) || flen == len);
108  memcpy(dest, data, len);
109  break;
110 
111  default:
112  case DATA_SYS_CHILD:
113  case DATA_SYS:
114  /* These column types should never be shipped to MySQL. */
115  ut_ad(0);
116 
117  case DATA_CHAR:
118  case DATA_FIXBINARY:
119  case DATA_FLOAT:
120  case DATA_DOUBLE:
121  case DATA_DECIMAL:
122  /* Above are the valid column types for MySQL data. */
123  ut_ad(flen == len);
124 #else /* UNIV_DEBUG */
125  default:
126 #endif /* UNIV_DEBUG */
127  memcpy(dest, data, len);
128  }
129 }
130 
131 /*************************************************************/
133 UNIV_INTERN
134 void
136 /*==================*/
137  Table* table,
138  const rec_t* rec,
139  const dict_index_t* index,
140  const ulint* offsets)
142 {
143  uint n_fields = table->getShare()->sizeFields();
144  uint i;
145 
146  ut_ad(n_fields == dict_table_get_n_user_cols(index->table));
147 
148  for (i = 0; i < n_fields; i++) {
149  Field* field = table->getField(i);
150  ulint ipos;
151  ulint ilen;
152  const unsigned char* ifield;
153 
154  field->reset();
155 
156  ipos = dict_index_get_nth_col_pos(index, i);
157 
158  if (UNIV_UNLIKELY(ipos == ULINT_UNDEFINED)) {
159 null_field:
160  field->set_null();
161  continue;
162  }
163 
164  ifield = rec_get_nth_field(rec, offsets, ipos, &ilen);
165 
166  /* Assign the NULL flag */
167  if (ilen == UNIV_SQL_NULL) {
168  ut_ad(field->real_maybe_null());
169  goto null_field;
170  }
171 
172  field->set_notnull();
173 
176  dict_index_get_nth_field(index, ipos)),
177  ifield, ilen, field);
178  }
179 }
180 
181 /*************************************************************/
183 UNIV_INTERN
184 void
186 /*===============*/
187  Table* table)
188 {
189  uint n_fields = table->getShare()->sizeFields();
190  uint i;
191 
192  for (i = 0; i < n_fields; i++) {
193  table->getField(i)->set_default();
194  }
195 }
196 
197 #if 0 // This is a part of the fast index code.
198 /******************************************************************/
200 static
201 void
202 innobase_convert_tablename(
203 /*=======================*/
204  char* s)
205 {
206 
207  char* slash = strchr(s, '/');
208 
209  if (slash) {
210  char* t;
211  /* Temporarily replace the '/' with NUL. */
212  *slash = 0;
213  strncpy(s, s, slash - s + 1);
214 
215  t = s + strlen(s);
216  ut_ad(slash >= t);
217  /* Append a '.' after the database name. */
218  *t++ = '.';
219  slash++;
220  /* Convert the table name. */
221  strncpy(t, slash, slash - t + strlen(slash));
222  }
223 }
224 
225 
226 /*******************************************************************/
229 static
230 int
231 innobase_check_index_keys(
232 /*======================*/
233  const KeyInfo* key_info,
234  ulint num_of_keys,
236  const dict_table_t* table)
237 {
238  ulint key_num;
239 
240  ut_ad(key_info);
241  ut_ad(num_of_keys);
242 
243  for (key_num = 0; key_num < num_of_keys; key_num++) {
244  const KeyInfo& key = key_info[key_num];
245 
246  /* Check that the same index name does not appear
247  twice in indexes to be created. */
248 
249  for (ulint i = 0; i < key_num; i++) {
250  const KeyInfo& key2 = key_info[i];
251 
252  if (0 == strcmp(key.name, key2.name)) {
253  my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0),
254  key.name);
255 
256  return(ER_WRONG_NAME_FOR_INDEX);
257  }
258  }
259 
260  /* Check that the same index name does not already exist. */
261 
262  for (const dict_index_t* index
263  = dict_table_get_first_index(table);
264  index; index = dict_table_get_next_index(index)) {
265 
266  if (0 == strcmp(key.name, index->name)) {
267  my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0),
268  key.name);
269 
270  return(ER_WRONG_NAME_FOR_INDEX);
271  }
272  }
273 
274  /* Check that MySQL does not try to create a column
275  prefix index field on an inappropriate data type and
276  that the same column does not appear twice in the index. */
277 
278  for (ulint i = 0; i < key.key_parts; i++) {
279  const KeyPartInfo& key_part1
280  = key.key_part[i];
281  const Field* field
282  = key_part1.field;
283  ibool is_unsigned;
284 
286  &is_unsigned, field)) {
287  default:
288  break;
289  case DATA_INT:
290  case DATA_FLOAT:
291  case DATA_DOUBLE:
292  case DATA_DECIMAL:
293  if (field->type() == DRIZZLE_TYPE_VARCHAR) {
294  if (key_part1.length
295  >= field->pack_length()
296  - ((Field_varstring*) field)
297  ->length_bytes) {
298  break;
299  }
300  } else {
301  if (key_part1.length
302  >= field->pack_length()) {
303  break;
304  }
305  }
306 
307  my_error(ER_WRONG_KEY_COLUMN, MYF(0),
308  field->field_name);
309  return(ER_WRONG_KEY_COLUMN);
310  }
311 
312  for (ulint j = 0; j < i; j++) {
313  const KeyPartInfo& key_part2
314  = key.key_part[j];
315 
316  if (strcmp(key_part1.field->field_name,
317  key_part2.field->field_name)) {
318  continue;
319  }
320 
321  my_error(ER_WRONG_KEY_COLUMN, MYF(0),
322  key_part1.field->field_name);
323  return(ER_WRONG_KEY_COLUMN);
324  }
325  }
326  }
327 
328  return(0);
329 }
330 
331 /*******************************************************************/
333 static
334 void
335 innobase_create_index_field_def(
336 /*============================*/
337  KeyPartInfo* key_part,
338  mem_heap_t* heap,
339  merge_index_field_t* index_field)
341 {
342  Field* field;
343  ibool is_unsigned;
344  ulint col_type;
345 
346  ut_ad(key_part);
347  ut_ad(index_field);
348 
349  field = key_part->field;
350  ut_a(field);
351 
352  col_type = get_innobase_type_from_mysql_type(&is_unsigned, field);
353 
354  if (DATA_BLOB == col_type
355  || (key_part->length < field->pack_length()
356  && field->type() != DRIZZLE_TYPE_VARCHAR)
357  || (field->type() == DRIZZLE_TYPE_VARCHAR
358  && key_part->length < field->pack_length()
359  - ((Field_varstring*)field)->length_bytes)) {
360 
361  index_field->prefix_len = key_part->length;
362  } else {
363  index_field->prefix_len = 0;
364  }
365 
366  index_field->field_name = mem_heap_strdup(heap, field->field_name);
367 
368  return;
369 }
370 
371 /*******************************************************************/
373 static
374 void
375 innobase_create_index_def(
376 /*======================*/
377  KeyInfo* key,
378  bool new_primary,
381  bool key_primary,
383  merge_index_def_t* index,
384  mem_heap_t* heap)
386 {
387  ulint i;
388  ulint len;
389  ulint n_fields = key->key_parts;
390  char* index_name;
391 
393  heap, n_fields * sizeof *index->fields);
394 
395  index->ind_type = 0;
396  index->n_fields = n_fields;
397  len = strlen(key->name) + 1;
398  index->name = index_name = (char*) mem_heap_alloc(heap,
399  len + !new_primary);
400 
401  if (UNIV_LIKELY(!new_primary)) {
402  *index_name++ = TEMP_INDEX_PREFIX;
403  }
404 
405  memcpy(index_name, key->name, len);
406 
407  if (key->flags & HA_NOSAME) {
408  index->ind_type |= DICT_UNIQUE;
409  }
410 
411  if (key_primary) {
412  index->ind_type |= DICT_CLUSTERED;
413  }
414 
415  for (i = 0; i < n_fields; i++) {
416  innobase_create_index_field_def(&key->key_part[i], heap,
417  &index->fields[i]);
418  }
419 
420  return;
421 }
422 
423 /*******************************************************************/
425 static
426 void
427 innobase_copy_index_field_def(
428 /*==========================*/
429  const dict_field_t* field,
430  merge_index_field_t* index_field)
431 {
432  assert(field != NULL);
433  assert(index_field != NULL);
434 
435  index_field->field_name = field->name;
436  index_field->prefix_len = field->prefix_len;
437 
438  return;
439 }
440 
441 /*******************************************************************/
443 static
444 void
445 innobase_copy_index_def(
446 /*====================*/
447  const dict_index_t* index,
448  merge_index_def_t* new_index,
449  mem_heap_t* heap)
450 {
451  ulint n_fields;
452  ulint i;
453 
454  /* Note that we take only those fields that user defined to be
455  in the index. In the internal representation more colums were
456  added and those colums are not copied .*/
457 
458  n_fields = index->n_user_defined_cols;
459 
460  new_index->fields = (merge_index_field_t*) mem_heap_alloc(
461  heap, n_fields * sizeof *new_index->fields);
462 
463  /* When adding a PRIMARY KEY, we may convert a previous
464  clustered index to a secondary index (UNIQUE NOT NULL). */
465  new_index->ind_type = index->type & ~DICT_CLUSTERED;
466  new_index->n_fields = n_fields;
467  new_index->name = index->name;
468 
469  for (i = 0; i < n_fields; i++) {
470  innobase_copy_index_field_def(&index->fields[i],
471  &new_index->fields[i]);
472  }
473 
474  return;
475 }
476 
477 /*******************************************************************/
495 static
497 innobase_create_key_def(
498 /*====================*/
499  trx_t* trx,
500  const dict_table_t*table,
501  mem_heap_t* heap,
503  KeyInfo* key_info,
504  ulint& n_keys)
506 {
507  ulint i = 0;
508  merge_index_def_t* indexdef;
509  merge_index_def_t* indexdefs;
510  bool new_primary;
511 
512  indexdef = indexdefs = (merge_index_def_t*)
513  mem_heap_alloc(heap, sizeof *indexdef
514  * (n_keys + UT_LIST_GET_LEN(table->indexes)));
515 
516  /* If there is a primary key, it is always the first index
517  defined for the table. */
518 
519  new_primary = !system_charset_info->strcasecmp(key_info->name, "PRIMARY");
520 
521  /* If there is a UNIQUE INDEX consisting entirely of NOT NULL
522  columns and if the index does not contain column prefix(es)
523  (only prefix/part of the column is indexed), MySQL will treat the
524  index as a PRIMARY KEY unless the table already has one. */
525 
526  if (!new_primary && (key_info->flags & HA_NOSAME)
527  && (!(key_info->flags & HA_KEY_HAS_PART_KEY_SEG))
529  uint key_part = key_info->key_parts;
530 
531  new_primary = TRUE;
532 
533  while (key_part--) {
534  if (key_info->key_part[key_part].null_bit == 0) {
535  new_primary = FALSE;
536  break;
537  }
538  }
539  }
540 
541  if (new_primary) {
542  const dict_index_t* index;
543 
544  /* Create the PRIMARY key index definition */
545  innobase_create_index_def(&key_info[i++], TRUE, TRUE,
546  indexdef++, heap);
547 
548  row_mysql_lock_data_dictionary(trx);
549 
550  index = dict_table_get_first_index(table);
551 
552  /* Copy the index definitions of the old table. Skip
553  the old clustered index if it is a generated clustered
554  index or a PRIMARY KEY. If the clustered index is a
555  UNIQUE INDEX, it must be converted to a secondary index. */
556 
557  if (dict_index_get_nth_col(index, 0)->mtype == DATA_SYS
558  || !system_charset_info->strcasecmp(index->name, "PRIMARY"))
559  {
560  index = dict_table_get_next_index(index);
561  }
562 
563  while (index) {
564  innobase_copy_index_def(index, indexdef++, heap);
565  index = dict_table_get_next_index(index);
566  }
567 
569  }
570 
571  /* Create definitions for added secondary indexes. */
572 
573  while (i < n_keys) {
574  innobase_create_index_def(&key_info[i++], new_primary, FALSE,
575  indexdef++, heap);
576  }
577 
578  n_keys = indexdef - indexdefs;
579 
580  return(indexdefs);
581 }
582 
583 /*******************************************************************/
586 static
587 char*
588 innobase_create_temporary_tablename(
589 /*================================*/
590  mem_heap_t* heap,
591  char id,
592  const char* table_name)
593 {
594  char* name;
595  ulint len;
596  static const char suffix[] = "@0023 "; /* "# " */
597 
598  len = strlen(table_name);
599 
600  name = (char*) mem_heap_alloc(heap, len + sizeof suffix);
601  memcpy(name, table_name, len);
602  memcpy(name + len, suffix, sizeof suffix);
603  name[len + (sizeof suffix - 2)] = id;
604 
605  return(name);
606 }
607 
608 
609 /*******************************************************************/
612 UNIV_INTERN
613 int
614 ha_innobase::add_index(
615 /*===================*/
616  Session *session,
617  Table* i_table,
618  KeyInfo* key_info,
619  uint num_of_keys)
620 {
621  dict_index_t** index;
622  dict_table_t* innodb_table;
623  dict_table_t* indexed_table;
624  merge_index_def_t* index_defs;
625  mem_heap_t* heap;
626  trx_t* trx;
627  ulint num_of_idx;
628  ulint num_created = 0;
629  ibool dict_locked = FALSE;
630  ulint new_primary;
631  int error;
632 
633  ut_a(i_table);
634  ut_a(key_info);
635  ut_a(num_of_keys);
636 
637  if (srv_created_new_raw || srv_force_recovery) {
638  return(HA_ERR_WRONG_COMMAND);
639  }
640 
641  update_session(session);
642 
643  heap = mem_heap_create(1024);
644 
645  /* In case MySQL calls this in the middle of a SELECT query, release
646  possible adaptive hash latch to avoid deadlocks of threads. */
649 
650  /* Create a background transaction for the operations on
651  the data dictionary tables. */
654 
655  innodb_table = indexed_table
656  = dict_table_get(prebuilt->table->name, FALSE);
657 
658  if (UNIV_UNLIKELY(!innodb_table)) {
659  error = HA_ERR_NO_SUCH_TABLE;
660  goto err_exit;
661  }
662 
663  /* Check if the index name is reserved. */
664  if (innobase_index_name_is_reserved(trx, key_info, num_of_keys)) {
665  error = -1;
666  } else {
667  /* Check that index keys are sensible */
668  error = innobase_check_index_keys(key_info, num_of_keys,
669  innodb_table);
670  }
671 
672  if (UNIV_UNLIKELY(error)) {
673 err_exit:
674  mem_heap_free(heap);
676  trx_free_for_mysql(trx);
678  return(error);
679  }
680 
681  /* Create table containing all indexes to be built in this
682  alter table add index so that they are in the correct order
683  in the table. */
684 
685  num_of_idx = num_of_keys;
686 
687  index_defs = innobase_create_key_def(
688  trx, innodb_table, heap, key_info, num_of_idx);
689 
690  new_primary = DICT_CLUSTERED & index_defs[0].ind_type;
691 
692  /* Allocate memory for dictionary index definitions */
693 
694  index = (dict_index_t**) mem_heap_alloc(
695  heap, num_of_idx * sizeof *index);
696 
697  /* Flag this transaction as a dictionary operation, so that
698  the data dictionary will be locked in crash recovery. */
700 
701  /* Acquire a lock on the table before creating any indexes. */
702  error = row_merge_lock_table(prebuilt->trx, innodb_table,
703  new_primary ? LOCK_X : LOCK_S);
704 
705  if (UNIV_UNLIKELY(error != DB_SUCCESS)) {
706 
707  goto error_handling;
708  }
709 
710  /* Latch the InnoDB data dictionary exclusively so that no deadlocks
711  or lock waits can happen in it during an index create operation. */
712 
713  row_mysql_lock_data_dictionary(trx);
714  dict_locked = TRUE;
715 
716  ut_d(dict_table_check_for_dup_indexes(innodb_table, FALSE));
717 
718  /* If a new primary key is defined for the table we need
719  to drop the original table and rebuild all indexes. */
720 
721  if (UNIV_UNLIKELY(new_primary)) {
722  /* This transaction should be the only one
723  operating on the table. */
724  ut_a(innodb_table->n_mysql_handles_opened == 1);
725 
726  char* new_table_name = innobase_create_temporary_tablename(
727  heap, '1', innodb_table->name);
728 
729  /* Clone the table. */
731  indexed_table = row_merge_create_temporary_table(
732  new_table_name, index_defs, innodb_table, trx);
733 
734  if (!indexed_table) {
735 
736  switch (trx->error_state) {
737  case DB_TABLESPACE_ALREADY_EXISTS:
738  case DB_DUPLICATE_KEY:
739  innobase_convert_tablename(new_table_name);
740  my_error(HA_ERR_TABLE_EXIST, MYF(0),
741  new_table_name);
742  error = HA_ERR_TABLE_EXIST;
743  break;
744  default:
746  trx->error_state, innodb_table->flags,
747  user_session);
748  }
749 
750  ut_d(dict_table_check_for_dup_indexes(innodb_table,
751  FALSE));
753  goto err_exit;
754  }
755 
756  trx->table_id = indexed_table->id;
757  }
758 
759  /* Create the indexes in SYS_INDEXES and load into dictionary. */
760 
761  for (ulint i = 0; i < num_of_idx; i++) {
762 
763  index[i] = row_merge_create_index(trx, indexed_table,
764  &index_defs[i]);
765 
766  if (!index[i]) {
767  error = trx->error_state;
768  goto error_handling;
769  }
770 
771  num_created++;
772  }
773 
774  ut_ad(error == DB_SUCCESS);
775 
776  /* We will need to rebuild index translation table. Set
777  valid index entry count in the translation table to zero */
779 
780  /* Commit the data dictionary transaction in order to release
781  the table locks on the system tables. This means that if
782  MySQL crashes while creating a new primary key inside
783  row_merge_build_indexes(), indexed_table will not be dropped
784  by trx_rollback_active(). It will have to be recovered or
785  dropped by the database administrator. */
787 
789  dict_locked = FALSE;
790 
791  ut_a(trx->n_active_thrs == 0);
792  ut_a(UT_LIST_GET_LEN(trx->signals) == 0);
793 
794  if (UNIV_UNLIKELY(new_primary)) {
795  /* A primary key is to be built. Acquire an exclusive
796  table lock also on the table that is being created. */
797  ut_ad(indexed_table != innodb_table);
798 
799  error = row_merge_lock_table(prebuilt->trx, indexed_table,
800  LOCK_X);
801 
802  if (UNIV_UNLIKELY(error != DB_SUCCESS)) {
803 
804  goto error_handling;
805  }
806  }
807 
808  /* Read the clustered index of the table and build indexes
809  based on this information using temporary files and merge sort. */
811  innodb_table, indexed_table,
812  index, num_of_idx, i_table);
813 
814 error_handling:
815 
816  /* After an error, remove all those index definitions from the
817  dictionary which were defined. */
818 
819  switch (error) {
820  const char* old_name;
821  char* tmp_name;
822  case DB_SUCCESS:
823  ut_a(!dict_locked);
824  row_mysql_lock_data_dictionary(trx);
825  dict_locked = TRUE;
826 
827  ut_d(dict_table_check_for_dup_indexes(prebuilt->table, TRUE));
828 
829  if (!new_primary) {
830  error = row_merge_rename_indexes(trx, indexed_table);
831 
832  if (error != DB_SUCCESS) {
833  row_merge_drop_indexes(trx, indexed_table,
834  index, num_created);
835  }
836 
837  goto convert_error;
838  }
839 
840  /* If a new primary key was defined for the table and
841  there was no error at this point, we can now rename
842  the old table as a temporary table, rename the new
843  temporary table as the old table and drop the old table. */
844  old_name = innodb_table->name;
845  tmp_name = innobase_create_temporary_tablename(heap, '2',
846  old_name);
847 
848  error = row_merge_rename_tables(innodb_table, indexed_table,
849  tmp_name, trx);
850 
851  if (error != DB_SUCCESS) {
852 
853  row_merge_drop_table(trx, indexed_table);
854 
855  switch (error) {
856  case DB_TABLESPACE_ALREADY_EXISTS:
857  case DB_DUPLICATE_KEY:
858  innobase_convert_tablename(tmp_name);
859  my_error(HA_ERR_TABLE_EXIST, MYF(0), tmp_name);
860  error = HA_ERR_TABLE_EXIST;
861  break;
862  default:
863  goto convert_error;
864  }
865  break;
866  }
867 
870  prebuilt = row_create_prebuilt(indexed_table);
871 
872  indexed_table->n_mysql_handles_opened++;
873 
874  error = row_merge_drop_table(trx, innodb_table);
875  innodb_table = indexed_table;
876  goto convert_error;
877 
878  case DB_TOO_BIG_RECORD:
879  my_error(HA_ERR_TO_BIG_ROW, MYF(0));
880  goto error;
881  case DB_PRIMARY_KEY_IS_NULL:
882  my_error(ER_PRIMARY_CANT_HAVE_NULL, MYF(0));
883  /* fall through */
884  case DB_DUPLICATE_KEY:
885 error:
886  prebuilt->trx->error_info = NULL;
887  /* fall through */
888  default:
889  trx->error_state = DB_SUCCESS;
890 
891  if (new_primary) {
892  if (indexed_table != innodb_table) {
893  row_merge_drop_table(trx, indexed_table);
894  }
895  } else {
896  if (!dict_locked) {
897  row_mysql_lock_data_dictionary(trx);
898  dict_locked = TRUE;
899  }
900 
901  row_merge_drop_indexes(trx, indexed_table,
902  index, num_created);
903  }
904 
905 convert_error:
906  error = convert_error_code_to_mysql(error,
907  innodb_table->flags,
908  user_session);
909  }
910 
911  mem_heap_free(heap);
913  if (prebuilt->trx) {
915  }
916 
917  if (dict_locked) {
918  ut_d(dict_table_check_for_dup_indexes(innodb_table, FALSE));
920  }
921 
922  trx_free_for_mysql(trx);
923 
924  /* There might be work for utility threads.*/
926 
927  return(error);
928 }
929 
930 /*******************************************************************/
933 UNIV_INTERN
934 int
935 ha_innobase::prepare_drop_index(
936 /*============================*/
937  Session *session,
938  Table* i_table,
939  uint* key_num,
940  uint num_of_keys)
941 {
942  trx_t* trx;
943  int err = 0;
944  uint n_key;
945 
946  ut_ad(i_table);
947  ut_ad(key_num);
948  ut_ad(num_of_keys);
949  if (srv_created_new_raw || srv_force_recovery) {
950  return(HA_ERR_WRONG_COMMAND);
951  }
952 
953  update_session(session);
954 
956  trx = prebuilt->trx;
957 
958  /* Test and mark all the indexes to be dropped */
959 
960  row_mysql_lock_data_dictionary(trx);
961  ut_d(dict_table_check_for_dup_indexes(prebuilt->table, FALSE));
962 
963  /* Check that none of the indexes have previously been flagged
964  for deletion. */
965  {
966  const dict_index_t* index
967  = dict_table_get_first_index(prebuilt->table);
968  do {
969  ut_a(!index->to_be_dropped);
970  index = dict_table_get_next_index(index);
971  } while (index);
972  }
973 
974  for (n_key = 0; n_key < num_of_keys; n_key++) {
975  const KeyInfo* key;
976  dict_index_t* index;
977 
978  key = i_table->key_info + key_num[n_key];
979  index = dict_table_get_index_on_name_and_min_id(
980  prebuilt->table, key->name);
981 
982  if (!index) {
983  errmsg_printf(ERRMSG_LVL_ERROR, "InnoDB could not find key n:o %u "
984  "with name %s for table %s",
985  key_num[n_key],
986  key ? key->name : "NULL",
987  prebuilt->table->name);
988 
989  err = HA_ERR_KEY_NOT_FOUND;
990  goto func_exit;
991  }
992 
993  /* Refuse to drop the clustered index. It would be
994  better to automatically generate a clustered index,
995  but drizzled::alter_table() will call this method only
996  after ha_innobase::add_index(). */
997 
998  if (dict_index_is_clust(index)) {
999  my_error(ER_REQUIRES_PRIMARY_KEY, MYF(0));
1000  err = -1;
1001  goto func_exit;
1002  }
1003 
1004  index->to_be_dropped = TRUE;
1005  }
1006 
1007  /* If FOREIGN_KEY_CHECKS = 1 you may not drop an index defined
1008  for a foreign key constraint because InnoDB requires that both
1009  tables contain indexes for the constraint. Such index can
1010  be dropped only if FOREIGN_KEY_CHECKS is set to 0.
1011  Note that CREATE INDEX id ON table does a CREATE INDEX and
1012  DROP INDEX, and we can ignore here foreign keys because a
1013  new index for the foreign key has already been created.
1014 
1015  We check for the foreign key constraints after marking the
1016  candidate indexes for deletion, because when we check for an
1017  equivalent foreign index we don't want to select an index that
1018  is later deleted. */
1019 
1020  if (trx->check_foreigns
1021  && session_sql_command(user_session) != SQLCOM_CREATE_INDEX) {
1022  dict_index_t* index;
1023 
1024  for (index = dict_table_get_first_index(prebuilt->table);
1025  index;
1026  index = dict_table_get_next_index(index)) {
1027  dict_foreign_t* foreign;
1028 
1029  if (!index->to_be_dropped) {
1030 
1031  continue;
1032  }
1033 
1034  /* Check if the index is referenced. */
1035  foreign = dict_table_get_referenced_constraint(
1036  prebuilt->table, index);
1037 
1038  if (foreign) {
1039 index_needed:
1041  trx,
1042  "Index needed in foreign key "
1043  "constraint");
1044 
1045  trx->error_info = index;
1046 
1047  err = HA_ERR_DROP_INDEX_FK;
1048  break;
1049  } else {
1050  /* Check if this index references some
1051  other table */
1052  foreign = dict_table_get_foreign_constraint(
1053  prebuilt->table, index);
1054 
1055  if (foreign) {
1056  ut_a(foreign->foreign_index == index);
1057 
1058  /* Search for an equivalent index that
1059  the foreign key constraint could use
1060  if this index were to be deleted. */
1061  if (!dict_foreign_find_equiv_index(
1062  foreign)) {
1063 
1064  goto index_needed;
1065  }
1066  }
1067  }
1068  }
1069  } else if (session_sql_command(user_session) == SQLCOM_CREATE_INDEX) {
1070  /* This is a drop of a foreign key constraint index that
1071  was created by MySQL when the constraint was added. MySQL
1072  does this when the user creates an index explicitly which
1073  can be used in place of the automatically generated index. */
1074 
1075  dict_index_t* index;
1076 
1077  for (index = dict_table_get_first_index(prebuilt->table);
1078  index;
1079  index = dict_table_get_next_index(index)) {
1080  dict_foreign_t* foreign;
1081 
1082  if (!index->to_be_dropped) {
1083 
1084  continue;
1085  }
1086 
1087  /* Check if this index references some other table */
1088  foreign = dict_table_get_foreign_constraint(
1089  prebuilt->table, index);
1090 
1091  if (foreign == NULL) {
1092 
1093  continue;
1094  }
1095 
1096  ut_a(foreign->foreign_index == index);
1097 
1098  /* Search for an equivalent index that the
1099  foreign key constraint could use if this index
1100  were to be deleted. */
1101 
1102  if (!dict_foreign_find_equiv_index(foreign)) {
1104  trx,
1105  "Index needed in foreign key "
1106  "constraint");
1107 
1108  trx->error_info = foreign->foreign_index;
1109 
1110  err = HA_ERR_DROP_INDEX_FK;
1111  break;
1112  }
1113  }
1114  }
1115 
1116 func_exit:
1117  if (err) {
1118  /* Undo our changes since there was some sort of error. */
1119  dict_index_t* index
1120  = dict_table_get_first_index(prebuilt->table);
1121 
1122  do {
1123  index->to_be_dropped = FALSE;
1124  index = dict_table_get_next_index(index);
1125  } while (index);
1126  }
1127 
1128  ut_d(dict_table_check_for_dup_indexes(prebuilt->table, FALSE));
1130 
1131  return(err);
1132 }
1133 
1134 /*******************************************************************/
1137 UNIV_INTERN
1138 int
1139 ha_innobase::final_drop_index(
1140 /*==========================*/
1141  Session *session,
1142  Table* )
1143 {
1144  dict_index_t* index;
1145  trx_t* trx;
1146  int err;
1147 
1148  if (srv_created_new_raw || srv_force_recovery) {
1149  return(HA_ERR_WRONG_COMMAND);
1150  }
1151 
1152  update_session(session);
1153 
1156 
1157  /* Create a background transaction for the operations on
1158  the data dictionary tables. */
1161 
1162  /* Flag this transaction as a dictionary operation, so that
1163  the data dictionary will be locked in crash recovery. */
1165 
1166  /* Lock the table exclusively, to ensure that no active
1167  transaction depends on an index that is being dropped. */
1171 
1172  row_mysql_lock_data_dictionary(trx);
1173  ut_d(dict_table_check_for_dup_indexes(prebuilt->table, FALSE));
1174 
1175  if (UNIV_UNLIKELY(err)) {
1176 
1177  /* Unmark the indexes to be dropped. */
1178  for (index = dict_table_get_first_index(prebuilt->table);
1179  index; index = dict_table_get_next_index(index)) {
1180 
1181  index->to_be_dropped = FALSE;
1182  }
1183 
1184  goto func_exit;
1185  }
1186 
1187  /* Drop indexes marked to be dropped */
1188 
1189  index = dict_table_get_first_index(prebuilt->table);
1190 
1191  while (index) {
1192  dict_index_t* next_index;
1193 
1194  next_index = dict_table_get_next_index(index);
1195 
1196  if (index->to_be_dropped) {
1197 
1198  row_merge_drop_index(index, prebuilt->table, trx);
1199  }
1200 
1201  index = next_index;
1202  }
1203 
1204  /* Check that all flagged indexes were dropped. */
1205  for (index = dict_table_get_first_index(prebuilt->table);
1206  index; index = dict_table_get_next_index(index)) {
1207  ut_a(!index->to_be_dropped);
1208  }
1209 
1210  /* We will need to rebuild index translation table. Set
1211  valid index entry count in the translation table to zero */
1213 
1214 func_exit:
1215  ut_d(dict_table_check_for_dup_indexes(prebuilt->table, FALSE));
1216  trx_commit_for_mysql(trx);
1219 
1220  /* Flush the log to reduce probability that the .frm files and
1221  the InnoDB data dictionary get out-of-sync if the user runs
1222  with innodb_flush_log_at_trx_commit = 0 */
1223 
1225 
1226  trx_free_for_mysql(trx);
1227 
1228  /* Tell the InnoDB server that there might be work for
1229  utility threads: */
1230 
1232 
1233  return(err);
1234 }
1235 #endif