1 /*****************************************************************************
3 Copyright (c) 2005, 2012, Oracle and/or its affiliates. All Rights Reserved.
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.
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.
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.,
15 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
17 *****************************************************************************/
19 /**************************************************//**
20 @file handler/handler0alter.cc
22 *******************************************************/
24 #include <mysql_priv.h>
25 #include <mysqld_error.h>
29 #include "row0merge.h"
33 #include "ha_prototypes.h"
34 #include "handler0alter.h"
37 #include "ha_innodb.h"
39 /*************************************************************//**
40 Copies an InnoDB column to a MySQL field. This function is
41 adapted from row_sel_field_store_in_mysql_format(). */
44 innobase_col_to_mysql(
45 /*==================*/
46 const dict_col_t
* col
, /*!< in: InnoDB column */
47 const uchar
* data
, /*!< in: InnoDB column data */
48 ulint len
, /*!< in: length of data, in bytes */
49 Field
* field
) /*!< in/out: MySQL field */
52 uchar
* dest
= field
->ptr
;
53 ulint flen
= field
->pack_length();
59 /* Convert integer data from Innobase to little-endian
60 format, sign bit restored to normal */
62 for (ptr
= dest
+ len
; ptr
!= dest
; ) {
66 if (!(field
->flags
& UNSIGNED_FLAG
)) {
67 ((byte
*) dest
)[len
- 1] ^= 0x80;
77 if (field
->type() == MYSQL_TYPE_VARCHAR
) {
78 /* This is a >= 5.0.3 type true VARCHAR. Store the
79 length of the data to the first byte or the first
82 dest
= row_mysql_store_true_var_len(
83 dest
, len
, flen
- field
->key_length());
86 /* Copy the actual data */
87 memcpy(dest
, data
, len
);
91 /* Store a pointer to the BLOB buffer to dest: the BLOB was
92 already copied to the buffer in row_sel_store_mysql_rec */
94 row_mysql_store_blob_ref(dest
, flen
, data
, len
);
100 ut_ad(col
->mbmaxlen
>= col
->mbminlen
);
101 memcpy(dest
, data
, len
);
107 /* These column types should never be shipped to MySQL. */
114 /* Above are the valid column types for MySQL data. */
118 /* We may have flen > len when there is a shorter
119 prefix on a CHAR column. */
121 #else /* UNIV_DEBUG */
123 #endif /* UNIV_DEBUG */
124 memcpy(dest
, data
, len
);
128 /*************************************************************//**
129 Copies an InnoDB record to table->record[0]. */
130 extern "C" UNIV_INTERN
132 innobase_rec_to_mysql(
133 /*==================*/
134 TABLE
* table
, /*!< in/out: MySQL table */
135 const rec_t
* rec
, /*!< in: record */
136 const dict_index_t
* index
, /*!< in: index */
137 const ulint
* offsets
) /*!< in: rec_get_offsets(
140 uint n_fields
= table
->s
->fields
;
143 ut_ad(n_fields
== dict_table_get_n_user_cols(index
->table
));
145 for (i
= 0; i
< n_fields
; i
++) {
146 Field
* field
= table
->field
[i
];
153 ipos
= dict_index_get_nth_col_or_prefix_pos(index
, i
, TRUE
);
155 if (UNIV_UNLIKELY(ipos
== ULINT_UNDEFINED
)) {
161 ifield
= rec_get_nth_field(rec
, offsets
, ipos
, &ilen
);
163 /* Assign the NULL flag */
164 if (ilen
== UNIV_SQL_NULL
) {
165 ut_ad(field
->real_maybe_null());
169 field
->set_notnull();
171 innobase_col_to_mysql(
173 dict_index_get_nth_field(index
, ipos
)),
174 ifield
, ilen
, field
);
178 /*************************************************************//**
179 Resets table->record[0]. */
180 extern "C" UNIV_INTERN
184 TABLE
* table
) /*!< in/out: MySQL table */
186 uint n_fields
= table
->s
->fields
;
189 for (i
= 0; i
< n_fields
; i
++) {
190 table
->field
[i
]->set_default();
194 /******************************************************************//**
195 Removes the filename encoding of a database and table name. */
198 innobase_convert_tablename(
199 /*=======================*/
200 char* s
) /*!< in: identifier; out: decoded identifier */
204 char* slash
= strchr(s
, '/');
208 /* Temporarily replace the '/' with NUL. */
210 /* Convert the database name. */
211 strconvert(&my_charset_filename
, s
, system_charset_info
,
212 s
, slash
- s
+ 1, &errors
);
216 /* Append a '.' after the database name. */
219 /* Convert the table name. */
220 strconvert(&my_charset_filename
, slash
, system_charset_info
,
221 t
, slash
- t
+ strlen(slash
), &errors
);
223 strconvert(&my_charset_filename
, s
,
224 system_charset_info
, s
, strlen(s
), &errors
);
228 /*******************************************************************//**
229 This function checks that index keys are sensible.
230 @return 0 or error number */
233 innobase_check_index_keys(
234 /*======================*/
235 const KEY
* key_info
, /*!< in: Indexes to be
237 ulint num_of_keys
, /*!< in: Number of
238 indexes to be created */
239 const dict_table_t
* table
) /*!< in: Existing indexes */
246 for (key_num
= 0; key_num
< num_of_keys
; key_num
++) {
247 const KEY
& key
= key_info
[key_num
];
249 /* Check that the same index name does not appear
250 twice in indexes to be created. */
252 for (ulint i
= 0; i
< key_num
; i
++) {
253 const KEY
& key2
= key_info
[i
];
255 if (0 == strcmp(key
.name
, key2
.name
)) {
256 my_error(ER_WRONG_NAME_FOR_INDEX
, MYF(0),
259 return(ER_WRONG_NAME_FOR_INDEX
);
263 /* Check that the same index name does not already exist. */
265 for (const dict_index_t
* index
266 = dict_table_get_first_index(table
);
267 index
; index
= dict_table_get_next_index(index
)) {
269 if (0 == strcmp(key
.name
, index
->name
)) {
270 my_error(ER_WRONG_NAME_FOR_INDEX
, MYF(0),
273 return(ER_WRONG_NAME_FOR_INDEX
);
277 /* Check that MySQL does not try to create a column
278 prefix index field on an inappropriate data type and
279 that the same column does not appear twice in the index. */
281 for (ulint i
= 0; i
< key
.key_parts
; i
++) {
282 const KEY_PART_INFO
& key_part1
288 switch (get_innobase_type_from_mysql_type(
289 &is_unsigned
, field
)) {
296 if (field
->type() == MYSQL_TYPE_VARCHAR
) {
298 >= field
->pack_length()
299 - ((Field_varstring
*) field
)
305 >= field
->pack_length()) {
310 my_error(ER_WRONG_KEY_COLUMN
, MYF(0),
312 return(ER_WRONG_KEY_COLUMN
);
315 for (ulint j
= 0; j
< i
; j
++) {
316 const KEY_PART_INFO
& key_part2
319 if (strcmp(key_part1
.field
->field_name
,
320 key_part2
.field
->field_name
)) {
324 my_error(ER_WRONG_KEY_COLUMN
, MYF(0),
325 key_part1
.field
->field_name
);
326 return(ER_WRONG_KEY_COLUMN
);
334 /*******************************************************************//**
335 Create index field definition for key part */
338 innobase_create_index_field_def(
339 /*============================*/
340 KEY_PART_INFO
* key_part
, /*!< in: MySQL key definition */
341 mem_heap_t
* heap
, /*!< in: memory heap */
342 merge_index_field_t
* index_field
) /*!< out: index field
343 definition for key_part */
349 DBUG_ENTER("innobase_create_index_field_def");
354 field
= key_part
->field
;
357 col_type
= get_innobase_type_from_mysql_type(&is_unsigned
, field
);
359 if (DATA_BLOB
== col_type
360 || (key_part
->length
< field
->pack_length()
361 && field
->type() != MYSQL_TYPE_VARCHAR
)
362 || (field
->type() == MYSQL_TYPE_VARCHAR
363 && key_part
->length
< field
->pack_length()
364 - ((Field_varstring
*)field
)->length_bytes
)) {
366 index_field
->prefix_len
= key_part
->length
;
368 index_field
->prefix_len
= 0;
371 index_field
->field_name
= mem_heap_strdup(heap
, field
->field_name
);
376 /*******************************************************************//**
377 Create index definition for key */
380 innobase_create_index_def(
381 /*======================*/
382 KEY
* key
, /*!< in: key definition */
383 bool new_primary
, /*!< in: TRUE=generating
386 bool key_primary
, /*!< in: TRUE if this key
388 merge_index_def_t
* index
, /*!< out: index definition */
389 mem_heap_t
* heap
) /*!< in: heap where memory
394 ulint n_fields
= key
->key_parts
;
397 DBUG_ENTER("innobase_create_index_def");
399 index
->fields
= (merge_index_field_t
*) mem_heap_alloc(
400 heap
, n_fields
* sizeof *index
->fields
);
403 index
->n_fields
= n_fields
;
404 len
= strlen(key
->name
) + 1;
405 index
->name
= index_name
= (char*) mem_heap_alloc(heap
,
408 if (UNIV_LIKELY(!new_primary
)) {
409 *index_name
++ = TEMP_INDEX_PREFIX
;
412 memcpy(index_name
, key
->name
, len
);
414 if (key
->flags
& HA_NOSAME
) {
415 index
->ind_type
|= DICT_UNIQUE
;
419 index
->ind_type
|= DICT_CLUSTERED
;
422 for (i
= 0; i
< n_fields
; i
++) {
423 innobase_create_index_field_def(&key
->key_part
[i
], heap
,
430 /*******************************************************************//**
431 Copy index field definition */
434 innobase_copy_index_field_def(
435 /*==========================*/
436 const dict_field_t
* field
, /*!< in: definition to copy */
437 merge_index_field_t
* index_field
) /*!< out: copied definition */
439 DBUG_ENTER("innobase_copy_index_field_def");
440 DBUG_ASSERT(field
!= NULL
);
441 DBUG_ASSERT(index_field
!= NULL
);
443 index_field
->field_name
= field
->name
;
444 index_field
->prefix_len
= field
->prefix_len
;
449 /*******************************************************************//**
450 Copy index definition for the index */
453 innobase_copy_index_def(
454 /*====================*/
455 const dict_index_t
* index
, /*!< in: index definition to copy */
456 merge_index_def_t
* new_index
,/*!< out: Index definition */
457 mem_heap_t
* heap
) /*!< in: heap where allocated */
462 DBUG_ENTER("innobase_copy_index_def");
464 /* Note that we take only those fields that user defined to be
465 in the index. In the internal representation more colums were
466 added and those colums are not copied .*/
468 n_fields
= index
->n_user_defined_cols
;
470 new_index
->fields
= (merge_index_field_t
*) mem_heap_alloc(
471 heap
, n_fields
* sizeof *new_index
->fields
);
473 /* When adding a PRIMARY KEY, we may convert a previous
474 clustered index to a secondary index (UNIQUE NOT NULL). */
475 new_index
->ind_type
= index
->type
& ~DICT_CLUSTERED
;
476 new_index
->n_fields
= n_fields
;
477 new_index
->name
= index
->name
;
479 for (i
= 0; i
< n_fields
; i
++) {
480 innobase_copy_index_field_def(&index
->fields
[i
],
481 &new_index
->fields
[i
]);
487 /*******************************************************************//**
488 Create an index table where indexes are ordered as follows:
490 IF a new primary key is defined for the table THEN
493 2) Original secondary indexes
494 3) New secondary indexes
498 1) All new indexes in the order they arrive from MySQL
503 @return key definitions or NULL */
506 innobase_create_key_def(
507 /*====================*/
508 trx_t
* trx
, /*!< in: trx */
509 const dict_table_t
*table
, /*!< in: table definition */
510 mem_heap_t
* heap
, /*!< in: heap where space for key
511 definitions are allocated */
512 KEY
* key_info
, /*!< in: Indexes to be created */
513 ulint
& n_keys
) /*!< in/out: Number of indexes to
517 merge_index_def_t
* indexdef
;
518 merge_index_def_t
* indexdefs
;
521 DBUG_ENTER("innobase_create_key_def");
523 indexdef
= indexdefs
= (merge_index_def_t
*)
524 mem_heap_alloc(heap
, sizeof *indexdef
525 * (n_keys
+ UT_LIST_GET_LEN(table
->indexes
)));
527 /* If there is a primary key, it is always the first index
528 defined for the table. */
530 new_primary
= !my_strcasecmp(system_charset_info
,
531 key_info
->name
, "PRIMARY");
533 /* If there is a UNIQUE INDEX consisting entirely of NOT NULL
534 columns and if the index does not contain column prefix(es)
535 (only prefix/part of the column is indexed), MySQL will treat the
536 index as a PRIMARY KEY unless the table already has one. */
538 if (!new_primary
&& (key_info
->flags
& HA_NOSAME
)
539 && (!(key_info
->flags
& HA_KEY_HAS_PART_KEY_SEG
))
540 && row_table_got_default_clust_index(table
)) {
541 uint key_part
= key_info
->key_parts
;
546 if (key_info
->key_part
[key_part
].key_type
547 & FIELDFLAG_MAYBE_NULL
) {
555 const dict_index_t
* index
;
557 /* Create the PRIMARY key index definition */
558 innobase_create_index_def(&key_info
[i
++], TRUE
, TRUE
,
561 row_mysql_lock_data_dictionary(trx
);
563 index
= dict_table_get_first_index(table
);
565 /* Copy the index definitions of the old table. Skip
566 the old clustered index if it is a generated clustered
567 index or a PRIMARY KEY. If the clustered index is a
568 UNIQUE INDEX, it must be converted to a secondary index. */
570 if (dict_index_get_nth_col(index
, 0)->mtype
== DATA_SYS
571 || !my_strcasecmp(system_charset_info
,
572 index
->name
, "PRIMARY")) {
573 index
= dict_table_get_next_index(index
);
577 innobase_copy_index_def(index
, indexdef
++, heap
);
578 index
= dict_table_get_next_index(index
);
581 row_mysql_unlock_data_dictionary(trx
);
584 /* Create definitions for added secondary indexes. */
587 innobase_create_index_def(&key_info
[i
++], new_primary
, FALSE
,
591 n_keys
= indexdef
- indexdefs
;
593 DBUG_RETURN(indexdefs
);
596 /*******************************************************************//**
597 Create a temporary tablename using query id, thread id, and id
598 @return temporary tablename */
601 innobase_create_temporary_tablename(
602 /*================================*/
603 mem_heap_t
* heap
, /*!< in: memory heap */
604 char id
, /*!< in: identifier [0-9a-zA-Z] */
605 const char* table_name
) /*!< in: table name */
609 static const char suffix
[] = "@0023 "; /* "# " */
611 len
= strlen(table_name
);
613 name
= (char*) mem_heap_alloc(heap
, len
+ sizeof suffix
);
614 memcpy(name
, table_name
, len
);
615 memcpy(name
+ len
, suffix
, sizeof suffix
);
616 name
[len
+ (sizeof suffix
- 2)] = id
;
621 /*******************************************************************//**
623 @return 0 or error number */
626 ha_innobase::add_index(
627 /*===================*/
628 TABLE
* table
, /*!< in: Table where indexes are created */
629 KEY
* key_info
, /*!< in: Indexes to be created */
630 uint num_of_keys
) /*!< in: Number of indexes to be created */
632 dict_index_t
** index
; /*!< Index to be created */
633 dict_table_t
* innodb_table
; /*!< InnoDB table in dictionary */
634 dict_table_t
* indexed_table
; /*!< Table where indexes are created */
635 merge_index_def_t
* index_defs
; /*!< Index definitions */
636 mem_heap_t
* heap
; /*!< Heap for index definitions */
637 trx_t
* trx
; /*!< Transaction */
639 ulint num_created
= 0;
640 ibool dict_locked
= FALSE
;
644 DBUG_ENTER("ha_innobase::add_index");
649 if (srv_created_new_raw
|| srv_force_recovery
) {
650 DBUG_RETURN(HA_ERR_WRONG_COMMAND
);
655 /* In case MySQL calls this in the middle of a SELECT query, release
656 possible adaptive hash latch to avoid deadlocks of threads. */
657 trx_search_latch_release_if_reserved(prebuilt
->trx
);
659 /* Check if the index name is reserved. */
660 if (innobase_index_name_is_reserved(user_thd
, key_info
, num_of_keys
)) {
664 innodb_table
= indexed_table
665 = dict_table_get(prebuilt
->table
->name
, FALSE
);
667 if (UNIV_UNLIKELY(!innodb_table
)) {
668 DBUG_RETURN(HA_ERR_NO_SUCH_TABLE
);
671 if (innodb_table
->tablespace_discarded
) {
675 /* Check that index keys are sensible */
676 error
= innobase_check_index_keys(key_info
, num_of_keys
, innodb_table
);
678 if (UNIV_UNLIKELY(error
)) {
682 heap
= mem_heap_create(1024);
683 trx_start_if_not_started(prebuilt
->trx
);
685 /* Create a background transaction for the operations on
686 the data dictionary tables. */
687 trx
= innobase_trx_allocate(user_thd
);
688 trx_start_if_not_started(trx
);
690 /* Create table containing all indexes to be built in this
691 alter table add index so that they are in the correct order
694 num_of_idx
= num_of_keys
;
696 index_defs
= innobase_create_key_def(
697 trx
, innodb_table
, heap
, key_info
, num_of_idx
);
699 new_primary
= DICT_CLUSTERED
& index_defs
[0].ind_type
;
701 /* Allocate memory for dictionary index definitions */
703 index
= (dict_index_t
**) mem_heap_alloc(
704 heap
, num_of_idx
* sizeof *index
);
706 /* Flag this transaction as a dictionary operation, so that
707 the data dictionary will be locked in crash recovery. */
708 trx_set_dict_operation(trx
, TRX_DICT_OP_INDEX
);
710 /* Acquire a lock on the table before creating any indexes. */
711 error
= row_merge_lock_table(prebuilt
->trx
, innodb_table
,
712 new_primary
? LOCK_X
: LOCK_S
);
714 if (UNIV_UNLIKELY(error
!= DB_SUCCESS
)) {
719 /* Latch the InnoDB data dictionary exclusively so that no deadlocks
720 or lock waits can happen in it during an index create operation. */
722 row_mysql_lock_data_dictionary(trx
);
725 ut_d(dict_table_check_for_dup_indexes(innodb_table
, FALSE
));
727 /* If a new primary key is defined for the table we need
728 to drop the original table and rebuild all indexes. */
730 if (UNIV_UNLIKELY(new_primary
)) {
731 /* This transaction should be the only one
732 operating on the table. */
733 ut_a(innodb_table
->n_mysql_handles_opened
== 1);
735 char* new_table_name
= innobase_create_temporary_tablename(
736 heap
, '1', innodb_table
->name
);
738 /* Clone the table. */
739 trx_set_dict_operation(trx
, TRX_DICT_OP_TABLE
);
740 indexed_table
= row_merge_create_temporary_table(
741 new_table_name
, index_defs
, innodb_table
, trx
);
743 if (!indexed_table
) {
745 switch (trx
->error_state
) {
746 case DB_TABLESPACE_ALREADY_EXISTS
:
747 case DB_DUPLICATE_KEY
:
748 innobase_convert_tablename(new_table_name
);
749 my_error(HA_ERR_TABLE_EXIST
, MYF(0),
751 error
= HA_ERR_TABLE_EXIST
;
754 error
= convert_error_code_to_mysql(
755 trx
->error_state
, innodb_table
->flags
,
759 ut_d(dict_table_check_for_dup_indexes(innodb_table
,
762 trx_general_rollback_for_mysql(trx
, NULL
);
763 row_mysql_unlock_data_dictionary(trx
);
764 trx_free_for_mysql(trx
);
765 trx_commit_for_mysql(prebuilt
->trx
);
769 trx
->table_id
= indexed_table
->id
;
772 /* Create the indexes in SYS_INDEXES and load into dictionary. */
774 for (ulint i
= 0; i
< num_of_idx
; i
++) {
776 index
[i
] = row_merge_create_index(trx
, indexed_table
,
780 error
= trx
->error_state
;
787 ut_ad(error
== DB_SUCCESS
);
789 /* Commit the data dictionary transaction in order to release
790 the table locks on the system tables. This means that if
791 MySQL crashes while creating a new primary key inside
792 row_merge_build_indexes(), indexed_table will not be dropped
793 by trx_rollback_active(). It will have to be recovered or
794 dropped by the database administrator. */
795 trx_commit_for_mysql(trx
);
797 row_mysql_unlock_data_dictionary(trx
);
800 ut_a(trx
->n_active_thrs
== 0);
801 ut_a(UT_LIST_GET_LEN(trx
->signals
) == 0);
803 if (UNIV_UNLIKELY(new_primary
)) {
804 /* A primary key is to be built. Acquire an exclusive
805 table lock also on the table that is being created. */
806 ut_ad(indexed_table
!= innodb_table
);
808 error
= row_merge_lock_table(prebuilt
->trx
, indexed_table
,
811 if (UNIV_UNLIKELY(error
!= DB_SUCCESS
)) {
817 /* Read the clustered index of the table and build indexes
818 based on this information using temporary files and merge sort. */
819 error
= row_merge_build_indexes(prebuilt
->trx
,
820 innodb_table
, indexed_table
,
821 index
, num_of_idx
, table
);
824 /* After an error, remove all those index definitions from the
825 dictionary which were defined. */
828 const char* old_name
;
832 row_mysql_lock_data_dictionary(trx
);
835 ut_d(dict_table_check_for_dup_indexes(prebuilt
->table
, TRUE
));
838 error
= row_merge_rename_indexes(trx
, indexed_table
);
840 if (error
!= DB_SUCCESS
) {
841 row_merge_drop_indexes(trx
, indexed_table
,
848 /* If a new primary key was defined for the table and
849 there was no error at this point, we can now rename
850 the old table as a temporary table, rename the new
851 temporary table as the old table and drop the old table. */
852 old_name
= innodb_table
->name
;
853 tmp_name
= innobase_create_temporary_tablename(heap
, '2',
856 error
= row_merge_rename_tables(innodb_table
, indexed_table
,
859 if (error
!= DB_SUCCESS
) {
861 row_merge_drop_table(trx
, indexed_table
);
864 case DB_TABLESPACE_ALREADY_EXISTS
:
865 case DB_DUPLICATE_KEY
:
866 innobase_convert_tablename(tmp_name
);
867 my_error(HA_ERR_TABLE_EXIST
, MYF(0), tmp_name
);
868 error
= HA_ERR_TABLE_EXIST
;
876 trx_commit_for_mysql(prebuilt
->trx
);
877 row_prebuilt_free(prebuilt
, TRUE
);
878 prebuilt
= row_create_prebuilt(indexed_table
);
880 indexed_table
->n_mysql_handles_opened
++;
882 error
= row_merge_drop_table(trx
, innodb_table
);
883 innodb_table
= indexed_table
;
886 case DB_TOO_BIG_RECORD
:
887 my_error(HA_ERR_TO_BIG_ROW
, MYF(0));
889 case DB_PRIMARY_KEY_IS_NULL
:
890 my_error(ER_PRIMARY_CANT_HAVE_NULL
, MYF(0));
892 case DB_DUPLICATE_KEY
:
894 prebuilt
->trx
->error_info
= NULL
;
897 trx
->error_state
= DB_SUCCESS
;
900 if (indexed_table
!= innodb_table
) {
901 row_merge_drop_table(trx
, indexed_table
);
905 row_mysql_lock_data_dictionary(trx
);
909 row_merge_drop_indexes(trx
, indexed_table
,
914 if (error
== DB_SUCCESS
) {
915 /* Build index is successful. We will need to
916 rebuild index translation table. Reset the
917 index entry count in the translation table
918 to zero, so that translation table will be rebuilt */
919 share
->idx_trans_tbl
.index_count
= 0;
922 error
= convert_error_code_to_mysql(error
,
928 trx_commit_for_mysql(trx
);
930 trx_commit_for_mysql(prebuilt
->trx
);
934 ut_d(dict_table_check_for_dup_indexes(innodb_table
, FALSE
));
935 row_mysql_unlock_data_dictionary(trx
);
938 trx_free_for_mysql(trx
);
940 /* There might be work for utility threads.*/
941 srv_active_wake_master_thread();
946 /*******************************************************************//**
947 Prepare to drop some indexes of a table.
948 @return 0 or error number */
951 ha_innobase::prepare_drop_index(
952 /*============================*/
953 TABLE
* table
, /*!< in: Table where indexes are dropped */
954 uint
* key_num
, /*!< in: Key nums to be dropped */
955 uint num_of_keys
) /*!< in: Number of keys to be dropped */
961 DBUG_ENTER("ha_innobase::prepare_drop_index");
965 if (srv_created_new_raw
|| srv_force_recovery
) {
966 DBUG_RETURN(HA_ERR_WRONG_COMMAND
);
971 trx_search_latch_release_if_reserved(prebuilt
->trx
);
974 /* Test and mark all the indexes to be dropped */
976 row_mysql_lock_data_dictionary(trx
);
977 ut_d(dict_table_check_for_dup_indexes(prebuilt
->table
, FALSE
));
979 /* Check that none of the indexes have previously been flagged
982 const dict_index_t
* index
983 = dict_table_get_first_index(prebuilt
->table
);
985 ut_a(!index
->to_be_dropped
);
986 index
= dict_table_get_next_index(index
);
990 for (n_key
= 0; n_key
< num_of_keys
; n_key
++) {
994 key
= table
->key_info
+ key_num
[n_key
];
995 index
= dict_table_get_index_on_name_and_min_id(
996 prebuilt
->table
, key
->name
);
999 sql_print_error("InnoDB could not find key n:o %u "
1000 "with name %s for table %s",
1002 key
? key
->name
: "NULL",
1003 prebuilt
->table
->name
);
1005 err
= HA_ERR_KEY_NOT_FOUND
;
1009 /* Refuse to drop the clustered index. It would be
1010 better to automatically generate a clustered index,
1011 but mysql_alter_table() will call this method only
1012 after ha_innobase::add_index(). */
1014 if (dict_index_is_clust(index
)) {
1015 my_error(ER_REQUIRES_PRIMARY_KEY
, MYF(0));
1020 rw_lock_x_lock(dict_index_get_lock(index
));
1021 index
->to_be_dropped
= TRUE
;
1022 rw_lock_x_unlock(dict_index_get_lock(index
));
1025 /* If FOREIGN_KEY_CHECKS = 1 you may not drop an index defined
1026 for a foreign key constraint because InnoDB requires that both
1027 tables contain indexes for the constraint. Such index can
1028 be dropped only if FOREIGN_KEY_CHECKS is set to 0.
1029 Note that CREATE INDEX id ON table does a CREATE INDEX and
1030 DROP INDEX, and we can ignore here foreign keys because a
1031 new index for the foreign key has already been created.
1033 We check for the foreign key constraints after marking the
1034 candidate indexes for deletion, because when we check for an
1035 equivalent foreign index we don't want to select an index that
1036 is later deleted. */
1038 if (trx
->check_foreigns
1039 && thd_sql_command(user_thd
) != SQLCOM_CREATE_INDEX
) {
1040 dict_index_t
* index
;
1042 for (index
= dict_table_get_first_index(prebuilt
->table
);
1044 index
= dict_table_get_next_index(index
)) {
1045 dict_foreign_t
* foreign
;
1047 if (!index
->to_be_dropped
) {
1052 /* Check if the index is referenced. */
1053 foreign
= dict_table_get_referenced_constraint(
1054 prebuilt
->table
, index
);
1058 trx_set_detailed_error(
1060 "Index needed in foreign key "
1063 trx
->error_info
= index
;
1065 err
= HA_ERR_DROP_INDEX_FK
;
1068 /* Check if this index references some
1070 foreign
= dict_table_get_foreign_constraint(
1071 prebuilt
->table
, index
);
1074 ut_a(foreign
->foreign_index
== index
);
1076 /* Search for an equivalent index that
1077 the foreign key constraint could use
1078 if this index were to be deleted. */
1079 if (!dict_foreign_find_equiv_index(
1087 } else if (thd_sql_command(user_thd
) == SQLCOM_CREATE_INDEX
) {
1088 /* This is a drop of a foreign key constraint index that
1089 was created by MySQL when the constraint was added. MySQL
1090 does this when the user creates an index explicitly which
1091 can be used in place of the automatically generated index. */
1093 dict_index_t
* index
;
1095 for (index
= dict_table_get_first_index(prebuilt
->table
);
1097 index
= dict_table_get_next_index(index
)) {
1098 dict_foreign_t
* foreign
;
1100 if (!index
->to_be_dropped
) {
1105 /* Check if this index references some other table */
1106 foreign
= dict_table_get_foreign_constraint(
1107 prebuilt
->table
, index
);
1109 if (foreign
== NULL
) {
1114 ut_a(foreign
->foreign_index
== index
);
1116 /* Search for an equivalent index that the
1117 foreign key constraint could use if this index
1118 were to be deleted. */
1120 if (!dict_foreign_find_equiv_index(foreign
)) {
1121 trx_set_detailed_error(
1123 "Index needed in foreign key "
1126 trx
->error_info
= foreign
->foreign_index
;
1128 err
= HA_ERR_DROP_INDEX_FK
;
1136 /* Undo our changes since there was some sort of error. */
1138 = dict_table_get_first_index(prebuilt
->table
);
1141 rw_lock_x_lock(dict_index_get_lock(index
));
1142 index
->to_be_dropped
= FALSE
;
1143 rw_lock_x_unlock(dict_index_get_lock(index
));
1144 index
= dict_table_get_next_index(index
);
1148 ut_d(dict_table_check_for_dup_indexes(prebuilt
->table
, FALSE
));
1149 row_mysql_unlock_data_dictionary(trx
);
1154 /*******************************************************************//**
1155 Drop the indexes that were passed to a successful prepare_drop_index().
1156 @return 0 or error number */
1159 ha_innobase::final_drop_index(
1160 /*==========================*/
1161 TABLE
* table
) /*!< in: Table where indexes are dropped */
1163 dict_index_t
* index
; /*!< Index to be dropped */
1164 trx_t
* trx
; /*!< Transaction */
1167 DBUG_ENTER("ha_innobase::final_drop_index");
1170 if (srv_created_new_raw
|| srv_force_recovery
) {
1171 DBUG_RETURN(HA_ERR_WRONG_COMMAND
);
1176 trx_search_latch_release_if_reserved(prebuilt
->trx
);
1177 trx_start_if_not_started(prebuilt
->trx
);
1179 /* Create a background transaction for the operations on
1180 the data dictionary tables. */
1181 trx
= innobase_trx_allocate(user_thd
);
1182 trx_start_if_not_started(trx
);
1184 /* Flag this transaction as a dictionary operation, so that
1185 the data dictionary will be locked in crash recovery. */
1186 trx_set_dict_operation(trx
, TRX_DICT_OP_INDEX
);
1188 /* Lock the table exclusively, to ensure that no active
1189 transaction depends on an index that is being dropped. */
1190 err
= convert_error_code_to_mysql(
1191 row_merge_lock_table(prebuilt
->trx
, prebuilt
->table
, LOCK_X
),
1192 prebuilt
->table
->flags
, user_thd
);
1194 row_mysql_lock_data_dictionary(trx
);
1195 ut_d(dict_table_check_for_dup_indexes(prebuilt
->table
, FALSE
));
1197 if (UNIV_UNLIKELY(err
)) {
1199 /* Unmark the indexes to be dropped. */
1200 for (index
= dict_table_get_first_index(prebuilt
->table
);
1201 index
; index
= dict_table_get_next_index(index
)) {
1203 rw_lock_x_lock(dict_index_get_lock(index
));
1204 index
->to_be_dropped
= FALSE
;
1205 rw_lock_x_unlock(dict_index_get_lock(index
));
1211 /* Drop indexes marked to be dropped */
1213 index
= dict_table_get_first_index(prebuilt
->table
);
1216 dict_index_t
* next_index
;
1218 next_index
= dict_table_get_next_index(index
);
1220 if (index
->to_be_dropped
) {
1222 row_merge_drop_index(index
, prebuilt
->table
, trx
);
1228 /* Check that all flagged indexes were dropped. */
1229 for (index
= dict_table_get_first_index(prebuilt
->table
);
1230 index
; index
= dict_table_get_next_index(index
)) {
1231 ut_a(!index
->to_be_dropped
);
1234 /* We will need to rebuild index translation table. Set
1235 valid index entry count in the translation table to zero */
1236 share
->idx_trans_tbl
.index_count
= 0;
1239 ut_d(dict_table_check_for_dup_indexes(prebuilt
->table
, FALSE
));
1240 trx_commit_for_mysql(trx
);
1241 trx_commit_for_mysql(prebuilt
->trx
);
1242 row_mysql_unlock_data_dictionary(trx
);
1244 /* Flush the log to reduce probability that the .frm files and
1245 the InnoDB data dictionary get out-of-sync if the user runs
1246 with innodb_flush_log_at_trx_commit = 0 */
1248 log_buffer_flush_to_disk();
1250 trx_free_for_mysql(trx
);
1252 /* Tell the InnoDB server that there might be work for
1255 srv_active_wake_master_thread();