1 /*****************************************************************************
2
3 Copyright (c) 2005, 2013, Oracle and/or its affiliates. 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.,
15 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
16
17 *****************************************************************************/
18
19 /**************************************************//**
20 @file handler/handler0alter.cc
21 Smart ALTER TABLE
22 *******************************************************/
23
24 #include <unireg.h>
25 #include <mysqld_error.h>
26 #include <sql_lex.h> // SQLCOM_CREATE_INDEX
27 #include <mysql/innodb_priv.h>
28
29 extern "C" {
30 #include "log0log.h"
31 #include "row0merge.h"
32 #include "srv0srv.h"
33 #include "trx0trx.h"
34 #include "trx0roll.h"
35 #include "ha_prototypes.h"
36 #include "handler0alter.h"
37 }
38
39 #include "ha_innodb.h"
40
41 /*************************************************************//**
42 Copies an InnoDB column to a MySQL field. This function is
43 adapted from row_sel_field_store_in_mysql_format(). */
44 static
45 void
innobase_col_to_mysql(const dict_col_t * col,const uchar * data,ulint len,Field * field)46 innobase_col_to_mysql(
47 /*==================*/
48 const dict_col_t* col, /*!< in: InnoDB column */
49 const uchar* data, /*!< in: InnoDB column data */
50 ulint len, /*!< in: length of data, in bytes */
51 Field* field) /*!< in/out: MySQL field */
52 {
53 uchar* ptr;
54 uchar* dest = field->ptr;
55 ulint flen = field->pack_length();
56
57 switch (col->mtype) {
58 case DATA_INT:
59 ut_ad(len == flen);
60
61 /* Convert integer data from Innobase to little-endian
62 format, sign bit restored to normal */
63
64 for (ptr = dest + len; ptr != dest; ) {
65 *--ptr = *data++;
66 }
67
68 if (!(field->flags & UNSIGNED_FLAG)) {
69 ((byte*) dest)[len - 1] ^= 0x80;
70 }
71
72 break;
73
74 case DATA_VARCHAR:
75 case DATA_VARMYSQL:
76 case DATA_BINARY:
77 field->reset();
78
79 if (field->type() == MYSQL_TYPE_VARCHAR) {
80 /* This is a >= 5.0.3 type true VARCHAR. Store the
81 length of the data to the first byte or the first
82 two bytes of dest. */
83
84 dest = row_mysql_store_true_var_len(
85 dest, len, flen - field->key_length());
86 }
87
88 /* Copy the actual data */
89 memcpy(dest, data, len);
90 break;
91
92 case DATA_BLOB:
93 /* Store a pointer to the BLOB buffer to dest: the BLOB was
94 already copied to the buffer in row_sel_store_mysql_rec */
95
96 row_mysql_store_blob_ref(dest, flen, data, len);
97 break;
98
99 #ifdef UNIV_DEBUG
100 case DATA_MYSQL:
101 ut_ad(flen >= len);
102 ut_ad(DATA_MBMAXLEN(col->mbminmaxlen)
103 >= DATA_MBMINLEN(col->mbminmaxlen));
104 memcpy(dest, data, len);
105 break;
106
107 default:
108 case DATA_SYS_CHILD:
109 case DATA_SYS:
110 /* These column types should never be shipped to MySQL. */
111 ut_ad(0);
112
113 case DATA_FLOAT:
114 case DATA_DOUBLE:
115 case DATA_DECIMAL:
116 /* Above are the valid column types for MySQL data. */
117 ut_ad(flen == len);
118 /* fall through */
119 case DATA_FIXBINARY:
120 case DATA_CHAR:
121 /* We may have flen > len when there is a shorter
122 prefix on the CHAR and BINARY column. */
123 ut_ad(flen >= len);
124 #else /* UNIV_DEBUG */
125 default:
126 #endif /* UNIV_DEBUG */
127 memcpy(dest, data, len);
128 }
129 }
130
131 /*************************************************************//**
132 Copies an InnoDB record to table->record[0]. */
133 extern "C" UNIV_INTERN
134 void
innobase_rec_to_mysql(TABLE * table,const rec_t * rec,const dict_index_t * index,const ulint * offsets)135 innobase_rec_to_mysql(
136 /*==================*/
137 TABLE* table, /*!< in/out: MySQL table */
138 const rec_t* rec, /*!< in: record */
139 const dict_index_t* index, /*!< in: index */
140 const ulint* offsets) /*!< in: rec_get_offsets(
141 rec, index, ...) */
142 {
143 uint n_fields = table->s->fields;
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->field[i];
150 ulint ipos;
151 ulint ilen;
152 const uchar* ifield;
153
154 field->reset();
155
156 ipos = dict_index_get_nth_col_or_prefix_pos(index, i, TRUE);
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
174 innobase_col_to_mysql(
175 dict_field_get_col(
176 dict_index_get_nth_field(index, ipos)),
177 ifield, ilen, field);
178 }
179 }
180
181 /*************************************************************//**
182 Resets table->record[0]. */
183 extern "C" UNIV_INTERN
184 void
innobase_rec_reset(TABLE * table)185 innobase_rec_reset(
186 /*===============*/
187 TABLE* table) /*!< in/out: MySQL table */
188 {
189 uint n_fields = table->s->fields;
190 uint i;
191
192 for (i = 0; i < n_fields; i++) {
193 table->field[i]->set_default();
194 }
195 }
196
197 /******************************************************************//**
198 Removes the filename encoding of a database and table name. */
199 static
200 void
innobase_convert_tablename(char * s)201 innobase_convert_tablename(
202 /*=======================*/
203 char* s) /*!< in: identifier; out: decoded identifier */
204 {
205 uint errors;
206
207 char* slash = strchr(s, '/');
208
209 if (slash) {
210 char* t;
211 /* Temporarily replace the '/' with NUL. */
212 *slash = 0;
213 /* Convert the database name. */
214 strconvert(&my_charset_filename, s, system_charset_info,
215 s, slash - s + 1, &errors);
216
217 t = s + strlen(s);
218 ut_ad(slash >= t);
219 /* Append a '.' after the database name. */
220 *t++ = '.';
221 slash++;
222 /* Convert the table name. */
223 strconvert(&my_charset_filename, slash, system_charset_info,
224 t, slash - t + strlen(slash), &errors);
225 } else {
226 strconvert(&my_charset_filename, s,
227 system_charset_info, s, strlen(s), &errors);
228 }
229 }
230
231 /*******************************************************************//**
232 This function checks that index keys are sensible.
233 @return 0 or error number */
234 static
235 int
innobase_check_index_keys(const KEY * key_info,ulint num_of_keys,const dict_table_t * table)236 innobase_check_index_keys(
237 /*======================*/
238 const KEY* key_info, /*!< in: Indexes to be
239 created */
240 ulint num_of_keys, /*!< in: Number of
241 indexes to be created */
242 const dict_table_t* table) /*!< in: Existing indexes */
243 {
244 ulint key_num;
245
246 ut_ad(key_info);
247 ut_ad(num_of_keys);
248
249 for (key_num = 0; key_num < num_of_keys; key_num++) {
250 const KEY& key = key_info[key_num];
251
252 /* Check that the same index name does not appear
253 twice in indexes to be created. */
254
255 for (ulint i = 0; i < key_num; i++) {
256 const KEY& key2 = key_info[i];
257
258 if (0 == strcmp(key.name, key2.name)) {
259 my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0),
260 key.name);
261
262 return(ER_WRONG_NAME_FOR_INDEX);
263 }
264 }
265
266 /* Check that the same index name does not already exist. */
267
268 for (const dict_index_t* index
269 = dict_table_get_first_index(table);
270 index; index = dict_table_get_next_index(index)) {
271
272 if (0 == strcmp(key.name, index->name)) {
273 my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0),
274 key.name);
275
276 return(ER_WRONG_NAME_FOR_INDEX);
277 }
278 }
279
280 /* Check that MySQL does not try to create a column
281 prefix index field on an inappropriate data type and
282 that the same column does not appear twice in the index. */
283
284 for (ulint i = 0; i < key.key_parts; i++) {
285 const KEY_PART_INFO& key_part1
286 = key.key_part[i];
287 const Field* field
288 = key_part1.field;
289 ibool is_unsigned;
290
291 switch (get_innobase_type_from_mysql_type(
292 &is_unsigned, field)) {
293 default:
294 break;
295 case DATA_INT:
296 case DATA_FLOAT:
297 case DATA_DOUBLE:
298 case DATA_DECIMAL:
299 if (field->type() == MYSQL_TYPE_VARCHAR) {
300 if (key_part1.length
301 >= field->pack_length()
302 - ((Field_varstring*) field)
303 ->length_bytes) {
304 break;
305 }
306 } else {
307 if (key_part1.length
308 >= field->pack_length()) {
309 break;
310 }
311 }
312
313 my_error(ER_WRONG_KEY_COLUMN, MYF(0),
314 field->field_name);
315 return(ER_WRONG_KEY_COLUMN);
316 }
317
318 for (ulint j = 0; j < i; j++) {
319 const KEY_PART_INFO& key_part2
320 = key.key_part[j];
321
322 if (strcmp(key_part1.field->field_name,
323 key_part2.field->field_name)) {
324 continue;
325 }
326
327 my_error(ER_WRONG_KEY_COLUMN, MYF(0),
328 key_part1.field->field_name);
329 return(ER_WRONG_KEY_COLUMN);
330 }
331 }
332 }
333
334 return(0);
335 }
336
337 /*******************************************************************//**
338 Create index field definition for key part */
339 static
340 void
innobase_create_index_field_def(KEY_PART_INFO * key_part,mem_heap_t * heap,merge_index_field_t * index_field)341 innobase_create_index_field_def(
342 /*============================*/
343 KEY_PART_INFO* key_part, /*!< in: MySQL key definition */
344 mem_heap_t* heap, /*!< in: memory heap */
345 merge_index_field_t* index_field) /*!< out: index field
346 definition for key_part */
347 {
348 Field* field;
349 ibool is_unsigned;
350 ulint col_type;
351
352 DBUG_ENTER("innobase_create_index_field_def");
353
354 ut_ad(key_part);
355 ut_ad(index_field);
356
357 field = key_part->field;
358 ut_a(field);
359
360 col_type = get_innobase_type_from_mysql_type(&is_unsigned, field);
361
362 if (DATA_BLOB == col_type
363 || (key_part->length < field->pack_length()
364 && field->type() != MYSQL_TYPE_VARCHAR)
365 || (field->type() == MYSQL_TYPE_VARCHAR
366 && key_part->length < field->pack_length()
367 - ((Field_varstring*)field)->length_bytes)) {
368
369 index_field->prefix_len = key_part->length;
370 } else {
371 index_field->prefix_len = 0;
372 }
373
374 index_field->field_name = mem_heap_strdup(heap, field->field_name);
375
376 DBUG_VOID_RETURN;
377 }
378
379 /*******************************************************************//**
380 Create index definition for key */
381 static
382 void
innobase_create_index_def(KEY * key,bool new_primary,bool key_primary,merge_index_def_t * index,mem_heap_t * heap)383 innobase_create_index_def(
384 /*======================*/
385 KEY* key, /*!< in: key definition */
386 bool new_primary, /*!< in: TRUE=generating
387 a new primary key
388 on the table */
389 bool key_primary, /*!< in: TRUE if this key
390 is a primary key */
391 merge_index_def_t* index, /*!< out: index definition */
392 mem_heap_t* heap) /*!< in: heap where memory
393 is allocated */
394 {
395 ulint i;
396 ulint len;
397 ulint n_fields = key->key_parts;
398 char* index_name;
399
400 DBUG_ENTER("innobase_create_index_def");
401
402 index->fields = (merge_index_field_t*) mem_heap_alloc(
403 heap, n_fields * sizeof *index->fields);
404
405 index->ind_type = 0;
406 index->n_fields = n_fields;
407 len = strlen(key->name) + 1;
408 index->name = index_name = (char*) mem_heap_alloc(heap,
409 len + !new_primary);
410
411 if (UNIV_LIKELY(!new_primary)) {
412 *index_name++ = TEMP_INDEX_PREFIX;
413 }
414
415 memcpy(index_name, key->name, len);
416
417 if (key->flags & HA_NOSAME) {
418 index->ind_type |= DICT_UNIQUE;
419 }
420
421 if (key_primary) {
422 index->ind_type |= DICT_CLUSTERED;
423 }
424
425 for (i = 0; i < n_fields; i++) {
426 innobase_create_index_field_def(&key->key_part[i], heap,
427 &index->fields[i]);
428 }
429
430 DBUG_VOID_RETURN;
431 }
432
433 /*******************************************************************//**
434 Copy index field definition */
435 static
436 void
innobase_copy_index_field_def(const dict_field_t * field,merge_index_field_t * index_field)437 innobase_copy_index_field_def(
438 /*==========================*/
439 const dict_field_t* field, /*!< in: definition to copy */
440 merge_index_field_t* index_field) /*!< out: copied definition */
441 {
442 DBUG_ENTER("innobase_copy_index_field_def");
443 DBUG_ASSERT(field != NULL);
444 DBUG_ASSERT(index_field != NULL);
445
446 index_field->field_name = field->name;
447 index_field->prefix_len = field->prefix_len;
448
449 DBUG_VOID_RETURN;
450 }
451
452 /*******************************************************************//**
453 Copy index definition for the index */
454 static
455 void
innobase_copy_index_def(const dict_index_t * index,merge_index_def_t * new_index,mem_heap_t * heap)456 innobase_copy_index_def(
457 /*====================*/
458 const dict_index_t* index, /*!< in: index definition to copy */
459 merge_index_def_t* new_index,/*!< out: Index definition */
460 mem_heap_t* heap) /*!< in: heap where allocated */
461 {
462 ulint n_fields;
463 ulint i;
464
465 DBUG_ENTER("innobase_copy_index_def");
466
467 /* Note that we take only those fields that user defined to be
468 in the index. In the internal representation more colums were
469 added and those colums are not copied .*/
470
471 n_fields = index->n_user_defined_cols;
472
473 new_index->fields = (merge_index_field_t*) mem_heap_alloc(
474 heap, n_fields * sizeof *new_index->fields);
475
476 /* When adding a PRIMARY KEY, we may convert a previous
477 clustered index to a secondary index (UNIQUE NOT NULL). */
478 new_index->ind_type = index->type & ~DICT_CLUSTERED;
479 new_index->n_fields = n_fields;
480 new_index->name = index->name;
481
482 for (i = 0; i < n_fields; i++) {
483 innobase_copy_index_field_def(&index->fields[i],
484 &new_index->fields[i]);
485 }
486
487 DBUG_VOID_RETURN;
488 }
489
490 /*******************************************************************//**
491 Create an index table where indexes are ordered as follows:
492
493 IF a new primary key is defined for the table THEN
494
495 1) New primary key
496 2) Original secondary indexes
497 3) New secondary indexes
498
499 ELSE
500
501 1) All new indexes in the order they arrive from MySQL
502
503 ENDIF
504
505
506 @return key definitions or NULL */
507 static
508 merge_index_def_t*
innobase_create_key_def(trx_t * trx,const dict_table_t * table,mem_heap_t * heap,KEY * key_info,ulint & n_keys)509 innobase_create_key_def(
510 /*====================*/
511 trx_t* trx, /*!< in: trx */
512 const dict_table_t*table, /*!< in: table definition */
513 mem_heap_t* heap, /*!< in: heap where space for key
514 definitions are allocated */
515 KEY* key_info, /*!< in: Indexes to be created */
516 ulint& n_keys) /*!< in/out: Number of indexes to
517 be created */
518 {
519 ulint i = 0;
520 merge_index_def_t* indexdef;
521 merge_index_def_t* indexdefs;
522 bool new_primary;
523
524 DBUG_ENTER("innobase_create_key_def");
525
526 indexdef = indexdefs = (merge_index_def_t*)
527 mem_heap_alloc(heap, sizeof *indexdef
528 * (n_keys + UT_LIST_GET_LEN(table->indexes)));
529
530 /* If there is a primary key, it is always the first index
531 defined for the table. */
532
533 new_primary = !my_strcasecmp(system_charset_info,
534 key_info->name, "PRIMARY");
535
536 /* If there is a UNIQUE INDEX consisting entirely of NOT NULL
537 columns and if the index does not contain column prefix(es)
538 (only prefix/part of the column is indexed), MySQL will treat the
539 index as a PRIMARY KEY unless the table already has one. */
540
541 if (!new_primary && (key_info->flags & HA_NOSAME)
542 && (!(key_info->flags & HA_KEY_HAS_PART_KEY_SEG))
543 && row_table_got_default_clust_index(table)) {
544 uint key_part = key_info->key_parts;
545
546 new_primary = TRUE;
547
548 while (key_part--) {
549 if (key_info->key_part[key_part].key_type
550 & FIELDFLAG_MAYBE_NULL) {
551 new_primary = FALSE;
552 break;
553 }
554 }
555 }
556
557 if (new_primary) {
558 const dict_index_t* index;
559
560 /* Create the PRIMARY key index definition */
561 innobase_create_index_def(&key_info[i++], TRUE, TRUE,
562 indexdef++, heap);
563
564 row_mysql_lock_data_dictionary(trx);
565
566 index = dict_table_get_first_index(table);
567
568 /* Copy the index definitions of the old table. Skip
569 the old clustered index if it is a generated clustered
570 index or a PRIMARY KEY. If the clustered index is a
571 UNIQUE INDEX, it must be converted to a secondary index. */
572
573 if (dict_index_get_nth_col(index, 0)->mtype == DATA_SYS
574 || !my_strcasecmp(system_charset_info,
575 index->name, "PRIMARY")) {
576 index = dict_table_get_next_index(index);
577 }
578
579 while (index) {
580 innobase_copy_index_def(index, indexdef++, heap);
581 index = dict_table_get_next_index(index);
582 }
583
584 row_mysql_unlock_data_dictionary(trx);
585 }
586
587 /* Create definitions for added secondary indexes. */
588
589 while (i < n_keys) {
590 innobase_create_index_def(&key_info[i++], new_primary, FALSE,
591 indexdef++, heap);
592 }
593
594 n_keys = indexdef - indexdefs;
595
596 DBUG_RETURN(indexdefs);
597 }
598
599 /*******************************************************************//**
600 Check each index column size, make sure they do not exceed the max limit
601 @return HA_ERR_INDEX_COL_TOO_LONG if index column size exceeds limit */
602 static
603 int
innobase_check_column_length(const dict_table_t * table,const KEY * key_info)604 innobase_check_column_length(
605 /*=========================*/
606 const dict_table_t*table, /*!< in: table definition */
607 const KEY* key_info) /*!< in: Indexes to be created */
608 {
609 ulint max_col_len = DICT_MAX_FIELD_LEN_BY_FORMAT(table);
610
611 for (ulint key_part = 0; key_part < key_info->key_parts; key_part++) {
612 if (key_info->key_part[key_part].length > max_col_len) {
613 my_error(ER_INDEX_COLUMN_TOO_LONG, MYF(0), max_col_len);
614 return(HA_ERR_INDEX_COL_TOO_LONG);
615 }
616 }
617 return(0);
618 }
619
620 /*******************************************************************//**
621 Create a temporary tablename using query id, thread id, and id
622 @return temporary tablename */
623 static
624 char*
innobase_create_temporary_tablename(mem_heap_t * heap,char id,const char * table_name)625 innobase_create_temporary_tablename(
626 /*================================*/
627 mem_heap_t* heap, /*!< in: memory heap */
628 char id, /*!< in: identifier [0-9a-zA-Z] */
629 const char* table_name) /*!< in: table name */
630 {
631 char* name;
632 ulint len;
633 static const char suffix[] = "@0023 "; /* "# " */
634
635 len = strlen(table_name);
636
637 name = (char*) mem_heap_alloc(heap, len + sizeof suffix);
638 memcpy(name, table_name, len);
639 memcpy(name + len, suffix, sizeof suffix);
640 name[len + (sizeof suffix - 2)] = id;
641
642 return(name);
643 }
644
645 class ha_innobase_add_index : public handler_add_index
646 {
647 public:
648 /** table where the indexes are being created */
649 dict_table_t* indexed_table;
ha_innobase_add_index(TABLE * table,KEY * key_info,uint num_of_keys,dict_table_t * indexed_table_arg)650 ha_innobase_add_index(TABLE* table, KEY* key_info, uint num_of_keys,
651 dict_table_t* indexed_table_arg) :
652 handler_add_index(table, key_info, num_of_keys),
653 indexed_table (indexed_table_arg) {}
~ha_innobase_add_index()654 ~ha_innobase_add_index() {}
655 };
656
657 /*******************************************************************//**
658 Create indexes.
659 @return 0 or error number */
660 UNIV_INTERN
661 int
add_index(TABLE * table,KEY * key_info,uint num_of_keys,handler_add_index ** add)662 ha_innobase::add_index(
663 /*===================*/
664 TABLE* table, /*!< in: Table where indexes
665 are created */
666 KEY* key_info, /*!< in: Indexes
667 to be created */
668 uint num_of_keys, /*!< in: Number of indexes
669 to be created */
670 handler_add_index** add) /*!< out: context */
671 {
672 dict_index_t** index; /*!< Index to be created */
673 dict_table_t* indexed_table; /*!< Table where indexes are created */
674 merge_index_def_t* index_defs; /*!< Index definitions */
675 mem_heap_t* heap; /*!< Heap for index definitions */
676 trx_t* trx; /*!< Transaction */
677 ulint num_of_idx;
678 ulint num_created = 0;
679 ibool dict_locked = FALSE;
680 ulint new_primary;
681 int error;
682
683 DBUG_ENTER("ha_innobase::add_index");
684 ut_a(table);
685 ut_a(key_info);
686 ut_a(num_of_keys);
687
688 *add = NULL;
689
690 if (srv_created_new_raw || srv_force_recovery) {
691 DBUG_RETURN(HA_ERR_WRONG_COMMAND);
692 }
693
694 update_thd();
695
696 /* In case MySQL calls this in the middle of a SELECT query, release
697 possible adaptive hash latch to avoid deadlocks of threads. */
698 trx_search_latch_release_if_reserved(prebuilt->trx);
699
700 /* Check if the index name is reserved. */
701 if (innobase_index_name_is_reserved(user_thd, key_info, num_of_keys)) {
702 DBUG_RETURN(-1);
703 }
704
705 indexed_table = dict_table_get(prebuilt->table->name, FALSE,
706 DICT_ERR_IGNORE_NONE);
707
708 if (UNIV_UNLIKELY(!indexed_table)) {
709 DBUG_RETURN(HA_ERR_NO_SUCH_TABLE);
710 }
711
712 ut_a(indexed_table == prebuilt->table);
713
714 if (indexed_table->tablespace_discarded) {
715 DBUG_RETURN(-1);
716 }
717
718 /* Check that index keys are sensible */
719 error = innobase_check_index_keys(key_info, num_of_keys, prebuilt->table);
720
721 if (UNIV_UNLIKELY(error)) {
722 DBUG_RETURN(error);
723 }
724
725 /* Check each index's column length to make sure they do not
726 exceed limit */
727 for (ulint i = 0; i < num_of_keys; i++) {
728 error = innobase_check_column_length(prebuilt->table,
729 &key_info[i]);
730
731 if (error) {
732 DBUG_RETURN(error);
733 }
734 }
735
736 heap = mem_heap_create(1024);
737 trx_start_if_not_started(prebuilt->trx);
738
739 /* Create a background transaction for the operations on
740 the data dictionary tables. */
741 trx = innobase_trx_allocate(user_thd);
742 trx_start_if_not_started(trx);
743
744 /* Create table containing all indexes to be built in this
745 alter table add index so that they are in the correct order
746 in the table. */
747
748 num_of_idx = num_of_keys;
749
750 index_defs = innobase_create_key_def(
751 trx, prebuilt->table, heap, key_info, num_of_idx);
752
753 new_primary = DICT_CLUSTERED & index_defs[0].ind_type;
754
755 /* Allocate memory for dictionary index definitions */
756
757 index = (dict_index_t**) mem_heap_alloc(
758 heap, num_of_idx * sizeof *index);
759
760 /* Flag this transaction as a dictionary operation, so that
761 the data dictionary will be locked in crash recovery. */
762 trx_set_dict_operation(trx, TRX_DICT_OP_INDEX);
763
764 /* Acquire a lock on the table before creating any indexes. */
765 error = row_merge_lock_table(prebuilt->trx, prebuilt->table,
766 new_primary ? LOCK_X : LOCK_S);
767
768 if (UNIV_UNLIKELY(error != DB_SUCCESS)) {
769
770 goto error_handling;
771 }
772
773 /* Latch the InnoDB data dictionary exclusively so that no deadlocks
774 or lock waits can happen in it during an index create operation. */
775
776 row_mysql_lock_data_dictionary(trx);
777 dict_locked = TRUE;
778
779 ut_d(dict_table_check_for_dup_indexes(prebuilt->table, TRUE));
780
781 /* If a new primary key is defined for the table we need
782 to drop the original table and rebuild all indexes. */
783
784 if (UNIV_UNLIKELY(new_primary)) {
785 /* This transaction should be the only one
786 operating on the table. */
787 ut_a(prebuilt->table->n_mysql_handles_opened == 1);
788
789 char* new_table_name = innobase_create_temporary_tablename(
790 heap, '1', prebuilt->table->name);
791
792 /* Clone the table. */
793 trx_set_dict_operation(trx, TRX_DICT_OP_TABLE);
794 indexed_table = row_merge_create_temporary_table(
795 new_table_name, index_defs, prebuilt->table, trx);
796
797 if (!indexed_table) {
798
799 switch (trx->error_state) {
800 case DB_TABLESPACE_ALREADY_EXISTS:
801 case DB_DUPLICATE_KEY:
802 innobase_convert_tablename(new_table_name);
803 my_error(HA_ERR_TABLE_EXIST, MYF(0),
804 new_table_name);
805 error = HA_ERR_TABLE_EXIST;
806 break;
807 default:
808 error = convert_error_code_to_mysql(
809 trx->error_state,
810 prebuilt->table->flags,
811 user_thd);
812 }
813
814 ut_d(dict_table_check_for_dup_indexes(prebuilt->table,
815 TRUE));
816 mem_heap_free(heap);
817 trx_general_rollback_for_mysql(trx, NULL);
818 row_mysql_unlock_data_dictionary(trx);
819 trx_free_for_mysql(trx);
820 trx_commit_for_mysql(prebuilt->trx);
821 DBUG_RETURN(error);
822 }
823
824 trx->table_id = indexed_table->id;
825 }
826
827 /* Create the indexes in SYS_INDEXES and load into dictionary. */
828
829 for (num_created = 0; num_created < num_of_idx; num_created++) {
830
831 index[num_created] = row_merge_create_index(
832 trx, indexed_table, &index_defs[num_created]);
833
834 if (!index[num_created]) {
835 error = trx->error_state;
836 goto error_handling;
837 }
838 }
839
840 ut_ad(error == DB_SUCCESS);
841
842 /* Commit the data dictionary transaction in order to release
843 the table locks on the system tables. This means that if
844 MySQL crashes while creating a new primary key inside
845 row_merge_build_indexes(), indexed_table will not be dropped
846 by trx_rollback_active(). It will have to be recovered or
847 dropped by the database administrator. */
848 trx_commit_for_mysql(trx);
849
850 row_mysql_unlock_data_dictionary(trx);
851 dict_locked = FALSE;
852
853 ut_a(trx->n_active_thrs == 0);
854 ut_a(UT_LIST_GET_LEN(trx->signals) == 0);
855
856 if (UNIV_UNLIKELY(new_primary)) {
857 /* A primary key is to be built. Acquire an exclusive
858 table lock also on the table that is being created. */
859 ut_ad(indexed_table != prebuilt->table);
860
861 error = row_merge_lock_table(prebuilt->trx, indexed_table,
862 LOCK_X);
863
864 if (UNIV_UNLIKELY(error != DB_SUCCESS)) {
865
866 goto error_handling;
867 }
868 }
869
870 /* Read the clustered index of the table and build indexes
871 based on this information using temporary files and merge sort. */
872 error = row_merge_build_indexes(prebuilt->trx,
873 prebuilt->table, indexed_table,
874 index, num_of_idx, table);
875
876 error_handling:
877 /* After an error, remove all those index definitions from the
878 dictionary which were defined. */
879
880 switch (error) {
881 case DB_SUCCESS:
882 ut_a(!dict_locked);
883
884 ut_d(mutex_enter(&dict_sys->mutex));
885 ut_d(dict_table_check_for_dup_indexes(prebuilt->table, TRUE));
886 ut_d(mutex_exit(&dict_sys->mutex));
887 *add = new ha_innobase_add_index(table, key_info, num_of_keys,
888 indexed_table);
889 break;
890
891 case DB_TOO_BIG_RECORD:
892 my_error(HA_ERR_TO_BIG_ROW, MYF(0));
893 goto error;
894 case DB_PRIMARY_KEY_IS_NULL:
895 my_error(ER_PRIMARY_CANT_HAVE_NULL, MYF(0));
896 /* fall through */
897 case DB_DUPLICATE_KEY:
898 error:
899 prebuilt->trx->error_info = NULL;
900 /* fall through */
901 default:
902 trx->error_state = DB_SUCCESS;
903
904 if (new_primary) {
905 if (indexed_table != prebuilt->table) {
906 row_merge_drop_table(trx, indexed_table);
907 }
908 } else {
909 if (!dict_locked) {
910 row_mysql_lock_data_dictionary(trx);
911 dict_locked = TRUE;
912 }
913
914 row_merge_drop_indexes(trx, indexed_table,
915 index, num_created);
916 }
917 }
918
919 trx_commit_for_mysql(trx);
920 if (prebuilt->trx) {
921 trx_commit_for_mysql(prebuilt->trx);
922 }
923
924 if (dict_locked) {
925 row_mysql_unlock_data_dictionary(trx);
926 }
927
928 trx_free_for_mysql(trx);
929 mem_heap_free(heap);
930
931 /* There might be work for utility threads.*/
932 srv_active_wake_master_thread();
933
934 DBUG_RETURN(convert_error_code_to_mysql(error, prebuilt->table->flags,
935 user_thd));
936 }
937
938 /*******************************************************************//**
939 Finalize or undo add_index().
940 @return 0 or error number */
941 UNIV_INTERN
942 int
final_add_index(handler_add_index * add_arg,bool commit)943 ha_innobase::final_add_index(
944 /*=========================*/
945 handler_add_index* add_arg,/*!< in: context from add_index() */
946 bool commit) /*!< in: true=commit, false=rollback */
947 {
948 ha_innobase_add_index* add;
949 trx_t* trx;
950 int err = 0;
951
952 DBUG_ENTER("ha_innobase::final_add_index");
953
954 ut_ad(add_arg);
955 add = static_cast<class ha_innobase_add_index*>(add_arg);
956
957 /* Create a background transaction for the operations on
958 the data dictionary tables. */
959 trx = innobase_trx_allocate(user_thd);
960 trx_start_if_not_started(trx);
961
962 /* Flag this transaction as a dictionary operation, so that
963 the data dictionary will be locked in crash recovery. */
964 trx_set_dict_operation(trx, TRX_DICT_OP_INDEX);
965
966 /* Latch the InnoDB data dictionary exclusively so that no deadlocks
967 or lock waits can happen in it during an index create operation. */
968 row_mysql_lock_data_dictionary(trx);
969
970 if (add->indexed_table != prebuilt->table) {
971 ulint error;
972
973 /* We copied the table (new_primary). */
974 if (commit) {
975 mem_heap_t* heap;
976 char* tmp_name;
977
978 heap = mem_heap_create(1024);
979
980 /* A new primary key was defined for the table
981 and there was no error at this point. We can
982 now rename the old table as a temporary table,
983 rename the new temporary table as the old
984 table and drop the old table. */
985 tmp_name = innobase_create_temporary_tablename(
986 heap, '2', prebuilt->table->name);
987
988 error = row_merge_rename_tables(
989 prebuilt->table, add->indexed_table,
990 tmp_name, trx);
991
992 switch (error) {
993 case DB_TABLESPACE_ALREADY_EXISTS:
994 case DB_DUPLICATE_KEY:
995 innobase_convert_tablename(tmp_name);
996 my_error(HA_ERR_TABLE_EXIST, MYF(0), tmp_name);
997 err = HA_ERR_TABLE_EXIST;
998 break;
999 default:
1000 err = convert_error_code_to_mysql(
1001 error, prebuilt->table->flags,
1002 user_thd);
1003 break;
1004 }
1005
1006 mem_heap_free(heap);
1007 }
1008
1009 if (!commit || err) {
1010 error = row_merge_drop_table(trx, add->indexed_table);
1011 trx_commit_for_mysql(prebuilt->trx);
1012 } else {
1013 dict_table_t* old_table = prebuilt->table;
1014 trx_commit_for_mysql(prebuilt->trx);
1015 row_prebuilt_free(prebuilt, TRUE);
1016 error = row_merge_drop_table(trx, old_table);
1017 add->indexed_table->n_mysql_handles_opened++;
1018 prebuilt = row_create_prebuilt(add->indexed_table,
1019 0 /* XXX Do we know the mysql_row_len here?
1020 Before the addition of this parameter to
1021 row_create_prebuilt() the mysql_row_len
1022 member was left 0 (from zalloc) in the
1023 prebuilt object. */);
1024 }
1025
1026 err = convert_error_code_to_mysql(
1027 error, prebuilt->table->flags, user_thd);
1028 } else {
1029 /* We created secondary indexes (!new_primary). */
1030
1031 if (commit) {
1032 err = convert_error_code_to_mysql(
1033 row_merge_rename_indexes(trx, prebuilt->table),
1034 prebuilt->table->flags, user_thd);
1035 }
1036
1037 if (!commit || err) {
1038 dict_index_t* index;
1039 dict_index_t* next_index;
1040
1041 for (index = dict_table_get_first_index(
1042 prebuilt->table);
1043 index; index = next_index) {
1044
1045 next_index = dict_table_get_next_index(index);
1046
1047 if (*index->name == TEMP_INDEX_PREFIX) {
1048 row_merge_drop_index(
1049 index, prebuilt->table, trx);
1050 }
1051 }
1052 }
1053 }
1054
1055 /* If index is successfully built, we will need to rebuild index
1056 translation table. Set valid index entry count in the translation
1057 table to zero. */
1058 if (err == 0 && commit) {
1059 share->idx_trans_tbl.index_count = 0;
1060 }
1061
1062 trx_commit_for_mysql(trx);
1063 if (prebuilt->trx) {
1064 trx_commit_for_mysql(prebuilt->trx);
1065 }
1066
1067 ut_d(dict_table_check_for_dup_indexes(prebuilt->table, TRUE));
1068 row_mysql_unlock_data_dictionary(trx);
1069
1070 trx_free_for_mysql(trx);
1071
1072 /* There might be work for utility threads.*/
1073 srv_active_wake_master_thread();
1074
1075 delete add;
1076 DBUG_RETURN(err);
1077 }
1078
1079 /*******************************************************************//**
1080 Prepare to drop some indexes of a table.
1081 @return 0 or error number */
1082 UNIV_INTERN
1083 int
prepare_drop_index(TABLE * table,uint * key_num,uint num_of_keys)1084 ha_innobase::prepare_drop_index(
1085 /*============================*/
1086 TABLE* table, /*!< in: Table where indexes are dropped */
1087 uint* key_num, /*!< in: Key nums to be dropped */
1088 uint num_of_keys) /*!< in: Number of keys to be dropped */
1089 {
1090 trx_t* trx;
1091 int err = 0;
1092 uint n_key;
1093
1094 DBUG_ENTER("ha_innobase::prepare_drop_index");
1095 ut_ad(table);
1096 ut_ad(key_num);
1097 ut_ad(num_of_keys);
1098 if (srv_created_new_raw || srv_force_recovery) {
1099 DBUG_RETURN(HA_ERR_WRONG_COMMAND);
1100 }
1101
1102 update_thd();
1103
1104 trx_search_latch_release_if_reserved(prebuilt->trx);
1105 trx = prebuilt->trx;
1106
1107 /* Test and mark all the indexes to be dropped */
1108
1109 row_mysql_lock_data_dictionary(trx);
1110 ut_d(dict_table_check_for_dup_indexes(prebuilt->table, TRUE));
1111
1112 /* Check that none of the indexes have previously been flagged
1113 for deletion. */
1114 {
1115 const dict_index_t* index
1116 = dict_table_get_first_index(prebuilt->table);
1117 do {
1118 ut_a(!index->to_be_dropped);
1119 index = dict_table_get_next_index(index);
1120 } while (index);
1121 }
1122
1123 for (n_key = 0; n_key < num_of_keys; n_key++) {
1124 const KEY* key;
1125 dict_index_t* index;
1126
1127 key = table->key_info + key_num[n_key];
1128 index = dict_table_get_index_on_name_and_min_id(
1129 prebuilt->table, key->name);
1130
1131 if (!index) {
1132 sql_print_error("InnoDB could not find key n:o %u "
1133 "with name %s for table %s",
1134 key_num[n_key],
1135 key ? key->name : "NULL",
1136 prebuilt->table->name);
1137
1138 err = HA_ERR_KEY_NOT_FOUND;
1139 goto func_exit;
1140 }
1141
1142 /* Refuse to drop the clustered index. It would be
1143 better to automatically generate a clustered index,
1144 but mysql_alter_table() will call this method only
1145 after ha_innobase::add_index(). */
1146
1147 if (dict_index_is_clust(index)) {
1148 my_error(ER_REQUIRES_PRIMARY_KEY, MYF(0));
1149 err = -1;
1150 goto func_exit;
1151 }
1152
1153 rw_lock_x_lock(dict_index_get_lock(index));
1154 index->to_be_dropped = TRUE;
1155 rw_lock_x_unlock(dict_index_get_lock(index));
1156 }
1157
1158 /* If FOREIGN_KEY_CHECKS = 1 you may not drop an index defined
1159 for a foreign key constraint because InnoDB requires that both
1160 tables contain indexes for the constraint. Such index can
1161 be dropped only if FOREIGN_KEY_CHECKS is set to 0.
1162 Note that CREATE INDEX id ON table does a CREATE INDEX and
1163 DROP INDEX, and we can ignore here foreign keys because a
1164 new index for the foreign key has already been created.
1165
1166 We check for the foreign key constraints after marking the
1167 candidate indexes for deletion, because when we check for an
1168 equivalent foreign index we don't want to select an index that
1169 is later deleted. */
1170
1171 if (trx->check_foreigns
1172 && thd_sql_command(user_thd) != SQLCOM_CREATE_INDEX) {
1173 dict_index_t* index;
1174
1175 for (index = dict_table_get_first_index(prebuilt->table);
1176 index;
1177 index = dict_table_get_next_index(index)) {
1178 dict_foreign_t* foreign;
1179
1180 if (!index->to_be_dropped) {
1181
1182 continue;
1183 }
1184
1185 /* Check if the index is referenced. */
1186 foreign = dict_table_get_referenced_constraint(
1187 prebuilt->table, index);
1188
1189 if (foreign) {
1190 index_needed:
1191 trx_set_detailed_error(
1192 trx,
1193 "Index needed in foreign key "
1194 "constraint");
1195
1196 trx->error_info = index;
1197
1198 err = HA_ERR_DROP_INDEX_FK;
1199 break;
1200 } else {
1201 /* Check if this index references some
1202 other table */
1203 foreign = dict_table_get_foreign_constraint(
1204 prebuilt->table, index);
1205
1206 if (foreign) {
1207 ut_a(foreign->foreign_index == index);
1208
1209 /* Search for an equivalent index that
1210 the foreign key constraint could use
1211 if this index were to be deleted. */
1212 if (!dict_foreign_find_equiv_index(
1213 foreign)) {
1214
1215 goto index_needed;
1216 }
1217 }
1218 }
1219 }
1220 } else if (thd_sql_command(user_thd) == SQLCOM_CREATE_INDEX) {
1221 /* This is a drop of a foreign key constraint index that
1222 was created by MySQL when the constraint was added. MySQL
1223 does this when the user creates an index explicitly which
1224 can be used in place of the automatically generated index. */
1225
1226 dict_index_t* index;
1227
1228 for (index = dict_table_get_first_index(prebuilt->table);
1229 index;
1230 index = dict_table_get_next_index(index)) {
1231 dict_foreign_t* foreign;
1232
1233 if (!index->to_be_dropped) {
1234
1235 continue;
1236 }
1237
1238 /* Check if this index references some other table */
1239 foreign = dict_table_get_foreign_constraint(
1240 prebuilt->table, index);
1241
1242 if (foreign == NULL) {
1243
1244 continue;
1245 }
1246
1247 ut_a(foreign->foreign_index == index);
1248
1249 /* Search for an equivalent index that the
1250 foreign key constraint could use if this index
1251 were to be deleted. */
1252
1253 if (!dict_foreign_find_equiv_index(foreign)) {
1254 trx_set_detailed_error(
1255 trx,
1256 "Index needed in foreign key "
1257 "constraint");
1258
1259 trx->error_info = foreign->foreign_index;
1260
1261 err = HA_ERR_DROP_INDEX_FK;
1262 break;
1263 }
1264 }
1265 }
1266
1267 func_exit:
1268 if (err) {
1269 /* Undo our changes since there was some sort of error. */
1270 dict_index_t* index
1271 = dict_table_get_first_index(prebuilt->table);
1272
1273 do {
1274 rw_lock_x_lock(dict_index_get_lock(index));
1275 index->to_be_dropped = FALSE;
1276 rw_lock_x_unlock(dict_index_get_lock(index));
1277 index = dict_table_get_next_index(index);
1278 } while (index);
1279 }
1280
1281 ut_d(dict_table_check_for_dup_indexes(prebuilt->table, TRUE));
1282 row_mysql_unlock_data_dictionary(trx);
1283
1284 DBUG_RETURN(err);
1285 }
1286
1287 /*******************************************************************//**
1288 Drop the indexes that were passed to a successful prepare_drop_index().
1289 @return 0 or error number */
1290 UNIV_INTERN
1291 int
final_drop_index(TABLE * table)1292 ha_innobase::final_drop_index(
1293 /*==========================*/
1294 TABLE* table) /*!< in: Table where indexes are dropped */
1295 {
1296 dict_index_t* index; /*!< Index to be dropped */
1297 trx_t* trx; /*!< Transaction */
1298 int err;
1299
1300 DBUG_ENTER("ha_innobase::final_drop_index");
1301 ut_ad(table);
1302
1303 if (srv_created_new_raw || srv_force_recovery) {
1304 DBUG_RETURN(HA_ERR_WRONG_COMMAND);
1305 }
1306
1307 update_thd();
1308
1309 trx_search_latch_release_if_reserved(prebuilt->trx);
1310 trx_start_if_not_started(prebuilt->trx);
1311
1312 /* Create a background transaction for the operations on
1313 the data dictionary tables. */
1314 trx = innobase_trx_allocate(user_thd);
1315 trx_start_if_not_started(trx);
1316
1317 /* Flag this transaction as a dictionary operation, so that
1318 the data dictionary will be locked in crash recovery. */
1319 trx_set_dict_operation(trx, TRX_DICT_OP_INDEX);
1320
1321 /* Lock the table exclusively, to ensure that no active
1322 transaction depends on an index that is being dropped. */
1323 err = convert_error_code_to_mysql(
1324 row_merge_lock_table(prebuilt->trx, prebuilt->table, LOCK_X),
1325 prebuilt->table->flags, user_thd);
1326
1327 row_mysql_lock_data_dictionary(trx);
1328 ut_d(dict_table_check_for_dup_indexes(prebuilt->table, TRUE));
1329
1330 if (UNIV_UNLIKELY(err)) {
1331
1332 /* Unmark the indexes to be dropped. */
1333 for (index = dict_table_get_first_index(prebuilt->table);
1334 index; index = dict_table_get_next_index(index)) {
1335
1336 rw_lock_x_lock(dict_index_get_lock(index));
1337 index->to_be_dropped = FALSE;
1338 rw_lock_x_unlock(dict_index_get_lock(index));
1339 }
1340
1341 goto func_exit;
1342 }
1343
1344 /* Drop indexes marked to be dropped */
1345
1346 index = dict_table_get_first_index(prebuilt->table);
1347
1348 while (index) {
1349 dict_index_t* next_index;
1350
1351 next_index = dict_table_get_next_index(index);
1352
1353 if (index->to_be_dropped) {
1354
1355 row_merge_drop_index(index, prebuilt->table, trx);
1356 }
1357
1358 index = next_index;
1359 }
1360
1361 /* Check that all flagged indexes were dropped. */
1362 for (index = dict_table_get_first_index(prebuilt->table);
1363 index; index = dict_table_get_next_index(index)) {
1364 ut_a(!index->to_be_dropped);
1365 }
1366
1367 /* We will need to rebuild index translation table. Set
1368 valid index entry count in the translation table to zero */
1369 share->idx_trans_tbl.index_count = 0;
1370
1371 func_exit:
1372 ut_d(dict_table_check_for_dup_indexes(prebuilt->table, TRUE));
1373 trx_commit_for_mysql(trx);
1374 trx_commit_for_mysql(prebuilt->trx);
1375 row_mysql_unlock_data_dictionary(trx);
1376
1377 /* Flush the log to reduce probability that the .frm files and
1378 the InnoDB data dictionary get out-of-sync if the user runs
1379 with innodb_flush_log_at_trx_commit = 0 */
1380
1381 log_buffer_flush_to_disk();
1382
1383 trx_free_for_mysql(trx);
1384
1385 /* Tell the InnoDB server that there might be work for
1386 utility threads: */
1387
1388 srv_active_wake_master_thread();
1389
1390 DBUG_RETURN(err);
1391 }
1392