MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
handler0alter.cc
Go to the documentation of this file.
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 /**************************************************/
24 #include <unireg.h>
25 #include <mysqld_error.h>
26 #include <log.h>
27 #include <debug_sync.h>
28 #include <mysql/innodb_priv.h>
29 #include <sql_alter.h>
30 #include <sql_class.h>
31 
32 #include "dict0crea.h"
33 #include "dict0dict.h"
34 #include "dict0priv.h"
35 #include "dict0stats.h"
36 #include "dict0stats_bg.h"
37 #include "log0log.h"
38 #include "rem0types.h"
39 #include "row0log.h"
40 #include "row0merge.h"
41 #include "srv0srv.h"
42 #include "trx0trx.h"
43 #include "trx0roll.h"
44 #include "ha_prototypes.h"
45 #include "handler0alter.h"
46 #include "srv0mon.h"
47 #include "fts0priv.h"
48 #include "pars0pars.h"
49 
50 #include "ha_innodb.h"
51 
53 static const Alter_inplace_info::HA_ALTER_FLAGS INNOBASE_ONLINE_CREATE
54  = Alter_inplace_info::ADD_INDEX
55  | Alter_inplace_info::ADD_UNIQUE_INDEX;
56 
58 static const Alter_inplace_info::HA_ALTER_FLAGS INNOBASE_ALTER_REBUILD
59  = Alter_inplace_info::ADD_PK_INDEX
60  | Alter_inplace_info::DROP_PK_INDEX
61  | Alter_inplace_info::CHANGE_CREATE_OPTION
62  /* CHANGE_CREATE_OPTION needs to check innobase_need_rebuild() */
63  | Alter_inplace_info::ALTER_COLUMN_NULLABLE
64  | Alter_inplace_info::ALTER_COLUMN_NOT_NULLABLE
65  | Alter_inplace_info::ALTER_COLUMN_ORDER
66  | Alter_inplace_info::DROP_COLUMN
67  | Alter_inplace_info::ADD_COLUMN
68  /*
69  | Alter_inplace_info::ALTER_COLUMN_TYPE
70  | Alter_inplace_info::ALTER_COLUMN_EQUAL_PACK_LENGTH
71  */
72  ;
73 
75 static const Alter_inplace_info::HA_ALTER_FLAGS INNOBASE_ALTER_DATA
76  = INNOBASE_ONLINE_CREATE | INNOBASE_ALTER_REBUILD;
77 
79 static const Alter_inplace_info::HA_ALTER_FLAGS INNOBASE_INPLACE_IGNORE
80  = Alter_inplace_info::ALTER_COLUMN_DEFAULT
81  | Alter_inplace_info::ALTER_COLUMN_COLUMN_FORMAT
82  | Alter_inplace_info::ALTER_COLUMN_STORAGE_TYPE
83  | Alter_inplace_info::ALTER_RENAME;
84 
86 static const Alter_inplace_info::HA_ALTER_FLAGS INNOBASE_FOREIGN_OPERATIONS
87  = Alter_inplace_info::DROP_FOREIGN_KEY
88  | Alter_inplace_info::ADD_FOREIGN_KEY;
89 
91 static const Alter_inplace_info::HA_ALTER_FLAGS INNOBASE_ALTER_NOREBUILD
92  = INNOBASE_ONLINE_CREATE
93  | INNOBASE_FOREIGN_OPERATIONS
94  | Alter_inplace_info::DROP_INDEX
95  | Alter_inplace_info::DROP_UNIQUE_INDEX
96  | Alter_inplace_info::ALTER_COLUMN_NAME;
97 
98 /* Report an InnoDB error to the client by invoking my_error(). */
99 static UNIV_COLD __attribute__((nonnull))
100 void
101 my_error_innodb(
102 /*============*/
103  dberr_t error,
104  const char* table,
105  ulint flags)
106 {
107  switch (error) {
108  case DB_MISSING_HISTORY:
109  my_error(ER_TABLE_DEF_CHANGED, MYF(0));
110  break;
111  case DB_RECORD_NOT_FOUND:
112  my_error(ER_KEY_NOT_FOUND, MYF(0), table);
113  break;
114  case DB_DEADLOCK:
115  my_error(ER_LOCK_DEADLOCK, MYF(0));
116  break;
118  my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0));
119  break;
120  case DB_INTERRUPTED:
121  my_error(ER_QUERY_INTERRUPTED, MYF(0));
122  break;
123  case DB_OUT_OF_MEMORY:
124  my_error(ER_OUT_OF_RESOURCES, MYF(0));
125  break;
126  case DB_OUT_OF_FILE_SPACE:
127  my_error(ER_RECORD_FILE_FULL, MYF(0), table);
128  break;
130  my_error(ER_INDEX_COLUMN_TOO_LONG, MYF(0),
131  DICT_MAX_FIELD_LEN_BY_FORMAT_FLAG(flags));
132  break;
134  my_error(ER_TOO_MANY_CONCURRENT_TRXS, MYF(0));
135  break;
136  case DB_LOCK_TABLE_FULL:
137  my_error(ER_LOCK_TABLE_FULL, MYF(0));
138  break;
140  my_error(ER_UNDO_RECORD_TOO_BIG, MYF(0));
141  break;
142  case DB_CORRUPTION:
143  my_error(ER_NOT_KEYFILE, MYF(0), table);
144  break;
145  case DB_TOO_BIG_RECORD:
146  my_error(ER_TOO_BIG_ROWSIZE, MYF(0),
148  flags & DICT_TF_COMPACT) / 2);
149  break;
150  case DB_INVALID_NULL:
151  /* TODO: report the row, as we do for DB_DUPLICATE_KEY */
152  my_error(ER_INVALID_USE_OF_NULL, MYF(0));
153  break;
154 #ifdef UNIV_DEBUG
155  case DB_SUCCESS:
156  case DB_DUPLICATE_KEY:
159  /* These codes should not be passed here. */
160  ut_error;
161 #endif /* UNIV_DEBUG */
162  default:
163  my_error(ER_GET_ERRNO, MYF(0), error);
164  break;
165  }
166 }
167 
171 static
172 bool
173 innobase_fulltext_exist(
174 /*====================*/
175  const TABLE* table)
176 {
177  for (uint i = 0; i < table->s->keys; i++) {
178  if (table->key_info[i].flags & HA_FULLTEXT) {
179  return(true);
180  }
181  }
182 
183  return(false);
184 }
185 
186 /*******************************************************************/
190 static __attribute__((nonnull, warn_unused_result))
191 bool
192 innobase_need_rebuild(
193 /*==================*/
194  const Alter_inplace_info* ha_alter_info)
195 {
196  if (ha_alter_info->handler_flags
197  == Alter_inplace_info::CHANGE_CREATE_OPTION
198  && !(ha_alter_info->create_info->used_fields
199  & (HA_CREATE_USED_ROW_FORMAT
200  | HA_CREATE_USED_KEY_BLOCK_SIZE))) {
201  /* Any other CHANGE_CREATE_OPTION than changing
202  ROW_FORMAT or KEY_BLOCK_SIZE is ignored. */
203  return(false);
204  }
205 
206  return(!!(ha_alter_info->handler_flags & INNOBASE_ALTER_REBUILD));
207 }
208 
223 UNIV_INTERN
224 enum_alter_inplace_result
226 /*==========================================*/
227  TABLE* altered_table,
228  Alter_inplace_info* ha_alter_info)
229 {
230  DBUG_ENTER("check_if_supported_inplace_alter");
231 
232  if (srv_read_only_mode) {
233  ha_alter_info->unsupported_reason =
234  innobase_get_err_msg(ER_READ_ONLY_MODE);
235  DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
236  } else if (srv_created_new_raw || srv_force_recovery) {
237  ha_alter_info->unsupported_reason =
238  innobase_get_err_msg(ER_READ_ONLY_MODE);
239  DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
240  }
241 
242  if (altered_table->s->fields > REC_MAX_N_USER_FIELDS) {
243  /* Deny the inplace ALTER TABLE. MySQL will try to
244  re-create the table and ha_innobase::create() will
245  return an error too. This is how we effectively
246  deny adding too many columns to a table. */
247  ha_alter_info->unsupported_reason =
248  innobase_get_err_msg(ER_TOO_MANY_FIELDS);
249  DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
250  }
251 
252  update_thd();
254 
255  if (ha_alter_info->handler_flags
256  & ~(INNOBASE_INPLACE_IGNORE
257  | INNOBASE_ALTER_NOREBUILD
258  | INNOBASE_ALTER_REBUILD)) {
259 
260  if (ha_alter_info->handler_flags
262  | Alter_inplace_info::ALTER_COLUMN_TYPE))
263  ha_alter_info->unsupported_reason = innobase_get_err_msg(
264  ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_COLUMN_TYPE);
265  DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
266  }
267 
268  /* Only support online add foreign key constraint when
269  check_foreigns is turned off */
270  if ((ha_alter_info->handler_flags
271  & Alter_inplace_info::ADD_FOREIGN_KEY)
272  && prebuilt->trx->check_foreigns) {
273  ha_alter_info->unsupported_reason = innobase_get_err_msg(
274  ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FK_CHECK);
275  DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
276  }
277 
278  if (!(ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE)) {
279  DBUG_RETURN(HA_ALTER_INPLACE_NO_LOCK);
280  }
281 
282  /* Only support NULL -> NOT NULL change if strict table sql_mode
283  is set. Fall back to COPY for conversion if not strict tables.
284  In-Place will fail with an error when trying to convert
285  NULL to a NOT NULL value. */
286  if ((ha_alter_info->handler_flags
287  & Alter_inplace_info::ALTER_COLUMN_NOT_NULLABLE)
288  && !thd_is_strict_mode(user_thd)) {
289  ha_alter_info->unsupported_reason = innobase_get_err_msg(
290  ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOT_NULL);
291  DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
292  }
293 
294  /* InnoDB cannot IGNORE when creating unique indexes. IGNORE
295  should silently delete some duplicate rows. Our inplace_alter
296  code will not delete anything from existing indexes. */
297  if (ha_alter_info->ignore
298  && (ha_alter_info->handler_flags
299  & (Alter_inplace_info::ADD_PK_INDEX
300  | Alter_inplace_info::ADD_UNIQUE_INDEX))) {
301  ha_alter_info->unsupported_reason = innobase_get_err_msg(
302  ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_IGNORE);
303  DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
304  }
305 
306  /* DROP PRIMARY KEY is only allowed in combination with ADD
307  PRIMARY KEY. */
308  if ((ha_alter_info->handler_flags
309  & (Alter_inplace_info::ADD_PK_INDEX
310  | Alter_inplace_info::DROP_PK_INDEX))
311  == Alter_inplace_info::DROP_PK_INDEX) {
312  ha_alter_info->unsupported_reason = innobase_get_err_msg(
313  ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOPK);
314  DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
315  }
316 
317  /* If a column change from NOT NULL to NULL,
318  and there's a implict pk on this column. the
319  table should be rebuild. The change should
320  only go through the "Copy" method.*/
321  if ((ha_alter_info->handler_flags
322  & Alter_inplace_info::ALTER_COLUMN_NULLABLE)) {
323  uint primary_key = altered_table->s->primary_key;
324 
325  /* See if MYSQL table has no pk but we do.*/
326  if (UNIV_UNLIKELY(primary_key >= MAX_KEY)
327  && !row_table_got_default_clust_index(prebuilt->table)) {
328  ha_alter_info->unsupported_reason = innobase_get_err_msg(
329  ER_PRIMARY_CANT_HAVE_NULL);
330  DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
331  }
332  }
333 
334  /* We should be able to do the operation in-place.
335  See if we can do it online (LOCK=NONE). */
336  bool online = true;
337 
339  ha_alter_info->alter_info->create_list);
340 
341  /* Fix the key parts. */
342  for (KEY* new_key = ha_alter_info->key_info_buffer;
343  new_key < ha_alter_info->key_info_buffer
344  + ha_alter_info->key_count;
345  new_key++) {
346  for (KEY_PART_INFO* key_part = new_key->key_part;
347  key_part < new_key->key_part + new_key->user_defined_key_parts;
348  key_part++) {
349  const Create_field* new_field;
350 
351  DBUG_ASSERT(key_part->fieldnr
352  < altered_table->s->fields);
353 
354  cf_it.rewind();
355  for (uint fieldnr = 0; (new_field = cf_it++);
356  fieldnr++) {
357  if (fieldnr == key_part->fieldnr) {
358  break;
359  }
360  }
361 
362  DBUG_ASSERT(new_field);
363 
364  key_part->field = altered_table->field[
365  key_part->fieldnr];
366  /* In some special cases InnoDB emits "false"
367  duplicate key errors with NULL key values. Let
368  us play safe and ensure that we can correctly
369  print key values even in such cases .*/
370  key_part->null_offset = key_part->field->null_offset();
371  key_part->null_bit = key_part->field->null_bit;
372 
373  if (new_field->field) {
374  /* This is an existing column. */
375  continue;
376  }
377 
378  /* This is an added column. */
379  DBUG_ASSERT(ha_alter_info->handler_flags
380  & Alter_inplace_info::ADD_COLUMN);
381 
382  /* We cannot replace a hidden FTS_DOC_ID
383  with a user-visible FTS_DOC_ID. */
384  if (prebuilt->table->fts
385  && innobase_fulltext_exist(altered_table)
386  && !my_strcasecmp(
387  system_charset_info,
388  key_part->field->field_name,
390  ha_alter_info->unsupported_reason = innobase_get_err_msg(
391  ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_HIDDEN_FTS);
392  DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
393  }
394 
395  DBUG_ASSERT((MTYP_TYPENR(key_part->field->unireg_check)
396  == Field::NEXT_NUMBER)
397  == !!(key_part->field->flags
398  & AUTO_INCREMENT_FLAG));
399 
400  if (key_part->field->flags & AUTO_INCREMENT_FLAG) {
401  /* We cannot assign an AUTO_INCREMENT
402  column values during online ALTER. */
403  DBUG_ASSERT(key_part->field == altered_table
404  -> found_next_number_field);
405  ha_alter_info->unsupported_reason = innobase_get_err_msg(
406  ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_AUTOINC);
407  online = false;
408  }
409  }
410  }
411 
412  DBUG_ASSERT(!prebuilt->table->fts || prebuilt->table->fts->doc_col
413  <= table->s->fields);
414  DBUG_ASSERT(!prebuilt->table->fts || prebuilt->table->fts->doc_col
415  < dict_table_get_n_user_cols(prebuilt->table));
416 
417  if (prebuilt->table->fts
418  && innobase_fulltext_exist(altered_table)) {
419  /* FULLTEXT indexes are supposed to remain. */
420  /* Disallow DROP INDEX FTS_DOC_ID_INDEX */
421 
422  for (uint i = 0; i < ha_alter_info->index_drop_count; i++) {
423  if (!my_strcasecmp(
424  system_charset_info,
425  ha_alter_info->index_drop_buffer[i]->name,
427  ha_alter_info->unsupported_reason = innobase_get_err_msg(
428  ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_CHANGE_FTS);
429  DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
430  }
431  }
432 
433  /* InnoDB can have a hidden FTS_DOC_ID_INDEX on a
434  visible FTS_DOC_ID column as well. Prevent dropping or
435  renaming the FTS_DOC_ID. */
436 
437  for (Field** fp = table->field; *fp; fp++) {
438  if (!((*fp)->flags
439  & (FIELD_IS_RENAMED | FIELD_IS_DROPPED))) {
440  continue;
441  }
442 
443  if (!my_strcasecmp(
444  system_charset_info,
445  (*fp)->field_name,
447  ha_alter_info->unsupported_reason = innobase_get_err_msg(
448  ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_CHANGE_FTS);
449  DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
450  }
451  }
452  }
453 
454  prebuilt->trx->will_lock++;
455 
456  if (!online) {
457  /* We already determined that only a non-locking
458  operation is possible. */
459  } else if (((ha_alter_info->handler_flags
460  & Alter_inplace_info::ADD_PK_INDEX)
461  || innobase_need_rebuild(ha_alter_info))
462  && (innobase_fulltext_exist(altered_table)
463  || (prebuilt->table->flags2
465  /* Refuse to rebuild the table online, if
466  fulltext indexes are to survive the rebuild,
467  or if the table contains a hidden FTS_DOC_ID column. */
468  online = false;
469  /* If the table already contains fulltext indexes,
470  refuse to rebuild the table natively altogether. */
471  if (prebuilt->table->fts) {
472  ha_alter_info->unsupported_reason = innobase_get_err_msg(
473  ER_INNODB_FT_LIMIT);
474  DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
475  }
476  ha_alter_info->unsupported_reason = innobase_get_err_msg(
477  ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FTS);
478  } else if ((ha_alter_info->handler_flags
479  & Alter_inplace_info::ADD_INDEX)) {
480  /* Building a full-text index requires a lock.
481  We could do without a lock if the table already contains
482  an FTS_DOC_ID column, but in that case we would have
483  to apply the modification log to the full-text indexes. */
484 
485  for (uint i = 0; i < ha_alter_info->index_add_count; i++) {
486  const KEY* key =
487  &ha_alter_info->key_info_buffer[
488  ha_alter_info->index_add_buffer[i]];
489  if (key->flags & HA_FULLTEXT) {
490  DBUG_ASSERT(!(key->flags & HA_KEYFLAG_MASK
491  & ~(HA_FULLTEXT
492  | HA_PACK_KEY
493  | HA_GENERATED_KEY
494  | HA_BINARY_PACK_KEY)));
495  ha_alter_info->unsupported_reason = innobase_get_err_msg(
496  ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FTS);
497  online = false;
498  break;
499  }
500  }
501  }
502 
503  DBUG_RETURN(online
504  ? HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE
505  : HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE);
506 }
507 
508 /*************************************************************/
511 static __attribute__((nonnull(1,3,5,7)))
512 bool
513 innobase_init_foreign(
514 /*==================*/
515  dict_foreign_t* foreign,
517  char* constraint_name,
519  dict_table_t* table,
521  const char** column_names,
523  ulint num_field,
524  const char* referenced_table_name,
528  const char** referenced_column_names,
530  ulint referenced_num_field)
532 {
533  ut_ad(mutex_own(&dict_sys->mutex));
534 
535  if (constraint_name) {
536  ulint db_len;
537 
538  /* Catenate 'databasename/' to the constraint name specified
539  by the user: we conceive the constraint as belonging to the
540  same MySQL 'database' as the table itself. We store the name
541  to foreign->id. */
542 
543  db_len = dict_get_db_name_len(table->name);
544 
545  foreign->id = static_cast<char*>(mem_heap_alloc(
546  foreign->heap, db_len + strlen(constraint_name) + 2));
547 
548  ut_memcpy(foreign->id, table->name, db_len);
549  foreign->id[db_len] = '/';
550  strcpy(foreign->id + db_len + 1, constraint_name);
551 
552  /* Check if any existing foreign key has the same id,
553  this is needed only if user supplies the constraint name */
554 
555  for (const dict_foreign_t* existing_foreign
556  = UT_LIST_GET_FIRST(table->foreign_list);
557  existing_foreign != 0;
558  existing_foreign = UT_LIST_GET_NEXT(
559  foreign_list, existing_foreign)) {
560 
561  if (ut_strcmp(existing_foreign->id, foreign->id) == 0) {
562  return(false);
563  }
564  }
565  }
566 
567  foreign->foreign_table = table;
568  foreign->foreign_table_name = mem_heap_strdup(
569  foreign->heap, table->name);
571 
572  foreign->foreign_index = index;
573  foreign->n_fields = (unsigned int) num_field;
574 
575  foreign->foreign_col_names = static_cast<const char**>(
576  mem_heap_alloc(foreign->heap, num_field * sizeof(void*)));
577 
578  for (ulint i = 0; i < foreign->n_fields; i++) {
579  foreign->foreign_col_names[i] = mem_heap_strdup(
580  foreign->heap, column_names[i]);
581  }
582 
583  foreign->referenced_index = referenced_index;
584  foreign->referenced_table = referenced_table;
585 
586  foreign->referenced_table_name = mem_heap_strdup(
587  foreign->heap, referenced_table_name);
589 
590  foreign->referenced_col_names = static_cast<const char**>(
591  mem_heap_alloc(foreign->heap,
592  referenced_num_field * sizeof(void*)));
593 
594  for (ulint i = 0; i < foreign->n_fields; i++) {
595  foreign->referenced_col_names[i]
596  = mem_heap_strdup(foreign->heap,
597  referenced_column_names[i]);
598  }
599 
600  return(true);
601 }
602 
603 /*************************************************************/
606 static __attribute__((nonnull, warn_unused_result))
607 bool
608 innobase_check_fk_option(
609 /*=====================*/
610  const dict_foreign_t* foreign)
611 {
612  if (!foreign->foreign_index) {
613  return(true);
614  }
615 
616  if (foreign->type & (DICT_FOREIGN_ON_UPDATE_SET_NULL
618 
619  for (ulint j = 0; j < foreign->n_fields; j++) {
621  foreign->foreign_index, j)->prtype)
622  & DATA_NOT_NULL) {
623 
624  /* It is not sensible to define
625  SET NULL if the column is not
626  allowed to be NULL! */
627  return(false);
628  }
629  }
630  }
631 
632  return(true);
633 }
634 
635 /*************************************************************/
638 static __attribute__((nonnull, warn_unused_result))
639 bool
640 innobase_set_foreign_key_option(
641 /*============================*/
642  dict_foreign_t* foreign,
643  Foreign_key* fk_key)
645 {
646  ut_ad(!foreign->type);
647 
648  switch (fk_key->delete_opt) {
649  case Foreign_key::FK_OPTION_NO_ACTION:
650  case Foreign_key::FK_OPTION_RESTRICT:
651  case Foreign_key::FK_OPTION_DEFAULT:
652  foreign->type = DICT_FOREIGN_ON_DELETE_NO_ACTION;
653  break;
654  case Foreign_key::FK_OPTION_CASCADE:
655  foreign->type = DICT_FOREIGN_ON_DELETE_CASCADE;
656  break;
657  case Foreign_key::FK_OPTION_SET_NULL:
658  foreign->type = DICT_FOREIGN_ON_DELETE_SET_NULL;
659  break;
660  }
661 
662  switch (fk_key->update_opt) {
663  case Foreign_key::FK_OPTION_NO_ACTION:
664  case Foreign_key::FK_OPTION_RESTRICT:
665  case Foreign_key::FK_OPTION_DEFAULT:
666  foreign->type |= DICT_FOREIGN_ON_UPDATE_NO_ACTION;
667  break;
668  case Foreign_key::FK_OPTION_CASCADE:
669  foreign->type |= DICT_FOREIGN_ON_UPDATE_CASCADE;
670  break;
671  case Foreign_key::FK_OPTION_SET_NULL:
672  foreign->type |= DICT_FOREIGN_ON_UPDATE_SET_NULL;
673  break;
674  }
675 
676  return(innobase_check_fk_option(foreign));
677 }
678 
679 /*******************************************************************/
683 static __attribute__((nonnull, warn_unused_result))
684 const KEY*
685 innobase_find_equiv_index(
686 /*======================*/
687  const char*const* col_names,
689  uint n_cols,
690  const KEY* keys,
691  const uint* add,
692  uint n_add)
693 {
694  for (uint i = 0; i < n_add; i++) {
695  const KEY* key = &keys[add[i]];
696 
697  if (key->user_defined_key_parts < n_cols) {
698 no_match:
699  continue;
700  }
701 
702  for (uint j = 0; j < n_cols; j++) {
703  const KEY_PART_INFO& key_part = key->key_part[j];
704  uint32 col_len
705  = key_part.field->pack_length();
706 
707  /* The MySQL pack length contains 1 or 2 bytes
708  length field for a true VARCHAR. */
709 
710  if (key_part.field->type() == MYSQL_TYPE_VARCHAR) {
711  col_len -= static_cast<const Field_varstring*>(
712  key_part.field)->length_bytes;
713  }
714 
715  if (key_part.length < col_len) {
716 
717  /* Column prefix indexes cannot be
718  used for FOREIGN KEY constraints. */
719  goto no_match;
720  }
721 
722  if (innobase_strcasecmp(col_names[j],
723  key_part.field->field_name)) {
724  /* Name mismatch */
725  goto no_match;
726  }
727  }
728 
729  return(key);
730  }
731 
732  return(NULL);
733 }
734 
735 /*************************************************************/
739 static __attribute__((nonnull(1,2,6), warn_unused_result))
741 innobase_find_fk_index(
742 /*===================*/
743  Alter_inplace_info* ha_alter_info,
745  dict_table_t* table,
746  const char** col_names,
749  dict_index_t** drop_index,
751  ulint n_drop_index,
753  const char** columns,
754  ulint n_cols)
755 {
757 
758  index = dict_table_get_first_index(table);
759 
760  while (index != NULL) {
761  if (!(index->type & DICT_FTS)
763  table, col_names, columns, n_cols,
764  index, NULL, true, 0)) {
765  for (ulint i = 0; i < n_drop_index; i++) {
766  if (index == drop_index[i]) {
767  /* Skip to-be-dropped indexes. */
768  goto next_rec;
769  }
770  }
771 
772  return(index);
773  }
774 
775 next_rec:
776  index = dict_table_get_next_index(index);
777  }
778 
779  return(NULL);
780 }
781 
782 /*************************************************************/
786 static __attribute__((nonnull(1,2,3,7,8), warn_unused_result))
787 bool
788 innobase_get_foreign_key_info(
789 /*==========================*/
791  ha_alter_info,
792  const TABLE_SHARE*
793  table_share,
794  dict_table_t* table,
795  const char** col_names,
797  dict_index_t** drop_index,
798  ulint n_drop_index,
799  dict_foreign_t**add_fk,
800  ulint* n_add_fk,
802  const trx_t* trx)
803 {
804  Key* key;
805  Foreign_key* fk_key;
806  dict_table_t* referenced_table = NULL;
807  char* referenced_table_name = NULL;
808  ulint num_fk = 0;
809  Alter_info* alter_info = ha_alter_info->alter_info;
810 
811  *n_add_fk = 0;
812 
813  List_iterator<Key> key_iterator(alter_info->key_list);
814 
815  while ((key=key_iterator++)) {
816  if (key->type != Key::FOREIGN_KEY) {
817  continue;
818  }
819 
820  const char* column_names[MAX_NUM_FK_COLUMNS];
821  dict_index_t* index = NULL;
822  const char* referenced_column_names[MAX_NUM_FK_COLUMNS];
823  dict_index_t* referenced_index = NULL;
824  ulint num_col = 0;
825  ulint referenced_num_col = 0;
826  bool correct_option;
827  char* db_namep = NULL;
828  char* tbl_namep = NULL;
829  ulint db_name_len = 0;
830  ulint tbl_name_len = 0;
831 #ifdef __WIN__
832  char db_name[MAX_DATABASE_NAME_LEN];
833  char tbl_name[MAX_TABLE_NAME_LEN];
834 #endif
835 
836  fk_key = static_cast<Foreign_key*>(key);
837 
838  if (fk_key->columns.elements > 0) {
839  ulint i = 0;
840  Key_part_spec* column;
841  List_iterator<Key_part_spec> key_part_iterator(
842  fk_key->columns);
843 
844  /* Get all the foreign key column info for the
845  current table */
846  while ((column = key_part_iterator++)) {
847  column_names[i] = column->field_name.str;
849  i++;
850  }
851 
852  index = innobase_find_fk_index(
853  ha_alter_info,
854  table, col_names,
855  drop_index, n_drop_index,
856  column_names, i);
857 
858  /* MySQL would add a index in the creation
859  list if no such index for foreign table,
860  so we have to use DBUG_EXECUTE_IF to simulate
861  the scenario */
862  DBUG_EXECUTE_IF("innodb_test_no_foreign_idx",
863  index = NULL;);
864 
865  /* Check whether there exist such
866  index in the the index create clause */
867  if (!index && !innobase_find_equiv_index(
868  column_names, i,
869  ha_alter_info->key_info_buffer,
870  ha_alter_info->index_add_buffer,
871  ha_alter_info->index_add_count)) {
872  my_error(
873  ER_FK_NO_INDEX_CHILD,
874  MYF(0),
875  fk_key->name.str
876  ? fk_key->name.str : "",
877  table_share->table_name.str);
878  goto err_exit;
879  }
880 
881  num_col = i;
882  }
883 
884  add_fk[num_fk] = dict_mem_foreign_create();
885 
886 #ifndef __WIN__
887  tbl_namep = fk_key->ref_table.str;
888  tbl_name_len = fk_key->ref_table.length;
889  db_namep = fk_key->ref_db.str;
890  db_name_len = fk_key->ref_db.length;
891 #else
892  ut_ad(fk_key->ref_table.str);
893 
894  memcpy(tbl_name, fk_key->ref_table.str,
895  fk_key->ref_table.length);
896  tbl_name[fk_key->ref_table.length] = 0;
897  innobase_casedn_str(tbl_name);
898  tbl_name_len = strlen(tbl_name);
899  tbl_namep = &tbl_name[0];
900 
901  if (fk_key->ref_db.str != NULL) {
902  memcpy(db_name, fk_key->ref_db.str,
903  fk_key->ref_db.length);
904  db_name[fk_key->ref_db.length] = 0;
905  innobase_casedn_str(db_name);
906  db_name_len = strlen(db_name);
907  db_namep = &db_name[0];
908  }
909 #endif
910  mutex_enter(&dict_sys->mutex);
911 
912  referenced_table_name = dict_get_referenced_table(
913  table->name,
914  db_namep,
915  db_name_len,
916  tbl_namep,
917  tbl_name_len,
918  &referenced_table,
919  add_fk[num_fk]->heap);
920 
921  /* Test the case when referenced_table failed to
922  open, if trx->check_foreigns is not set, we should
923  still be able to add the foreign key */
924  DBUG_EXECUTE_IF("innodb_test_open_ref_fail",
925  referenced_table = NULL;);
926 
927  if (!referenced_table && trx->check_foreigns) {
928  mutex_exit(&dict_sys->mutex);
929  my_error(ER_FK_CANNOT_OPEN_PARENT,
930  MYF(0), tbl_namep);
931 
932  goto err_exit;
933  }
934 
935  if (fk_key->ref_columns.elements > 0) {
936  ulint i = 0;
937  Key_part_spec* column;
938  List_iterator<Key_part_spec> key_part_iterator(
939  fk_key->ref_columns);
940 
941  while ((column = key_part_iterator++)) {
942  referenced_column_names[i] =
943  column->field_name.str;
945  i++;
946  }
947 
948  if (referenced_table) {
949  referenced_index =
951  referenced_table, 0,
952  referenced_column_names,
953  i, index,
954  TRUE, FALSE);
955 
956  DBUG_EXECUTE_IF(
957  "innodb_test_no_reference_idx",
958  referenced_index = NULL;);
959 
960  /* Check whether there exist such
961  index in the the index create clause */
962  if (!referenced_index) {
963  mutex_exit(&dict_sys->mutex);
964  my_error(ER_FK_NO_INDEX_PARENT, MYF(0),
965  fk_key->name.str
966  ? fk_key->name.str : "",
967  tbl_namep);
968  goto err_exit;
969  }
970  } else {
971  ut_a(!trx->check_foreigns);
972  }
973 
974  referenced_num_col = i;
975  }
976 
977  if (!innobase_init_foreign(
978  add_fk[num_fk], fk_key->name.str,
979  table, index, column_names,
980  num_col, referenced_table_name,
981  referenced_table, referenced_index,
982  referenced_column_names, referenced_num_col)) {
983  mutex_exit(&dict_sys->mutex);
984  my_error(
985  ER_FK_DUP_NAME,
986  MYF(0),
987  add_fk[num_fk]->id);
988  goto err_exit;
989  }
990 
991  mutex_exit(&dict_sys->mutex);
992 
993  correct_option = innobase_set_foreign_key_option(
994  add_fk[num_fk], fk_key);
995 
996  DBUG_EXECUTE_IF("innodb_test_wrong_fk_option",
997  correct_option = false;);
998 
999  if (!correct_option) {
1000  my_error(ER_FK_INCORRECT_OPTION,
1001  MYF(0),
1002  table_share->table_name.str,
1003  add_fk[num_fk]->id);
1004  goto err_exit;
1005  }
1006 
1007  num_fk++;
1008  }
1009 
1010  *n_add_fk = num_fk;
1011 
1012  return(true);
1013 err_exit:
1014  for (ulint i = 0; i <= num_fk; i++) {
1015  if (add_fk[i]) {
1016  dict_foreign_free(add_fk[i]);
1017  }
1018  }
1019 
1020  return(false);
1021 }
1022 
1023 /*************************************************************/
1026 static
1027 void
1028 innobase_col_to_mysql(
1029 /*==================*/
1030  const dict_col_t* col,
1031  const uchar* data,
1032  ulint len,
1033  Field* field)
1034 {
1035  uchar* ptr;
1036  uchar* dest = field->ptr;
1037  ulint flen = field->pack_length();
1038 
1039  switch (col->mtype) {
1040  case DATA_INT:
1041  ut_ad(len == flen);
1042 
1043  /* Convert integer data from Innobase to little-endian
1044  format, sign bit restored to normal */
1045 
1046  for (ptr = dest + len; ptr != dest; ) {
1047  *--ptr = *data++;
1048  }
1049 
1050  if (!(field->flags & UNSIGNED_FLAG)) {
1051  ((byte*) dest)[len - 1] ^= 0x80;
1052  }
1053 
1054  break;
1055 
1056  case DATA_VARCHAR:
1057  case DATA_VARMYSQL:
1058  case DATA_BINARY:
1059  field->reset();
1060 
1061  if (field->type() == MYSQL_TYPE_VARCHAR) {
1062  /* This is a >= 5.0.3 type true VARCHAR. Store the
1063  length of the data to the first byte or the first
1064  two bytes of dest. */
1065 
1067  dest, len, flen - field->key_length());
1068  }
1069 
1070  /* Copy the actual data */
1071  memcpy(dest, data, len);
1072  break;
1073 
1074  case DATA_BLOB:
1075  /* Skip MySQL BLOBs when reporting an erroneous row
1076  during index creation or table rebuild. */
1077  field->set_null();
1078  break;
1079 
1080 #ifdef UNIV_DEBUG
1081  case DATA_MYSQL:
1082  ut_ad(flen >= len);
1083  ut_ad(DATA_MBMAXLEN(col->mbminmaxlen)
1084  >= DATA_MBMINLEN(col->mbminmaxlen));
1085  memcpy(dest, data, len);
1086  break;
1087 
1088  default:
1089  case DATA_SYS_CHILD:
1090  case DATA_SYS:
1091  /* These column types should never be shipped to MySQL. */
1092  ut_ad(0);
1093 
1094  case DATA_FIXBINARY:
1095  case DATA_FLOAT:
1096  case DATA_DOUBLE:
1097  case DATA_DECIMAL:
1098  /* Above are the valid column types for MySQL data. */
1099  ut_ad(flen == len);
1100  /* fall through */
1101  case DATA_CHAR:
1102  /* We may have flen > len when there is a shorter
1103  prefix on a CHAR column. */
1104  ut_ad(flen >= len);
1105 #else /* UNIV_DEBUG */
1106  default:
1107 #endif /* UNIV_DEBUG */
1108  memcpy(dest, data, len);
1109  }
1110 }
1111 
1112 /*************************************************************/
1114 UNIV_INTERN
1115 void
1117 /*==================*/
1118  struct TABLE* table,
1119  const rec_t* rec,
1120  const dict_index_t* index,
1121  const ulint* offsets)
1123 {
1124  uint n_fields = table->s->fields;
1125 
1126  ut_ad(n_fields == dict_table_get_n_user_cols(index->table)
1127  - !!(DICT_TF2_FLAG_IS_SET(index->table,
1129 
1130  for (uint i = 0; i < n_fields; i++) {
1131  Field* field = table->field[i];
1132  ulint ipos;
1133  ulint ilen;
1134  const uchar* ifield;
1135 
1136  field->reset();
1137 
1138  ipos = dict_index_get_nth_col_or_prefix_pos(index, i, TRUE);
1139 
1140  if (ipos == ULINT_UNDEFINED
1141  || rec_offs_nth_extern(offsets, ipos)) {
1142 null_field:
1143  field->set_null();
1144  continue;
1145  }
1146 
1147  ifield = rec_get_nth_field(rec, offsets, ipos, &ilen);
1148 
1149  /* Assign the NULL flag */
1150  if (ilen == UNIV_SQL_NULL) {
1151  ut_ad(field->real_maybe_null());
1152  goto null_field;
1153  }
1154 
1155  field->set_notnull();
1156 
1157  innobase_col_to_mysql(
1159  dict_index_get_nth_field(index, ipos)),
1160  ifield, ilen, field);
1161  }
1162 }
1163 
1164 /*************************************************************/
1166 UNIV_INTERN
1167 void
1169 /*=====================*/
1170  struct TABLE* table,
1171  const dict_index_t* index,
1172  const dfield_t* fields)
1173 {
1174  uint n_fields = table->s->fields;
1175 
1176  ut_ad(n_fields == dict_table_get_n_user_cols(index->table)
1177  - !!(DICT_TF2_FLAG_IS_SET(index->table,
1179 
1180  for (uint i = 0; i < n_fields; i++) {
1181  Field* field = table->field[i];
1182  ulint ipos;
1183 
1184  field->reset();
1185 
1186  ipos = dict_index_get_nth_col_or_prefix_pos(index, i, TRUE);
1187 
1188  if (ipos == ULINT_UNDEFINED
1189  || dfield_is_ext(&fields[ipos])
1190  || dfield_is_null(&fields[ipos])) {
1191 
1192  field->set_null();
1193  } else {
1194  field->set_notnull();
1195 
1196  const dfield_t* df = &fields[ipos];
1197 
1198  innobase_col_to_mysql(
1200  dict_index_get_nth_field(index, ipos)),
1201  static_cast<const uchar*>(dfield_get_data(df)),
1202  dfield_get_len(df), field);
1203  }
1204  }
1205 }
1206 
1207 /*************************************************************/
1209 UNIV_INTERN
1210 void
1212 /*==================*/
1213  struct TABLE* table,
1214  const dict_table_t* itab,
1215  const dtuple_t* row)
1216 {
1217  uint n_fields = table->s->fields;
1218 
1219  /* The InnoDB row may contain an extra FTS_DOC_ID column at the end. */
1220  ut_ad(row->n_fields == dict_table_get_n_cols(itab));
1221  ut_ad(n_fields == row->n_fields - DATA_N_SYS_COLS
1222  - !!(DICT_TF2_FLAG_IS_SET(itab, DICT_TF2_FTS_HAS_DOC_ID)));
1223 
1224  for (uint i = 0; i < n_fields; i++) {
1225  Field* field = table->field[i];
1226  const dfield_t* df = dtuple_get_nth_field(row, i);
1227 
1228  field->reset();
1229 
1230  if (dfield_is_ext(df) || dfield_is_null(df)) {
1231  field->set_null();
1232  } else {
1233  field->set_notnull();
1234 
1235  innobase_col_to_mysql(
1236  dict_table_get_nth_col(itab, i),
1237  static_cast<const uchar*>(dfield_get_data(df)),
1238  dfield_get_len(df), field);
1239  }
1240  }
1241 }
1242 
1243 /*************************************************************/
1245 UNIV_INTERN
1246 void
1248 /*===============*/
1249  TABLE* table)
1250 {
1251  uint n_fields = table->s->fields;
1252  uint i;
1253 
1254  for (i = 0; i < n_fields; i++) {
1255  table->field[i]->set_default();
1256  }
1257 }
1258 
1259 /*******************************************************************/
1262 static __attribute__((nonnull, warn_unused_result))
1263 int
1264 innobase_check_index_keys(
1265 /*======================*/
1266  const Alter_inplace_info* info,
1268  const dict_table_t* innodb_table)
1270 {
1271  for (uint key_num = 0; key_num < info->index_add_count;
1272  key_num++) {
1273  const KEY& key = info->key_info_buffer[
1274  info->index_add_buffer[key_num]];
1275 
1276  /* Check that the same index name does not appear
1277  twice in indexes to be created. */
1278 
1279  for (ulint i = 0; i < key_num; i++) {
1280  const KEY& key2 = info->key_info_buffer[
1281  info->index_add_buffer[i]];
1282 
1283  if (0 == strcmp(key.name, key2.name)) {
1284  my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0),
1285  key.name);
1286 
1287  return(ER_WRONG_NAME_FOR_INDEX);
1288  }
1289  }
1290 
1291  /* Check that the same index name does not already exist. */
1292 
1293  const dict_index_t* index;
1294 
1295  for (index = dict_table_get_first_index(innodb_table);
1296  index; index = dict_table_get_next_index(index)) {
1297 
1298  if (!strcmp(key.name, index->name)) {
1299  break;
1300  }
1301  }
1302 
1303  if (index) {
1304  /* If a key by the same name is being created and
1305  dropped, the name clash is OK. */
1306  for (uint i = 0; i < info->index_drop_count;
1307  i++) {
1308  const KEY* drop_key
1309  = info->index_drop_buffer[i];
1310 
1311  if (0 == strcmp(key.name, drop_key->name)) {
1312  goto name_ok;
1313  }
1314  }
1315 
1316  my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key.name);
1317 
1318  return(ER_WRONG_NAME_FOR_INDEX);
1319  }
1320 
1321 name_ok:
1322  for (ulint i = 0; i < key.user_defined_key_parts; i++) {
1323  const KEY_PART_INFO& key_part1
1324  = key.key_part[i];
1325  const Field* field
1326  = key_part1.field;
1327  ibool is_unsigned;
1328 
1330  &is_unsigned, field)) {
1331  default:
1332  break;
1333  case DATA_INT:
1334  case DATA_FLOAT:
1335  case DATA_DOUBLE:
1336  case DATA_DECIMAL:
1337  /* Check that MySQL does not try to
1338  create a column prefix index field on
1339  an inappropriate data type. */
1340 
1341  if (field->type() == MYSQL_TYPE_VARCHAR) {
1342  if (key_part1.length
1343  >= field->pack_length()
1344  - ((Field_varstring*) field)
1345  ->length_bytes) {
1346  break;
1347  }
1348  } else {
1349  if (key_part1.length
1350  >= field->pack_length()) {
1351  break;
1352  }
1353  }
1354 
1355  my_error(ER_WRONG_KEY_COLUMN, MYF(0),
1356  field->field_name);
1357  return(ER_WRONG_KEY_COLUMN);
1358  }
1359 
1360  /* Check that the same column does not appear
1361  twice in the index. */
1362 
1363  for (ulint j = 0; j < i; j++) {
1364  const KEY_PART_INFO& key_part2
1365  = key.key_part[j];
1366 
1367  if (key_part1.fieldnr != key_part2.fieldnr) {
1368  continue;
1369  }
1370 
1371  my_error(ER_WRONG_KEY_COLUMN, MYF(0),
1372  field->field_name);
1373  return(ER_WRONG_KEY_COLUMN);
1374  }
1375  }
1376  }
1377 
1378  return(0);
1379 }
1380 
1381 /*******************************************************************/
1383 static __attribute__((nonnull(2,3)))
1384 void
1385 innobase_create_index_field_def(
1386 /*============================*/
1387  const TABLE* altered_table,
1391  const KEY_PART_INFO* key_part,
1392  index_field_t* index_field)
1394 {
1395  const Field* field;
1396  ibool is_unsigned;
1397  ulint col_type;
1398 
1399  DBUG_ENTER("innobase_create_index_field_def");
1400 
1401  ut_ad(key_part);
1402  ut_ad(index_field);
1403 
1404  field = altered_table
1405  ? altered_table->field[key_part->fieldnr]
1406  : key_part->field;
1407  ut_a(field);
1408 
1409  index_field->col_no = key_part->fieldnr;
1410 
1411  col_type = get_innobase_type_from_mysql_type(&is_unsigned, field);
1412 
1413  if (DATA_BLOB == col_type
1414  || (key_part->length < field->pack_length()
1415  && field->type() != MYSQL_TYPE_VARCHAR)
1416  || (field->type() == MYSQL_TYPE_VARCHAR
1417  && key_part->length < field->pack_length()
1418  - ((Field_varstring*) field)->length_bytes)) {
1419 
1420  index_field->prefix_len = key_part->length;
1421  } else {
1422  index_field->prefix_len = 0;
1423  }
1424 
1425  DBUG_VOID_RETURN;
1426 }
1427 
1428 /*******************************************************************/
1430 static __attribute__((nonnull))
1431 void
1432 innobase_create_index_def(
1433 /*======================*/
1434  const TABLE* altered_table,
1436  const KEY* keys,
1437  ulint key_number,
1438  bool new_clustered,
1441  bool key_clustered,
1443  index_def_t* index,
1444  mem_heap_t* heap)
1446 {
1447  const KEY* key = &keys[key_number];
1448  ulint i;
1449  ulint len;
1450  ulint n_fields = key->user_defined_key_parts;
1451  char* index_name;
1452 
1453  DBUG_ENTER("innobase_create_index_def");
1454  DBUG_ASSERT(!key_clustered || new_clustered);
1455 
1456  index->fields = static_cast<index_field_t*>(
1457  mem_heap_alloc(heap, n_fields * sizeof *index->fields));
1458 
1459  index->ind_type = 0;
1460  index->key_number = key_number;
1461  index->n_fields = n_fields;
1462  len = strlen(key->name) + 1;
1463  index->name = index_name = static_cast<char*>(
1464  mem_heap_alloc(heap, len + !new_clustered));
1465 
1466  if (!new_clustered) {
1467  *index_name++ = TEMP_INDEX_PREFIX;
1468  }
1469 
1470  memcpy(index_name, key->name, len);
1471 
1472  if (key->flags & HA_NOSAME) {
1473  index->ind_type |= DICT_UNIQUE;
1474  }
1475 
1476  if (key_clustered) {
1477  DBUG_ASSERT(!(key->flags & HA_FULLTEXT));
1478  index->ind_type |= DICT_CLUSTERED;
1479  } else if (key->flags & HA_FULLTEXT) {
1480  DBUG_ASSERT(!(key->flags & HA_KEYFLAG_MASK
1481  & ~(HA_FULLTEXT
1482  | HA_PACK_KEY
1483  | HA_BINARY_PACK_KEY)));
1484  DBUG_ASSERT(!(key->flags & HA_NOSAME));
1485  DBUG_ASSERT(!index->ind_type);
1486  index->ind_type |= DICT_FTS;
1487  }
1488 
1489  if (!new_clustered) {
1490  altered_table = NULL;
1491  }
1492 
1493  for (i = 0; i < n_fields; i++) {
1494  innobase_create_index_field_def(
1495  altered_table, &key->key_part[i], &index->fields[i]);
1496  }
1497 
1498  DBUG_VOID_RETURN;
1499 }
1500 
1501 /*******************************************************************/
1504 static
1505 bool
1506 innobase_fts_check_doc_id_col(
1507 /*==========================*/
1508  const dict_table_t* table,
1510  const TABLE* altered_table,
1513  ulint* fts_doc_col_no)
1517 {
1518  *fts_doc_col_no = ULINT_UNDEFINED;
1519 
1520  const uint n_cols = altered_table->s->fields;
1521  uint i;
1522 
1523  for (i = 0; i < n_cols; i++) {
1524  const Field* field = altered_table->field[i];
1525 
1526  if (my_strcasecmp(system_charset_info,
1527  field->field_name, FTS_DOC_ID_COL_NAME)) {
1528  continue;
1529  }
1530 
1531  if (strcmp(field->field_name, FTS_DOC_ID_COL_NAME)) {
1532  my_error(ER_WRONG_COLUMN_NAME, MYF(0),
1533  field->field_name);
1534  } else if (field->type() != MYSQL_TYPE_LONGLONG
1535  || field->pack_length() != 8
1536  || field->real_maybe_null()
1537  || !(field->flags & UNSIGNED_FLAG)) {
1538  my_error(ER_INNODB_FT_WRONG_DOCID_COLUMN, MYF(0),
1539  field->field_name);
1540  } else {
1541  *fts_doc_col_no = i;
1542  }
1543 
1544  return(true);
1545  }
1546 
1547  if (!table) {
1548  return(false);
1549  }
1550 
1551  for (; i + DATA_N_SYS_COLS < (uint) table->n_cols; i++) {
1552  const char* name = dict_table_get_col_name(table, i);
1553 
1554  if (strcmp(name, FTS_DOC_ID_COL_NAME) == 0) {
1555 #ifdef UNIV_DEBUG
1556  const dict_col_t* col;
1557 
1558  col = dict_table_get_nth_col(table, i);
1559 
1560  /* Because the FTS_DOC_ID does not exist in
1561  the MySQL data dictionary, this must be the
1562  internally created FTS_DOC_ID column. */
1563  ut_ad(col->mtype == DATA_INT);
1564  ut_ad(col->len == 8);
1565  ut_ad(col->prtype & DATA_NOT_NULL);
1566  ut_ad(col->prtype & DATA_UNSIGNED);
1567 #endif /* UNIV_DEBUG */
1568  *fts_doc_col_no = i;
1569  return(true);
1570  }
1571  }
1572 
1573  return(false);
1574 }
1575 
1576 /*******************************************************************/
1580 UNIV_INTERN
1581 enum fts_doc_id_index_enum
1582 innobase_fts_check_doc_id_index(
1583 /*============================*/
1584  const dict_table_t* table,
1585  const TABLE* altered_table,
1587  ulint* fts_doc_col_no)
1591 {
1592  const dict_index_t* index;
1593  const dict_field_t* field;
1594 
1595  if (altered_table) {
1596  /* Check if a unique index with the name of
1597  FTS_DOC_ID_INDEX_NAME is being created. */
1598 
1599  for (uint i = 0; i < altered_table->s->keys; i++) {
1600  const KEY& key = altered_table->key_info[i];
1601 
1602  if (innobase_strcasecmp(
1603  key.name, FTS_DOC_ID_INDEX_NAME)) {
1604  continue;
1605  }
1606 
1607  if ((key.flags & HA_NOSAME)
1608  && key.user_defined_key_parts == 1
1609  && !strcmp(key.name, FTS_DOC_ID_INDEX_NAME)
1610  && !strcmp(key.key_part[0].field->field_name,
1612  if (fts_doc_col_no) {
1613  *fts_doc_col_no = ULINT_UNDEFINED;
1614  }
1615  return(FTS_EXIST_DOC_ID_INDEX);
1616  } else {
1617  return(FTS_INCORRECT_DOC_ID_INDEX);
1618  }
1619  }
1620  }
1621 
1622  if (!table) {
1623  return(FTS_NOT_EXIST_DOC_ID_INDEX);
1624  }
1625 
1626  for (index = dict_table_get_first_index(table);
1627  index; index = dict_table_get_next_index(index)) {
1628 
1629  /* Check if there exists a unique index with the name of
1630  FTS_DOC_ID_INDEX_NAME */
1632  continue;
1633  }
1634 
1635  if (!dict_index_is_unique(index)
1636  || dict_index_get_n_unique(index) > 1
1637  || strcmp(index->name, FTS_DOC_ID_INDEX_NAME)) {
1638  return(FTS_INCORRECT_DOC_ID_INDEX);
1639  }
1640 
1641  /* Check whether the index has FTS_DOC_ID as its
1642  first column */
1643  field = dict_index_get_nth_field(index, 0);
1644 
1645  /* The column would be of a BIGINT data type */
1646  if (strcmp(field->name, FTS_DOC_ID_COL_NAME) == 0
1647  && field->col->mtype == DATA_INT
1648  && field->col->len == 8
1649  && field->col->prtype & DATA_NOT_NULL) {
1650  if (fts_doc_col_no) {
1651  *fts_doc_col_no = dict_col_get_no(field->col);
1652  }
1653  return(FTS_EXIST_DOC_ID_INDEX);
1654  } else {
1655  return(FTS_INCORRECT_DOC_ID_INDEX);
1656  }
1657  }
1658 
1659 
1660  /* Not found */
1661  return(FTS_NOT_EXIST_DOC_ID_INDEX);
1662 }
1663 /*******************************************************************/
1668 UNIV_INTERN
1669 enum fts_doc_id_index_enum
1670 innobase_fts_check_doc_id_index_in_def(
1671 /*===================================*/
1672  ulint n_key,
1673  const KEY* key_info)
1674 {
1675  /* Check whether there is a "FTS_DOC_ID_INDEX" in the to be built index
1676  list */
1677  for (ulint j = 0; j < n_key; j++) {
1678  const KEY* key = &key_info[j];
1679 
1681  continue;
1682  }
1683 
1684  /* Do a check on FTS DOC ID_INDEX, it must be unique,
1685  named as "FTS_DOC_ID_INDEX" and on column "FTS_DOC_ID" */
1686  if (!(key->flags & HA_NOSAME)
1687  || key->user_defined_key_parts != 1
1688  || strcmp(key->name, FTS_DOC_ID_INDEX_NAME)
1689  || strcmp(key->key_part[0].field->field_name,
1691  return(FTS_INCORRECT_DOC_ID_INDEX);
1692  }
1693 
1694  return(FTS_EXIST_DOC_ID_INDEX);
1695  }
1696 
1697  return(FTS_NOT_EXIST_DOC_ID_INDEX);
1698 }
1699 /*******************************************************************/
1714 static __attribute__((nonnull, warn_unused_result, malloc))
1715 index_def_t*
1716 innobase_create_key_defs(
1717 /*=====================*/
1718  mem_heap_t* heap,
1721  const Alter_inplace_info* ha_alter_info,
1723  const TABLE* altered_table,
1725  ulint& n_add,
1727  ulint& n_fts_add,
1729  bool got_default_clust,
1731  ulint& fts_doc_id_col,
1733  bool& add_fts_doc_id,
1736  bool& add_fts_doc_idx)
1739 {
1740  index_def_t* indexdef;
1741  index_def_t* indexdefs;
1742  bool new_primary;
1743  const uint*const add
1744  = ha_alter_info->index_add_buffer;
1745  const KEY*const key_info
1746  = ha_alter_info->key_info_buffer;
1747 
1748  DBUG_ENTER("innobase_create_key_defs");
1749  DBUG_ASSERT(!add_fts_doc_id || add_fts_doc_idx);
1750  DBUG_ASSERT(ha_alter_info->index_add_count == n_add);
1751 
1752  /* If there is a primary key, it is always the first index
1753  defined for the innodb_table. */
1754 
1755  new_primary = n_add > 0
1756  && !my_strcasecmp(system_charset_info,
1757  key_info[*add].name, "PRIMARY");
1758  n_fts_add = 0;
1759 
1760  /* If there is a UNIQUE INDEX consisting entirely of NOT NULL
1761  columns and if the index does not contain column prefix(es)
1762  (only prefix/part of the column is indexed), MySQL will treat the
1763  index as a PRIMARY KEY unless the table already has one. */
1764 
1765  if (n_add > 0 && !new_primary && got_default_clust
1766  && (key_info[*add].flags & HA_NOSAME)
1767  && !(key_info[*add].flags & HA_KEY_HAS_PART_KEY_SEG)) {
1768  uint key_part = key_info[*add].user_defined_key_parts;
1769 
1770  new_primary = true;
1771 
1772  while (key_part--) {
1773  const uint maybe_null
1774  = key_info[*add].key_part[key_part].key_type
1775  & FIELDFLAG_MAYBE_NULL;
1776  DBUG_ASSERT(!maybe_null
1777  == !key_info[*add].key_part[key_part].
1778  field->real_maybe_null());
1779 
1780  if (maybe_null) {
1781  new_primary = false;
1782  break;
1783  }
1784  }
1785  }
1786 
1787  const bool rebuild = new_primary || add_fts_doc_id
1788  || innobase_need_rebuild(ha_alter_info);
1789  /* Reserve one more space if new_primary is true, and we might
1790  need to add the FTS_DOC_ID_INDEX */
1791  indexdef = indexdefs = static_cast<index_def_t*>(
1793  heap, sizeof *indexdef
1794  * (ha_alter_info->key_count
1795  + rebuild
1796  + got_default_clust)));
1797 
1798  if (rebuild) {
1799  ulint primary_key_number;
1800 
1801  if (new_primary) {
1802  DBUG_ASSERT(n_add > 0);
1803  primary_key_number = *add;
1804  } else if (got_default_clust) {
1805  /* Create the GEN_CLUST_INDEX */
1806  index_def_t* index = indexdef++;
1807 
1808  index->fields = NULL;
1809  index->n_fields = 0;
1810  index->ind_type = DICT_CLUSTERED;
1811  index->name = mem_heap_strdup(
1813  index->key_number = ~0;
1814  primary_key_number = ULINT_UNDEFINED;
1815  goto created_clustered;
1816  } else {
1817  primary_key_number = 0;
1818  }
1819 
1820  /* Create the PRIMARY key index definition */
1821  innobase_create_index_def(
1822  altered_table, key_info, primary_key_number,
1823  TRUE, TRUE, indexdef++, heap);
1824 
1825 created_clustered:
1826  n_add = 1;
1827 
1828  for (ulint i = 0; i < ha_alter_info->key_count; i++) {
1829  if (i == primary_key_number) {
1830  continue;
1831  }
1832  /* Copy the index definitions. */
1833  innobase_create_index_def(
1834  altered_table, key_info, i, TRUE, FALSE,
1835  indexdef, heap);
1836 
1837  if (indexdef->ind_type & DICT_FTS) {
1838  n_fts_add++;
1839  }
1840 
1841  indexdef++;
1842  n_add++;
1843  }
1844 
1845  if (n_fts_add > 0) {
1846  if (!add_fts_doc_id
1847  && !innobase_fts_check_doc_id_col(
1848  NULL, altered_table,
1849  &fts_doc_id_col)) {
1850  fts_doc_id_col = altered_table->s->fields;
1851  add_fts_doc_id = true;
1852  }
1853 
1854  if (!add_fts_doc_idx) {
1855  fts_doc_id_index_enum ret;
1856  ulint doc_col_no;
1857 
1858  ret = innobase_fts_check_doc_id_index(
1859  NULL, altered_table, &doc_col_no);
1860 
1861  /* This should have been checked before */
1862  ut_ad(ret != FTS_INCORRECT_DOC_ID_INDEX);
1863 
1864  if (ret == FTS_NOT_EXIST_DOC_ID_INDEX) {
1865  add_fts_doc_idx = true;
1866  } else {
1867  ut_ad(ret == FTS_EXIST_DOC_ID_INDEX);
1868  ut_ad(doc_col_no == ULINT_UNDEFINED
1869  || doc_col_no == fts_doc_id_col);
1870  }
1871  }
1872  }
1873  } else {
1874  /* Create definitions for added secondary indexes. */
1875 
1876  for (ulint i = 0; i < n_add; i++) {
1877  innobase_create_index_def(
1878  altered_table, key_info, add[i], FALSE, FALSE,
1879  indexdef, heap);
1880 
1881  if (indexdef->ind_type & DICT_FTS) {
1882  n_fts_add++;
1883  }
1884 
1885  indexdef++;
1886  }
1887  }
1888 
1889  DBUG_ASSERT(indexdefs + n_add == indexdef);
1890 
1891  if (add_fts_doc_idx) {
1892  index_def_t* index = indexdef++;
1893 
1894  index->fields = static_cast<index_field_t*>(
1895  mem_heap_alloc(heap, sizeof *index->fields));
1896  index->n_fields = 1;
1897  index->fields->col_no = fts_doc_id_col;
1898  index->fields->prefix_len = 0;
1899  index->ind_type = DICT_UNIQUE;
1900 
1901  if (rebuild) {
1902  index->name = mem_heap_strdup(
1903  heap, FTS_DOC_ID_INDEX_NAME);
1904  ut_ad(!add_fts_doc_id
1905  || fts_doc_id_col == altered_table->s->fields);
1906  } else {
1907  char* index_name;
1908  index->name = index_name = static_cast<char*>(
1910  heap,
1911  1 + sizeof FTS_DOC_ID_INDEX_NAME));
1912  *index_name++ = TEMP_INDEX_PREFIX;
1913  memcpy(index_name, FTS_DOC_ID_INDEX_NAME,
1914  sizeof FTS_DOC_ID_INDEX_NAME);
1915  }
1916 
1917  /* TODO: assign a real MySQL key number for this */
1918  index->key_number = ULINT_UNDEFINED;
1919  n_add++;
1920  }
1921 
1922  DBUG_ASSERT(indexdef > indexdefs);
1923  DBUG_ASSERT((ulint) (indexdef - indexdefs)
1924  <= ha_alter_info->key_count
1925  + add_fts_doc_idx + got_default_clust);
1926  DBUG_ASSERT(ha_alter_info->index_add_count <= n_add);
1927  DBUG_RETURN(indexdefs);
1928 }
1929 
1930 /*******************************************************************/
1933 static __attribute__((nonnull, warn_unused_result))
1934 bool
1935 innobase_check_column_length(
1936 /*=========================*/
1937  ulint max_col_len,
1938  const KEY* key_info)
1939 {
1940  for (ulint key_part = 0; key_part < key_info->user_defined_key_parts; key_part++) {
1941  if (key_info->key_part[key_part].length > max_col_len) {
1942  return(true);
1943  }
1944  }
1945  return(false);
1946 }
1947 
1948 struct ha_innobase_inplace_ctx : public inplace_alter_handler_ctx
1949 {
1951  que_thr_t* thr;
1953  row_prebuilt_t*&prebuilt;
1955  dict_index_t** add_index;
1957  const ulint* add_key_numbers;
1959  ulint num_to_add_index;
1961  dict_index_t** drop_index;
1963  const ulint num_to_drop_index;
1965  dict_foreign_t** drop_fk;
1967  const ulint num_to_drop_fk;
1969  dict_foreign_t** add_fk;
1971  const ulint num_to_add_fk;
1973  bool online;
1975  mem_heap_t* heap;
1977  trx_t* trx;
1979  dict_table_t* old_table;
1981  dict_table_t* new_table;
1983  const ulint* col_map;
1985  const char** col_names;
1987  const ulint add_autoinc;
1989  const dtuple_t* add_cols;
1991  ib_sequence_t sequence;
1993  ulonglong max_autoinc;
1995  const char* tmp_name;
1996 
1997  ha_innobase_inplace_ctx(row_prebuilt_t*& prebuilt_arg,
1998  dict_index_t** drop_arg,
1999  ulint num_to_drop_arg,
2000  dict_foreign_t** drop_fk_arg,
2001  ulint num_to_drop_fk_arg,
2002  dict_foreign_t** add_fk_arg,
2003  ulint num_to_add_fk_arg,
2004  bool online_arg,
2005  mem_heap_t* heap_arg,
2006  dict_table_t* new_table_arg,
2007  const char** col_names_arg,
2008  ulint add_autoinc_arg,
2009  ulonglong autoinc_col_min_value_arg,
2010  ulonglong autoinc_col_max_value_arg) :
2012  prebuilt (prebuilt_arg),
2013  add_index (0), add_key_numbers (0), num_to_add_index (0),
2014  drop_index (drop_arg), num_to_drop_index (num_to_drop_arg),
2015  drop_fk (drop_fk_arg), num_to_drop_fk (num_to_drop_fk_arg),
2016  add_fk (add_fk_arg), num_to_add_fk (num_to_add_fk_arg),
2017  online (online_arg), heap (heap_arg), trx (0),
2018  old_table (prebuilt_arg->table),
2019  new_table (new_table_arg),
2020  col_map (0), col_names (col_names_arg),
2021  add_autoinc (add_autoinc_arg),
2022  add_cols (0),
2023  sequence(prebuilt->trx->mysql_thd,
2024  autoinc_col_min_value_arg, autoinc_col_max_value_arg),
2025  max_autoinc (0),
2026  tmp_name (0)
2027  {
2028 #ifdef UNIV_DEBUG
2029  for (ulint i = 0; i < num_to_add_index; i++) {
2030  ut_ad(!add_index[i]->to_be_dropped);
2031  }
2032  for (ulint i = 0; i < num_to_drop_index; i++) {
2033  ut_ad(drop_index[i]->to_be_dropped);
2034  }
2035 #endif /* UNIV_DEBUG */
2036 
2037  thr = pars_complete_graph_for_exec(NULL, prebuilt->trx, heap);
2038  }
2039 
2040  ~ha_innobase_inplace_ctx()
2041  {
2042  mem_heap_free(heap);
2043  }
2044 
2047  bool need_rebuild () const { return(old_table != new_table); }
2048 
2049 private:
2050  // Disable copying
2051  ha_innobase_inplace_ctx(const ha_innobase_inplace_ctx&);
2052  ha_innobase_inplace_ctx& operator=(const ha_innobase_inplace_ctx&);
2053 };
2054 
2055 /********************************************************************/
2058 static
2059 void
2060 online_retry_drop_indexes_low(
2061 /*==========================*/
2062  dict_table_t* table,
2063  trx_t* trx)
2064 {
2065  ut_ad(mutex_own(&dict_sys->mutex));
2066  ut_ad(trx->dict_operation_lock_mode == RW_X_LATCH);
2068 
2069  /* We can have table->n_ref_count > 1, because other threads
2070  may have prebuilt->table pointing to the table. However, these
2071  other threads should be between statements, waiting for the
2072  next statement to execute, or for a meta-data lock. */
2073  ut_ad(table->n_ref_count >= 1);
2074 
2075  if (table->drop_aborted) {
2076  row_merge_drop_indexes(trx, table, TRUE);
2077  }
2078 }
2079 
2080 /********************************************************************/
2083 static __attribute__((nonnull))
2084 void
2085 online_retry_drop_indexes(
2086 /*======================*/
2087  dict_table_t* table,
2088  THD* user_thd)
2089 {
2090  if (table->drop_aborted) {
2091  trx_t* trx = innobase_trx_allocate(user_thd);
2092 
2093  trx_start_for_ddl(trx, TRX_DICT_OP_INDEX);
2094 
2095  row_mysql_lock_data_dictionary(trx);
2096  online_retry_drop_indexes_low(table, trx);
2097  trx_commit_for_mysql(trx);
2099  trx_free_for_mysql(trx);
2100  }
2101 
2102 #ifdef UNIV_DEBUG
2103  mutex_enter(&dict_sys->mutex);
2104  dict_table_check_for_dup_indexes(table, CHECK_ALL_COMPLETE);
2105  mutex_exit(&dict_sys->mutex);
2106  ut_a(!table->drop_aborted);
2107 #endif /* UNIV_DEBUG */
2108 }
2109 
2110 /********************************************************************/
2113 static __attribute__((nonnull))
2114 void
2115 online_retry_drop_indexes_with_trx(
2116 /*===============================*/
2117  dict_table_t* table,
2118  trx_t* trx)
2119 {
2120  ut_ad(trx_state_eq(trx, TRX_STATE_NOT_STARTED));
2121  ut_ad(trx->dict_operation_lock_mode == RW_X_LATCH);
2122 
2123  /* Now that the dictionary is being locked, check if we can
2124  drop any incompletely created indexes that may have been left
2125  behind in rollback_inplace_alter_table() earlier. */
2126  if (table->drop_aborted) {
2127 
2128  trx->table_id = 0;
2129 
2130  trx_start_for_ddl(trx, TRX_DICT_OP_INDEX);
2131 
2132  online_retry_drop_indexes_low(table, trx);
2133  trx_commit_for_mysql(trx);
2134  }
2135 }
2136 
2142 inline __attribute__((pure, nonnull, warn_unused_result))
2143 bool
2144 innobase_dropping_foreign(
2145 /*======================*/
2146  const dict_foreign_t* foreign,
2147  dict_foreign_t** drop_fk,
2148  ulint n_drop_fk)
2149 {
2150  while (n_drop_fk--) {
2151  if (*drop_fk++ == foreign) {
2152  return(true);
2153  }
2154  }
2155 
2156  return(false);
2157 }
2158 
2169 static __attribute__((pure, nonnull, warn_unused_result))
2170 bool
2171 innobase_check_foreigns_low(
2172 /*========================*/
2173  const dict_table_t* user_table,
2174  dict_foreign_t** drop_fk,
2175  ulint n_drop_fk,
2176  const char* col_name,
2177  bool drop)
2178 {
2179  ut_ad(mutex_own(&dict_sys->mutex));
2180 
2181  /* Check if any FOREIGN KEY constraints are defined on this
2182  column. */
2183  for (const dict_foreign_t* foreign = UT_LIST_GET_FIRST(
2184  user_table->foreign_list);
2185  foreign;
2186  foreign = UT_LIST_GET_NEXT(foreign_list, foreign)) {
2187  if (!drop && !(foreign->type
2190  continue;
2191  }
2192 
2193  if (innobase_dropping_foreign(foreign, drop_fk, n_drop_fk)) {
2194  continue;
2195  }
2196 
2197  for (unsigned f = 0; f < foreign->n_fields; f++) {
2198  if (!strcmp(foreign->foreign_col_names[f],
2199  col_name)) {
2200  my_error(drop
2201  ? ER_FK_COLUMN_CANNOT_DROP
2202  : ER_FK_COLUMN_NOT_NULL, MYF(0),
2203  col_name, foreign->id);
2204  return(true);
2205  }
2206  }
2207  }
2208 
2209  if (!drop) {
2210  /* SET NULL clauses on foreign key constraints of
2211  child tables affect the child tables, not the parent table.
2212  The column can be NOT NULL in the parent table. */
2213  return(false);
2214  }
2215 
2216  /* Check if any FOREIGN KEY constraints in other tables are
2217  referring to the column that is being dropped. */
2218  for (const dict_foreign_t* foreign = UT_LIST_GET_FIRST(
2219  user_table->referenced_list);
2220  foreign;
2221  foreign = UT_LIST_GET_NEXT(referenced_list, foreign)) {
2222  if (innobase_dropping_foreign(foreign, drop_fk, n_drop_fk)) {
2223  continue;
2224  }
2225 
2226  for (unsigned f = 0; f < foreign->n_fields; f++) {
2227  char display_name[FN_REFLEN];
2228 
2229  if (strcmp(foreign->referenced_col_names[f],
2230  col_name)) {
2231  continue;
2232  }
2233 
2234  char* buf_end = innobase_convert_name(
2235  display_name, (sizeof display_name) - 1,
2236  foreign->foreign_table_name,
2237  strlen(foreign->foreign_table_name),
2238  NULL, TRUE);
2239  *buf_end = '\0';
2240  my_error(ER_FK_COLUMN_CANNOT_DROP_CHILD,
2241  MYF(0), col_name, foreign->id,
2242  display_name);
2243 
2244  return(true);
2245  }
2246  }
2247 
2248  return(false);
2249 }
2250 
2262 static __attribute__((pure, nonnull, warn_unused_result))
2263 bool
2264 innobase_check_foreigns(
2265 /*====================*/
2266  Alter_inplace_info* ha_alter_info,
2267  const TABLE* altered_table,
2268  const TABLE* old_table,
2269  const dict_table_t* user_table,
2270  dict_foreign_t** drop_fk,
2271  ulint n_drop_fk)
2272 {
2274  ha_alter_info->alter_info->create_list);
2275 
2276  for (Field** fp = old_table->field; *fp; fp++) {
2277  cf_it.rewind();
2278  const Create_field* new_field;
2279 
2280  ut_ad(!(*fp)->real_maybe_null()
2281  == !!((*fp)->flags & NOT_NULL_FLAG));
2282 
2283  while ((new_field = cf_it++)) {
2284  if (new_field->field == *fp) {
2285  break;
2286  }
2287  }
2288 
2289  if (!new_field || (new_field->flags & NOT_NULL_FLAG)) {
2290  if (innobase_check_foreigns_low(
2291  user_table, drop_fk, n_drop_fk,
2292  (*fp)->field_name, !new_field)) {
2293  return(true);
2294  }
2295  }
2296  }
2297 
2298  return(false);
2299 }
2300 
2307 static __attribute__((nonnull))
2308 void
2309 innobase_build_col_map_add(
2310 /*=======================*/
2311  mem_heap_t* heap,
2312  dfield_t* dfield,
2313  const Field* field,
2314  ulint comp)
2315 {
2316  if (field->is_real_null()) {
2317  dfield_set_null(dfield);
2318  return;
2319  }
2320 
2321  ulint size = field->pack_length();
2322 
2323  byte* buf = static_cast<byte*>(mem_heap_alloc(heap, size));
2324 
2326  dfield, buf, TRUE, field->ptr, size, comp);
2327 }
2328 
2341 static __attribute__((nonnull(1,2,3,4,5,7), warn_unused_result))
2342 const ulint*
2343 innobase_build_col_map(
2344 /*===================*/
2345  Alter_inplace_info* ha_alter_info,
2346  const TABLE* altered_table,
2347  const TABLE* table,
2348  const dict_table_t* new_table,
2349  const dict_table_t* old_table,
2350  dtuple_t* add_cols,
2351  mem_heap_t* heap)
2352 {
2353  DBUG_ENTER("innobase_build_col_map");
2354  DBUG_ASSERT(altered_table != table);
2355  DBUG_ASSERT(new_table != old_table);
2356  DBUG_ASSERT(dict_table_get_n_cols(new_table)
2357  >= altered_table->s->fields + DATA_N_SYS_COLS);
2358  DBUG_ASSERT(dict_table_get_n_cols(old_table)
2359  >= table->s->fields + DATA_N_SYS_COLS);
2360  DBUG_ASSERT(!!add_cols == !!(ha_alter_info->handler_flags
2361  & Alter_inplace_info::ADD_COLUMN));
2362  DBUG_ASSERT(!add_cols || dtuple_get_n_fields(add_cols)
2363  == dict_table_get_n_cols(new_table));
2364 
2365  ulint* col_map = static_cast<ulint*>(
2366  mem_heap_alloc(heap, old_table->n_cols * sizeof *col_map));
2367 
2369  ha_alter_info->alter_info->create_list);
2370  uint i = 0;
2371 
2372  /* Any dropped columns will map to ULINT_UNDEFINED. */
2373  for (uint old_i = 0; old_i + DATA_N_SYS_COLS < old_table->n_cols;
2374  old_i++) {
2375  col_map[old_i] = ULINT_UNDEFINED;
2376  }
2377 
2378  while (const Create_field* new_field = cf_it++) {
2379  for (uint old_i = 0; table->field[old_i]; old_i++) {
2380  const Field* field = table->field[old_i];
2381  if (new_field->field == field) {
2382  col_map[old_i] = i;
2383  goto found_col;
2384  }
2385  }
2386 
2387  innobase_build_col_map_add(
2388  heap, dtuple_get_nth_field(add_cols, i),
2389  altered_table->field[i],
2390  dict_table_is_comp(new_table));
2391 found_col:
2392  i++;
2393  }
2394 
2395  DBUG_ASSERT(i == altered_table->s->fields);
2396 
2397  i = table->s->fields;
2398 
2399  /* Add the InnoDB hidden FTS_DOC_ID column, if any. */
2400  if (i + DATA_N_SYS_COLS < old_table->n_cols) {
2401  /* There should be exactly one extra field,
2402  the FTS_DOC_ID. */
2403  DBUG_ASSERT(DICT_TF2_FLAG_IS_SET(old_table,
2405  DBUG_ASSERT(i + DATA_N_SYS_COLS + 1 == old_table->n_cols);
2406  DBUG_ASSERT(!strcmp(dict_table_get_col_name(
2407  old_table, table->s->fields),
2409  if (altered_table->s->fields + DATA_N_SYS_COLS
2410  < new_table->n_cols) {
2411  DBUG_ASSERT(DICT_TF2_FLAG_IS_SET(
2412  new_table,
2414  DBUG_ASSERT(altered_table->s->fields
2415  + DATA_N_SYS_COLS + 1
2416  == new_table->n_cols);
2417  col_map[i] = altered_table->s->fields;
2418  } else {
2419  DBUG_ASSERT(!DICT_TF2_FLAG_IS_SET(
2420  new_table,
2422  col_map[i] = ULINT_UNDEFINED;
2423  }
2424 
2425  i++;
2426  } else {
2427  DBUG_ASSERT(!DICT_TF2_FLAG_IS_SET(
2428  old_table,
2430  }
2431 
2432  for (; i < old_table->n_cols; i++) {
2433  col_map[i] = i + new_table->n_cols - old_table->n_cols;
2434  }
2435 
2436  DBUG_RETURN(col_map);
2437 }
2438 
2445 static
2446 dberr_t
2447 innobase_drop_fts_index_table(
2448 /*==========================*/
2449  dict_table_t* table,
2450  trx_t* trx)
2451 {
2452  dberr_t ret_err = DB_SUCCESS;
2453 
2454  for (dict_index_t* index = dict_table_get_first_index(table);
2455  index != NULL;
2456  index = dict_table_get_next_index(index)) {
2457  if (index->type & DICT_FTS) {
2458  dberr_t err;
2459 
2460  err = fts_drop_index_tables(trx, index);
2461 
2462  if (err != DB_SUCCESS) {
2463  ret_err = err;
2464  }
2465  }
2466  }
2467 
2468  return(ret_err);
2469 }
2470 
2477 static __attribute__((nonnull, warn_unused_result))
2478 const char**
2479 innobase_get_col_names(
2480 /*===================*/
2481  Alter_inplace_info* ha_alter_info,
2482  const TABLE* altered_table,
2483  const dict_table_t* user_table,
2484  mem_heap_t* heap)
2485 {
2486  const char** cols;
2487  uint i;
2488 
2489  DBUG_ENTER("innobase_get_col_names");
2490  DBUG_ASSERT(user_table->n_def > altered_table->s->fields);
2491  DBUG_ASSERT(ha_alter_info->handler_flags
2492  & Alter_inplace_info::ALTER_COLUMN_NAME);
2493 
2494  cols = static_cast<const char**>(
2495  mem_heap_alloc(heap, user_table->n_def * sizeof *cols));
2496 
2497  for (i = 0; i < altered_table->s->fields; i++) {
2498  const Field* field = altered_table->field[i];
2499  cols[i] = field->field_name;
2500  }
2501 
2502  /* Copy the internal column names. */
2503  cols[i] = dict_table_get_col_name(user_table, i);
2504 
2505  while (++i < user_table->n_def) {
2506  cols[i] = cols[i - 1] + strlen(cols[i - 1]) + 1;
2507  }
2508 
2509  DBUG_RETURN(cols);
2510 }
2511 
2528 static __attribute__((warn_unused_result, nonnull(1,2,3,4)))
2529 bool
2530 prepare_inplace_alter_table_dict(
2531 /*=============================*/
2532  Alter_inplace_info* ha_alter_info,
2533  const TABLE* altered_table,
2534  const TABLE* old_table,
2535  const char* table_name,
2536  ulint flags,
2537  ulint flags2,
2538  ulint fts_doc_id_col,
2539  bool add_fts_doc_id,
2540  bool add_fts_doc_id_idx)
2541 {
2542  bool dict_locked = false;
2543  ulint* add_key_nums; /* MySQL key numbers */
2544  index_def_t* index_defs; /* index definitions */
2545  dict_table_t* user_table;
2546  dict_index_t* fts_index = NULL;
2547  ulint new_clustered = 0;
2548  dberr_t error;
2549  ulint num_fts_index;
2550  ha_innobase_inplace_ctx*ctx;
2551 
2552  DBUG_ENTER("prepare_inplace_alter_table_dict");
2553 
2554  ctx = static_cast<ha_innobase_inplace_ctx*>
2555  (ha_alter_info->handler_ctx);
2556 
2557  DBUG_ASSERT((ctx->add_autoinc != ULINT_UNDEFINED)
2558  == (ctx->sequence.m_max_value > 0));
2559  DBUG_ASSERT(!ctx->num_to_drop_index == !ctx->drop_index);
2560  DBUG_ASSERT(!ctx->num_to_drop_fk == !ctx->drop_fk);
2561  DBUG_ASSERT(!add_fts_doc_id || add_fts_doc_id_idx);
2562  DBUG_ASSERT(!add_fts_doc_id_idx
2563  || innobase_fulltext_exist(altered_table));
2564  DBUG_ASSERT(!ctx->add_cols);
2565  DBUG_ASSERT(!ctx->add_index);
2566  DBUG_ASSERT(!ctx->add_key_numbers);
2567  DBUG_ASSERT(!ctx->num_to_add_index);
2568 
2569  user_table = ctx->new_table;
2570 
2571  trx_start_if_not_started_xa(ctx->prebuilt->trx);
2572 
2573  /* Create a background transaction for the operations on
2574  the data dictionary tables. */
2575  ctx->trx = innobase_trx_allocate(ctx->prebuilt->trx->mysql_thd);
2576 
2577  trx_start_for_ddl(ctx->trx, TRX_DICT_OP_INDEX);
2578 
2579  /* Create table containing all indexes to be built in this
2580  ALTER TABLE ADD INDEX so that they are in the correct order
2581  in the table. */
2582 
2583  ctx->num_to_add_index = ha_alter_info->index_add_count;
2584 
2585  index_defs = innobase_create_key_defs(
2586  ctx->heap, ha_alter_info, altered_table, ctx->num_to_add_index,
2587  num_fts_index,
2588  row_table_got_default_clust_index(ctx->new_table),
2589  fts_doc_id_col, add_fts_doc_id, add_fts_doc_id_idx);
2590 
2591  new_clustered = DICT_CLUSTERED & index_defs[0].ind_type;
2592 
2593  if (num_fts_index > 1) {
2594  my_error(ER_INNODB_FT_LIMIT, MYF(0));
2595  goto error_handled;
2596  }
2597 
2598  if (!ctx->online) {
2599  /* This is not an online operation (LOCK=NONE). */
2600  } else if (ctx->add_autoinc == ULINT_UNDEFINED
2601  && num_fts_index == 0
2602  && (!innobase_need_rebuild(ha_alter_info)
2603  || !innobase_fulltext_exist(altered_table))) {
2604  /* InnoDB can perform an online operation (LOCK=NONE). */
2605  } else {
2606  /* This should have been blocked in
2607  check_if_supported_inplace_alter(). */
2608  ut_ad(0);
2609  my_error(ER_NOT_SUPPORTED_YET, MYF(0),
2610  thd_query_string(ctx->prebuilt->trx->mysql_thd)->str);
2611  goto error_handled;
2612  }
2613 
2614  /* The primary index would be rebuilt if a FTS Doc ID
2615  column is to be added, and the primary index definition
2616  is just copied from old table and stored in indexdefs[0] */
2617  DBUG_ASSERT(!add_fts_doc_id || new_clustered);
2618  DBUG_ASSERT(!!new_clustered ==
2619  (innobase_need_rebuild(ha_alter_info)
2620  || add_fts_doc_id));
2621 
2622  /* Allocate memory for dictionary index definitions */
2623 
2624  ctx->add_index = static_cast<dict_index_t**>(
2625  mem_heap_alloc(ctx->heap, ctx->num_to_add_index
2626  * sizeof *ctx->add_index));
2627  ctx->add_key_numbers = add_key_nums = static_cast<ulint*>(
2628  mem_heap_alloc(ctx->heap, ctx->num_to_add_index
2629  * sizeof *ctx->add_key_numbers));
2630 
2631  /* This transaction should be dictionary operation, so that
2632  the data dictionary will be locked during crash recovery. */
2633 
2634  ut_ad(ctx->trx->dict_operation == TRX_DICT_OP_INDEX);
2635 
2636  /* Acquire a lock on the table before creating any indexes. */
2637 
2638  if (ctx->online) {
2639  error = DB_SUCCESS;
2640  } else {
2641  error = row_merge_lock_table(
2642  ctx->prebuilt->trx, ctx->new_table, LOCK_S);
2643 
2644  if (error != DB_SUCCESS) {
2645 
2646  goto error_handling;
2647  }
2648  }
2649 
2650  /* Latch the InnoDB data dictionary exclusively so that no deadlocks
2651  or lock waits can happen in it during an index create operation. */
2652 
2653  row_mysql_lock_data_dictionary(ctx->trx);
2654  dict_locked = true;
2655 
2656  /* Wait for background stats processing to stop using the table that
2657  we are going to alter. We know bg stats will not start using it again
2658  until we are holding the data dict locked and we are holding it here
2659  at least until checking ut_ad(user_table->n_ref_count == 1) below.
2660  XXX what may happen if bg stats opens the table after we
2661  have unlocked data dictionary below? */
2662  dict_stats_wait_bg_to_stop_using_table(user_table, ctx->trx);
2663 
2664  online_retry_drop_indexes_low(ctx->new_table, ctx->trx);
2665 
2666  ut_d(dict_table_check_for_dup_indexes(
2667  ctx->new_table, CHECK_ABORTED_OK));
2668 
2669  /* If a new clustered index is defined for the table we need
2670  to rebuild the table with a temporary name. */
2671 
2672  if (new_clustered) {
2673  const char* new_table_name
2674  = dict_mem_create_temporary_tablename(
2675  ctx->heap,
2676  ctx->new_table->name,
2677  ctx->new_table->id);
2678  ulint n_cols;
2679  dtuple_t* add_cols;
2680 
2681  if (innobase_check_foreigns(
2682  ha_alter_info, altered_table, old_table,
2683  user_table, ctx->drop_fk, ctx->num_to_drop_fk)) {
2684  goto new_clustered_failed;
2685  }
2686 
2687  n_cols = altered_table->s->fields;
2688 
2689  if (add_fts_doc_id) {
2690  n_cols++;
2691  DBUG_ASSERT(flags2 & DICT_TF2_FTS);
2692  DBUG_ASSERT(add_fts_doc_id_idx);
2693  flags2 |= DICT_TF2_FTS_ADD_DOC_ID
2695  | DICT_TF2_FTS;
2696  }
2697 
2698  DBUG_ASSERT(!add_fts_doc_id_idx || (flags2 & DICT_TF2_FTS));
2699 
2700  /* Create the table. */
2702 
2703  if (dict_table_get_low(new_table_name)) {
2704  my_error(ER_TABLE_EXISTS_ERROR, MYF(0),
2705  new_table_name);
2706  goto new_clustered_failed;
2707  }
2708 
2709  /* The initial space id 0 may be overridden later. */
2710  ctx->new_table = dict_mem_table_create(
2711  new_table_name, 0, n_cols, flags, flags2);
2712  /* The rebuilt indexed_table will use the renamed
2713  column names. */
2714  ctx->col_names = NULL;
2715 
2716  if (DICT_TF_HAS_DATA_DIR(flags)) {
2717  ctx->new_table->data_dir_path =
2718  mem_heap_strdup(ctx->new_table->heap,
2719  user_table->data_dir_path);
2720  }
2721 
2722  for (uint i = 0; i < altered_table->s->fields; i++) {
2723  const Field* field = altered_table->field[i];
2724  ulint is_unsigned;
2725  ulint field_type
2726  = (ulint) field->type();
2727  ulint col_type
2729  &is_unsigned, field);
2730  ulint charset_no;
2731  ulint col_len;
2732 
2733  /* we assume in dtype_form_prtype() that this
2734  fits in two bytes */
2735  ut_a(field_type <= MAX_CHAR_COLL_NUM);
2736 
2737  if (!field->real_maybe_null()) {
2738  field_type |= DATA_NOT_NULL;
2739  }
2740 
2741  if (field->binary()) {
2742  field_type |= DATA_BINARY_TYPE;
2743  }
2744 
2745  if (is_unsigned) {
2746  field_type |= DATA_UNSIGNED;
2747  }
2748 
2749  if (dtype_is_string_type(col_type)) {
2750  charset_no = (ulint) field->charset()->number;
2751 
2752  if (charset_no > MAX_CHAR_COLL_NUM) {
2754  ctx->new_table);
2755  my_error(ER_WRONG_KEY_COLUMN, MYF(0),
2756  field->field_name);
2757  goto new_clustered_failed;
2758  }
2759  } else {
2760  charset_no = 0;
2761  }
2762 
2763  col_len = field->pack_length();
2764 
2765  /* The MySQL pack length contains 1 or 2 bytes
2766  length field for a true VARCHAR. Let us
2767  subtract that, so that the InnoDB column
2768  length in the InnoDB data dictionary is the
2769  real maximum byte length of the actual data. */
2770 
2771  if (field->type() == MYSQL_TYPE_VARCHAR) {
2772  uint32 length_bytes
2773  = static_cast<const Field_varstring*>(
2774  field)->length_bytes;
2775 
2776  col_len -= length_bytes;
2777 
2778  if (length_bytes == 2) {
2779  field_type |= DATA_LONG_TRUE_VARCHAR;
2780  }
2781  }
2782 
2783  if (dict_col_name_is_reserved(field->field_name)) {
2784  dict_mem_table_free(ctx->new_table);
2785  my_error(ER_WRONG_COLUMN_NAME, MYF(0),
2786  field->field_name);
2787  goto new_clustered_failed;
2788  }
2789 
2791  ctx->new_table, ctx->heap,
2792  field->field_name,
2793  col_type,
2794  dtype_form_prtype(field_type, charset_no),
2795  col_len);
2796  }
2797 
2798  if (add_fts_doc_id) {
2799  fts_add_doc_id_column(ctx->new_table, ctx->heap);
2800  ctx->new_table->fts->doc_col = fts_doc_id_col;
2801  ut_ad(fts_doc_id_col == altered_table->s->fields);
2802  } else if (ctx->new_table->fts) {
2803  ctx->new_table->fts->doc_col = fts_doc_id_col;
2804  }
2805 
2807  ctx->new_table, ctx->trx, false);
2808 
2809  switch (error) {
2810  dict_table_t* temp_table;
2811  case DB_SUCCESS:
2812  /* We need to bump up the table ref count and
2813  before we can use it we need to open the
2814  table. The new_table must be in the data
2815  dictionary cache, because we are still holding
2816  the dict_sys->mutex. */
2817  ut_ad(mutex_own(&dict_sys->mutex));
2818  temp_table = dict_table_open_on_name(
2819  ctx->new_table->name, TRUE, FALSE,
2821  ut_a(ctx->new_table == temp_table);
2822  /* n_ref_count must be 1, because purge cannot
2823  be executing on this very table as we are
2824  holding dict_operation_lock X-latch. */
2825  DBUG_ASSERT(ctx->new_table->n_ref_count == 1);
2826  break;
2827  case DB_TABLESPACE_EXISTS:
2828  my_error(ER_TABLESPACE_EXISTS, MYF(0),
2829  new_table_name);
2830  goto new_clustered_failed;
2831  case DB_DUPLICATE_KEY:
2832  my_error(HA_ERR_TABLE_EXIST, MYF(0),
2833  altered_table->s->table_name.str);
2834  goto new_clustered_failed;
2835  default:
2836  my_error_innodb(error, table_name, flags);
2837  new_clustered_failed:
2838  DBUG_ASSERT(ctx->trx != ctx->prebuilt->trx);
2839  trx_rollback_to_savepoint(ctx->trx, NULL);
2840 
2841  ut_ad(user_table->n_ref_count == 1);
2842 
2843  online_retry_drop_indexes_with_trx(
2844  user_table, ctx->trx);
2845  goto err_exit;
2846  }
2847 
2848  if (ha_alter_info->handler_flags
2849  & Alter_inplace_info::ADD_COLUMN) {
2850  add_cols = dtuple_create(
2851  ctx->heap,
2852  dict_table_get_n_cols(ctx->new_table));
2853 
2854  dict_table_copy_types(add_cols, ctx->new_table);
2855  } else {
2856  add_cols = NULL;
2857  }
2858 
2859  ctx->col_map = innobase_build_col_map(
2860  ha_alter_info, altered_table, old_table,
2861  ctx->new_table, user_table,
2862  add_cols, ctx->heap);
2863  ctx->add_cols = add_cols;
2864  } else {
2865  DBUG_ASSERT(!innobase_need_rebuild(ha_alter_info));
2866 
2867  if (!ctx->new_table->fts
2868  && innobase_fulltext_exist(altered_table)) {
2869  ctx->new_table->fts = fts_create(
2870  ctx->new_table);
2871  ctx->new_table->fts->doc_col = fts_doc_id_col;
2872  }
2873  }
2874 
2875  /* Assign table_id, so that no table id of
2876  fts_create_index_tables() will be written to the undo logs. */
2877  DBUG_ASSERT(ctx->new_table->id != 0);
2878  ctx->trx->table_id = ctx->new_table->id;
2879 
2880  /* Create the indexes in SYS_INDEXES and load into dictionary. */
2881 
2882  for (ulint a = 0; a < ctx->num_to_add_index; a++) {
2883 
2884  ctx->add_index[a] = row_merge_create_index(
2885  ctx->trx, ctx->new_table,
2886  &index_defs[a]);
2887 
2888  add_key_nums[a] = index_defs[a].key_number;
2889 
2890  if (!ctx->add_index[a]) {
2891  error = ctx->trx->error_state;
2892  DBUG_ASSERT(error != DB_SUCCESS);
2893  goto error_handling;
2894  }
2895 
2896  if (ctx->add_index[a]->type & DICT_FTS) {
2897  DBUG_ASSERT(num_fts_index);
2898  DBUG_ASSERT(!fts_index);
2899  DBUG_ASSERT(ctx->add_index[a]->type == DICT_FTS);
2900  fts_index = ctx->add_index[a];
2901  }
2902 
2903  /* If only online ALTER TABLE operations have been
2904  requested, allocate a modification log. If the table
2905  will be locked anyway, the modification
2906  log is unnecessary. When rebuilding the table
2907  (new_clustered), we will allocate the log for the
2908  clustered index of the old table, later. */
2909  if (new_clustered
2910  || !ctx->online
2911  || user_table->ibd_file_missing
2912  || dict_table_is_discarded(user_table)) {
2913  /* No need to allocate a modification log. */
2914  ut_ad(!ctx->add_index[a]->online_log);
2915  } else if (ctx->add_index[a]->type & DICT_FTS) {
2916  /* Fulltext indexes are not covered
2917  by a modification log. */
2918  } else {
2919  DBUG_EXECUTE_IF("innodb_OOM_prepare_inplace_alter",
2920  error = DB_OUT_OF_MEMORY;
2921  goto error_handling;);
2922  rw_lock_x_lock(&ctx->add_index[a]->lock);
2923  bool ok = row_log_allocate(ctx->add_index[a],
2924  NULL, true, NULL, NULL);
2925  rw_lock_x_unlock(&ctx->add_index[a]->lock);
2926 
2927  if (!ok) {
2928  error = DB_OUT_OF_MEMORY;
2929  goto error_handling;
2930  }
2931  }
2932  }
2933 
2934  ut_ad(new_clustered == ctx->need_rebuild());
2935 
2936  DBUG_EXECUTE_IF("innodb_OOM_prepare_inplace_alter",
2937  error = DB_OUT_OF_MEMORY;
2938  goto error_handling;);
2939 
2940  if (new_clustered && ctx->online) {
2941  /* Allocate a log for online table rebuild. */
2942  dict_index_t* clust_index = dict_table_get_first_index(
2943  user_table);
2944 
2945  rw_lock_x_lock(&clust_index->lock);
2946  bool ok = row_log_allocate(
2947  clust_index, ctx->new_table,
2948  !(ha_alter_info->handler_flags
2949  & Alter_inplace_info::ADD_PK_INDEX),
2950  ctx->add_cols, ctx->col_map);
2951  rw_lock_x_unlock(&clust_index->lock);
2952 
2953  if (!ok) {
2954  error = DB_OUT_OF_MEMORY;
2955  goto error_handling;
2956  }
2957  }
2958 
2959  if (ctx->online) {
2960  /* Assign a consistent read view for
2961  row_merge_read_clustered_index(). */
2962  trx_assign_read_view(ctx->prebuilt->trx);
2963  }
2964 
2965  if (fts_index) {
2966  /* Ensure that the dictionary operation mode will
2967  not change while creating the auxiliary tables. */
2968  trx_dict_op_t op = trx_get_dict_operation(ctx->trx);
2969 
2970 #ifdef UNIV_DEBUG
2971  switch (op) {
2972  case TRX_DICT_OP_NONE:
2973  break;
2974  case TRX_DICT_OP_TABLE:
2975  case TRX_DICT_OP_INDEX:
2976  goto op_ok;
2977  }
2978  ut_error;
2979 op_ok:
2980 #endif /* UNIV_DEBUG */
2981  ut_ad(ctx->trx->dict_operation_lock_mode == RW_X_LATCH);
2982  ut_ad(mutex_own(&dict_sys->mutex));
2983 #ifdef UNIV_SYNC_DEBUG
2984  ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
2985 #endif /* UNIV_SYNC_DEBUG */
2986 
2987  DICT_TF2_FLAG_SET(ctx->new_table, DICT_TF2_FTS);
2988 
2989  /* This function will commit the transaction and reset
2990  the trx_t::dict_operation flag on success. */
2991 
2992  error = fts_create_index_tables(ctx->trx, fts_index);
2993 
2994  DBUG_EXECUTE_IF("innodb_test_fail_after_fts_index_table",
2995  error = DB_LOCK_WAIT_TIMEOUT;
2996  goto error_handling;);
2997 
2998  if (error != DB_SUCCESS) {
2999  goto error_handling;
3000  }
3001 
3002  trx_start_for_ddl(ctx->trx, op);
3003 
3004  if (!ctx->new_table->fts
3005  || ib_vector_size(ctx->new_table->fts->indexes) == 0) {
3006  error = fts_create_common_tables(
3007  ctx->trx, ctx->new_table,
3008  user_table->name, TRUE);
3009 
3010  DBUG_EXECUTE_IF(
3011  "innodb_test_fail_after_fts_common_table",
3012  error = DB_LOCK_WAIT_TIMEOUT;);
3013 
3014  if (error != DB_SUCCESS) {
3015  goto error_handling;
3016  }
3017 
3018  ctx->new_table->fts->fts_status
3019  |= TABLE_DICT_LOCKED;
3020 
3021  error = innobase_fts_load_stopword(
3022  ctx->new_table, ctx->trx,
3023  ctx->prebuilt->trx->mysql_thd)
3024  ? DB_SUCCESS : DB_ERROR;
3025  ctx->new_table->fts->fts_status
3026  &= ~TABLE_DICT_LOCKED;
3027 
3028  if (error != DB_SUCCESS) {
3029  goto error_handling;
3030  }
3031  }
3032 
3033  ut_ad(trx_get_dict_operation(ctx->trx) == op);
3034  }
3035 
3036  DBUG_ASSERT(error == DB_SUCCESS);
3037 
3038  /* Commit the data dictionary transaction in order to release
3039  the table locks on the system tables. This means that if
3040  MySQL crashes while creating a new primary key inside
3041  row_merge_build_indexes(), ctx->new_table will not be dropped
3042  by trx_rollback_active(). It will have to be recovered or
3043  dropped by the database administrator. */
3044  trx_commit_for_mysql(ctx->trx);
3045 
3047  dict_locked = false;
3048 
3049  ut_a(ctx->trx->lock.n_active_thrs == 0);
3050 
3051 error_handling:
3052  /* After an error, remove all those index definitions from the
3053  dictionary which were defined. */
3054 
3055  switch (error) {
3056  case DB_SUCCESS:
3057  ut_a(!dict_locked);
3058 
3059  ut_d(mutex_enter(&dict_sys->mutex));
3060  ut_d(dict_table_check_for_dup_indexes(
3061  user_table, CHECK_PARTIAL_OK));
3062  ut_d(mutex_exit(&dict_sys->mutex));
3063  DBUG_RETURN(false);
3064  case DB_TABLESPACE_EXISTS:
3065  my_error(ER_TABLESPACE_EXISTS, MYF(0), "(unknown)");
3066  break;
3067  case DB_DUPLICATE_KEY:
3068  my_error(ER_DUP_KEY, MYF(0), "SYS_INDEXES");
3069  break;
3070  default:
3071  my_error_innodb(error, table_name, user_table->flags);
3072  }
3073 
3074 error_handled:
3075 
3076  ctx->prebuilt->trx->error_info = NULL;
3077  ctx->trx->error_state = DB_SUCCESS;
3078 
3079  if (!dict_locked) {
3080  row_mysql_lock_data_dictionary(ctx->trx);
3081  }
3082 
3083  if (new_clustered) {
3084  if (ctx->need_rebuild()) {
3085 
3086  if (DICT_TF2_FLAG_IS_SET(
3087  ctx->new_table, DICT_TF2_FTS)) {
3088  innobase_drop_fts_index_table(
3089  ctx->new_table, ctx->trx);
3090  }
3091 
3092  dict_table_close(ctx->new_table, TRUE, FALSE);
3093 
3094 #if defined UNIV_DEBUG || defined UNIV_DDL_DEBUG
3095  /* Nobody should have initialized the stats of the
3096  newly created table yet. When this is the case, we
3097  know that it has not been added for background stats
3098  gathering. */
3099  ut_a(!ctx->new_table->stat_initialized);
3100 #endif /* UNIV_DEBUG || UNIV_DDL_DEBUG */
3101 
3102  row_merge_drop_table(ctx->trx, ctx->new_table);
3103 
3104  /* Free the log for online table rebuild, if
3105  one was allocated. */
3106 
3107  dict_index_t* clust_index = dict_table_get_first_index(
3108  user_table);
3109 
3110  rw_lock_x_lock(&clust_index->lock);
3111 
3112  if (clust_index->online_log) {
3113  ut_ad(ctx->online);
3114  row_log_abort_sec(clust_index);
3115  clust_index->online_status
3117  }
3118 
3119  rw_lock_x_unlock(&clust_index->lock);
3120  }
3121 
3122  trx_commit_for_mysql(ctx->trx);
3123  /* n_ref_count must be 1, because purge cannot
3124  be executing on this very table as we are
3125  holding dict_operation_lock X-latch. */
3126  DBUG_ASSERT(user_table->n_ref_count == 1 || ctx->online);
3127 
3128  online_retry_drop_indexes_with_trx(user_table, ctx->trx);
3129  } else {
3130  ut_ad(!ctx->need_rebuild());
3131  row_merge_drop_indexes(ctx->trx, user_table, TRUE);
3132  trx_commit_for_mysql(ctx->trx);
3133  }
3134 
3135  ut_d(dict_table_check_for_dup_indexes(user_table, CHECK_ALL_COMPLETE));
3136  ut_ad(!user_table->drop_aborted);
3137 
3138 err_exit:
3139  /* Clear the to_be_dropped flag in the data dictionary cache. */
3140  for (ulint i = 0; i < ctx->num_to_drop_index; i++) {
3141  DBUG_ASSERT(*ctx->drop_index[i]->name != TEMP_INDEX_PREFIX);
3142  DBUG_ASSERT(ctx->drop_index[i]->to_be_dropped);
3143  ctx->drop_index[i]->to_be_dropped = 0;
3144  }
3145 
3147 
3148  trx_free_for_mysql(ctx->trx);
3149  trx_commit_for_mysql(ctx->prebuilt->trx);
3150 
3151  delete ctx;
3152  ha_alter_info->handler_ctx = NULL;
3153 
3154  /* There might be work for utility threads.*/
3156 
3157  DBUG_RETURN(true);
3158 }
3159 
3160 /* Check whether an index is needed for the foreign key constraint.
3161 If so, if it is dropped, is there an equivalent index can play its role.
3162 @return true if the index is needed and can't be dropped */
3163 static __attribute__((nonnull(1,2,3,5), warn_unused_result))
3164 bool
3165 innobase_check_foreign_key_index(
3166 /*=============================*/
3167  Alter_inplace_info* ha_alter_info,
3170  dict_index_t* index,
3171  dict_table_t* indexed_table,
3173  const char** col_names,
3175  trx_t* trx,
3176  dict_foreign_t** drop_fk,
3178  ulint n_drop_fk)
3180 {
3181  dict_foreign_t* foreign;
3182 
3183  /* Check if the index is referenced. */
3184  foreign = dict_table_get_referenced_constraint(indexed_table, index);
3185 
3186  ut_ad(!foreign || indexed_table
3187  == foreign->referenced_table);
3188 
3189  if (foreign
3191  indexed_table, col_names,
3192  foreign->referenced_col_names,
3193  foreign->n_fields, index,
3194  /*check_charsets=*/TRUE,
3195  /*check_null=*/FALSE)
3196  && !innobase_find_equiv_index(
3197  foreign->referenced_col_names,
3198  foreign->n_fields,
3199  ha_alter_info->key_info_buffer,
3200  ha_alter_info->index_add_buffer,
3201  ha_alter_info->index_add_count)
3202  ) {
3203  trx->error_info = index;
3204  return(true);
3205  }
3206 
3207  /* Check if this index references some
3208  other table */
3210  indexed_table, index);
3211 
3212  ut_ad(!foreign || indexed_table
3213  == foreign->foreign_table);
3214 
3215  if (foreign
3216  && !innobase_dropping_foreign(
3217  foreign, drop_fk, n_drop_fk)
3219  indexed_table, col_names,
3220  foreign->foreign_col_names,
3221  foreign->n_fields, index,
3222  /*check_charsets=*/TRUE,
3223  /*check_null=*/FALSE)
3224  && !innobase_find_equiv_index(
3225  foreign->foreign_col_names,
3226  foreign->n_fields,
3227  ha_alter_info->key_info_buffer,
3228  ha_alter_info->index_add_buffer,
3229  ha_alter_info->index_add_count)
3230  ) {
3231  trx->error_info = index;
3232  return(true);
3233  }
3234 
3235  return(false);
3236 }
3237 
3250 UNIV_INTERN
3251 bool
3253 /*=====================================*/
3254  TABLE* altered_table,
3255  Alter_inplace_info* ha_alter_info)
3256 {
3257  dict_index_t** drop_index;
3258  ulint n_drop_index;
3259  dict_foreign_t**drop_fk;
3260  ulint n_drop_fk;
3261  dict_foreign_t**add_fk = NULL;
3262  ulint n_add_fk;
3263  dict_table_t* indexed_table;
3264  mem_heap_t* heap;
3265  const char** col_names;
3266  int error;
3267  ulint flags;
3268  ulint flags2;
3269  ulint max_col_len;
3270  ulint add_autoinc_col_no = ULINT_UNDEFINED;
3271  ulonglong autoinc_col_max_value = 0;
3272  ulint fts_doc_col_no = ULINT_UNDEFINED;
3273  bool add_fts_doc_id = false;
3274  bool add_fts_doc_id_idx = false;
3275 
3276  DBUG_ENTER("prepare_inplace_alter_table");
3277  DBUG_ASSERT(!ha_alter_info->handler_ctx);
3278  DBUG_ASSERT(ha_alter_info->create_info);
3279  DBUG_ASSERT(!srv_read_only_mode);
3280 
3281  MONITOR_ATOMIC_INC(MONITOR_PENDING_ALTER_TABLE);
3282 
3283 #ifdef UNIV_DEBUG
3284  for (dict_index_t* index = dict_table_get_first_index(prebuilt->table);
3285  index;
3286  index = dict_table_get_next_index(index)) {
3287  ut_ad(!index->to_be_dropped);
3288  }
3289 #endif /* UNIV_DEBUG */
3290 
3291  ut_d(mutex_enter(&dict_sys->mutex));
3292  ut_d(dict_table_check_for_dup_indexes(
3293  prebuilt->table, CHECK_ABORTED_OK));
3294  ut_d(mutex_exit(&dict_sys->mutex));
3295 
3296  if (!(ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE)) {
3297  /* Nothing to do */
3298  goto func_exit;
3299  }
3300 
3301  if (ha_alter_info->handler_flags
3302  & Alter_inplace_info::CHANGE_CREATE_OPTION) {
3303  if (const char* invalid_opt = create_options_are_invalid(
3304  user_thd, altered_table,
3305  ha_alter_info->create_info,
3306  prebuilt->table->space != 0)) {
3307  my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0),
3308  table_type(), invalid_opt);
3309  goto err_exit_no_heap;
3310  }
3311  }
3312 
3313  /* Check if any index name is reserved. */
3314  if (innobase_index_name_is_reserved(
3315  user_thd,
3316  ha_alter_info->key_info_buffer,
3317  ha_alter_info->key_count)) {
3318 err_exit_no_heap:
3319  DBUG_ASSERT(prebuilt->trx->dict_operation_lock_mode == 0);
3320  if (ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE) {
3321  online_retry_drop_indexes(prebuilt->table, user_thd);
3322  }
3323  DBUG_RETURN(true);
3324  }
3325 
3326  indexed_table = prebuilt->table;
3327 
3328  /* Check that index keys are sensible */
3329  error = innobase_check_index_keys(ha_alter_info, indexed_table);
3330 
3331  if (error) {
3332  goto err_exit_no_heap;
3333  }
3334 
3335  /* Prohibit renaming a column to something that the table
3336  already contains. */
3337  if (ha_alter_info->handler_flags
3338  & Alter_inplace_info::ALTER_COLUMN_NAME) {
3340  ha_alter_info->alter_info->create_list);
3341 
3342  for (Field** fp = table->field; *fp; fp++) {
3343  if (!((*fp)->flags & FIELD_IS_RENAMED)) {
3344  continue;
3345  }
3346 
3347  const char* name = 0;
3348 
3349  cf_it.rewind();
3350  while (Create_field* cf = cf_it++) {
3351  if (cf->field == *fp) {
3352  name = cf->field_name;
3353  goto check_if_ok_to_rename;
3354  }
3355  }
3356 
3357  ut_error;
3358 check_if_ok_to_rename:
3359  /* Prohibit renaming a column from FTS_DOC_ID
3360  if full-text indexes exist. */
3361  if (!my_strcasecmp(system_charset_info,
3362  (*fp)->field_name,
3364  && innobase_fulltext_exist(altered_table)) {
3365  my_error(ER_INNODB_FT_WRONG_DOCID_COLUMN,
3366  MYF(0), name);
3367  goto err_exit_no_heap;
3368  }
3369 
3370  /* Prohibit renaming a column to an internal column. */
3371  const char* s = prebuilt->table->col_names;
3372  unsigned j;
3373  /* Skip user columns.
3374  MySQL should have checked these already.
3375  We want to allow renaming of c1 to c2, c2 to c1. */
3376  for (j = 0; j < table->s->fields; j++) {
3377  s += strlen(s) + 1;
3378  }
3379 
3380  for (; j < prebuilt->table->n_def; j++) {
3381  if (!my_strcasecmp(
3382  system_charset_info, name, s)) {
3383  my_error(ER_WRONG_COLUMN_NAME, MYF(0),
3384  s);
3385  goto err_exit_no_heap;
3386  }
3387 
3388  s += strlen(s) + 1;
3389  }
3390  }
3391  }
3392 
3393  if (!innobase_table_flags(altered_table,
3394  ha_alter_info->create_info,
3395  user_thd,
3397  || indexed_table->space != 0,
3398  &flags, &flags2)) {
3399  goto err_exit_no_heap;
3400  }
3401 
3402  max_col_len = DICT_MAX_FIELD_LEN_BY_FORMAT_FLAG(flags);
3403 
3404  /* Check each index's column length to make sure they do not
3405  exceed limit */
3406  for (ulint i = 0; i < ha_alter_info->index_add_count; i++) {
3407  const KEY* key = &ha_alter_info->key_info_buffer[
3408  ha_alter_info->index_add_buffer[i]];
3409 
3410  if (key->flags & HA_FULLTEXT) {
3411  /* The column length does not matter for
3412  fulltext search indexes. But, UNIQUE
3413  fulltext indexes are not supported. */
3414  DBUG_ASSERT(!(key->flags & HA_NOSAME));
3415  DBUG_ASSERT(!(key->flags & HA_KEYFLAG_MASK
3416  & ~(HA_FULLTEXT
3417  | HA_PACK_KEY
3418  | HA_BINARY_PACK_KEY)));
3419  continue;
3420  }
3421 
3422  if (innobase_check_column_length(max_col_len, key)) {
3423  my_error(ER_INDEX_COLUMN_TOO_LONG, MYF(0),
3424  max_col_len);
3425  goto err_exit_no_heap;
3426  }
3427  }
3428 
3429  /* Check existing index definitions for too-long column
3430  prefixes as well, in case max_col_len shrunk. */
3431  for (const dict_index_t* index
3432  = dict_table_get_first_index(indexed_table);
3433  index;
3434  index = dict_table_get_next_index(index)) {
3435  if (index->type & DICT_FTS) {
3436  DBUG_ASSERT(index->type == DICT_FTS
3437  || (index->type & DICT_CORRUPT));
3438  continue;
3439  }
3440 
3441  for (ulint i = 0; i < dict_index_get_n_fields(index); i++) {
3442  const dict_field_t* field
3443  = dict_index_get_nth_field(index, i);
3444  if (field->prefix_len > max_col_len) {
3445  my_error(ER_INDEX_COLUMN_TOO_LONG, MYF(0),
3446  max_col_len);
3447  goto err_exit_no_heap;
3448  }
3449  }
3450  }
3451 
3452  n_drop_index = 0;
3453  n_drop_fk = 0;
3454 
3455  if (ha_alter_info->handler_flags
3456  & (INNOBASE_ALTER_NOREBUILD | INNOBASE_ALTER_REBUILD)) {
3457  heap = mem_heap_create(1024);
3458 
3459  if (ha_alter_info->handler_flags
3460  & Alter_inplace_info::ALTER_COLUMN_NAME) {
3461  col_names = innobase_get_col_names(
3462  ha_alter_info, altered_table, indexed_table,
3463  heap);
3464  } else {
3465  col_names = NULL;
3466  }
3467  } else {
3468  heap = NULL;
3469  col_names = NULL;
3470  }
3471 
3472  if (ha_alter_info->handler_flags
3473  & Alter_inplace_info::DROP_FOREIGN_KEY) {
3474  DBUG_ASSERT(ha_alter_info->alter_info->drop_list.elements > 0);
3475 
3476  drop_fk = static_cast<dict_foreign_t**>(
3478  heap,
3479  ha_alter_info->alter_info->drop_list.elements
3480  * sizeof(dict_foreign_t*)));
3481 
3482  List_iterator<Alter_drop> drop_it(
3483  ha_alter_info->alter_info->drop_list);
3484 
3485  while (Alter_drop* drop = drop_it++) {
3486  if (drop->type != Alter_drop::FOREIGN_KEY) {
3487  continue;
3488  }
3489 
3490  for (dict_foreign_t* foreign = UT_LIST_GET_FIRST(
3491  prebuilt->table->foreign_list);
3492  foreign != NULL;
3493  foreign = UT_LIST_GET_NEXT(
3494  foreign_list, foreign)) {
3495  const char* fid = strchr(foreign->id, '/');
3496 
3497  DBUG_ASSERT(fid);
3498  /* If no database/ prefix was present in
3499  the FOREIGN KEY constraint name, compare
3500  to the full constraint name. */
3501  fid = fid ? fid + 1 : foreign->id;
3502 
3503  if (!my_strcasecmp(system_charset_info,
3504  fid, drop->name)) {
3505  drop_fk[n_drop_fk++] = foreign;
3506  goto found_fk;
3507  }
3508  }
3509 
3510  my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0),
3511  drop->name);
3512  goto err_exit;
3513 found_fk:
3514  continue;
3515  }
3516 
3517  DBUG_ASSERT(n_drop_fk > 0);
3518  DBUG_ASSERT(n_drop_fk
3519  == ha_alter_info->alter_info->drop_list.elements);
3520  } else {
3521  drop_fk = NULL;
3522  }
3523 
3524  if (ha_alter_info->index_drop_count) {
3525  dict_index_t* drop_primary = NULL;
3526 
3527  DBUG_ASSERT(ha_alter_info->handler_flags
3528  & (Alter_inplace_info::DROP_INDEX
3529  | Alter_inplace_info::DROP_UNIQUE_INDEX
3530  | Alter_inplace_info::DROP_PK_INDEX));
3531  /* Check which indexes to drop. */
3532  drop_index = static_cast<dict_index_t**>(
3534  heap, (ha_alter_info->index_drop_count + 1)
3535  * sizeof *drop_index));
3536 
3537  for (uint i = 0; i < ha_alter_info->index_drop_count; i++) {
3538  const KEY* key
3539  = ha_alter_info->index_drop_buffer[i];
3540  dict_index_t* index
3542  indexed_table, key->name);
3543 
3544  if (!index) {
3545  push_warning_printf(
3546  user_thd,
3547  Sql_condition::WARN_LEVEL_WARN,
3548  HA_ERR_WRONG_INDEX,
3549  "InnoDB could not find key "
3550  "with name %s", key->name);
3551  } else {
3552  ut_ad(!index->to_be_dropped);
3553  if (!dict_index_is_clust(index)) {
3554  drop_index[n_drop_index++] = index;
3555  } else {
3556  drop_primary = index;
3557  }
3558  }
3559  }
3560 
3561  /* If all FULLTEXT indexes were removed, drop an
3562  internal FTS_DOC_ID_INDEX as well, unless it exists in
3563  the table. */
3564 
3565  if (innobase_fulltext_exist(table)
3566  && !innobase_fulltext_exist(altered_table)
3567  && !DICT_TF2_FLAG_IS_SET(
3568  indexed_table, DICT_TF2_FTS_HAS_DOC_ID)) {
3569  dict_index_t* fts_doc_index
3571  indexed_table, FTS_DOC_ID_INDEX_NAME);
3572 
3573  // Add some fault tolerance for non-debug builds.
3574  if (fts_doc_index == NULL) {
3575  goto check_if_can_drop_indexes;
3576  }
3577 
3578  DBUG_ASSERT(!fts_doc_index->to_be_dropped);
3579 
3580  for (uint i = 0; i < table->s->keys; i++) {
3581  if (!my_strcasecmp(
3582  system_charset_info,
3584  table->key_info[i].name)) {
3585  /* The index exists in the MySQL
3586  data dictionary. Do not drop it,
3587  even though it is no longer needed
3588  by InnoDB fulltext search. */
3589  goto check_if_can_drop_indexes;
3590  }
3591  }
3592 
3593  drop_index[n_drop_index++] = fts_doc_index;
3594  }
3595 
3596 check_if_can_drop_indexes:
3597  /* Check if the indexes can be dropped. */
3598 
3599  /* Prevent a race condition between DROP INDEX and
3600  CREATE TABLE adding FOREIGN KEY constraints. */
3601  row_mysql_lock_data_dictionary(prebuilt->trx);
3602 
3603  if (!n_drop_index) {
3604  drop_index = NULL;
3605  } else {
3606  /* Flag all indexes that are to be dropped. */
3607  for (ulint i = 0; i < n_drop_index; i++) {
3608  ut_ad(!drop_index[i]->to_be_dropped);
3609  drop_index[i]->to_be_dropped = 1;
3610  }
3611  }
3612 
3613  if (prebuilt->trx->check_foreigns) {
3614  for (uint i = 0; i < n_drop_index; i++) {
3615  dict_index_t* index = drop_index[i];
3616 
3617  if (innobase_check_foreign_key_index(
3618  ha_alter_info, index,
3619  indexed_table, col_names,
3620  prebuilt->trx, drop_fk, n_drop_fk)) {
3622  prebuilt->trx);
3623  prebuilt->trx->error_info = index;
3624  print_error(HA_ERR_DROP_INDEX_FK,
3625  MYF(0));
3626  goto err_exit;
3627  }
3628  }
3629 
3630  /* If a primary index is dropped, need to check
3631  any depending foreign constraints get affected */
3632  if (drop_primary
3633  && innobase_check_foreign_key_index(
3634  ha_alter_info, drop_primary,
3635  indexed_table, col_names,
3636  prebuilt->trx, drop_fk, n_drop_fk)) {
3638  print_error(HA_ERR_DROP_INDEX_FK, MYF(0));
3639  goto err_exit;
3640  }
3641  }
3642 
3644  } else {
3645  drop_index = NULL;
3646  }
3647 
3648  n_add_fk = 0;
3649 
3650  if (ha_alter_info->handler_flags
3651  & Alter_inplace_info::ADD_FOREIGN_KEY) {
3652  ut_ad(!prebuilt->trx->check_foreigns);
3653 
3654  add_fk = static_cast<dict_foreign_t**>(
3656  heap,
3657  ha_alter_info->alter_info->key_list.elements
3658  * sizeof(dict_foreign_t*)));
3659 
3660  if (!innobase_get_foreign_key_info(
3661  ha_alter_info, table_share,
3662  prebuilt->table, col_names,
3663  drop_index, n_drop_index,
3664  add_fk, &n_add_fk, prebuilt->trx)) {
3665 err_exit:
3666  if (n_drop_index) {
3667  row_mysql_lock_data_dictionary(prebuilt->trx);
3668 
3669  /* Clear the to_be_dropped flags, which might
3670  have been set at this point. */
3671  for (ulint i = 0; i < n_drop_index; i++) {
3672  DBUG_ASSERT(*drop_index[i]->name
3673  != TEMP_INDEX_PREFIX);
3674  drop_index[i]->to_be_dropped = 0;
3675  }
3676 
3678  }
3679 
3680  if (heap) {
3681  mem_heap_free(heap);
3682  }
3683 
3684  goto err_exit_no_heap;
3685  }
3686  }
3687 
3688  if (!(ha_alter_info->handler_flags & INNOBASE_ALTER_DATA)
3689  || (ha_alter_info->handler_flags
3690  == Alter_inplace_info::CHANGE_CREATE_OPTION
3691  && !innobase_need_rebuild(ha_alter_info))) {
3692 
3693  if (heap) {
3694  ha_alter_info->handler_ctx
3695  = new ha_innobase_inplace_ctx(
3696  prebuilt,
3697  drop_index, n_drop_index,
3698  drop_fk, n_drop_fk,
3699  add_fk, n_add_fk,
3700  ha_alter_info->online,
3701  heap, indexed_table,
3702  col_names, ULINT_UNDEFINED, 0, 0);
3703  }
3704 
3705 func_exit:
3706  DBUG_ASSERT(prebuilt->trx->dict_operation_lock_mode == 0);
3707  if (ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE) {
3708  online_retry_drop_indexes(prebuilt->table, user_thd);
3709  }
3710  DBUG_RETURN(false);
3711  }
3712 
3713  /* If we are to build a full-text search index, check whether
3714  the table already has a DOC ID column. If not, we will need to
3715  add a Doc ID hidden column and rebuild the primary index */
3716  if (innobase_fulltext_exist(altered_table)) {
3717  ulint doc_col_no;
3718 
3719  if (!innobase_fts_check_doc_id_col(
3720  prebuilt->table, altered_table, &fts_doc_col_no)) {
3721  fts_doc_col_no = altered_table->s->fields;
3722  add_fts_doc_id = true;
3723  add_fts_doc_id_idx = true;
3724 
3725  push_warning_printf(
3726  user_thd,
3727  Sql_condition::WARN_LEVEL_WARN,
3728  HA_ERR_WRONG_INDEX,
3729  "InnoDB rebuilding table to add column "
3731  } else if (fts_doc_col_no == ULINT_UNDEFINED) {
3732  goto err_exit;
3733  }
3734 
3735  switch (innobase_fts_check_doc_id_index(
3736  prebuilt->table, altered_table, &doc_col_no)) {
3737  case FTS_NOT_EXIST_DOC_ID_INDEX:
3738  add_fts_doc_id_idx = true;
3739  break;
3740  case FTS_INCORRECT_DOC_ID_INDEX:
3741  my_error(ER_INNODB_FT_WRONG_DOCID_INDEX, MYF(0),
3743  goto err_exit;
3744  case FTS_EXIST_DOC_ID_INDEX:
3745  DBUG_ASSERT(doc_col_no == fts_doc_col_no
3746  || doc_col_no == ULINT_UNDEFINED
3747  || (ha_alter_info->handler_flags
3748  & (Alter_inplace_info::ALTER_COLUMN_ORDER
3749  | Alter_inplace_info::DROP_COLUMN
3750  | Alter_inplace_info::ADD_COLUMN)));
3751  }
3752  }
3753 
3754  /* See if an AUTO_INCREMENT column was added. */
3755  uint i = 0;
3757  ha_alter_info->alter_info->create_list);
3758  while (const Create_field* new_field = cf_it++) {
3759  const Field* field;
3760 
3761  DBUG_ASSERT(i < altered_table->s->fields);
3762 
3763  for (uint old_i = 0; table->field[old_i]; old_i++) {
3764  if (new_field->field == table->field[old_i]) {
3765  goto found_col;
3766  }
3767  }
3768 
3769  /* This is an added column. */
3770  DBUG_ASSERT(!new_field->field);
3771  DBUG_ASSERT(ha_alter_info->handler_flags
3772  & Alter_inplace_info::ADD_COLUMN);
3773 
3774  field = altered_table->field[i];
3775 
3776  DBUG_ASSERT((MTYP_TYPENR(field->unireg_check)
3777  == Field::NEXT_NUMBER)
3778  == !!(field->flags & AUTO_INCREMENT_FLAG));
3779 
3780  if (field->flags & AUTO_INCREMENT_FLAG) {
3781  if (add_autoinc_col_no != ULINT_UNDEFINED) {
3782  /* This should have been blocked earlier. */
3783  ut_ad(0);
3784  my_error(ER_WRONG_AUTO_KEY, MYF(0));
3785  goto err_exit;
3786  }
3787  add_autoinc_col_no = i;
3788 
3789  autoinc_col_max_value = innobase_get_int_col_max_value(
3790  field);
3791  }
3792 found_col:
3793  i++;
3794  }
3795 
3796  DBUG_ASSERT(heap);
3797  DBUG_ASSERT(user_thd == prebuilt->trx->mysql_thd);
3798  DBUG_ASSERT(!ha_alter_info->handler_ctx);
3799 
3800  ha_alter_info->handler_ctx = new ha_innobase_inplace_ctx(
3801  prebuilt,
3802  drop_index, n_drop_index,
3803  drop_fk, n_drop_fk, add_fk, n_add_fk,
3804  ha_alter_info->online,
3805  heap, prebuilt->table, col_names,
3806  add_autoinc_col_no,
3807  ha_alter_info->create_info->auto_increment_value,
3808  autoinc_col_max_value);
3809 
3810  DBUG_RETURN(prepare_inplace_alter_table_dict(
3811  ha_alter_info, altered_table, table,
3812  table_share->table_name.str,
3813  flags, flags2,
3814  fts_doc_col_no, add_fts_doc_id,
3815  add_fts_doc_id_idx));
3816 }
3817 
3830 UNIV_INTERN
3831 bool
3833 /*=============================*/
3834  TABLE* altered_table,
3835  Alter_inplace_info* ha_alter_info)
3836 {
3837  dberr_t error;
3838 
3839  DBUG_ENTER("inplace_alter_table");
3840  DBUG_ASSERT(!srv_read_only_mode);
3841 
3842 #ifdef UNIV_SYNC_DEBUG
3843  ut_ad(!rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
3844  ut_ad(!rw_lock_own(&dict_operation_lock, RW_LOCK_SHARED));
3845 #endif /* UNIV_SYNC_DEBUG */
3846 
3847  DEBUG_SYNC(user_thd, "innodb_inplace_alter_table_enter");
3848 
3849  if (!(ha_alter_info->handler_flags & INNOBASE_ALTER_DATA)) {
3850 ok_exit:
3851  DEBUG_SYNC(user_thd, "innodb_after_inplace_alter_table");
3852  DBUG_RETURN(false);
3853  }
3854 
3855  if (ha_alter_info->handler_flags
3856  == Alter_inplace_info::CHANGE_CREATE_OPTION
3857  && !innobase_need_rebuild(ha_alter_info)) {
3858  goto ok_exit;
3859  }
3860 
3861  ha_innobase_inplace_ctx* ctx
3862  = static_cast<ha_innobase_inplace_ctx*>
3863  (ha_alter_info->handler_ctx);
3864 
3865  DBUG_ASSERT(ctx);
3866  DBUG_ASSERT(ctx->trx);
3867  DBUG_ASSERT(ctx->prebuilt == prebuilt);
3868 
3869  if (prebuilt->table->ibd_file_missing
3870  || dict_table_is_discarded(prebuilt->table)) {
3871  goto all_done;
3872  }
3873 
3874  /* Read the clustered index of the table and build
3875  indexes based on this information using temporary
3876  files and merge sort. */
3877  DBUG_EXECUTE_IF("innodb_OOM_inplace_alter",
3878  error = DB_OUT_OF_MEMORY; goto oom;);
3879  error = row_merge_build_indexes(
3880  prebuilt->trx,
3881  prebuilt->table, ctx->new_table,
3882  ctx->online,
3883  ctx->add_index, ctx->add_key_numbers, ctx->num_to_add_index,
3884  altered_table, ctx->add_cols, ctx->col_map,
3885  ctx->add_autoinc, ctx->sequence);
3886 #ifndef DBUG_OFF
3887 oom:
3888 #endif /* !DBUG_OFF */
3889  if (error == DB_SUCCESS && ctx->online && ctx->need_rebuild()) {
3890  DEBUG_SYNC_C("row_log_table_apply1_before");
3891  error = row_log_table_apply(
3892  ctx->thr, prebuilt->table, altered_table);
3893  }
3894 
3895  DEBUG_SYNC_C("inplace_after_index_build");
3896 
3897  DBUG_EXECUTE_IF("create_index_fail",
3898  error = DB_DUPLICATE_KEY;);
3899 
3900  /* After an error, remove all those index definitions
3901  from the dictionary which were defined. */
3902 
3903  switch (error) {
3904  KEY* dup_key;
3905  all_done:
3906  case DB_SUCCESS:
3907  ut_d(mutex_enter(&dict_sys->mutex));
3908  ut_d(dict_table_check_for_dup_indexes(
3909  prebuilt->table, CHECK_PARTIAL_OK));
3910  ut_d(mutex_exit(&dict_sys->mutex));
3911  /* prebuilt->table->n_ref_count can be anything here,
3912  given that we hold at most a shared lock on the table. */
3913  goto ok_exit;
3914  case DB_DUPLICATE_KEY:
3915  if (prebuilt->trx->error_key_num == ULINT_UNDEFINED
3916  || ha_alter_info->key_count == 0) {
3917  /* This should be the hidden index on
3918  FTS_DOC_ID, or there is no PRIMARY KEY in the
3919  table. Either way, we should be seeing and
3920  reporting a bogus duplicate key error. */
3921  dup_key = NULL;
3922  } else {
3923  DBUG_ASSERT(prebuilt->trx->error_key_num
3924  < ha_alter_info->key_count);
3925  dup_key = &ha_alter_info->key_info_buffer[
3926  prebuilt->trx->error_key_num];
3927  }
3928  print_keydup_error(altered_table, dup_key, MYF(0));
3929  break;
3930  case DB_ONLINE_LOG_TOO_BIG:
3931  DBUG_ASSERT(ctx->online);
3932  my_error(ER_INNODB_ONLINE_LOG_TOO_BIG, MYF(0),
3933  (prebuilt->trx->error_key_num == ULINT_UNDEFINED)
3935  : ha_alter_info->key_info_buffer[
3936  prebuilt->trx->error_key_num].name);
3937  break;
3938  case DB_INDEX_CORRUPT:
3939  my_error(ER_INDEX_CORRUPT, MYF(0),
3940  (prebuilt->trx->error_key_num == ULINT_UNDEFINED)
3942  : ha_alter_info->key_info_buffer[
3943  prebuilt->trx->error_key_num].name);
3944  break;
3945  default:
3946  my_error_innodb(error,
3947  table_share->table_name.str,
3948  prebuilt->table->flags);
3949  }
3950 
3951  /* prebuilt->table->n_ref_count can be anything here, given
3952  that we hold at most a shared lock on the table. */
3953  prebuilt->trx->error_info = NULL;
3954  ctx->trx->error_state = DB_SUCCESS;
3955 
3956  DBUG_RETURN(true);
3957 }
3958 
3961 static
3962 void
3963 innobase_online_rebuild_log_free(
3964 /*=============================*/
3965  dict_table_t* table)
3966 {
3967  dict_index_t* clust_index = dict_table_get_first_index(table);
3968 
3969  ut_ad(mutex_own(&dict_sys->mutex));
3970 #ifdef UNIV_SYNC_DEBUG
3971  ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
3972 #endif /* UNIV_SYNC_DEBUG */
3973 
3974  rw_lock_x_lock(&clust_index->lock);
3975 
3976  if (clust_index->online_log) {
3977  ut_ad(dict_index_get_online_status(clust_index)
3979  clust_index->online_status = ONLINE_INDEX_COMPLETE;
3980  row_log_free(clust_index->online_log);
3981  DEBUG_SYNC_C("innodb_online_rebuild_log_free_aborted");
3982  }
3983 
3984  DBUG_ASSERT(dict_index_get_online_status(clust_index)
3986  rw_lock_x_unlock(&clust_index->lock);
3987 }
3988 
3996 static __attribute__((nonnull))
3997 void
3998 innobase_rollback_sec_index(
3999 /*========================*/
4000  dict_table_t* user_table,
4001  const TABLE* table,
4002  ibool locked,
4003  trx_t* trx)
4004 {
4005  row_merge_drop_indexes(trx, user_table, locked);
4006 
4007  /* Free the table->fts only if there is no FTS_DOC_ID
4008  in the table */
4009  if (user_table->fts
4010  && !DICT_TF2_FLAG_IS_SET(user_table,
4012  && !innobase_fulltext_exist(table)) {
4013  fts_free(user_table);
4014  }
4015 }
4016 
4030 inline __attribute__((nonnull, warn_unused_result))
4031 bool
4032 rollback_inplace_alter_table(
4033 /*=========================*/
4034  Alter_inplace_info* ha_alter_info,
4035  const TABLE* table,
4036  row_prebuilt_t* prebuilt)
4037 {
4038  bool fail = false;
4039 
4040  ha_innobase_inplace_ctx* ctx
4041  = static_cast<ha_innobase_inplace_ctx*>
4042  (ha_alter_info->handler_ctx);
4043 
4044  DBUG_ENTER("rollback_inplace_alter_table");
4045 
4046  if (!ctx || !ctx->trx) {
4047  /* If we have not started a transaction yet,
4048  (almost) nothing has been or needs to be done. */
4049  goto func_exit;
4050  }
4051 
4052  row_mysql_lock_data_dictionary(ctx->trx);
4053 
4054  if (ctx->need_rebuild()) {
4055  dberr_t err;
4056  ulint flags = ctx->new_table->flags;
4057 
4058  /* DML threads can access ctx->new_table via the
4059  online rebuild log. Free it first. */
4060  innobase_online_rebuild_log_free(prebuilt->table);
4061 
4062  /* Since the FTS index specific auxiliary tables has
4063  not yet registered with "table->fts" by fts_add_index(),
4064  we will need explicitly delete them here */
4065  if (DICT_TF2_FLAG_IS_SET(ctx->new_table, DICT_TF2_FTS)) {
4066 
4067  err = innobase_drop_fts_index_table(
4068  ctx->new_table, ctx->trx);
4069 
4070  if (err != DB_SUCCESS) {
4071  my_error_innodb(
4072  err, table->s->table_name.str,
4073  flags);
4074  fail = true;
4075  }
4076  }
4077 
4078  /* Drop the table. */
4079  dict_table_close(ctx->new_table, TRUE, FALSE);
4080 
4081 #if defined UNIV_DEBUG || defined UNIV_DDL_DEBUG
4082  /* Nobody should have initialized the stats of the
4083  newly created table yet. When this is the case, we
4084  know that it has not been added for background stats
4085  gathering. */
4086  ut_a(!ctx->new_table->stat_initialized);
4087 #endif /* UNIV_DEBUG || UNIV_DDL_DEBUG */
4088 
4089  err = row_merge_drop_table(ctx->trx, ctx->new_table);
4090 
4091  switch (err) {
4092  case DB_SUCCESS:
4093  break;
4094  default:
4095  my_error_innodb(err, table->s->table_name.str,
4096  flags);
4097  fail = true;
4098  }
4099  } else {
4100  DBUG_ASSERT(!(ha_alter_info->handler_flags
4101  & Alter_inplace_info::ADD_PK_INDEX));
4102  DBUG_ASSERT(ctx->new_table == prebuilt->table);
4103 
4104  trx_start_for_ddl(ctx->trx, TRX_DICT_OP_INDEX);
4105 
4106  innobase_rollback_sec_index(
4107  prebuilt->table, table, FALSE, ctx->trx);
4108  }
4109 
4110  trx_commit_for_mysql(ctx->trx);
4112  trx_free_for_mysql(ctx->trx);
4113 
4114 func_exit:
4115 #ifndef DBUG_OFF
4116  dict_index_t* clust_index = dict_table_get_first_index(
4117  prebuilt->table);
4118  DBUG_ASSERT(!clust_index->online_log);
4119  DBUG_ASSERT(dict_index_get_online_status(clust_index)
4121 #endif /* !DBUG_OFF */
4122 
4123  if (ctx) {
4124  DBUG_ASSERT(ctx->prebuilt == prebuilt);
4125 
4126  if (ctx->num_to_add_fk) {
4127  for (ulint i = 0; i < ctx->num_to_add_fk; i++) {
4128  dict_foreign_free(ctx->add_fk[i]);
4129  }
4130  }
4131 
4132  if (ctx->num_to_drop_index) {
4133  row_mysql_lock_data_dictionary(prebuilt->trx);
4134 
4135  /* Clear the to_be_dropped flags
4136  in the data dictionary cache.
4137  The flags may already have been cleared,
4138  in case an error was detected in
4139  commit_inplace_alter_table(). */
4140  for (ulint i = 0; i < ctx->num_to_drop_index; i++) {
4141  dict_index_t* index = ctx->drop_index[i];
4142  DBUG_ASSERT(*index->name != TEMP_INDEX_PREFIX);
4143 
4144  index->to_be_dropped = 0;
4145  }
4146 
4147  row_mysql_unlock_data_dictionary(prebuilt->trx);
4148  }
4149  }
4150 
4151  trx_commit_for_mysql(prebuilt->trx);
4153  MONITOR_ATOMIC_DEC(MONITOR_PENDING_ALTER_TABLE);
4154  DBUG_RETURN(fail);
4155 }
4156 
4163 static __attribute__((nonnull, warn_unused_result))
4164 bool
4165 innobase_drop_foreign_try(
4166 /*======================*/
4167  trx_t* trx,
4168  const char* table_name,
4169  const char* foreign_id)
4170 {
4171  DBUG_ENTER("innobase_drop_foreign_try");
4172 
4173  DBUG_ASSERT(trx_get_dict_operation(trx) == TRX_DICT_OP_INDEX);
4174  ut_ad(trx->dict_operation_lock_mode == RW_X_LATCH);
4175  ut_ad(mutex_own(&dict_sys->mutex));
4176 #ifdef UNIV_SYNC_DEBUG
4177  ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
4178 #endif /* UNIV_SYNC_DEBUG */
4179 
4180  /* Drop the constraint from the data dictionary. */
4181  static const char sql[] =
4182  "PROCEDURE DROP_FOREIGN_PROC () IS\n"
4183  "BEGIN\n"
4184  "DELETE FROM SYS_FOREIGN WHERE ID=:id;\n"
4185  "DELETE FROM SYS_FOREIGN_COLS WHERE ID=:id;\n"
4186  "END;\n";
4187 
4188  dberr_t error;
4189  pars_info_t* info;
4190 
4191  info = pars_info_create();
4192  pars_info_add_str_literal(info, "id", foreign_id);
4193 
4194  trx->op_info = "dropping foreign key constraint from dictionary";
4195  error = que_eval_sql(info, sql, FALSE, trx);
4196  trx->op_info = "";
4197 
4198  DBUG_EXECUTE_IF("ib_drop_foreign_error",
4199  error = DB_OUT_OF_FILE_SPACE;);
4200 
4201  if (error != DB_SUCCESS) {
4202  my_error_innodb(error, table_name, 0);
4203  trx->error_state = DB_SUCCESS;
4204  DBUG_RETURN(true);
4205  }
4206 
4207  DBUG_RETURN(false);
4208 }
4209 
4220 static __attribute__((nonnull, warn_unused_result))
4221 bool
4222 innobase_rename_column_try(
4223 /*=======================*/
4224  const dict_table_t* user_table,
4225  trx_t* trx,
4226  const char* table_name,
4227  ulint nth_col,
4228  const char* from,
4229  const char* to,
4230  bool new_clustered)
4231 {
4232  pars_info_t* info;
4233  dberr_t error;
4234 
4235  DBUG_ENTER("innobase_rename_column_try");
4236 
4237  DBUG_ASSERT(trx_get_dict_operation(trx) == TRX_DICT_OP_INDEX);
4238  ut_ad(trx->dict_operation_lock_mode == RW_X_LATCH);
4239  ut_ad(mutex_own(&dict_sys->mutex));
4240 #ifdef UNIV_SYNC_DEBUG
4241  ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
4242 #endif /* UNIV_SYNC_DEBUG */
4243 
4244  if (new_clustered) {
4245  goto rename_foreign;
4246  }
4247 
4248  info = pars_info_create();
4249 
4250  pars_info_add_ull_literal(info, "tableid", user_table->id);
4251  pars_info_add_int4_literal(info, "nth", nth_col);
4252  pars_info_add_str_literal(info, "old", from);
4253  pars_info_add_str_literal(info, "new", to);
4254 
4255  trx->op_info = "renaming column in SYS_COLUMNS";
4256 
4257  error = que_eval_sql(
4258  info,
4259  "PROCEDURE RENAME_SYS_COLUMNS_PROC () IS\n"
4260  "BEGIN\n"
4261  "UPDATE SYS_COLUMNS SET NAME=:new\n"
4262  "WHERE TABLE_ID=:tableid AND NAME=:old\n"
4263  "AND POS=:nth;\n"
4264  "END;\n",
4265  FALSE, trx);
4266 
4267  DBUG_EXECUTE_IF("ib_rename_column_error",
4268  error = DB_OUT_OF_FILE_SPACE;);
4269 
4270  if (error != DB_SUCCESS) {
4271 err_exit:
4272  my_error_innodb(error, table_name, 0);
4273  trx->error_state = DB_SUCCESS;
4274  trx->op_info = "";
4275  DBUG_RETURN(true);
4276  }
4277 
4278  trx->op_info = "renaming column in SYS_FIELDS";
4279 
4280  for (const dict_index_t* index = dict_table_get_first_index(
4281  user_table);
4282  index != NULL;
4283  index = dict_table_get_next_index(index)) {
4284 
4285  for (ulint i = 0; i < dict_index_get_n_fields(index); i++) {
4286  if (strcmp(dict_index_get_nth_field(index, i)->name,
4287  from)) {
4288  continue;
4289  }
4290 
4291  info = pars_info_create();
4292 
4293  pars_info_add_ull_literal(info, "indexid", index->id);
4294  pars_info_add_int4_literal(info, "nth", i);
4295  pars_info_add_str_literal(info, "old", from);
4296  pars_info_add_str_literal(info, "new", to);
4297 
4298  error = que_eval_sql(
4299  info,
4300  "PROCEDURE RENAME_SYS_FIELDS_PROC () IS\n"
4301  "BEGIN\n"
4302 
4303  "UPDATE SYS_FIELDS SET COL_NAME=:new\n"
4304  "WHERE INDEX_ID=:indexid AND COL_NAME=:old\n"
4305  "AND POS=:nth;\n"
4306 
4307  /* Try again, in case there is a prefix_len
4308  encoded in SYS_FIELDS.POS */
4309 
4310  "UPDATE SYS_FIELDS SET COL_NAME=:new\n"
4311  "WHERE INDEX_ID=:indexid AND COL_NAME=:old\n"
4312  "AND POS>=65536*:nth AND POS<65536*(:nth+1);\n"
4313 
4314  "END;\n",
4315  FALSE, trx);
4316 
4317  if (error != DB_SUCCESS) {
4318  goto err_exit;
4319  }
4320  }
4321  }
4322 
4323 rename_foreign:
4324  trx->op_info = "renaming column in SYS_FOREIGN_COLS";
4325 
4326  for (const dict_foreign_t* foreign = UT_LIST_GET_FIRST(
4327  user_table->foreign_list);
4328  foreign != NULL;
4329  foreign = UT_LIST_GET_NEXT(foreign_list, foreign)) {
4330  for (unsigned i = 0; i < foreign->n_fields; i++) {
4331  if (strcmp(foreign->foreign_col_names[i], from)) {
4332  continue;
4333  }
4334 
4335  info = pars_info_create();
4336 
4337  pars_info_add_str_literal(info, "id", foreign->id);
4338  pars_info_add_int4_literal(info, "nth", i);
4339  pars_info_add_str_literal(info, "old", from);
4340  pars_info_add_str_literal(info, "new", to);
4341 
4342  error = que_eval_sql(
4343  info,
4344  "PROCEDURE RENAME_SYS_FOREIGN_F_PROC () IS\n"
4345  "BEGIN\n"
4346  "UPDATE SYS_FOREIGN_COLS\n"
4347  "SET FOR_COL_NAME=:new\n"
4348  "WHERE ID=:id AND POS=:nth\n"
4349  "AND FOR_COL_NAME=:old;\n"
4350  "END;\n",
4351  FALSE, trx);
4352 
4353  if (error != DB_SUCCESS) {
4354  goto err_exit;
4355  }
4356  }
4357  }
4358 
4359  for (const dict_foreign_t* foreign = UT_LIST_GET_FIRST(
4360  user_table->referenced_list);
4361  foreign != NULL;
4362  foreign = UT_LIST_GET_NEXT(referenced_list, foreign)) {
4363  for (unsigned i = 0; i < foreign->n_fields; i++) {
4364  if (strcmp(foreign->referenced_col_names[i], from)) {
4365  continue;
4366  }
4367 
4368  info = pars_info_create();
4369 
4370  pars_info_add_str_literal(info, "id", foreign->id);
4371  pars_info_add_int4_literal(info, "nth", i);
4372  pars_info_add_str_literal(info, "old", from);
4373  pars_info_add_str_literal(info, "new", to);
4374 
4375  error = que_eval_sql(
4376  info,
4377  "PROCEDURE RENAME_SYS_FOREIGN_R_PROC () IS\n"
4378  "BEGIN\n"
4379  "UPDATE SYS_FOREIGN_COLS\n"
4380  "SET REF_COL_NAME=:new\n"
4381  "WHERE ID=:id AND POS=:nth\n"
4382  "AND REF_COL_NAME=:old;\n"
4383  "END;\n",
4384  FALSE, trx);
4385 
4386  if (error != DB_SUCCESS) {
4387  goto err_exit;
4388  }
4389  }
4390  }
4391 
4392  trx->op_info = "";
4393  DBUG_RETURN(false);
4394 }
4395 
4404 static __attribute__((nonnull, warn_unused_result))
4405 bool
4406 innobase_rename_columns_try(
4407 /*========================*/
4408  Alter_inplace_info* ha_alter_info,
4409  ha_innobase_inplace_ctx*ctx,
4410  const TABLE* table,
4411  trx_t* trx,
4412  const char* table_name)
4413 {
4415  ha_alter_info->alter_info->create_list);
4416  uint i = 0;
4417 
4418  DBUG_ASSERT(ctx);
4419  DBUG_ASSERT(ha_alter_info->handler_flags
4420  & Alter_inplace_info::ALTER_COLUMN_NAME);
4421 
4422  for (Field** fp = table->field; *fp; fp++, i++) {
4423  if (!((*fp)->flags & FIELD_IS_RENAMED)) {
4424  continue;
4425  }
4426 
4427  cf_it.rewind();
4428  while (Create_field* cf = cf_it++) {
4429  if (cf->field == *fp) {
4430  if (innobase_rename_column_try(
4431  ctx->old_table, trx, table_name, i,
4432  cf->field->field_name,
4433  cf->field_name,
4434  ctx->need_rebuild())) {
4435  return(true);
4436  }
4437  goto processed_field;
4438  }
4439  }
4440 
4441  ut_error;
4442 processed_field:
4443  continue;
4444  }
4445 
4446  return(false);
4447 }
4448 
4454 static __attribute__((nonnull))
4455 void
4456 innobase_rename_columns_cache(
4457 /*==========================*/
4458  Alter_inplace_info* ha_alter_info,
4459  const TABLE* table,
4460  dict_table_t* user_table)
4461 {
4462  if (!(ha_alter_info->handler_flags
4463  & Alter_inplace_info::ALTER_COLUMN_NAME)) {
4464  return;
4465  }
4466 
4468  ha_alter_info->alter_info->create_list);
4469  uint i = 0;
4470 
4471  for (Field** fp = table->field; *fp; fp++, i++) {
4472  if (!((*fp)->flags & FIELD_IS_RENAMED)) {
4473  continue;
4474  }
4475 
4476  cf_it.rewind();
4477  while (Create_field* cf = cf_it++) {
4478  if (cf->field == *fp) {
4479  dict_mem_table_col_rename(user_table, i,
4480  cf->field->field_name,
4481  cf->field_name);
4482  goto processed_field;
4483  }
4484  }
4485 
4486  ut_error;
4487 processed_field:
4488  continue;
4489  }
4490 }
4491 
4498 static __attribute__((nonnull, warn_unused_result))
4499 ulonglong
4500 commit_get_autoinc(
4501 /*===============*/
4502  Alter_inplace_info* ha_alter_info,
4503  ha_innobase_inplace_ctx*ctx,
4504  const TABLE* altered_table,
4505  const TABLE* old_table)
4506 {
4507  ulonglong max_autoinc;
4508 
4509  DBUG_ENTER("commit_get_autoinc");
4510 
4511  if (!altered_table->found_next_number_field) {
4512  /* There is no AUTO_INCREMENT column in the table
4513  after the ALTER operation. */
4514  max_autoinc = 0;
4515  } else if (ctx->add_autoinc != ULINT_UNDEFINED) {
4516  /* An AUTO_INCREMENT column was added. Get the last
4517  value from the sequence, which may be based on a
4518  supplied AUTO_INCREMENT value. */
4519  max_autoinc = ctx->sequence.last();
4520  } else if ((ha_alter_info->handler_flags
4521  & Alter_inplace_info::CHANGE_CREATE_OPTION)
4522  && (ha_alter_info->create_info->used_fields
4523  & HA_CREATE_USED_AUTO)) {
4524  /* An AUTO_INCREMENT value was supplied, but the table
4525  was not rebuilt. Get the user-supplied value or the
4526  last value from the sequence. */
4527  ut_ad(old_table->found_next_number_field);
4528 
4529  max_autoinc = ha_alter_info->create_info->auto_increment_value;
4530 
4531  dict_table_autoinc_lock(ctx->old_table);
4532  if (max_autoinc < ctx->old_table->autoinc) {
4533  max_autoinc = ctx->old_table->autoinc;
4534  }
4535  dict_table_autoinc_unlock(ctx->old_table);
4536  } else {
4537  /* An AUTO_INCREMENT value was not specified.
4538  Read the old counter value from the table. */
4539  ut_ad(old_table->found_next_number_field);
4540  dict_table_autoinc_lock(ctx->old_table);
4541  max_autoinc = ctx->old_table->autoinc;
4542  dict_table_autoinc_unlock(ctx->old_table);
4543  }
4544 
4545  DBUG_RETURN(max_autoinc);
4546 }
4547 
4557 static __attribute__((nonnull, warn_unused_result))
4558 bool
4559 innobase_update_foreign_try(
4560 /*========================*/
4561  ha_innobase_inplace_ctx*ctx,
4562  trx_t* trx,
4563  const char* table_name)
4564 {
4565  ulint foreign_id;
4566  ulint i;
4567 
4568  DBUG_ENTER("innobase_update_foreign_try");
4569  DBUG_ASSERT(ctx);
4570 
4571  foreign_id = dict_table_get_highest_foreign_id(ctx->new_table);
4572 
4573  foreign_id++;
4574 
4575  for (i = 0; i < ctx->num_to_add_fk; i++) {
4576  dict_foreign_t* fk = ctx->add_fk[i];
4577 
4578  ut_ad(fk->foreign_table == ctx->new_table
4579  || fk->foreign_table == ctx->old_table);
4580 
4582  &foreign_id, ctx->old_table->name, fk);
4583 
4584  if (error != DB_SUCCESS) {
4585  my_error(ER_TOO_LONG_IDENT, MYF(0),
4586  fk->id);
4587  DBUG_RETURN(true);
4588  }
4589 
4590  if (!fk->foreign_index) {
4592  ctx->new_table, ctx->col_names,
4593  fk->foreign_col_names,
4594  fk->n_fields, fk->referenced_index, TRUE,
4595  fk->type
4598  if (!fk->foreign_index) {
4599  my_error(ER_FK_INCORRECT_OPTION,
4600  MYF(0), table_name, fk->id);
4601  DBUG_RETURN(true);
4602  }
4603  }
4604 
4605  /* The fk->foreign_col_names[] uses renamed column
4606  names, while the columns in ctx->old_table have not
4607  been renamed yet. */
4609  ctx->old_table->name, fk, trx);
4610 
4611  DBUG_EXECUTE_IF(
4612  "innodb_test_cannot_add_fk_system",
4613  error = DB_ERROR;);
4614 
4615  if (error != DB_SUCCESS) {
4616  my_error(ER_FK_FAIL_ADD_SYSTEM, MYF(0),
4617  fk->id);
4618  DBUG_RETURN(true);
4619  }
4620  }
4621 
4622  for (i = 0; i < ctx->num_to_drop_fk; i++) {
4623  dict_foreign_t* fk = ctx->drop_fk[i];
4624 
4625  DBUG_ASSERT(fk->foreign_table == ctx->old_table);
4626 
4627  if (innobase_drop_foreign_try(trx, table_name, fk->id)) {
4628  DBUG_RETURN(true);
4629  }
4630  }
4631 
4632  DBUG_RETURN(false);
4633 }
4634 
4639 static __attribute__((nonnull, warn_unused_result))
4640 dberr_t
4641 innobase_update_foreign_cache(
4642 /*==========================*/
4643  ha_innobase_inplace_ctx* ctx)
4644 {
4645  dict_table_t* user_table;
4646 
4647  DBUG_ENTER("innobase_update_foreign_cache");
4648 
4649  user_table = ctx->old_table;
4650 
4651  /* Discard the added foreign keys, because we will
4652  load them from the data dictionary. */
4653  for (ulint i = 0; i < ctx->num_to_add_fk; i++) {
4654  dict_foreign_t* fk = ctx->add_fk[i];
4655  dict_foreign_free(fk);
4656  }
4657 
4658  if (ctx->need_rebuild()) {
4659  /* The rebuilt table is already using the renamed
4660  column names. No need to pass col_names or to drop
4661  constraints from the data dictionary cache. */
4662  DBUG_ASSERT(!ctx->col_names);
4663  DBUG_ASSERT(UT_LIST_GET_LEN(user_table->foreign_list) == 0);
4664  DBUG_ASSERT(UT_LIST_GET_LEN(user_table->referenced_list) == 0);
4665  user_table = ctx->new_table;
4666  } else {
4667  /* Drop the foreign key constraints if the
4668  table was not rebuilt. If the table is rebuilt,
4669  there would not be any foreign key contraints for
4670  it yet in the data dictionary cache. */
4671  for (ulint i = 0; i < ctx->num_to_drop_fk; i++) {
4672  dict_foreign_t* fk = ctx->drop_fk[i];
4674  }
4675  }
4676 
4677  /* Load the old or added foreign keys from the data dictionary
4678  and prevent the table from being evicted from the data
4679  dictionary cache (work around the lack of WL#6049). */
4680  DBUG_RETURN(dict_load_foreigns(user_table->name,
4681  ctx->col_names, false, true,
4683 }
4684 
4697 inline __attribute__((nonnull, warn_unused_result))
4698 bool
4699 commit_try_rebuild(
4700 /*===============*/
4701  Alter_inplace_info* ha_alter_info,
4702  ha_innobase_inplace_ctx*ctx,
4703  TABLE* altered_table,
4704  const TABLE* old_table,
4705  trx_t* trx,
4706  const char* table_name)
4707 {
4708  dict_table_t* rebuilt_table = ctx->new_table;
4709  dict_table_t* user_table = ctx->old_table;
4710 
4711  DBUG_ENTER("commit_try_rebuild");
4712  DBUG_ASSERT(ctx->need_rebuild());
4713  DBUG_ASSERT(trx->dict_operation_lock_mode == RW_X_LATCH);
4714  DBUG_ASSERT(!(ha_alter_info->handler_flags
4715  & Alter_inplace_info::DROP_FOREIGN_KEY)
4716  || ctx->num_to_drop_fk > 0);
4717  DBUG_ASSERT(ctx->num_to_drop_fk
4718  == ha_alter_info->alter_info->drop_list.elements);
4719 
4720  for (dict_index_t* index = dict_table_get_first_index(rebuilt_table);
4721  index;
4722  index = dict_table_get_next_index(index)) {
4723  DBUG_ASSERT(dict_index_get_online_status(index)
4725  DBUG_ASSERT(*index->name != TEMP_INDEX_PREFIX);
4726  if (dict_index_is_corrupted(index)) {
4727  my_error(ER_INDEX_CORRUPT, MYF(0),
4728  index->name);
4729  DBUG_RETURN(true);
4730  }
4731  }
4732 
4733  if (innobase_update_foreign_try(ctx, trx, table_name)) {
4734  DBUG_RETURN(true);
4735  }
4736 
4737  dberr_t error;
4738 
4739  /* Clear the to_be_dropped flag in the data dictionary cache
4740  of user_table. */
4741  for (ulint i = 0; i < ctx->num_to_drop_index; i++) {
4742  dict_index_t* index = ctx->drop_index[i];
4743  DBUG_ASSERT(index->table == user_table);
4744  DBUG_ASSERT(*index->name != TEMP_INDEX_PREFIX);
4745  DBUG_ASSERT(index->to_be_dropped);
4746  index->to_be_dropped = 0;
4747  }
4748 
4749  /* We copied the table. Any indexes that were requested to be
4750  dropped were not created in the copy of the table. Apply any
4751  last bit of the rebuild log and then rename the tables. */
4752 
4753  if (ctx->online) {
4754  DEBUG_SYNC_C("row_log_table_apply2_before");
4755  error = row_log_table_apply(
4756  ctx->thr, user_table, altered_table);
4757  ulint err_key = thr_get_trx(ctx->thr)->error_key_num;
4758 
4759  switch (error) {
4760  KEY* dup_key;
4761  case DB_SUCCESS:
4762  break;
4763  case DB_DUPLICATE_KEY:
4764  if (err_key == ULINT_UNDEFINED) {
4765  /* This should be the hidden index on
4766  FTS_DOC_ID. */
4767  dup_key = NULL;
4768  } else {
4769  DBUG_ASSERT(err_key <
4770  ha_alter_info->key_count);
4771  dup_key = &ha_alter_info
4772  ->key_info_buffer[err_key];
4773  }
4774  print_keydup_error(altered_table, dup_key, MYF(0));
4775  DBUG_RETURN(true);
4776  case DB_ONLINE_LOG_TOO_BIG:
4777  my_error(ER_INNODB_ONLINE_LOG_TOO_BIG, MYF(0),
4778  ha_alter_info->key_info_buffer[0].name);
4779  DBUG_RETURN(true);
4780  case DB_INDEX_CORRUPT:
4781  my_error(ER_INDEX_CORRUPT, MYF(0),
4782  (err_key == ULINT_UNDEFINED)
4784  : ha_alter_info->key_info_buffer[err_key]
4785  .name);
4786  DBUG_RETURN(true);
4787  default:
4788  my_error_innodb(error, table_name, user_table->flags);
4789  DBUG_RETURN(true);
4790  }
4791  }
4792 
4793  if ((ha_alter_info->handler_flags
4794  & Alter_inplace_info::ALTER_COLUMN_NAME)
4795  && innobase_rename_columns_try(ha_alter_info, ctx, old_table,
4796  trx, table_name)) {
4797  DBUG_RETURN(true);
4798  }
4799 
4800  DBUG_EXECUTE_IF("ib_ddl_crash_before_rename", DBUG_SUICIDE(););
4801 
4802  /* The new table must inherit the flag from the
4803  "parent" table. */
4804  if (dict_table_is_discarded(user_table)) {
4805  rebuilt_table->ibd_file_missing = true;
4806  rebuilt_table->flags2 |= DICT_TF2_DISCARDED;
4807  }
4808 
4809  /* We can now rename the old table as a temporary table,
4810  rename the new temporary table as the old table and drop the
4811  old table. First, we only do this in the data dictionary
4812  tables. The actual renaming will be performed in
4813  commit_cache_rebuild(), once the data dictionary transaction
4814  has been successfully committed. */
4815 
4817  user_table, rebuilt_table, ctx->tmp_name, trx);
4818 
4819  /* We must be still holding a table handle. */
4820  DBUG_ASSERT(user_table->n_ref_count >= 1);
4821 
4822  DBUG_EXECUTE_IF("ib_ddl_crash_after_rename", DBUG_SUICIDE(););
4823  DBUG_EXECUTE_IF("ib_rebuild_cannot_rename", error = DB_ERROR;);
4824 
4825  if (user_table->n_ref_count > 1) {
4826  /* This should only occur when an innodb_memcached
4827  connection with innodb_api_enable_mdl=off was started
4828  before commit_inplace_alter_table() locked the data
4829  dictionary. We must roll back the ALTER TABLE, because
4830  we cannot drop a table while it is being used. */
4831 
4832  /* Normally, n_ref_count must be 1, because purge
4833  cannot be executing on this very table as we are
4834  holding dict_operation_lock X-latch. */
4835 
4836  error = DB_LOCK_WAIT_TIMEOUT;
4837  }
4838 
4839  switch (error) {
4840  case DB_SUCCESS:
4841  DBUG_RETURN(false);
4842  case DB_TABLESPACE_EXISTS:
4843  ut_a(rebuilt_table->n_ref_count == 1);
4844  my_error(ER_TABLESPACE_EXISTS, MYF(0), ctx->tmp_name);
4845  DBUG_RETURN(true);
4846  case DB_DUPLICATE_KEY:
4847  ut_a(rebuilt_table->n_ref_count == 1);
4848  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), ctx->tmp_name);
4849  DBUG_RETURN(true);
4850  default:
4851  my_error_innodb(error, table_name, user_table->flags);
4852  DBUG_RETURN(true);
4853  }
4854 }
4855 
4859 inline __attribute__((nonnull))
4860 void
4861 commit_cache_rebuild(
4862 /*=================*/
4863  ha_innobase_inplace_ctx* ctx)
4864 {
4865  dberr_t error;
4866 
4867  DBUG_ENTER("commit_cache_rebuild");
4868  DBUG_ASSERT(ctx->need_rebuild());
4869  DBUG_ASSERT(dict_table_is_discarded(ctx->old_table)
4870  == dict_table_is_discarded(ctx->new_table));
4871 
4872  const char* old_name = mem_heap_strdup(
4873  ctx->heap, ctx->old_table->name);
4874 
4875  /* We already committed and redo logged the renames,
4876  so this must succeed. */
4878  ctx->old_table, ctx->tmp_name, FALSE);
4879  ut_a(error == DB_SUCCESS);
4880 
4882  ctx->new_table, old_name, FALSE);
4883  ut_a(error == DB_SUCCESS);
4884 
4885  DBUG_VOID_RETURN;
4886 }
4887 
4899 inline __attribute__((nonnull, warn_unused_result))
4900 bool
4901 commit_try_norebuild(
4902 /*=================*/
4903  Alter_inplace_info* ha_alter_info,
4904  ha_innobase_inplace_ctx*ctx,
4905  const TABLE* old_table,
4906  trx_t* trx,
4907  const char* table_name)
4908 {
4909  DBUG_ENTER("commit_try_norebuild");
4910  DBUG_ASSERT(!ctx->need_rebuild());
4911  DBUG_ASSERT(trx->dict_operation_lock_mode == RW_X_LATCH);
4912  DBUG_ASSERT(!(ha_alter_info->handler_flags
4913  & Alter_inplace_info::DROP_FOREIGN_KEY)
4914  || ctx->num_to_drop_fk > 0);
4915  DBUG_ASSERT(ctx->num_to_drop_fk
4916  == ha_alter_info->alter_info->drop_list.elements);
4917 
4918  for (ulint i = 0; i < ctx->num_to_add_index; i++) {
4919  dict_index_t* index = ctx->add_index[i];
4920  DBUG_ASSERT(dict_index_get_online_status(index)
4922  DBUG_ASSERT(*index->name == TEMP_INDEX_PREFIX);
4923  if (dict_index_is_corrupted(index)) {
4924  /* Report a duplicate key
4925  error for the index that was
4926  flagged corrupted, most likely
4927  because a duplicate value was
4928  inserted (directly or by
4929  rollback) after
4930  ha_innobase::inplace_alter_table()
4931  completed.
4932  TODO: report this as a corruption
4933  with a detailed reason once
4934  WL#6379 has been implemented. */
4935  my_error(ER_DUP_UNKNOWN_IN_INDEX,
4936  MYF(0), index->name + 1);
4937  DBUG_RETURN(true);
4938  }
4939  }
4940 
4941  if (innobase_update_foreign_try(ctx, trx, table_name)) {
4942  DBUG_RETURN(true);
4943  }
4944 
4945  dberr_t error;
4946 
4947  /* We altered the table in place. */
4948  /* Lose the TEMP_INDEX_PREFIX. */
4949  for (ulint i = 0; i < ctx->num_to_add_index; i++) {
4950  dict_index_t* index = ctx->add_index[i];
4951  DBUG_ASSERT(dict_index_get_online_status(index)
4953  DBUG_ASSERT(*index->name
4954  == TEMP_INDEX_PREFIX);
4956  trx, ctx->new_table->id, index->id);
4957  if (error != DB_SUCCESS) {
4958  sql_print_error(
4959  "InnoDB: rename index to add: %lu\n",
4960  (ulong) error);
4961  DBUG_ASSERT(0);
4962  my_error(ER_INTERNAL_ERROR, MYF(0),
4963  "rename index to add");
4964  DBUG_RETURN(true);
4965  }
4966  }
4967 
4968  /* Drop any indexes that were requested to be dropped.
4969  Rename them to TEMP_INDEX_PREFIX in the data
4970  dictionary first. We do not bother to rename
4971  index->name in the dictionary cache, because the index
4972  is about to be freed after row_merge_drop_indexes_dict(). */
4973 
4974  for (ulint i = 0; i < ctx->num_to_drop_index; i++) {
4975  dict_index_t* index = ctx->drop_index[i];
4976  DBUG_ASSERT(*index->name != TEMP_INDEX_PREFIX);
4977  DBUG_ASSERT(index->table == ctx->new_table);
4978  DBUG_ASSERT(index->to_be_dropped);
4979 
4981  trx, index->table->id, index->id);
4982  if (error != DB_SUCCESS) {
4983  sql_print_error(
4984  "InnoDB: rename index to drop: %lu\n",
4985  (ulong) error);
4986  DBUG_ASSERT(0);
4987  my_error(ER_INTERNAL_ERROR, MYF(0),
4988  "rename index to drop");
4989  DBUG_RETURN(true);
4990  }
4991  }
4992 
4993  if (!(ha_alter_info->handler_flags
4994  & Alter_inplace_info::ALTER_COLUMN_NAME)) {
4995  DBUG_RETURN(false);
4996  }
4997 
4998  DBUG_RETURN(innobase_rename_columns_try(ha_alter_info, ctx,
4999  old_table, trx, table_name));
5000 }
5001 
5009 inline __attribute__((nonnull, warn_unused_result))
5010 bool
5011 commit_cache_norebuild(
5012 /*===================*/
5013  ha_innobase_inplace_ctx*ctx,
5014  const TABLE* table,
5015  trx_t* trx)
5016 {
5017  DBUG_ENTER("commit_cache_norebuild");
5018 
5019  bool found = true;
5020 
5021  DBUG_ASSERT(!ctx->need_rebuild());
5022 
5023  for (ulint i = 0; i < ctx->num_to_add_index; i++) {
5024  dict_index_t* index = ctx->add_index[i];
5025  DBUG_ASSERT(dict_index_get_online_status(index)
5027  DBUG_ASSERT(*index->name == TEMP_INDEX_PREFIX);
5028  index->name++;
5029  }
5030 
5031  if (ctx->num_to_drop_index) {
5032  /* Really drop the indexes that were dropped.
5033  The transaction had to be committed first
5034  (after renaming the indexes), so that in the
5035  event of a crash, crash recovery will drop the
5036  indexes, because it drops all indexes whose
5037  names start with TEMP_INDEX_PREFIX. Once we
5038  have started dropping an index tree, there is
5039  no way to roll it back. */
5040 
5041  for (ulint i = 0; i < ctx->num_to_drop_index; i++) {
5042  dict_index_t* index = ctx->drop_index[i];
5043  DBUG_ASSERT(*index->name != TEMP_INDEX_PREFIX);
5044  DBUG_ASSERT(index->table == ctx->new_table);
5045  DBUG_ASSERT(index->to_be_dropped);
5046 
5047  /* Replace the indexes in foreign key
5048  constraints if needed. */
5049 
5051  index->table, ctx->col_names, index)) {
5052  found = false;
5053  }
5054 
5055  /* Mark the index dropped
5056  in the data dictionary cache. */
5057  rw_lock_x_lock(dict_index_get_lock(index));
5058  index->page = FIL_NULL;
5059  rw_lock_x_unlock(dict_index_get_lock(index));
5060  }
5061 
5062  trx_start_for_ddl(trx, TRX_DICT_OP_INDEX);
5063  row_merge_drop_indexes_dict(trx, ctx->new_table->id);
5064 
5065  for (ulint i = 0; i < ctx->num_to_drop_index; i++) {
5066  dict_index_t* index = ctx->drop_index[i];
5067  DBUG_ASSERT(*index->name != TEMP_INDEX_PREFIX);
5068  DBUG_ASSERT(index->table == ctx->new_table);
5069 
5070  if (index->type & DICT_FTS) {
5071  DBUG_ASSERT(index->type == DICT_FTS
5072  || (index->type
5073  & DICT_CORRUPT));
5074  DBUG_ASSERT(index->table->fts);
5075  fts_drop_index(index->table, index, trx);
5076  }
5077 
5078  dict_index_remove_from_cache(index->table, index);
5079  }
5080 
5081  trx_commit_for_mysql(trx);
5082  }
5083 
5084  DBUG_RETURN(found);
5085 }
5086 
5096 static
5097 void
5098 alter_stats_norebuild(
5099 /*==================*/
5100  Alter_inplace_info* ha_alter_info,
5101  ha_innobase_inplace_ctx* ctx,
5102  TABLE* altered_table,
5103  const char* table_name,
5104  THD* thd)
5105 {
5106  ulint i;
5107 
5108  DBUG_ENTER("alter_stats_norebuild");
5109  DBUG_ASSERT(!ctx->need_rebuild());
5110 
5111  if (!dict_stats_is_persistent_enabled(ctx->new_table)) {
5112  DBUG_VOID_RETURN;
5113  }
5114 
5115  /* TODO: This will not drop the (unused) statistics for
5116  FTS_DOC_ID_INDEX if it was a hidden index, dropped together
5117  with the last renamining FULLTEXT index. */
5118  for (i = 0; i < ha_alter_info->index_drop_count; i++) {
5119  const KEY* key = ha_alter_info->index_drop_buffer[i];
5120 
5121  if (key->flags & HA_FULLTEXT) {
5122  /* There are no index cardinality
5123  statistics for FULLTEXT indexes. */
5124  continue;
5125  }
5126 
5127  char errstr[1024];
5128 
5130  ctx->new_table->name, key->name,
5131  errstr, sizeof errstr) != DB_SUCCESS) {
5132  push_warning(thd,
5133  Sql_condition::WARN_LEVEL_WARN,
5134  ER_LOCK_WAIT_TIMEOUT, errstr);
5135  }
5136  }
5137 
5138  for (i = 0; i < ctx->num_to_add_index; i++) {
5139  dict_index_t* index = ctx->add_index[i];
5140  DBUG_ASSERT(index->table == ctx->new_table);
5141 
5142  if (!(index->type & DICT_FTS)) {
5143  dict_stats_init(ctx->new_table);
5145  }
5146  }
5147 
5148  DBUG_VOID_RETURN;
5149 }
5150 
5158 static
5159 void
5160 alter_stats_rebuild(
5161 /*================*/
5162  dict_table_t* table,
5163  const char* table_name,
5164  THD* thd)
5165 {
5166  DBUG_ENTER("alter_stats_rebuild");
5167 
5168  if (dict_table_is_discarded(table)
5169  || !dict_stats_is_persistent_enabled(table)) {
5170  DBUG_VOID_RETURN;
5171  }
5172 
5173  dberr_t ret;
5174 
5175  ret = dict_stats_update(table, DICT_STATS_RECALC_PERSISTENT);
5176 
5177  if (ret != DB_SUCCESS) {
5178  push_warning_printf(
5179  thd,
5180  Sql_condition::WARN_LEVEL_WARN,
5181  ER_ALTER_INFO,
5182  "Error updating stats for table '%s' "
5183  "after table rebuild: %s",
5184  table_name, ut_strerr(ret));
5185  }
5186 
5187  DBUG_VOID_RETURN;
5188 }
5189 
5190 #ifndef DBUG_OFF
5191 # define DBUG_INJECT_CRASH(prefix, count) \
5192 do { \
5193  char buf[32]; \
5194  ut_snprintf(buf, sizeof buf, prefix "_%u", count); \
5195  DBUG_EXECUTE_IF(buf, DBUG_SUICIDE();); \
5196 } while (0)
5197 #else
5198 # define DBUG_INJECT_CRASH(prefix, count)
5199 #endif
5200 
5215 UNIV_INTERN
5216 bool
5218 /*====================================*/
5219  TABLE* altered_table,
5220  Alter_inplace_info* ha_alter_info,
5221  bool commit)
5222 {
5223  ha_innobase_inplace_ctx* ctx0
5224  = static_cast<ha_innobase_inplace_ctx*>
5225  (ha_alter_info->handler_ctx);
5226 #ifndef DBUG_OFF
5227  uint crash_inject_count = 1;
5228  uint crash_fail_inject_count = 1;
5229  uint failure_inject_count = 1;
5230 #endif
5231 
5232  DBUG_ENTER("commit_inplace_alter_table");
5233  DBUG_ASSERT(!srv_read_only_mode);
5234  DBUG_ASSERT(!ctx0 || ctx0->prebuilt == prebuilt);
5235  DBUG_ASSERT(!ctx0 || ctx0->old_table == prebuilt->table);
5236 
5237  DEBUG_SYNC_C("innodb_commit_inplace_alter_table_enter");
5238 
5239  DEBUG_SYNC_C("innodb_commit_inplace_alter_table_wait");
5240 
5241  if (!commit) {
5242  /* A rollback is being requested. So far we may at
5243  most have created some indexes. If any indexes were to
5244  be dropped, they would actually be dropped in this
5245  method if commit=true. */
5246  DBUG_RETURN(rollback_inplace_alter_table(
5247  ha_alter_info, table, prebuilt));
5248  }
5249 
5250  if (!(ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE)) {
5251  DBUG_ASSERT(!ctx0);
5252  MONITOR_ATOMIC_DEC(MONITOR_PENDING_ALTER_TABLE);
5253  ha_alter_info->group_commit_ctx = NULL;
5254  DBUG_RETURN(false);
5255  }
5256 
5257  DBUG_ASSERT(ctx0);
5258 
5259  inplace_alter_handler_ctx** ctx_array;
5260  inplace_alter_handler_ctx* ctx_single[2];
5261 
5262  if (ha_alter_info->group_commit_ctx) {
5263  ctx_array = ha_alter_info->group_commit_ctx;
5264  } else {
5265  ctx_single[0] = ctx0;
5266  ctx_single[1] = NULL;
5267  ctx_array = ctx_single;
5268  }
5269 
5270  DBUG_ASSERT(ctx0 == ctx_array[0]);
5271  ut_ad(prebuilt->table == ctx0->old_table);
5272  ha_alter_info->group_commit_ctx = NULL;
5273 
5274  /* Free the ctx->trx of other partitions, if any. We will only
5275  use the ctx0->trx here. Others may have been allocated in
5276  the prepare stage. */
5277 
5278  for (inplace_alter_handler_ctx** pctx = &ctx_array[1]; *pctx;
5279  pctx++) {
5280  ha_innobase_inplace_ctx* ctx
5281  = static_cast<ha_innobase_inplace_ctx*>(*pctx);
5282 
5283  if (ctx->trx) {
5284  trx_free_for_mysql(ctx->trx);
5285  ctx->trx = NULL;
5286  }
5287  }
5288 
5289  trx_start_if_not_started_xa(prebuilt->trx);
5290 
5291  for (inplace_alter_handler_ctx** pctx = ctx_array; *pctx; pctx++) {
5292  ha_innobase_inplace_ctx* ctx
5293  = static_cast<ha_innobase_inplace_ctx*>(*pctx);
5294  DBUG_ASSERT(ctx->prebuilt->trx == prebuilt->trx);
5295 
5296  /* Exclusively lock the table, to ensure that no other
5297  transaction is holding locks on the table while we
5298  change the table definition. The MySQL meta-data lock
5299  should normally guarantee that no conflicting locks
5300  exist. However, FOREIGN KEY constraints checks and any
5301  transactions collected during crash recovery could be
5302  holding InnoDB locks only, not MySQL locks. */
5303 
5304  dberr_t error = row_merge_lock_table(
5305  prebuilt->trx, ctx->old_table, LOCK_X);
5306 
5307  if (error != DB_SUCCESS) {
5308  my_error_innodb(
5309  error, table_share->table_name.str, 0);
5310  DBUG_RETURN(true);
5311  }
5312  }
5313 
5314  DEBUG_SYNC(user_thd, "innodb_alter_commit_after_lock_table");
5315 
5316  const bool new_clustered = ctx0->need_rebuild();
5317  trx_t* trx = ctx0->trx;
5318  bool fail = false;
5319 
5320  if (new_clustered) {
5321  for (inplace_alter_handler_ctx** pctx = ctx_array;
5322  *pctx; pctx++) {
5323  ha_innobase_inplace_ctx* ctx
5324  = static_cast<ha_innobase_inplace_ctx*>(*pctx);
5325  DBUG_ASSERT(ctx->need_rebuild());
5326 
5327  if (ctx->old_table->fts) {
5328  ut_ad(!ctx->old_table->fts->add_wq);
5330  ctx->old_table);
5331  }
5332 
5333  if (ctx->new_table->fts) {
5334  ut_ad(!ctx->new_table->fts->add_wq);
5336  ctx->new_table);
5337  }
5338  }
5339  }
5340 
5341  if (!trx) {
5342  DBUG_ASSERT(!new_clustered);
5343  trx = innobase_trx_allocate(user_thd);
5344  }
5345 
5346  trx_start_for_ddl(trx, TRX_DICT_OP_INDEX);
5347  /* Latch the InnoDB data dictionary exclusively so that no deadlocks
5348  or lock waits can happen in it during the data dictionary operation. */
5349  row_mysql_lock_data_dictionary(trx);
5350 
5351  /* Prevent the background statistics collection from accessing
5352  the tables. */
5353  for (;;) {
5354  bool retry = false;
5355 
5356  for (inplace_alter_handler_ctx** pctx = ctx_array;
5357  *pctx; pctx++) {
5358  ha_innobase_inplace_ctx* ctx
5359  = static_cast<ha_innobase_inplace_ctx*>(*pctx);
5360 
5361  DBUG_ASSERT(new_clustered == ctx->need_rebuild());
5362 
5363  if (new_clustered
5364  && !dict_stats_stop_bg(ctx->old_table)) {
5365  retry = true;
5366  }
5367 
5368  if (!dict_stats_stop_bg(ctx->new_table)) {
5369  retry = true;
5370  }
5371  }
5372 
5373  if (!retry) {
5374  break;
5375  }
5376 
5377  DICT_STATS_BG_YIELD(trx);
5378  }
5379 
5380  /* Apply the changes to the data dictionary tables, for all
5381  partitions. */
5382 
5383  for (inplace_alter_handler_ctx** pctx = ctx_array;
5384  *pctx && !fail; pctx++) {
5385  ha_innobase_inplace_ctx* ctx
5386  = static_cast<ha_innobase_inplace_ctx*>(*pctx);
5387 
5388  DBUG_ASSERT(new_clustered == ctx->need_rebuild());
5389 
5390  ctx->max_autoinc = commit_get_autoinc(
5391  ha_alter_info, ctx, altered_table, table);
5392 
5393  if (ctx->need_rebuild()) {
5394  ctx->tmp_name = dict_mem_create_temporary_tablename(
5395  ctx->heap, ctx->new_table->name,
5396  ctx->new_table->id);
5397 
5398  fail = commit_try_rebuild(
5399  ha_alter_info, ctx, altered_table, table,
5400  trx, table_share->table_name.str);
5401  } else {
5402  fail = commit_try_norebuild(
5403  ha_alter_info, ctx, table, trx,
5404  table_share->table_name.str);
5405  }
5406  DBUG_INJECT_CRASH("ib_commit_inplace_crash",
5407  crash_inject_count++);
5408 #ifndef DBUG_OFF
5409  {
5410  /* Generate a dynamic dbug text. */
5411  char buf[32];
5412  ut_snprintf(buf, sizeof buf, "ib_commit_inplace_fail_%u",
5413  failure_inject_count++);
5414  DBUG_EXECUTE_IF(buf,
5415  my_error(ER_INTERNAL_ERROR, MYF(0),
5416  "Injected error!");
5417  fail = true;
5418  );
5419  }
5420 #endif
5421  }
5422 
5423  /* Commit or roll back the changes to the data dictionary. */
5424 
5425  if (fail) {
5427  } else if (!new_clustered) {
5428  trx_commit_for_mysql(trx);
5429  } else {
5430  mtr_t mtr;
5431  mtr_start(&mtr);
5432 
5433  for (inplace_alter_handler_ctx** pctx = ctx_array;
5434  *pctx; pctx++) {
5435  ha_innobase_inplace_ctx* ctx
5436  = static_cast<ha_innobase_inplace_ctx*>(*pctx);
5437 
5438  DBUG_ASSERT(ctx->need_rebuild());
5439  /* Generate the redo log for the file
5440  operations that will be performed in
5441  commit_cache_rebuild(). */
5442  fil_mtr_rename_log(ctx->old_table->space,
5443  ctx->old_table->name,
5444  ctx->new_table->space,
5445  ctx->new_table->name,
5446  ctx->tmp_name, &mtr);
5447  DBUG_INJECT_CRASH("ib_commit_inplace_crash",
5448  crash_inject_count++);
5449  }
5450 
5451  /* Test what happens on crash if the redo logs
5452  are flushed to disk here. The log records
5453  about the rename should not be committed, and
5454  the data dictionary transaction should be
5455  rolled back, restoring the old table. */
5456  DBUG_EXECUTE_IF("innodb_alter_commit_crash_before_commit",
5458  DBUG_SUICIDE(););
5459  ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE));
5460  ut_ad(!trx->fts_trx);
5461  ut_ad(trx->insert_undo || trx->update_undo);
5462 
5463  /* The following call commits the
5464  mini-transaction, making the data dictionary
5465  transaction committed at mtr.end_lsn. The
5466  transaction becomes 'durable' by the time when
5467  log_buffer_flush_to_disk() returns. In the
5468  logical sense the commit in the file-based
5469  data structures happens here. */
5470  trx_commit_low(trx, &mtr);
5471 
5472  /* If server crashes here, the dictionary in
5473  InnoDB and MySQL will differ. The .ibd files
5474  and the .frm files must be swapped manually by
5475  the administrator. No loss of data. */
5476  DBUG_EXECUTE_IF("innodb_alter_commit_crash_after_commit",
5478  DBUG_SUICIDE(););
5479  }
5480 
5481  /* Flush the log to reduce probability that the .frm files and
5482  the InnoDB data dictionary get out-of-sync if the user runs
5483  with innodb_flush_log_at_trx_commit = 0 */
5484 
5486 
5487  /* At this point, the changes to the persistent storage have
5488  been committed or rolled back. What remains to be done is to
5489  update the in-memory structures, close some handles, release
5490  temporary files, and (unless we rolled back) update persistent
5491  statistics. */
5492  dberr_t error = DB_SUCCESS;
5493 
5494  for (inplace_alter_handler_ctx** pctx = ctx_array;
5495  *pctx; pctx++) {
5496  ha_innobase_inplace_ctx* ctx
5497  = static_cast<ha_innobase_inplace_ctx*>(*pctx);
5498 
5499  DBUG_ASSERT(ctx->need_rebuild() == new_clustered);
5500 
5501  if (new_clustered) {
5502  innobase_online_rebuild_log_free(ctx->old_table);
5503  }
5504 
5505  if (fail) {
5506  if (new_clustered) {
5507  dict_table_close(ctx->new_table,
5508  TRUE, FALSE);
5509 
5510 #if defined UNIV_DEBUG || defined UNIV_DDL_DEBUG
5511  /* Nobody should have initialized the
5512  stats of the newly created table
5513  yet. When this is the case, we know
5514  that it has not been added for
5515  background stats gathering. */
5516  ut_a(!ctx->new_table->stat_initialized);
5517 #endif /* UNIV_DEBUG || UNIV_DDL_DEBUG */
5518 
5519  trx_start_for_ddl(trx, TRX_DICT_OP_TABLE);
5520  row_merge_drop_table(trx, ctx->new_table);
5521  trx_commit_for_mysql(trx);
5522  ctx->new_table = NULL;
5523  } else {
5524  /* We failed, but did not rebuild the table.
5525  Roll back any ADD INDEX, or get rid of garbage
5526  ADD INDEX that was left over from a previous
5527  ALTER TABLE statement. */
5528  trx_start_for_ddl(trx, TRX_DICT_OP_INDEX);
5529  innobase_rollback_sec_index(
5530  ctx->new_table, table, TRUE, trx);
5531  trx_commit_for_mysql(trx);
5532  }
5533  DBUG_INJECT_CRASH("ib_commit_inplace_crash_fail",
5534  crash_fail_inject_count++);
5535 
5536  continue;
5537  }
5538 
5540  ctx->new_table, altered_table->s);
5541 
5542  if (new_clustered) {
5543  /* We will reload and refresh the
5544  in-memory foreign key constraint
5545  metadata. This is a rename operation
5546  in preparing for dropping the old
5547  table. Set the table to_be_dropped bit
5548  here, so to make sure DML foreign key
5549  constraint check does not use the
5550  stale dict_foreign_t. This is done
5551  because WL#6049 (FK MDL) has not been
5552  implemented yet. */
5553  ctx->old_table->to_be_dropped = true;
5554 
5555  /* Rename the tablespace files. */
5556  commit_cache_rebuild(ctx);
5557 
5558  error = innobase_update_foreign_cache(ctx);
5559  if (error != DB_SUCCESS) {
5560  goto foreign_fail;
5561  }
5562  } else {
5563  error = innobase_update_foreign_cache(ctx);
5564 
5565  if (error != DB_SUCCESS) {
5566 foreign_fail:
5567  /* The data dictionary cache
5568  should be corrupted now. The
5569  best solution should be to
5570  kill and restart the server,
5571  but the *.frm file has not
5572  been replaced yet. */
5573  my_error(ER_CANNOT_ADD_FOREIGN,
5574  MYF(0));
5575  sql_print_error(
5576  "InnoDB: dict_load_foreigns()"
5577  " returned %u for %s",
5578  (unsigned) error,
5579  thd_query_string(user_thd)
5580  ->str);
5581  ut_ad(0);
5582  } else {
5583  if (!commit_cache_norebuild(
5584  ctx, table, trx)) {
5585  ut_a(!prebuilt->trx->check_foreigns);
5586  }
5587 
5588  innobase_rename_columns_cache(
5589  ha_alter_info, table,
5590  ctx->new_table);
5591  }
5592  }
5593  DBUG_INJECT_CRASH("ib_commit_inplace_crash",
5594  crash_inject_count++);
5595  }
5596 
5597  /* Invalidate the index translation table. In partitioned
5598  tables, there is one TABLE_SHARE (and also only one TABLE)
5599  covering all partitions. */
5600  share->idx_trans_tbl.index_count = 0;
5601 
5602  if (trx == ctx0->trx) {
5603  ctx0->trx = NULL;
5604  }
5605 
5606  /* Tell the InnoDB server that there might be work for
5607  utility threads: */
5608 
5610 
5611  if (fail) {
5612  for (inplace_alter_handler_ctx** pctx = ctx_array;
5613  *pctx; pctx++) {
5614  ha_innobase_inplace_ctx* ctx
5615  = static_cast<ha_innobase_inplace_ctx*>
5616  (*pctx);
5617  DBUG_ASSERT(ctx->need_rebuild() == new_clustered);
5618 
5619  ut_d(dict_table_check_for_dup_indexes(
5620  ctx->old_table,
5621  CHECK_ABORTED_OK));
5622  ut_a(fts_check_cached_index(ctx->old_table));
5623  DBUG_INJECT_CRASH("ib_commit_inplace_crash_fail",
5624  crash_fail_inject_count++);
5625  }
5626 
5628  trx_free_for_mysql(trx);
5629  DBUG_RETURN(true);
5630  }
5631 
5632  /* Release the table locks. */
5633  trx_commit_for_mysql(prebuilt->trx);
5634 
5635  DBUG_EXECUTE_IF("ib_ddl_crash_after_user_trx_commit", DBUG_SUICIDE(););
5636 
5637  for (inplace_alter_handler_ctx** pctx = ctx_array;
5638  *pctx; pctx++) {
5639  ha_innobase_inplace_ctx* ctx
5640  = static_cast<ha_innobase_inplace_ctx*>
5641  (*pctx);
5642  DBUG_ASSERT(ctx->need_rebuild() == new_clustered);
5643 
5644  if (altered_table->found_next_number_field) {
5645  dict_table_t* t = ctx->new_table;
5646 
5648  dict_table_autoinc_initialize(t, ctx->max_autoinc);
5650  }
5651 
5652  bool add_fts = false;
5653 
5654  /* Publish the created fulltext index, if any.
5655  Note that a fulltext index can be created without
5656  creating the clustered index, if there already exists
5657  a suitable FTS_DOC_ID column. If not, one will be
5658  created, implying new_clustered */
5659  for (ulint i = 0; i < ctx->num_to_add_index; i++) {
5660  dict_index_t* index = ctx->add_index[i];
5661 
5662  if (index->type & DICT_FTS) {
5663  DBUG_ASSERT(index->type == DICT_FTS);
5664  fts_add_index(index, ctx->new_table);
5665  add_fts = true;
5666  }
5667  }
5668 
5669  ut_d(dict_table_check_for_dup_indexes(
5670  ctx->new_table, CHECK_ALL_COMPLETE));
5671 
5672  if (add_fts) {
5673  fts_optimize_add_table(ctx->new_table);
5674  }
5675 
5676  ut_d(dict_table_check_for_dup_indexes(
5677  ctx->new_table, CHECK_ABORTED_OK));
5678  ut_a(fts_check_cached_index(ctx->new_table));
5679 
5680  if (new_clustered) {
5681  /* Since the table has been rebuilt, we remove
5682  all persistent statistics corresponding to the
5683  old copy of the table (which was renamed to
5684  ctx->tmp_name). */
5685 
5686  char errstr[1024];
5687 
5688  DBUG_ASSERT(0 == strcmp(ctx->old_table->name,
5689  ctx->tmp_name));
5690 
5692  ctx->new_table->name,
5693  errstr, sizeof(errstr))
5694  != DB_SUCCESS) {
5695  push_warning_printf(
5696  user_thd,
5697  Sql_condition::WARN_LEVEL_WARN,
5698  ER_ALTER_INFO,
5699  "Deleting persistent statistics"
5700  " for rebuilt table '%s' in"
5701  " InnoDB failed: %s",
5702  table->s->table_name.str,
5703  errstr);
5704  }
5705 
5706  DBUG_EXECUTE_IF("ib_ddl_crash_before_commit",
5707  DBUG_SUICIDE(););
5708 
5709  trx_t* const user_trx = prebuilt->trx;
5710 
5711  row_prebuilt_free(ctx->prebuilt, TRUE);
5712 
5713  /* Drop the copy of the old table, which was
5714  renamed to ctx->tmp_name at the atomic DDL
5715  transaction commit. If the system crashes
5716  before this is completed, some orphan tables
5717  with ctx->tmp_name may be recovered. */
5718  trx_start_for_ddl(trx, TRX_DICT_OP_TABLE);
5719  row_merge_drop_table(trx, ctx->old_table);
5720  trx_commit_for_mysql(trx);
5721 
5722  /* Rebuild the prebuilt object. */
5723  ctx->prebuilt = row_create_prebuilt(
5724  ctx->new_table, altered_table->s->reclength);
5725  trx_start_if_not_started(user_trx);
5726  user_trx->will_lock++;
5727  prebuilt->trx = user_trx;
5728  }
5729  DBUG_INJECT_CRASH("ib_commit_inplace_crash",
5730  crash_inject_count++);
5731  }
5732 
5734  trx_free_for_mysql(trx);
5735 
5736  /* TODO: The following code could be executed
5737  while allowing concurrent access to the table
5738  (MDL downgrade). */
5739 
5740  if (new_clustered) {
5741  for (inplace_alter_handler_ctx** pctx = ctx_array;
5742  *pctx; pctx++) {
5743  ha_innobase_inplace_ctx* ctx
5744  = static_cast<ha_innobase_inplace_ctx*>
5745  (*pctx);
5746  DBUG_ASSERT(ctx->need_rebuild());
5747 
5748  alter_stats_rebuild(
5749  ctx->new_table, table->s->table_name.str,
5750  user_thd);
5751  DBUG_INJECT_CRASH("ib_commit_inplace_crash",
5752  crash_inject_count++);
5753  }
5754  } else {
5755  for (inplace_alter_handler_ctx** pctx = ctx_array;
5756  *pctx; pctx++) {
5757  ha_innobase_inplace_ctx* ctx
5758  = static_cast<ha_innobase_inplace_ctx*>
5759  (*pctx);
5760  DBUG_ASSERT(!ctx->need_rebuild());
5761 
5762  alter_stats_norebuild(
5763  ha_alter_info, ctx, altered_table,
5764  table->s->table_name.str, user_thd);
5765  DBUG_INJECT_CRASH("ib_commit_inplace_crash",
5766  crash_inject_count++);
5767  }
5768  }
5769 
5770  /* TODO: Also perform DROP TABLE and DROP INDEX after
5771  the MDL downgrade. */
5772 
5773 #ifndef DBUG_OFF
5774  dict_index_t* clust_index = dict_table_get_first_index(
5775  prebuilt->table);
5776  DBUG_ASSERT(!clust_index->online_log);
5777  DBUG_ASSERT(dict_index_get_online_status(clust_index)
5779 
5780  for (dict_index_t* index = dict_table_get_first_index(
5781  prebuilt->table);
5782  index;
5783  index = dict_table_get_next_index(index)) {
5784  DBUG_ASSERT(!index->to_be_dropped);
5785  }
5786 #endif /* DBUG_OFF */
5787 
5788  MONITOR_ATOMIC_DEC(MONITOR_PENDING_ALTER_TABLE);
5789  DBUG_RETURN(false);
5790 }
5791 
5796 UNIV_INTERN
5798  THD* thd,
5799  ulonglong start_value,
5800  ulonglong max_value)
5801  :
5802  m_max_value(max_value),
5803  m_increment(0),
5804  m_offset(0),
5805  m_next_value(start_value),
5806  m_eof(false)
5807 {
5808  if (thd != 0 && m_max_value > 0) {
5809 
5810  thd_get_autoinc(thd, &m_offset, &m_increment);
5811 
5812  if (m_increment > 1 || m_offset > 1) {
5813 
5814  /* If there is an offset or increment specified
5815  then we need to work out the exact next value. */
5816 
5817  m_next_value = innobase_next_autoinc(
5818  start_value, 1,
5819  m_increment, m_offset, m_max_value);
5820 
5821  } else if (start_value == 0) {
5822  /* The next value can never be 0. */
5823  m_next_value = 1;
5824  }
5825  } else {
5826  m_eof = true;
5827  }
5828 }
5829 
5833 UNIV_INTERN
5834 ulonglong
5835 ib_sequence_t::operator++(int) UNIV_NOTHROW
5836 {
5837  ulonglong current = m_next_value;
5838 
5839  ut_ad(!m_eof);
5840  ut_ad(m_max_value > 0);
5841 
5843  current, 1, m_increment, m_offset, m_max_value);
5844 
5845  if (m_next_value == m_max_value && current == m_next_value) {
5846  m_eof = true;
5847  }
5848 
5849  return(current);
5850 }