1/*****************************************************************************
2
3Copyright (c) 2005, 2018, Oracle and/or its affiliates. All Rights Reserved.
4Copyright (c) 2013, 2018, MariaDB Corporation.
5
6This program is free software; you can redistribute it and/or modify it under
7the terms of the GNU General Public License as published by the Free Software
8Foundation; version 2 of the License.
9
10This program is distributed in the hope that it will be useful, but WITHOUT
11ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License along with
15this program; if not, write to the Free Software Foundation, Inc.,
1651 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
17
18*****************************************************************************/
19
20/**************************************************//**
21@file handler/handler0alter.cc
22Smart ALTER TABLE
23*******************************************************/
24
25/* Include necessary SQL headers */
26#include "ha_prototypes.h"
27#include <debug_sync.h>
28#include <log.h>
29#include <sql_lex.h>
30#include <sql_class.h>
31#include <sql_table.h>
32#include <mysql/plugin.h>
33
34/* Include necessary InnoDB headers */
35#include "btr0sea.h"
36#include "dict0crea.h"
37#include "dict0dict.h"
38#include "dict0priv.h"
39#include "dict0stats.h"
40#include "dict0stats_bg.h"
41#include "fsp0sysspace.h"
42#include "log0log.h"
43#include "rem0types.h"
44#include "row0log.h"
45#include "row0merge.h"
46#include "row0ins.h"
47#include "row0row.h"
48#include "row0upd.h"
49#include "trx0trx.h"
50#include "trx0roll.h"
51#include "handler0alter.h"
52#include "srv0mon.h"
53#include "srv0srv.h"
54#include "fts0priv.h"
55#include "fts0plugin.h"
56#include "pars0pars.h"
57#include "row0sel.h"
58#include "ha_innodb.h"
59#include "ut0new.h"
60#include "ut0stage.h"
61#ifdef WITH_WSREP
62//#include "wsrep_api.h"
63#include <sql_acl.h> // PROCESS_ACL
64#endif
65
66static const char *MSG_UNSUPPORTED_ALTER_ONLINE_ON_VIRTUAL_COLUMN=
67 "INPLACE ADD or DROP of virtual columns cannot be "
68 "combined with other ALTER TABLE actions";
69
70/** Operations for creating secondary indexes (no rebuild needed) */
71static const alter_table_operations INNOBASE_ONLINE_CREATE
72 = ALTER_ADD_NON_UNIQUE_NON_PRIM_INDEX
73 | ALTER_ADD_UNIQUE_INDEX;
74
75/** Operations for rebuilding a table in place */
76static const alter_table_operations INNOBASE_ALTER_REBUILD
77 = ALTER_ADD_PK_INDEX
78 | ALTER_DROP_PK_INDEX
79 | ALTER_CHANGE_CREATE_OPTION
80 /* CHANGE_CREATE_OPTION needs to check create_option_need_rebuild() */
81 | ALTER_COLUMN_NULLABLE
82 | ALTER_COLUMN_NOT_NULLABLE
83 | ALTER_STORED_COLUMN_ORDER
84 | ALTER_DROP_STORED_COLUMN
85 | ALTER_ADD_STORED_BASE_COLUMN
86 | ALTER_RECREATE_TABLE
87 /*
88 | ALTER_STORED_COLUMN_TYPE
89 */
90 | ALTER_COLUMN_UNVERSIONED
91 | ALTER_ADD_SYSTEM_VERSIONING
92 | ALTER_DROP_SYSTEM_VERSIONING
93 ;
94
95/** Operations that require changes to data */
96static const alter_table_operations INNOBASE_ALTER_DATA
97 = INNOBASE_ONLINE_CREATE | INNOBASE_ALTER_REBUILD;
98
99/** Operations for altering a table that InnoDB does not care about */
100static const alter_table_operations INNOBASE_INPLACE_IGNORE
101 = ALTER_COLUMN_DEFAULT
102 | ALTER_CHANGE_COLUMN_DEFAULT
103 | ALTER_PARTITIONED
104 | ALTER_COLUMN_COLUMN_FORMAT
105 | ALTER_COLUMN_STORAGE_TYPE
106 | ALTER_VIRTUAL_GCOL_EXPR
107 | ALTER_DROP_CHECK_CONSTRAINT
108 | ALTER_RENAME;
109
110/** Operations on foreign key definitions (changing the schema only) */
111static const alter_table_operations INNOBASE_FOREIGN_OPERATIONS
112 = ALTER_DROP_FOREIGN_KEY
113 | ALTER_ADD_FOREIGN_KEY;
114
115/** Operations that InnoDB cares about and can perform without rebuild */
116static const alter_table_operations INNOBASE_ALTER_NOREBUILD
117 = INNOBASE_ONLINE_CREATE
118 | ALTER_DROP_NON_UNIQUE_NON_PRIM_INDEX
119 | ALTER_DROP_UNIQUE_INDEX
120#ifdef MYSQL_RENAME_INDEX
121 | ALTER_RENAME_INDEX
122#endif
123 ;
124
125/** Operations that can be performed instantly, without inplace_alter_table() */
126static const alter_table_operations INNOBASE_ALTER_INSTANT
127 = ALTER_VIRTUAL_COLUMN_ORDER
128 | ALTER_COLUMN_NAME
129 | ALTER_ADD_VIRTUAL_COLUMN
130 | INNOBASE_FOREIGN_OPERATIONS
131 | ALTER_COLUMN_EQUAL_PACK_LENGTH
132 | ALTER_DROP_VIRTUAL_COLUMN;
133
134static const alter_table_operations INNOBASE_DEFAULTS
135 = ALTER_COLUMN_NOT_NULLABLE
136 | ALTER_ADD_COLUMN;
137
138struct ha_innobase_inplace_ctx : public inplace_alter_handler_ctx
139{
140 /** Dummy query graph */
141 que_thr_t* thr;
142 /** The prebuilt struct of the creating instance */
143 row_prebuilt_t*& prebuilt;
144 /** InnoDB indexes being created */
145 dict_index_t** add_index;
146 /** MySQL key numbers for the InnoDB indexes that are being created */
147 const ulint* add_key_numbers;
148 /** number of InnoDB indexes being created */
149 ulint num_to_add_index;
150 /** InnoDB indexes being dropped */
151 dict_index_t** drop_index;
152 /** number of InnoDB indexes being dropped */
153 const ulint num_to_drop_index;
154 /** InnoDB indexes being renamed */
155 dict_index_t** rename;
156 /** number of InnoDB indexes being renamed */
157 const ulint num_to_rename;
158 /** InnoDB foreign key constraints being dropped */
159 dict_foreign_t** drop_fk;
160 /** number of InnoDB foreign key constraints being dropped */
161 const ulint num_to_drop_fk;
162 /** InnoDB foreign key constraints being added */
163 dict_foreign_t** add_fk;
164 /** number of InnoDB foreign key constraints being dropped */
165 const ulint num_to_add_fk;
166 /** whether to create the indexes online */
167 bool online;
168 /** memory heap */
169 mem_heap_t* heap;
170 /** dictionary transaction */
171 trx_t* trx;
172 /** original table (if rebuilt, differs from indexed_table) */
173 dict_table_t* old_table;
174 /** table where the indexes are being created or dropped */
175 dict_table_t* new_table;
176 /** table definition for instant ADD COLUMN */
177 dict_table_t* instant_table;
178 /** mapping of old column numbers to new ones, or NULL */
179 const ulint* col_map;
180 /** new column names, or NULL if nothing was renamed */
181 const char** col_names;
182 /** added AUTO_INCREMENT column position, or ULINT_UNDEFINED */
183 const ulint add_autoinc;
184 /** default values of ADD and CHANGE COLUMN, or NULL */
185 const dtuple_t* defaults;
186 /** autoinc sequence to use */
187 ib_sequence_t sequence;
188 /** temporary table name to use for old table when renaming tables */
189 const char* tmp_name;
190 /** whether the order of the clustered index is unchanged */
191 bool skip_pk_sort;
192 /** number of virtual columns to be added */
193 ulint num_to_add_vcol;
194 /** virtual columns to be added */
195 dict_v_col_t* add_vcol;
196 const char** add_vcol_name;
197 /** number of virtual columns to be dropped */
198 ulint num_to_drop_vcol;
199 /** virtual columns to be dropped */
200 dict_v_col_t* drop_vcol;
201 const char** drop_vcol_name;
202 /** ALTER TABLE stage progress recorder */
203 ut_stage_alter_t* m_stage;
204 /** original number of user columns in the table */
205 const unsigned old_n_cols;
206 /** original columns of the table */
207 dict_col_t* const old_cols;
208 /** original column names of the table */
209 const char* const old_col_names;
210
211 /** Whether alter ignore issued. */
212 const bool ignore;
213
214 ha_innobase_inplace_ctx(row_prebuilt_t*& prebuilt_arg,
215 dict_index_t** drop_arg,
216 ulint num_to_drop_arg,
217 dict_index_t** rename_arg,
218 ulint num_to_rename_arg,
219 dict_foreign_t** drop_fk_arg,
220 ulint num_to_drop_fk_arg,
221 dict_foreign_t** add_fk_arg,
222 ulint num_to_add_fk_arg,
223 bool online_arg,
224 mem_heap_t* heap_arg,
225 dict_table_t* new_table_arg,
226 const char** col_names_arg,
227 ulint add_autoinc_arg,
228 ulonglong autoinc_col_min_value_arg,
229 ulonglong autoinc_col_max_value_arg,
230 bool ignore_flag) :
231 inplace_alter_handler_ctx(),
232 prebuilt (prebuilt_arg),
233 add_index (0), add_key_numbers (0), num_to_add_index (0),
234 drop_index (drop_arg), num_to_drop_index (num_to_drop_arg),
235 rename (rename_arg), num_to_rename (num_to_rename_arg),
236 drop_fk (drop_fk_arg), num_to_drop_fk (num_to_drop_fk_arg),
237 add_fk (add_fk_arg), num_to_add_fk (num_to_add_fk_arg),
238 online (online_arg), heap (heap_arg), trx (0),
239 old_table (prebuilt_arg->table),
240 new_table (new_table_arg), instant_table (0),
241 col_map (0), col_names (col_names_arg),
242 add_autoinc (add_autoinc_arg),
243 defaults (0),
244 sequence(prebuilt->trx->mysql_thd,
245 autoinc_col_min_value_arg, autoinc_col_max_value_arg),
246 tmp_name (0),
247 skip_pk_sort(false),
248 num_to_add_vcol(0),
249 add_vcol(0),
250 add_vcol_name(0),
251 num_to_drop_vcol(0),
252 drop_vcol(0),
253 drop_vcol_name(0),
254 m_stage(NULL),
255 old_n_cols(prebuilt_arg->table->n_cols),
256 old_cols(prebuilt_arg->table->cols),
257 old_col_names(prebuilt_arg->table->col_names),
258 ignore(ignore_flag)
259 {
260 ut_ad(old_n_cols >= DATA_N_SYS_COLS);
261#ifdef UNIV_DEBUG
262 for (ulint i = 0; i < num_to_add_index; i++) {
263 ut_ad(!add_index[i]->to_be_dropped);
264 }
265 for (ulint i = 0; i < num_to_drop_index; i++) {
266 ut_ad(drop_index[i]->to_be_dropped);
267 }
268#endif /* UNIV_DEBUG */
269
270 thr = pars_complete_graph_for_exec(NULL, prebuilt->trx, heap,
271 prebuilt);
272 }
273
274 ~ha_innobase_inplace_ctx()
275 {
276 UT_DELETE(m_stage);
277 if (instant_table) {
278 while (dict_index_t* index
279 = UT_LIST_GET_LAST(instant_table->indexes)) {
280 UT_LIST_REMOVE(instant_table->indexes, index);
281 rw_lock_free(&index->lock);
282 dict_mem_index_free(index);
283 }
284 dict_mem_table_free(instant_table);
285 }
286 mem_heap_free(heap);
287 }
288
289 /** Determine if the table will be rebuilt.
290 @return whether the table will be rebuilt */
291 bool need_rebuild () const { return(old_table != new_table); }
292
293 /** Convert table-rebuilding ALTER to instant ALTER. */
294 void prepare_instant()
295 {
296 DBUG_ASSERT(need_rebuild());
297 DBUG_ASSERT(!is_instant());
298 DBUG_ASSERT(old_table->n_cols == old_table->n_def);
299 DBUG_ASSERT(new_table->n_cols == new_table->n_def);
300 DBUG_ASSERT(old_table->n_cols == old_n_cols);
301 DBUG_ASSERT(new_table->n_cols > old_table->n_cols);
302 instant_table = new_table;
303
304 new_table = old_table;
305 export_vars.innodb_instant_alter_column++;
306 }
307
308 /** Revert prepare_instant() if the transaction is rolled back. */
309 void rollback_instant()
310 {
311 if (!is_instant()) return;
312 old_table->rollback_instant(old_n_cols,
313 old_cols, old_col_names);
314 }
315
316 /** @return whether this is instant ALTER TABLE */
317 bool is_instant() const
318 {
319 DBUG_ASSERT(!instant_table || !instant_table->can_be_evicted);
320 return instant_table;
321 }
322
323private:
324 // Disable copying
325 ha_innobase_inplace_ctx(const ha_innobase_inplace_ctx&);
326 ha_innobase_inplace_ctx& operator=(const ha_innobase_inplace_ctx&);
327};
328
329/********************************************************************//**
330Get the upper limit of the MySQL integral and floating-point type.
331@return maximum allowed value for the field */
332UNIV_INTERN
333ulonglong
334innobase_get_int_col_max_value(
335/*===========================*/
336 const Field* field); /*!< in: MySQL field */
337
338/* Report an InnoDB error to the client by invoking my_error(). */
339static ATTRIBUTE_COLD __attribute__((nonnull))
340void
341my_error_innodb(
342/*============*/
343 dberr_t error, /*!< in: InnoDB error code */
344 const char* table, /*!< in: table name */
345 ulint flags) /*!< in: table flags */
346{
347 switch (error) {
348 case DB_MISSING_HISTORY:
349 my_error(ER_TABLE_DEF_CHANGED, MYF(0));
350 break;
351 case DB_RECORD_NOT_FOUND:
352 my_error(ER_KEY_NOT_FOUND, MYF(0), table);
353 break;
354 case DB_DEADLOCK:
355 my_error(ER_LOCK_DEADLOCK, MYF(0));
356 break;
357 case DB_LOCK_WAIT_TIMEOUT:
358 my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0));
359 break;
360 case DB_INTERRUPTED:
361 my_error(ER_QUERY_INTERRUPTED, MYF(0));
362 break;
363 case DB_OUT_OF_MEMORY:
364 my_error(ER_OUT_OF_RESOURCES, MYF(0));
365 break;
366 case DB_OUT_OF_FILE_SPACE:
367 my_error(ER_RECORD_FILE_FULL, MYF(0), table);
368 break;
369 case DB_TEMP_FILE_WRITE_FAIL:
370 my_error(ER_TEMP_FILE_WRITE_FAILURE, MYF(0));
371 break;
372 case DB_TOO_BIG_INDEX_COL:
373 my_error(ER_INDEX_COLUMN_TOO_LONG, MYF(0),
374 (ulong) DICT_MAX_FIELD_LEN_BY_FORMAT_FLAG(flags));
375 break;
376 case DB_TOO_MANY_CONCURRENT_TRXS:
377 my_error(ER_TOO_MANY_CONCURRENT_TRXS, MYF(0));
378 break;
379 case DB_LOCK_TABLE_FULL:
380 my_error(ER_LOCK_TABLE_FULL, MYF(0));
381 break;
382 case DB_UNDO_RECORD_TOO_BIG:
383 my_error(ER_UNDO_RECORD_TOO_BIG, MYF(0));
384 break;
385 case DB_CORRUPTION:
386 my_error(ER_NOT_KEYFILE, MYF(0), table);
387 break;
388 case DB_TOO_BIG_RECORD: {
389 /* Note that in page0zip.ic page_zip_rec_needs_ext() rec_size
390 is limited to COMPRESSED_REC_MAX_DATA_SIZE (16K) or
391 REDUNDANT_REC_MAX_DATA_SIZE (16K-1). */
392 bool comp = !!(flags & DICT_TF_COMPACT);
393 ulint free_space = page_get_free_space_of_empty(comp) / 2;
394
395 if (free_space >= ulint(comp ? COMPRESSED_REC_MAX_DATA_SIZE :
396 REDUNDANT_REC_MAX_DATA_SIZE)) {
397 free_space = (comp ? COMPRESSED_REC_MAX_DATA_SIZE :
398 REDUNDANT_REC_MAX_DATA_SIZE) - 1;
399 }
400
401 my_error(ER_TOO_BIG_ROWSIZE, MYF(0), free_space);
402 break;
403 }
404 case DB_INVALID_NULL:
405 /* TODO: report the row, as we do for DB_DUPLICATE_KEY */
406 my_error(ER_INVALID_USE_OF_NULL, MYF(0));
407 break;
408 case DB_CANT_CREATE_GEOMETRY_OBJECT:
409 my_error(ER_CANT_CREATE_GEOMETRY_OBJECT, MYF(0));
410 break;
411 case DB_TABLESPACE_EXISTS:
412 my_error(ER_TABLESPACE_EXISTS, MYF(0), table);
413 break;
414
415#ifdef UNIV_DEBUG
416 case DB_SUCCESS:
417 case DB_DUPLICATE_KEY:
418 case DB_ONLINE_LOG_TOO_BIG:
419 /* These codes should not be passed here. */
420 ut_error;
421#endif /* UNIV_DEBUG */
422 default:
423 my_error(ER_GET_ERRNO, MYF(0), error, "InnoDB");
424 break;
425 }
426}
427
428/** Determine if fulltext indexes exist in a given table.
429@param table MySQL table
430@return whether fulltext indexes exist on the table */
431static
432bool
433innobase_fulltext_exist(
434/*====================*/
435 const TABLE* table)
436{
437 for (uint i = 0; i < table->s->keys; i++) {
438 if (table->key_info[i].flags & HA_FULLTEXT) {
439 return(true);
440 }
441 }
442
443 return(false);
444}
445
446/** Determine whether indexed virtual columns exist in a table.
447@param[in] table table definition
448@return whether indexes exist on virtual columns */
449static bool innobase_indexed_virtual_exist(const TABLE* table)
450{
451 const KEY* const end = &table->key_info[table->s->keys];
452
453 for (const KEY* key = table->key_info; key < end; key++) {
454 const KEY_PART_INFO* const key_part_end = key->key_part
455 + key->user_defined_key_parts;
456 for (const KEY_PART_INFO* key_part = key->key_part;
457 key_part < key_part_end; key_part++) {
458 if (!key_part->field->stored_in_db())
459 return true;
460 }
461 }
462
463 return false;
464}
465
466/** Determine if spatial indexes exist in a given table.
467@param table MySQL table
468@return whether spatial indexes exist on the table */
469static
470bool
471innobase_spatial_exist(
472/*===================*/
473 const TABLE* table)
474{
475 for (uint i = 0; i < table->s->keys; i++) {
476 if (table->key_info[i].flags & HA_SPATIAL) {
477 return(true);
478 }
479 }
480
481 return(false);
482}
483
484/** Determine if CHANGE_CREATE_OPTION requires rebuilding the table.
485@param[in] ha_alter_info the ALTER TABLE operation
486@param[in] table metadata before ALTER TABLE
487@return whether it is mandatory to rebuild the table */
488static bool create_option_need_rebuild(
489 const Alter_inplace_info* ha_alter_info,
490 const TABLE* table)
491{
492 DBUG_ASSERT(ha_alter_info->handler_flags
493 & ALTER_CHANGE_CREATE_OPTION);
494
495 if (ha_alter_info->create_info->used_fields
496 & (HA_CREATE_USED_ROW_FORMAT
497 | HA_CREATE_USED_KEY_BLOCK_SIZE)) {
498 /* Specifying ROW_FORMAT or KEY_BLOCK_SIZE requires
499 rebuilding the table. (These attributes in the .frm
500 file may disagree with the InnoDB data dictionary, and
501 the interpretation of thse attributes depends on
502 InnoDB parameters. That is why we for now always
503 require a rebuild when these attributes are specified.) */
504 return true;
505 }
506
507 const ha_table_option_struct& alt_opt=
508 *ha_alter_info->create_info->option_struct;
509 const ha_table_option_struct& opt= *table->s->option_struct;
510
511 if (alt_opt.page_compressed != opt.page_compressed
512 || alt_opt.page_compression_level
513 != opt.page_compression_level
514 || alt_opt.encryption != opt.encryption
515 || alt_opt.encryption_key_id != opt.encryption_key_id) {
516 return(true);
517 }
518
519 return false;
520}
521
522/** Determine if ALTER TABLE needs to rebuild the table
523(or perform instant operation).
524@param[in] ha_alter_info the ALTER TABLE operation
525@param[in] table metadata before ALTER TABLE
526@return whether it is necessary to rebuild the table or to alter columns */
527static MY_ATTRIBUTE((nonnull, warn_unused_result))
528bool
529innobase_need_rebuild(
530 const Alter_inplace_info* ha_alter_info,
531 const TABLE* table)
532{
533 if ((ha_alter_info->handler_flags & ~(INNOBASE_INPLACE_IGNORE
534 | INNOBASE_ALTER_INSTANT))
535 == ALTER_CHANGE_CREATE_OPTION) {
536 return create_option_need_rebuild(ha_alter_info, table);
537 }
538
539 return !!(ha_alter_info->handler_flags & INNOBASE_ALTER_REBUILD);
540}
541
542/** Check if virtual column in old and new table are in order, excluding
543those dropped column. This is needed because when we drop a virtual column,
544ALTER_VIRTUAL_COLUMN_ORDER is also turned on, so we can't decide if this
545is a real ORDER change or just DROP COLUMN
546@param[in] table old TABLE
547@param[in] altered_table new TABLE
548@param[in] ha_alter_info Structure describing changes to be done
549by ALTER TABLE and holding data used during in-place alter.
550@return true is all columns in order, false otherwise. */
551static
552bool
553check_v_col_in_order(
554 const TABLE* table,
555 const TABLE* altered_table,
556 Alter_inplace_info* ha_alter_info)
557{
558 ulint j = 0;
559
560 /* We don't support any adding new virtual column before
561 existed virtual column. */
562 if (ha_alter_info->handler_flags
563 & ALTER_ADD_VIRTUAL_COLUMN) {
564 bool has_new = false;
565
566 List_iterator_fast<Create_field> cf_it(
567 ha_alter_info->alter_info->create_list);
568
569 cf_it.rewind();
570
571 while (const Create_field* new_field = cf_it++) {
572 if (!innobase_is_v_fld(new_field)) {
573 continue;
574 }
575
576 /* Found a new added virtual column. */
577 if (!new_field->field) {
578 has_new = true;
579 continue;
580 }
581
582 /* If there's any old virtual column
583 after the new added virtual column,
584 order must be changed. */
585 if (has_new) {
586 return(false);
587 }
588 }
589 }
590
591 /* directly return true if ALTER_VIRTUAL_COLUMN_ORDER is not on */
592 if (!(ha_alter_info->handler_flags
593 & ALTER_VIRTUAL_COLUMN_ORDER)) {
594 return(true);
595 }
596
597 for (ulint i = 0; i < table->s->fields; i++) {
598 Field* field = table->field[i];
599
600 if (field->stored_in_db()) {
601 continue;
602 }
603
604 ut_ad(innobase_is_v_fld(field));
605
606 if (field->flags & FIELD_IS_DROPPED) {
607 continue;
608 }
609
610 /* Now check if the next virtual column in altered table
611 matches this column */
612 while (j < altered_table->s->fields) {
613 Field* new_field = altered_table->s->field[j];
614
615 if (new_field->stored_in_db()) {
616 j++;
617 continue;
618 }
619
620 if (my_strcasecmp(system_charset_info,
621 field->field_name.str,
622 new_field->field_name.str) != 0) {
623 /* different column */
624 return(false);
625 } else {
626 j++;
627 break;
628 }
629 }
630
631 if (j > altered_table->s->fields) {
632 /* there should not be less column in new table
633 without them being in drop list */
634 ut_ad(0);
635 return(false);
636 }
637 }
638
639 return(true);
640}
641
642/** Determine if an instant operation is possible for altering columns.
643@param[in] ha_alter_info the ALTER TABLE operation
644@param[in] table table definition before ALTER TABLE */
645static
646bool
647instant_alter_column_possible(
648 const Alter_inplace_info* ha_alter_info,
649 const TABLE* table)
650{
651 // Making table system-versioned instantly is not implemented yet.
652 if (ha_alter_info->handler_flags & ALTER_ADD_SYSTEM_VERSIONING) {
653 return false;
654 }
655
656 if (~ha_alter_info->handler_flags
657 & ALTER_ADD_STORED_BASE_COLUMN) {
658 return false;
659 }
660
661 /* At the moment, we disallow ADD [UNIQUE] INDEX together with
662 instant ADD COLUMN.
663
664 The main reason is that the work of instant ADD must be done
665 in commit_inplace_alter_table(). For the rollback_instant()
666 to work, we must add the columns to dict_table_t beforehand,
667 and roll back those changes in case the transaction is rolled
668 back.
669
670 If we added the columns to the dictionary cache already in the
671 prepare_inplace_alter_table(), we would have to deal with
672 column number mismatch in ha_innobase::open(), write_row() and
673 other functions. */
674
675 /* FIXME: allow instant ADD COLUMN together with
676 INNOBASE_ONLINE_CREATE (ADD [UNIQUE] INDEX) on pre-existing
677 columns. */
678 if (ha_alter_info->handler_flags
679 & ((INNOBASE_ALTER_REBUILD | INNOBASE_ONLINE_CREATE)
680 & ~ALTER_ADD_STORED_BASE_COLUMN
681 & ~ALTER_CHANGE_CREATE_OPTION)) {
682 return false;
683 }
684
685 return !(ha_alter_info->handler_flags
686 & ALTER_CHANGE_CREATE_OPTION)
687 || !create_option_need_rebuild(ha_alter_info, table);
688}
689
690/** Check whether the non-const default value for the field
691@param[in] field field which could be added or changed
692@return true if the non-const default is present. */
693static bool is_non_const_value(Field* field)
694{
695 return field->default_value
696 && field->default_value->flags
697 & uint(~(VCOL_SESSION_FUNC | VCOL_TIME_FUNC));
698}
699
700/** Set default value for the field.
701@param[in] field field which could be added or changed
702@return true if the default value is set. */
703static bool set_default_value(Field* field)
704{
705 /* The added/changed NOT NULL column lacks a DEFAULT value,
706 or the DEFAULT is the same for all rows.
707 (Time functions, such as CURRENT_TIMESTAMP(),
708 are evaluated from a timestamp that is assigned
709 at the start of the statement. Session
710 functions, such as USER(), always evaluate the
711 same within a statement.) */
712
713 ut_ad(!is_non_const_value(field));
714
715 /* Compute the DEFAULT values of non-constant columns
716 (VCOL_SESSION_FUNC | VCOL_TIME_FUNC). */
717 switch (field->set_default()) {
718 case 0: /* OK */
719 case 3: /* DATETIME to TIME or DATE conversion */
720 return true;
721 case -1: /* OOM, or GEOMETRY type mismatch */
722 case 1: /* A number adjusted to the min/max value */
723 case 2: /* String truncation, or conversion problem */
724 break;
725 }
726
727 return false;
728}
729
730/** Check whether the table has the FTS_DOC_ID column
731@param[in] table InnoDB table with fulltext index
732@param[in] altered_table MySQL table with fulltext index
733@param[out] fts_doc_col_no The column number for Doc ID,
734 or ULINT_UNDEFINED if it is of wrong type
735@param[out] num_v Number of virtual column
736@param[in] check_only check only whether fts doc id exist.
737@return whether there exists an FTS_DOC_ID column */
738static
739bool
740innobase_fts_check_doc_id_col(
741 const dict_table_t* table,
742 const TABLE* altered_table,
743 ulint* fts_doc_col_no,
744 ulint* num_v,
745 bool check_only=false)
746{
747 *fts_doc_col_no = ULINT_UNDEFINED;
748
749 const uint n_cols = altered_table->s->fields;
750 ulint i;
751 int err = 0;
752 *num_v = 0;
753
754 for (i = 0; i < n_cols; i++) {
755 const Field* field = altered_table->field[i];
756
757 if (innobase_is_v_fld(field)) {
758 (*num_v)++;
759 }
760
761 if (my_strcasecmp(system_charset_info,
762 field->field_name.str, FTS_DOC_ID_COL_NAME)) {
763 continue;
764 }
765
766 if (strcmp(field->field_name.str, FTS_DOC_ID_COL_NAME)) {
767 err = ER_WRONG_COLUMN_NAME;
768 } else if (field->type() != MYSQL_TYPE_LONGLONG
769 || field->pack_length() != 8
770 || field->real_maybe_null()
771 || !(field->flags & UNSIGNED_FLAG)
772 || innobase_is_v_fld(field)) {
773 err = ER_INNODB_FT_WRONG_DOCID_COLUMN;
774 } else {
775 *fts_doc_col_no = i - *num_v;
776 }
777
778 if (err && !check_only) {
779 my_error(err, MYF(0), field->field_name.str);
780 }
781
782 return(true);
783 }
784
785 if (!table) {
786 return(false);
787 }
788
789 /* Not to count the virtual columns */
790 i -= *num_v;
791
792 for (; i + DATA_N_SYS_COLS < (uint) table->n_cols; i++) {
793 const char* name = dict_table_get_col_name(table, i);
794
795 if (strcmp(name, FTS_DOC_ID_COL_NAME) == 0) {
796#ifdef UNIV_DEBUG
797 const dict_col_t* col;
798
799 col = dict_table_get_nth_col(table, i);
800
801 /* Because the FTS_DOC_ID does not exist in
802 the MySQL data dictionary, this must be the
803 internally created FTS_DOC_ID column. */
804 ut_ad(col->mtype == DATA_INT);
805 ut_ad(col->len == 8);
806 ut_ad(col->prtype & DATA_NOT_NULL);
807 ut_ad(col->prtype & DATA_UNSIGNED);
808#endif /* UNIV_DEBUG */
809 *fts_doc_col_no = i;
810 return(true);
811 }
812 }
813
814 return(false);
815}
816
817/** Check if InnoDB supports a particular alter table in-place
818@param altered_table TABLE object for new version of table.
819@param ha_alter_info Structure describing changes to be done
820by ALTER TABLE and holding data used during in-place alter.
821
822@retval HA_ALTER_INPLACE_NOT_SUPPORTED Not supported
823@retval HA_ALTER_INPLACE_INSTANT
824MDL_EXCLUSIVE is needed for executing prepare_inplace_alter_table()
825and commit_inplace_alter_table(). inplace_alter_table() will not be called.
826@retval HA_ALTER_INPLACE_COPY_NO_LOCK
827MDL_EXCLUSIVE in prepare_inplace_alter_table(), which can be downgraded to
828LOCK=NONE for rebuilding the table in inplace_alter_table()
829@retval HA_ALTER_INPLACE_COPY_LOCK
830MDL_EXCLUSIVE in prepare_inplace_alter_table(), which can be downgraded to
831LOCK=SHARED for rebuilding the table in inplace_alter_table()
832@retval HA_ALTER_INPLACE_NOCOPY_NO_LOCK
833MDL_EXCLUSIVE in prepare_inplace_alter_table(), which can be downgraded to
834LOCK=NONE for inplace_alter_table() which will not rebuild the table
835@retval HA_ALTER_INPLACE_NOCOPY_LOCK
836MDL_EXCLUSIVE in prepare_inplace_alter_table(), which can be downgraded to
837LOCK=SHARED for inplace_alter_table() which will not rebuild the table
838*/
839
840enum_alter_inplace_result
841ha_innobase::check_if_supported_inplace_alter(
842 TABLE* altered_table,
843 Alter_inplace_info* ha_alter_info)
844{
845 DBUG_ENTER("check_if_supported_inplace_alter");
846
847 if ((table->versioned(VERS_TIMESTAMP)
848 || altered_table->versioned(VERS_TIMESTAMP))
849 && innobase_need_rebuild(ha_alter_info, table)) {
850 ha_alter_info->unsupported_reason =
851 "Not implemented for system-versioned tables";
852 DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
853 }
854
855 /* Before 10.2.2 information about virtual columns was not stored in
856 system tables. We need to do a full alter to rebuild proper 10.2.2+
857 metadata with the information about virtual columns */
858 if (table->s->mysql_version < 100202 && table->s->virtual_fields) {
859 DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
860 }
861
862 if (high_level_read_only) {
863 ha_alter_info->unsupported_reason =
864 my_get_err_msg(ER_READ_ONLY_MODE);
865
866 DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
867 }
868
869 if (altered_table->s->fields > REC_MAX_N_USER_FIELDS) {
870 /* Deny the inplace ALTER TABLE. MySQL will try to
871 re-create the table and ha_innobase::create() will
872 return an error too. This is how we effectively
873 deny adding too many columns to a table. */
874 ha_alter_info->unsupported_reason =
875 my_get_err_msg(ER_TOO_MANY_FIELDS);
876 DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
877 }
878
879 update_thd();
880
881 if (ha_alter_info->handler_flags
882 & ~(INNOBASE_INPLACE_IGNORE
883 | INNOBASE_ALTER_INSTANT
884 | INNOBASE_ALTER_NOREBUILD
885 | INNOBASE_ALTER_REBUILD)) {
886
887 if (ha_alter_info->handler_flags
888 & ALTER_STORED_COLUMN_TYPE) {
889 ha_alter_info->unsupported_reason = my_get_err_msg(
890 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_COLUMN_TYPE);
891 }
892
893 DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
894 }
895
896 /* Only support online add foreign key constraint when
897 check_foreigns is turned off */
898 if ((ha_alter_info->handler_flags & ALTER_ADD_FOREIGN_KEY)
899 && m_prebuilt->trx->check_foreigns) {
900 ha_alter_info->unsupported_reason = my_get_err_msg(
901 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FK_CHECK);
902 DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
903 }
904
905#if 0
906 if (altered_table->file->ht != ht) {
907 /* Non-native partitioning table engine. No longer supported,
908 due to implementation of native InnoDB partitioning. */
909 DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
910 }
911#endif
912
913 if (!(ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE)) {
914 DBUG_RETURN(HA_ALTER_INPLACE_INSTANT);
915 }
916
917 /* Only support NULL -> NOT NULL change if strict table sql_mode
918 is set. Fall back to COPY for conversion if not strict tables.
919 In-Place will fail with an error when trying to convert
920 NULL to a NOT NULL value. */
921 if ((ha_alter_info->handler_flags
922 & ALTER_COLUMN_NOT_NULLABLE)
923 && !thd_is_strict_mode(m_user_thd)) {
924 ha_alter_info->unsupported_reason = my_get_err_msg(
925 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOT_NULL);
926 DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
927 }
928
929 /* DROP PRIMARY KEY is only allowed in combination with ADD
930 PRIMARY KEY. */
931 if ((ha_alter_info->handler_flags
932 & (ALTER_ADD_PK_INDEX | ALTER_DROP_PK_INDEX))
933 == ALTER_DROP_PK_INDEX) {
934 ha_alter_info->unsupported_reason = my_get_err_msg(
935 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOPK);
936 DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
937 }
938
939 /* If a column change from NOT NULL to NULL,
940 and there's a implict pk on this column. the
941 table should be rebuild. The change should
942 only go through the "Copy" method. */
943 if ((ha_alter_info->handler_flags
944 & ALTER_COLUMN_NULLABLE)) {
945 const uint my_primary_key = altered_table->s->primary_key;
946
947 /* See if MYSQL table has no pk but we do. */
948 if (UNIV_UNLIKELY(my_primary_key >= MAX_KEY)
949 && !dict_index_is_auto_gen_clust(
950 dict_table_get_first_index(m_prebuilt->table))) {
951 ha_alter_info->unsupported_reason = my_get_err_msg(
952 ER_PRIMARY_CANT_HAVE_NULL);
953 DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
954 }
955 }
956
957 /*
958 InnoDB in different MariaDB versions was generating different mtype
959 codes for certain types. In some cases the signed/unsigned bit was
960 generated differently too.
961
962 Inplace ALTER would change the mtype/unsigned_flag (to what the
963 current code generates) without changing the underlying data
964 represenation, and it might result in data corruption.
965
966 Don't do inplace ALTER if mtype/unsigned_flag are wrong.
967 */
968 for (ulint i = 0, icol= 0; i < table->s->fields; i++) {
969 const Field* field = table->field[i];
970 const dict_col_t* col = dict_table_get_nth_col(
971 m_prebuilt->table, icol);
972 ulint unsigned_flag;
973
974 if (!field->stored_in_db()) {
975 continue;
976 }
977
978 icol++;
979
980 if (col->mtype != get_innobase_type_from_mysql_type(
981 &unsigned_flag, field)) {
982
983 DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
984 }
985
986 if ((col->prtype & DATA_UNSIGNED) != unsigned_flag) {
987
988 DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
989 }
990 }
991
992 ulint n_indexes = UT_LIST_GET_LEN((m_prebuilt->table)->indexes);
993
994 /* If InnoDB dictionary and MySQL frm file are not consistent
995 use "Copy" method. */
996 if (m_prebuilt->table->dict_frm_mismatch) {
997
998 ha_alter_info->unsupported_reason = my_get_err_msg(
999 ER_NO_SUCH_INDEX);
1000 ib_push_frm_error(m_user_thd, m_prebuilt->table, altered_table,
1001 n_indexes, true);
1002
1003 DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
1004 }
1005
1006 bool add_drop_v_cols = false;
1007
1008 /* If there is add or drop virtual columns, we will support operations
1009 with these 2 options alone with inplace interface for now */
1010
1011 if (ha_alter_info->handler_flags
1012 & (ALTER_ADD_VIRTUAL_COLUMN
1013 | ALTER_DROP_VIRTUAL_COLUMN
1014 | ALTER_VIRTUAL_COLUMN_ORDER)) {
1015 ulonglong flags = ha_alter_info->handler_flags;
1016
1017 /* TODO: uncomment the flags below, once we start to
1018 support them */
1019
1020 flags &= ~(ALTER_ADD_VIRTUAL_COLUMN
1021 | ALTER_DROP_VIRTUAL_COLUMN
1022 | ALTER_VIRTUAL_COLUMN_ORDER
1023 | ALTER_VIRTUAL_GCOL_EXPR
1024 | ALTER_COLUMN_VCOL
1025 /*
1026 | ALTER_ADD_STORED_BASE_COLUMN
1027 | ALTER_DROP_STORED_COLUMN
1028 | ALTER_STORED_COLUMN_ORDER
1029 | ALTER_ADD_UNIQUE_INDEX
1030 */
1031 | ALTER_ADD_NON_UNIQUE_NON_PRIM_INDEX
1032 | ALTER_DROP_NON_UNIQUE_NON_PRIM_INDEX);
1033
1034 if (flags != 0
1035 || IF_PARTITIONING((altered_table->s->partition_info_str
1036 && altered_table->s->partition_info_str_len), 0)
1037 || (!check_v_col_in_order(
1038 this->table, altered_table, ha_alter_info))) {
1039 ha_alter_info->unsupported_reason =
1040 MSG_UNSUPPORTED_ALTER_ONLINE_ON_VIRTUAL_COLUMN;
1041 DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
1042 }
1043
1044 add_drop_v_cols = true;
1045 }
1046
1047 /* We should be able to do the operation in-place.
1048 See if we can do it online (LOCK=NONE). */
1049 bool online = true;
1050
1051 List_iterator_fast<Create_field> cf_it(
1052 ha_alter_info->alter_info->create_list);
1053
1054 /* Fix the key parts. */
1055 for (KEY* new_key = ha_alter_info->key_info_buffer;
1056 new_key < ha_alter_info->key_info_buffer
1057 + ha_alter_info->key_count;
1058 new_key++) {
1059
1060 /* Do not support adding/droping a virtual column, while
1061 there is a table rebuild caused by adding a new FTS_DOC_ID */
1062 if ((new_key->flags & HA_FULLTEXT) && add_drop_v_cols
1063 && !DICT_TF2_FLAG_IS_SET(m_prebuilt->table,
1064 DICT_TF2_FTS_HAS_DOC_ID)) {
1065 ha_alter_info->unsupported_reason =
1066 MSG_UNSUPPORTED_ALTER_ONLINE_ON_VIRTUAL_COLUMN;
1067 DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
1068 }
1069
1070 for (KEY_PART_INFO* key_part = new_key->key_part;
1071 key_part < (new_key->key_part
1072 + new_key->user_defined_key_parts);
1073 key_part++) {
1074 const Create_field* new_field;
1075
1076 DBUG_ASSERT(key_part->fieldnr
1077 < altered_table->s->fields);
1078
1079 cf_it.rewind();
1080 for (uint fieldnr = 0; (new_field = cf_it++);
1081 fieldnr++) {
1082 if (fieldnr == key_part->fieldnr) {
1083 break;
1084 }
1085 }
1086
1087 DBUG_ASSERT(new_field);
1088
1089 key_part->field = altered_table->field[
1090 key_part->fieldnr];
1091
1092 /* In some special cases InnoDB emits "false"
1093 duplicate key errors with NULL key values. Let
1094 us play safe and ensure that we can correctly
1095 print key values even in such cases. */
1096 key_part->null_offset = key_part->field->null_offset();
1097 key_part->null_bit = key_part->field->null_bit;
1098
1099 if (new_field->field) {
1100 /* This is an existing column. */
1101 continue;
1102 }
1103
1104 /* This is an added column. */
1105 DBUG_ASSERT(ha_alter_info->handler_flags
1106 & ALTER_ADD_COLUMN);
1107
1108 /* We cannot replace a hidden FTS_DOC_ID
1109 with a user-visible FTS_DOC_ID. */
1110 if (m_prebuilt->table->fts
1111 && innobase_fulltext_exist(altered_table)
1112 && !my_strcasecmp(
1113 system_charset_info,
1114 key_part->field->field_name.str,
1115 FTS_DOC_ID_COL_NAME)) {
1116 ha_alter_info->unsupported_reason = my_get_err_msg(
1117 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_HIDDEN_FTS);
1118 DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
1119 }
1120
1121 DBUG_ASSERT((MTYP_TYPENR(key_part->field->unireg_check)
1122 == Field::NEXT_NUMBER)
1123 == !!(key_part->field->flags
1124 & AUTO_INCREMENT_FLAG));
1125
1126 if (key_part->field->flags & AUTO_INCREMENT_FLAG) {
1127 /* We cannot assign an AUTO_INCREMENT
1128 column values during online ALTER. */
1129 DBUG_ASSERT(key_part->field == altered_table
1130 -> found_next_number_field);
1131
1132 if (ha_alter_info->online) {
1133 ha_alter_info->unsupported_reason = my_get_err_msg(
1134 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_AUTOINC);
1135 }
1136
1137 online = false;
1138 }
1139
1140 if (innobase_is_v_fld(key_part->field)) {
1141 /* Do not support adding index on newly added
1142 virtual column, while there is also a drop
1143 virtual column in the same clause */
1144 if (ha_alter_info->handler_flags
1145 & ALTER_DROP_VIRTUAL_COLUMN) {
1146 ha_alter_info->unsupported_reason =
1147 MSG_UNSUPPORTED_ALTER_ONLINE_ON_VIRTUAL_COLUMN;
1148
1149 DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
1150 }
1151
1152 if (ha_alter_info->online
1153 && !ha_alter_info->unsupported_reason) {
1154 ha_alter_info->unsupported_reason =
1155 MSG_UNSUPPORTED_ALTER_ONLINE_ON_VIRTUAL_COLUMN;
1156 }
1157
1158 online = false;
1159 }
1160 }
1161 }
1162
1163 DBUG_ASSERT(!m_prebuilt->table->fts
1164 || (m_prebuilt->table->fts->doc_col <= table->s->fields));
1165
1166 DBUG_ASSERT(!m_prebuilt->table->fts
1167 || (m_prebuilt->table->fts->doc_col
1168 < dict_table_get_n_user_cols(m_prebuilt->table)));
1169
1170 if (m_prebuilt->table->fts && innobase_fulltext_exist(altered_table)) {
1171 /* FULLTEXT indexes are supposed to remain. */
1172 /* Disallow DROP INDEX FTS_DOC_ID_INDEX */
1173
1174 for (uint i = 0; i < ha_alter_info->index_drop_count; i++) {
1175 if (!my_strcasecmp(
1176 system_charset_info,
1177 ha_alter_info->index_drop_buffer[i]->name.str,
1178 FTS_DOC_ID_INDEX_NAME)) {
1179 ha_alter_info->unsupported_reason = my_get_err_msg(
1180 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_CHANGE_FTS);
1181 DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
1182 }
1183 }
1184
1185 /* InnoDB can have a hidden FTS_DOC_ID_INDEX on a
1186 visible FTS_DOC_ID column as well. Prevent dropping or
1187 renaming the FTS_DOC_ID. */
1188
1189 for (Field** fp = table->field; *fp; fp++) {
1190 if (!((*fp)->flags
1191 & (FIELD_IS_RENAMED | FIELD_IS_DROPPED))) {
1192 continue;
1193 }
1194
1195 if (!my_strcasecmp(
1196 system_charset_info,
1197 (*fp)->field_name.str,
1198 FTS_DOC_ID_COL_NAME)) {
1199 ha_alter_info->unsupported_reason = my_get_err_msg(
1200 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_CHANGE_FTS);
1201 DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
1202 }
1203 }
1204 }
1205
1206 m_prebuilt->trx->will_lock++;
1207
1208 /* When changing a NULL column to NOT NULL and specifying a
1209 DEFAULT value, ensure that the DEFAULT expression is a constant.
1210 Also, in ADD COLUMN, for now we only support a
1211 constant DEFAULT expression. */
1212 cf_it.rewind();
1213 Field **af = altered_table->field;
1214 bool add_column_not_last = false;
1215 uint n_stored_cols = 0, n_add_cols = 0;
1216
1217 while (Create_field* cf = cf_it++) {
1218 DBUG_ASSERT(cf->field
1219 || (ha_alter_info->handler_flags
1220 & ALTER_ADD_COLUMN));
1221
1222 if (const Field* f = cf->field) {
1223 /* This could be changing an existing column
1224 from NULL to NOT NULL. */
1225 switch ((*af)->type()) {
1226 case MYSQL_TYPE_TIMESTAMP:
1227 case MYSQL_TYPE_TIMESTAMP2:
1228 /* Inserting NULL into a TIMESTAMP column
1229 would cause the DEFAULT value to be
1230 replaced. Ensure that the DEFAULT
1231 expression is not changing during
1232 ALTER TABLE. */
1233 if (!f->real_maybe_null()
1234 || (*af)->real_maybe_null()) {
1235 /* The column was NOT NULL, or it
1236 will allow NULL after ALTER TABLE. */
1237 goto next_column;
1238 }
1239
1240 if (!(*af)->default_value
1241 && (*af)->is_real_null()) {
1242 /* No DEFAULT value is
1243 specified. We can report
1244 errors for any NULL values for
1245 the TIMESTAMP. */
1246
1247 goto next_column;
1248 }
1249 break;
1250 default:
1251 /* Changing from NULL to NOT NULL and
1252 set the default constant values. */
1253 if (f->real_maybe_null()
1254 && !(*af)->real_maybe_null()) {
1255
1256 if (is_non_const_value(*af)) {
1257 break;
1258 }
1259
1260 if (!set_default_value(*af)) {
1261 break;
1262 }
1263 }
1264
1265 /* For any other data type, NULL
1266 values are not converted.
1267 (An AUTO_INCREMENT attribute cannot
1268 be introduced to a column with
1269 ALGORITHM=INPLACE.) */
1270 ut_ad((MTYP_TYPENR((*af)->unireg_check)
1271 == Field::NEXT_NUMBER)
1272 == (MTYP_TYPENR(f->unireg_check)
1273 == Field::NEXT_NUMBER));
1274 goto next_column;
1275 }
1276
1277 ha_alter_info->unsupported_reason
1278 = my_get_err_msg(
1279 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOT_NULL);
1280 } else if (!is_non_const_value(*af)) {
1281
1282 n_add_cols++;
1283
1284 if (af < &altered_table->field[table_share->fields]) {
1285 add_column_not_last = true;
1286 }
1287
1288 if (set_default_value(*af)) {
1289 goto next_column;
1290 }
1291 }
1292
1293 DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
1294
1295next_column:
1296 n_stored_cols += (*af++)->stored_in_db();
1297 }
1298
1299 if (!add_column_not_last
1300 && uint(m_prebuilt->table->n_cols) - DATA_N_SYS_COLS + n_add_cols
1301 == n_stored_cols
1302 && m_prebuilt->table->supports_instant()
1303 && instant_alter_column_possible(ha_alter_info, table)) {
1304
1305 DBUG_RETURN(HA_ALTER_INPLACE_INSTANT);
1306 }
1307
1308 if (!(ha_alter_info->handler_flags & ~(INNOBASE_ALTER_INSTANT
1309 | INNOBASE_INPLACE_IGNORE))) {
1310 DBUG_RETURN(HA_ALTER_INPLACE_INSTANT);
1311 }
1312
1313 bool fts_need_rebuild = false;
1314
1315 if (!online) {
1316 /* We already determined that only a non-locking
1317 operation is possible. */
1318 } else if (((ha_alter_info->handler_flags
1319 & ALTER_ADD_PK_INDEX)
1320 || innobase_need_rebuild(ha_alter_info, table))
1321 && (innobase_fulltext_exist(altered_table)
1322 || innobase_spatial_exist(altered_table)
1323 || innobase_indexed_virtual_exist(altered_table))) {
1324 /* Refuse to rebuild the table online, if
1325 FULLTEXT OR SPATIAL indexes are to survive the rebuild. */
1326 online = false;
1327 /* If the table already contains fulltext indexes,
1328 refuse to rebuild the table natively altogether. */
1329 if (m_prebuilt->table->fts) {
1330cannot_create_many_fulltext_index:
1331 ha_alter_info->unsupported_reason =
1332 my_get_err_msg(ER_INNODB_FT_LIMIT);
1333 DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
1334 }
1335
1336 if (ha_alter_info->online
1337 && !ha_alter_info->unsupported_reason) {
1338
1339 if (innobase_spatial_exist(altered_table)) {
1340 ha_alter_info->unsupported_reason = my_get_err_msg(
1341 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_GIS);
1342 } else if (!innobase_fulltext_exist(altered_table)) {
1343 /* MDEV-14341 FIXME: Remove this limitation. */
1344 ha_alter_info->unsupported_reason =
1345 "online rebuild with indexed virtual columns";
1346 } else {
1347 ha_alter_info->unsupported_reason = my_get_err_msg(
1348 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FTS);
1349 }
1350 }
1351
1352 }
1353
1354 if (ha_alter_info->handler_flags
1355 & ALTER_ADD_NON_UNIQUE_NON_PRIM_INDEX) {
1356 /* ADD FULLTEXT|SPATIAL INDEX requires a lock.
1357
1358 We could do ADD FULLTEXT INDEX without a lock if the
1359 table already contains an FTS_DOC_ID column, but in
1360 that case we would have to apply the modification log
1361 to the full-text indexes.
1362
1363 We could also do ADD SPATIAL INDEX by implementing
1364 row_log_apply() for it. */
1365 bool add_fulltext = false;
1366
1367 for (uint i = 0; i < ha_alter_info->index_add_count; i++) {
1368 const KEY* key =
1369 &ha_alter_info->key_info_buffer[
1370 ha_alter_info->index_add_buffer[i]];
1371 if (key->flags & HA_FULLTEXT) {
1372 DBUG_ASSERT(!(key->flags & HA_KEYFLAG_MASK
1373 & ~(HA_FULLTEXT
1374 | HA_PACK_KEY
1375 | HA_GENERATED_KEY
1376 | HA_BINARY_PACK_KEY)));
1377 if (add_fulltext) {
1378 goto cannot_create_many_fulltext_index;
1379 }
1380
1381 add_fulltext = true;
1382 if (ha_alter_info->online
1383 && !ha_alter_info->unsupported_reason) {
1384 ha_alter_info->unsupported_reason = my_get_err_msg(
1385 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FTS);
1386 }
1387
1388 online = false;
1389
1390 /* Full text search index exists, check
1391 whether the table already has DOC ID column.
1392 If not, InnoDB have to rebuild the table to
1393 add a Doc ID hidden column and change
1394 primary index. */
1395 ulint fts_doc_col_no;
1396 ulint num_v = 0;
1397
1398 fts_need_rebuild =
1399 !innobase_fts_check_doc_id_col(
1400 m_prebuilt->table,
1401 altered_table,
1402 &fts_doc_col_no, &num_v, true);
1403 }
1404
1405 if (online && (key->flags & HA_SPATIAL)) {
1406
1407 if (ha_alter_info->online) {
1408 ha_alter_info->unsupported_reason = my_get_err_msg(
1409 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_GIS);
1410 }
1411
1412 online = false;
1413 }
1414 }
1415 }
1416
1417 // FIXME: implement Online DDL for system-versioned tables
1418 if ((table->versioned(VERS_TRX_ID)
1419 || altered_table->versioned(VERS_TRX_ID))
1420 && innobase_need_rebuild(ha_alter_info, table)) {
1421
1422 if (ha_alter_info->online) {
1423 ha_alter_info->unsupported_reason =
1424 "Not implemented for system-versioned tables";
1425 }
1426
1427 online = false;
1428 }
1429
1430 if (fts_need_rebuild || innobase_need_rebuild(ha_alter_info, table)) {
1431 DBUG_RETURN(online
1432 ? HA_ALTER_INPLACE_COPY_NO_LOCK
1433 : HA_ALTER_INPLACE_COPY_LOCK);
1434 }
1435
1436 if (ha_alter_info->unsupported_reason) {
1437 } else if (ha_alter_info->handler_flags & INNOBASE_ONLINE_CREATE) {
1438 ha_alter_info->unsupported_reason = "ADD INDEX";
1439 } else {
1440 ha_alter_info->unsupported_reason = "DROP INDEX";
1441 }
1442
1443 DBUG_RETURN(online
1444 ? HA_ALTER_INPLACE_NOCOPY_NO_LOCK
1445 : HA_ALTER_INPLACE_NOCOPY_LOCK);
1446}
1447
1448/*************************************************************//**
1449Initialize the dict_foreign_t structure with supplied info
1450@return true if added, false if duplicate foreign->id */
1451static MY_ATTRIBUTE((nonnull(1,3,5,7)))
1452bool
1453innobase_init_foreign(
1454/*==================*/
1455 dict_foreign_t* foreign, /*!< in/out: structure to
1456 initialize */
1457 const char* constraint_name, /*!< in/out: constraint name if
1458 exists */
1459 dict_table_t* table, /*!< in: foreign table */
1460 dict_index_t* index, /*!< in: foreign key index */
1461 const char** column_names, /*!< in: foreign key column
1462 names */
1463 ulint num_field, /*!< in: number of columns */
1464 const char* referenced_table_name, /*!< in: referenced table
1465 name */
1466 dict_table_t* referenced_table, /*!< in: referenced table */
1467 dict_index_t* referenced_index, /*!< in: referenced index */
1468 const char** referenced_column_names,/*!< in: referenced column
1469 names */
1470 ulint referenced_num_field) /*!< in: number of referenced
1471 columns */
1472{
1473 ut_ad(mutex_own(&dict_sys->mutex));
1474
1475 if (constraint_name) {
1476 ulint db_len;
1477
1478 /* Catenate 'databasename/' to the constraint name specified
1479 by the user: we conceive the constraint as belonging to the
1480 same MySQL 'database' as the table itself. We store the name
1481 to foreign->id. */
1482
1483 db_len = dict_get_db_name_len(table->name.m_name);
1484
1485 foreign->id = static_cast<char*>(mem_heap_alloc(
1486 foreign->heap, db_len + strlen(constraint_name) + 2));
1487
1488 ut_memcpy(foreign->id, table->name.m_name, db_len);
1489 foreign->id[db_len] = '/';
1490 strcpy(foreign->id + db_len + 1, constraint_name);
1491
1492 /* Check if any existing foreign key has the same id,
1493 this is needed only if user supplies the constraint name */
1494
1495 if (table->foreign_set.find(foreign)
1496 != table->foreign_set.end()) {
1497 return(false);
1498 }
1499 }
1500
1501 foreign->foreign_table = table;
1502 foreign->foreign_table_name = mem_heap_strdup(
1503 foreign->heap, table->name.m_name);
1504 dict_mem_foreign_table_name_lookup_set(foreign, TRUE);
1505
1506 foreign->foreign_index = index;
1507 foreign->n_fields = (unsigned int) num_field;
1508
1509 foreign->foreign_col_names = static_cast<const char**>(
1510 mem_heap_alloc(foreign->heap, num_field * sizeof(void*)));
1511
1512 for (ulint i = 0; i < foreign->n_fields; i++) {
1513 foreign->foreign_col_names[i] = mem_heap_strdup(
1514 foreign->heap, column_names[i]);
1515 }
1516
1517 foreign->referenced_index = referenced_index;
1518 foreign->referenced_table = referenced_table;
1519
1520 foreign->referenced_table_name = mem_heap_strdup(
1521 foreign->heap, referenced_table_name);
1522 dict_mem_referenced_table_name_lookup_set(foreign, TRUE);
1523
1524 foreign->referenced_col_names = static_cast<const char**>(
1525 mem_heap_alloc(foreign->heap,
1526 referenced_num_field * sizeof(void*)));
1527
1528 for (ulint i = 0; i < foreign->n_fields; i++) {
1529 foreign->referenced_col_names[i]
1530 = mem_heap_strdup(foreign->heap,
1531 referenced_column_names[i]);
1532 }
1533
1534 return(true);
1535}
1536
1537/*************************************************************//**
1538Check whether the foreign key options is legit
1539@return true if it is */
1540static MY_ATTRIBUTE((nonnull, warn_unused_result))
1541bool
1542innobase_check_fk_option(
1543/*=====================*/
1544 const dict_foreign_t* foreign) /*!< in: foreign key */
1545{
1546 if (!foreign->foreign_index) {
1547 return(true);
1548 }
1549
1550 if (foreign->type & (DICT_FOREIGN_ON_UPDATE_SET_NULL
1551 | DICT_FOREIGN_ON_DELETE_SET_NULL)) {
1552
1553 for (ulint j = 0; j < foreign->n_fields; j++) {
1554 if ((dict_index_get_nth_col(
1555 foreign->foreign_index, j)->prtype)
1556 & DATA_NOT_NULL) {
1557
1558 /* It is not sensible to define
1559 SET NULL if the column is not
1560 allowed to be NULL! */
1561 return(false);
1562 }
1563 }
1564 }
1565
1566 return(true);
1567}
1568
1569/*************************************************************//**
1570Set foreign key options
1571@return true if successfully set */
1572static MY_ATTRIBUTE((nonnull, warn_unused_result))
1573bool
1574innobase_set_foreign_key_option(
1575/*============================*/
1576 dict_foreign_t* foreign, /*!< in:InnoDB Foreign key */
1577 Foreign_key* fk_key) /*!< in: Foreign key info from
1578 MySQL */
1579{
1580 ut_ad(!foreign->type);
1581
1582 switch (fk_key->delete_opt) {
1583 case FK_OPTION_NO_ACTION:
1584 case FK_OPTION_RESTRICT:
1585 case FK_OPTION_SET_DEFAULT:
1586 foreign->type = DICT_FOREIGN_ON_DELETE_NO_ACTION;
1587 break;
1588 case FK_OPTION_CASCADE:
1589 foreign->type = DICT_FOREIGN_ON_DELETE_CASCADE;
1590 break;
1591 case FK_OPTION_SET_NULL:
1592 foreign->type = DICT_FOREIGN_ON_DELETE_SET_NULL;
1593 break;
1594 case FK_OPTION_UNDEF:
1595 break;
1596 }
1597
1598 switch (fk_key->update_opt) {
1599 case FK_OPTION_NO_ACTION:
1600 case FK_OPTION_RESTRICT:
1601 case FK_OPTION_SET_DEFAULT:
1602 foreign->type |= DICT_FOREIGN_ON_UPDATE_NO_ACTION;
1603 break;
1604 case FK_OPTION_CASCADE:
1605 foreign->type |= DICT_FOREIGN_ON_UPDATE_CASCADE;
1606 break;
1607 case FK_OPTION_SET_NULL:
1608 foreign->type |= DICT_FOREIGN_ON_UPDATE_SET_NULL;
1609 break;
1610 case FK_OPTION_UNDEF:
1611 break;
1612 }
1613
1614 return(innobase_check_fk_option(foreign));
1615}
1616
1617/*******************************************************************//**
1618Check if a foreign key constraint can make use of an index
1619that is being created.
1620@return useable index, or NULL if none found */
1621static MY_ATTRIBUTE((nonnull, warn_unused_result))
1622const KEY*
1623innobase_find_equiv_index(
1624/*======================*/
1625 const char*const* col_names,
1626 /*!< in: column names */
1627 uint n_cols, /*!< in: number of columns */
1628 const KEY* keys, /*!< in: index information */
1629 const uint* add, /*!< in: indexes being created */
1630 uint n_add) /*!< in: number of indexes to create */
1631{
1632 for (uint i = 0; i < n_add; i++) {
1633 const KEY* key = &keys[add[i]];
1634
1635 if (key->user_defined_key_parts < n_cols
1636 || key->flags & HA_SPATIAL) {
1637no_match:
1638 continue;
1639 }
1640
1641 for (uint j = 0; j < n_cols; j++) {
1642 const KEY_PART_INFO& key_part = key->key_part[j];
1643 uint32 col_len
1644 = key_part.field->pack_length();
1645
1646 /* Any index on virtual columns cannot be used
1647 for reference constaint */
1648 if (innobase_is_v_fld(key_part.field)) {
1649 goto no_match;
1650 }
1651
1652 /* The MySQL pack length contains 1 or 2 bytes
1653 length field for a true VARCHAR. */
1654
1655 if (key_part.field->type() == MYSQL_TYPE_VARCHAR) {
1656 col_len -= static_cast<const Field_varstring*>(
1657 key_part.field)->length_bytes;
1658 }
1659
1660 if (key_part.length < col_len) {
1661
1662 /* Column prefix indexes cannot be
1663 used for FOREIGN KEY constraints. */
1664 goto no_match;
1665 }
1666
1667 if (innobase_strcasecmp(col_names[j],
1668 key_part.field->field_name.str)) {
1669 /* Name mismatch */
1670 goto no_match;
1671 }
1672 }
1673
1674 return(key);
1675 }
1676
1677 return(NULL);
1678}
1679
1680/*************************************************************//**
1681Find an index whose first fields are the columns in the array
1682in the same order and is not marked for deletion
1683@return matching index, NULL if not found */
1684static MY_ATTRIBUTE((nonnull(1,5), warn_unused_result))
1685dict_index_t*
1686innobase_find_fk_index(
1687/*===================*/
1688 dict_table_t* table, /*!< in: table */
1689 const char** col_names,
1690 /*!< in: column names, or NULL
1691 to use table->col_names */
1692 dict_index_t** drop_index,
1693 /*!< in: indexes to be dropped */
1694 ulint n_drop_index,
1695 /*!< in: size of drop_index[] */
1696 const char** columns,/*!< in: array of column names */
1697 ulint n_cols) /*!< in: number of columns */
1698{
1699 dict_index_t* index;
1700
1701 index = dict_table_get_first_index(table);
1702
1703 while (index != NULL) {
1704 if (!(index->type & DICT_FTS)
1705 && dict_foreign_qualify_index(
1706 table, col_names, columns, n_cols,
1707 index, NULL, true, 0,
1708 NULL, NULL, NULL)) {
1709 for (ulint i = 0; i < n_drop_index; i++) {
1710 if (index == drop_index[i]) {
1711 /* Skip to-be-dropped indexes. */
1712 goto next_rec;
1713 }
1714 }
1715
1716 return(index);
1717 }
1718
1719next_rec:
1720 index = dict_table_get_next_index(index);
1721 }
1722
1723 return(NULL);
1724}
1725
1726/** Check whether given column is a base of stored column.
1727@param[in] col_name column name
1728@param[in] table table
1729@param[in] s_cols list of stored columns
1730@return true if the given column is a base of stored column,else false. */
1731static
1732bool
1733innobase_col_check_fk(
1734 const char* col_name,
1735 const dict_table_t* table,
1736 dict_s_col_list* s_cols)
1737{
1738 dict_s_col_list::const_iterator it;
1739
1740 for (it = s_cols->begin();
1741 it != s_cols->end(); ++it) {
1742 dict_s_col_t s_col = *it;
1743
1744 for (ulint j = 0; j < s_col.num_base; j++) {
1745 if (strcmp(col_name, dict_table_get_col_name(
1746 table,
1747 s_col.base_col[j]->ind)) == 0) {
1748 return(true);
1749 }
1750 }
1751 }
1752
1753 return(false);
1754}
1755
1756/** Check whether the foreign key constraint is on base of any stored columns.
1757@param[in] foreign Foriegn key constraing information
1758@param[in] table table to which the foreign key objects
1759to be added
1760@param[in] s_cols list of stored column information in the table.
1761@return true if yes, otherwise false. */
1762static
1763bool
1764innobase_check_fk_stored(
1765 const dict_foreign_t* foreign,
1766 const dict_table_t* table,
1767 dict_s_col_list* s_cols)
1768{
1769 ulint type = foreign->type;
1770
1771 type &= ~(DICT_FOREIGN_ON_DELETE_NO_ACTION
1772 | DICT_FOREIGN_ON_UPDATE_NO_ACTION);
1773
1774 if (type == 0 || s_cols == NULL) {
1775 return(false);
1776 }
1777
1778 for (ulint i = 0; i < foreign->n_fields; i++) {
1779 if (innobase_col_check_fk(
1780 foreign->foreign_col_names[i], table, s_cols)) {
1781 return(true);
1782 }
1783 }
1784
1785 return(false);
1786}
1787
1788/** Create InnoDB foreign key structure from MySQL alter_info
1789@param[in] ha_alter_info alter table info
1790@param[in] table_share TABLE_SHARE
1791@param[in] table table object
1792@param[in] col_names column names, or NULL to use
1793table->col_names
1794@param[in] drop_index indexes to be dropped
1795@param[in] n_drop_index size of drop_index
1796@param[out] add_fk foreign constraint added
1797@param[out] n_add_fk number of foreign constraints
1798added
1799@param[in] trx user transaction
1800@param[in] s_cols list of stored column information
1801@retval true if successful
1802@retval false on error (will call my_error()) */
1803static MY_ATTRIBUTE((nonnull(1,2,3,7,8), warn_unused_result))
1804bool
1805innobase_get_foreign_key_info(
1806 Alter_inplace_info*
1807 ha_alter_info,
1808 const TABLE_SHARE*
1809 table_share,
1810 dict_table_t* table,
1811 const char** col_names,
1812 dict_index_t** drop_index,
1813 ulint n_drop_index,
1814 dict_foreign_t**add_fk,
1815 ulint* n_add_fk,
1816 const trx_t* trx,
1817 dict_s_col_list*s_cols)
1818{
1819 Key* key;
1820 Foreign_key* fk_key;
1821 dict_table_t* referenced_table = NULL;
1822 char* referenced_table_name = NULL;
1823 ulint num_fk = 0;
1824 Alter_info* alter_info = ha_alter_info->alter_info;
1825
1826 DBUG_ENTER("innobase_get_foreign_key_info");
1827
1828 *n_add_fk = 0;
1829
1830 List_iterator<Key> key_iterator(alter_info->key_list);
1831
1832 while ((key=key_iterator++)) {
1833 if (key->type != Key::FOREIGN_KEY) {
1834 continue;
1835 }
1836
1837 const char* column_names[MAX_NUM_FK_COLUMNS];
1838 dict_index_t* index = NULL;
1839 const char* referenced_column_names[MAX_NUM_FK_COLUMNS];
1840 dict_index_t* referenced_index = NULL;
1841 ulint num_col = 0;
1842 ulint referenced_num_col = 0;
1843 bool correct_option;
1844 char* db_namep = NULL;
1845 char* tbl_namep = NULL;
1846 ulint db_name_len = 0;
1847 ulint tbl_name_len = 0;
1848 char db_name[MAX_DATABASE_NAME_LEN];
1849 char tbl_name[MAX_TABLE_NAME_LEN];
1850
1851 fk_key = static_cast<Foreign_key*>(key);
1852
1853 if (fk_key->columns.elements > 0) {
1854 ulint i = 0;
1855 Key_part_spec* column;
1856 List_iterator<Key_part_spec> key_part_iterator(
1857 fk_key->columns);
1858
1859 /* Get all the foreign key column info for the
1860 current table */
1861 while ((column = key_part_iterator++)) {
1862 column_names[i] = column->field_name.str;
1863 ut_ad(i < MAX_NUM_FK_COLUMNS);
1864 i++;
1865 }
1866
1867 index = innobase_find_fk_index(
1868 table, col_names,
1869 drop_index, n_drop_index,
1870 column_names, i);
1871
1872 /* MySQL would add a index in the creation
1873 list if no such index for foreign table,
1874 so we have to use DBUG_EXECUTE_IF to simulate
1875 the scenario */
1876 DBUG_EXECUTE_IF("innodb_test_no_foreign_idx",
1877 index = NULL;);
1878
1879 /* Check whether there exist such
1880 index in the the index create clause */
1881 if (!index && !innobase_find_equiv_index(
1882 column_names, static_cast<uint>(i),
1883 ha_alter_info->key_info_buffer,
1884 ha_alter_info->index_add_buffer,
1885 ha_alter_info->index_add_count)) {
1886 my_error(
1887 ER_FK_NO_INDEX_CHILD,
1888 MYF(0),
1889 fk_key->name.str
1890 ? fk_key->name.str : "",
1891 table_share->table_name.str);
1892 goto err_exit;
1893 }
1894
1895 num_col = i;
1896 }
1897
1898 add_fk[num_fk] = dict_mem_foreign_create();
1899
1900#ifndef _WIN32
1901 if (fk_key->ref_db.str) {
1902 tablename_to_filename(fk_key->ref_db.str, db_name,
1903 MAX_DATABASE_NAME_LEN);
1904 db_namep = db_name;
1905 db_name_len = strlen(db_name);
1906 }
1907 if (fk_key->ref_table.str) {
1908 tablename_to_filename(fk_key->ref_table.str, tbl_name,
1909 MAX_TABLE_NAME_LEN);
1910 tbl_namep = tbl_name;
1911 tbl_name_len = strlen(tbl_name);
1912 }
1913#else
1914 ut_ad(fk_key->ref_table.str);
1915 tablename_to_filename(fk_key->ref_table.str, tbl_name,
1916 MAX_TABLE_NAME_LEN);
1917 innobase_casedn_str(tbl_name);
1918 tbl_name_len = strlen(tbl_name);
1919 tbl_namep = &tbl_name[0];
1920
1921 if (fk_key->ref_db.str != NULL) {
1922 tablename_to_filename(fk_key->ref_db.str, db_name,
1923 MAX_DATABASE_NAME_LEN);
1924 innobase_casedn_str(db_name);
1925 db_name_len = strlen(db_name);
1926 db_namep = &db_name[0];
1927 }
1928#endif
1929 mutex_enter(&dict_sys->mutex);
1930
1931 referenced_table_name = dict_get_referenced_table(
1932 table->name.m_name,
1933 db_namep,
1934 db_name_len,
1935 tbl_namep,
1936 tbl_name_len,
1937 &referenced_table,
1938 add_fk[num_fk]->heap);
1939
1940 /* Test the case when referenced_table failed to
1941 open, if trx->check_foreigns is not set, we should
1942 still be able to add the foreign key */
1943 DBUG_EXECUTE_IF("innodb_test_open_ref_fail",
1944 referenced_table = NULL;);
1945
1946 if (!referenced_table && trx->check_foreigns) {
1947 mutex_exit(&dict_sys->mutex);
1948 my_error(ER_FK_CANNOT_OPEN_PARENT,
1949 MYF(0), tbl_namep);
1950
1951 goto err_exit;
1952 }
1953
1954 if (fk_key->ref_columns.elements > 0) {
1955 ulint i = 0;
1956 Key_part_spec* column;
1957 List_iterator<Key_part_spec> key_part_iterator(
1958 fk_key->ref_columns);
1959
1960 while ((column = key_part_iterator++)) {
1961 referenced_column_names[i] =
1962 column->field_name.str;
1963 ut_ad(i < MAX_NUM_FK_COLUMNS);
1964 i++;
1965 }
1966
1967 if (referenced_table) {
1968 referenced_index =
1969 dict_foreign_find_index(
1970 referenced_table, 0,
1971 referenced_column_names,
1972 i, index,
1973 TRUE, FALSE,
1974 NULL, NULL, NULL);
1975
1976 DBUG_EXECUTE_IF(
1977 "innodb_test_no_reference_idx",
1978 referenced_index = NULL;);
1979
1980 /* Check whether there exist such
1981 index in the the index create clause */
1982 if (!referenced_index) {
1983 mutex_exit(&dict_sys->mutex);
1984 my_error(ER_FK_NO_INDEX_PARENT, MYF(0),
1985 fk_key->name.str
1986 ? fk_key->name.str : "",
1987 tbl_namep);
1988 goto err_exit;
1989 }
1990 } else {
1991 ut_a(!trx->check_foreigns);
1992 }
1993
1994 referenced_num_col = i;
1995 } else {
1996 /* Not possible to add a foreign key without a
1997 referenced column */
1998 mutex_exit(&dict_sys->mutex);
1999 my_error(ER_CANNOT_ADD_FOREIGN, MYF(0), tbl_namep);
2000 goto err_exit;
2001 }
2002
2003 if (!innobase_init_foreign(
2004 add_fk[num_fk], fk_key->name.str,
2005 table, index, column_names,
2006 num_col, referenced_table_name,
2007 referenced_table, referenced_index,
2008 referenced_column_names, referenced_num_col)) {
2009 mutex_exit(&dict_sys->mutex);
2010 my_error(
2011 ER_DUP_CONSTRAINT_NAME,
2012 MYF(0),
2013 "FOREIGN KEY", add_fk[num_fk]->id);
2014 goto err_exit;
2015 }
2016
2017 mutex_exit(&dict_sys->mutex);
2018
2019 correct_option = innobase_set_foreign_key_option(
2020 add_fk[num_fk], fk_key);
2021
2022 DBUG_EXECUTE_IF("innodb_test_wrong_fk_option",
2023 correct_option = false;);
2024
2025 if (!correct_option) {
2026 my_error(ER_FK_INCORRECT_OPTION,
2027 MYF(0),
2028 table_share->table_name.str,
2029 add_fk[num_fk]->id);
2030 goto err_exit;
2031 }
2032
2033 if (innobase_check_fk_stored(
2034 add_fk[num_fk], table, s_cols)) {
2035 my_printf_error(
2036 HA_ERR_UNSUPPORTED,
2037 "Cannot add foreign key on the base column "
2038 "of stored column", MYF(0));
2039 goto err_exit;
2040 }
2041
2042 num_fk++;
2043 }
2044
2045 *n_add_fk = num_fk;
2046
2047 DBUG_RETURN(true);
2048err_exit:
2049 for (ulint i = 0; i <= num_fk; i++) {
2050 if (add_fk[i]) {
2051 dict_foreign_free(add_fk[i]);
2052 }
2053 }
2054
2055 DBUG_RETURN(false);
2056}
2057
2058/*************************************************************//**
2059Copies an InnoDB column to a MySQL field. This function is
2060adapted from row_sel_field_store_in_mysql_format(). */
2061static
2062void
2063innobase_col_to_mysql(
2064/*==================*/
2065 const dict_col_t* col, /*!< in: InnoDB column */
2066 const uchar* data, /*!< in: InnoDB column data */
2067 ulint len, /*!< in: length of data, in bytes */
2068 Field* field) /*!< in/out: MySQL field */
2069{
2070 uchar* ptr;
2071 uchar* dest = field->ptr;
2072 ulint flen = field->pack_length();
2073
2074 switch (col->mtype) {
2075 case DATA_INT:
2076 ut_ad(len == flen);
2077
2078 /* Convert integer data from Innobase to little-endian
2079 format, sign bit restored to normal */
2080
2081 for (ptr = dest + len; ptr != dest; ) {
2082 *--ptr = *data++;
2083 }
2084
2085 if (!(col->prtype & DATA_UNSIGNED)) {
2086 ((byte*) dest)[len - 1] ^= 0x80;
2087 }
2088
2089 break;
2090
2091 case DATA_VARCHAR:
2092 case DATA_VARMYSQL:
2093 case DATA_BINARY:
2094 field->reset();
2095
2096 if (field->type() == MYSQL_TYPE_VARCHAR) {
2097 /* This is a >= 5.0.3 type true VARCHAR. Store the
2098 length of the data to the first byte or the first
2099 two bytes of dest. */
2100
2101 dest = row_mysql_store_true_var_len(
2102 dest, len, flen - field->key_length());
2103 }
2104
2105 /* Copy the actual data */
2106 memcpy(dest, data, len);
2107 break;
2108
2109 case DATA_GEOMETRY:
2110 case DATA_BLOB:
2111 /* Skip MySQL BLOBs when reporting an erroneous row
2112 during index creation or table rebuild. */
2113 field->set_null();
2114 break;
2115
2116#ifdef UNIV_DEBUG
2117 case DATA_MYSQL:
2118 ut_ad(flen >= len);
2119 ut_ad(col->mbmaxlen >= col->mbminlen);
2120 memcpy(dest, data, len);
2121 break;
2122
2123 default:
2124 case DATA_SYS_CHILD:
2125 case DATA_SYS:
2126 /* These column types should never be shipped to MySQL. */
2127 ut_ad(0);
2128
2129 case DATA_FLOAT:
2130 case DATA_DOUBLE:
2131 case DATA_DECIMAL:
2132 /* Above are the valid column types for MySQL data. */
2133 ut_ad(flen == len);
2134 /* fall through */
2135 case DATA_FIXBINARY:
2136 case DATA_CHAR:
2137 /* We may have flen > len when there is a shorter
2138 prefix on the CHAR and BINARY column. */
2139 ut_ad(flen >= len);
2140#else /* UNIV_DEBUG */
2141 default:
2142#endif /* UNIV_DEBUG */
2143 memcpy(dest, data, len);
2144 }
2145}
2146
2147/*************************************************************//**
2148Copies an InnoDB record to table->record[0]. */
2149void
2150innobase_rec_to_mysql(
2151/*==================*/
2152 struct TABLE* table, /*!< in/out: MySQL table */
2153 const rec_t* rec, /*!< in: record */
2154 const dict_index_t* index, /*!< in: index */
2155 const ulint* offsets)/*!< in: rec_get_offsets(
2156 rec, index, ...) */
2157{
2158 uint n_fields = table->s->fields;
2159
2160 ut_ad(n_fields == dict_table_get_n_user_cols(index->table)
2161 - !!(DICT_TF2_FLAG_IS_SET(index->table,
2162 DICT_TF2_FTS_HAS_DOC_ID)));
2163
2164 for (uint i = 0; i < n_fields; i++) {
2165 Field* field = table->field[i];
2166 ulint ipos;
2167 ulint ilen;
2168 const uchar* ifield;
2169 ulint prefix_col;
2170
2171 field->reset();
2172
2173 ipos = dict_index_get_nth_col_or_prefix_pos(
2174 index, i, true, false, &prefix_col);
2175
2176 if (ipos == ULINT_UNDEFINED
2177 || rec_offs_nth_extern(offsets, ipos)) {
2178null_field:
2179 field->set_null();
2180 continue;
2181 }
2182
2183 ifield = rec_get_nth_cfield(rec, index, offsets, ipos, &ilen);
2184
2185 /* Assign the NULL flag */
2186 if (ilen == UNIV_SQL_NULL) {
2187 ut_ad(field->real_maybe_null());
2188 goto null_field;
2189 }
2190
2191 field->set_notnull();
2192
2193 innobase_col_to_mysql(
2194 dict_field_get_col(
2195 dict_index_get_nth_field(index, ipos)),
2196 ifield, ilen, field);
2197 }
2198}
2199
2200/*************************************************************//**
2201Copies an InnoDB index entry to table->record[0].
2202This is used in preparation for print_keydup_error() from
2203inline add index */
2204void
2205innobase_fields_to_mysql(
2206/*=====================*/
2207 struct TABLE* table, /*!< in/out: MySQL table */
2208 const dict_index_t* index, /*!< in: InnoDB index */
2209 const dfield_t* fields) /*!< in: InnoDB index fields */
2210{
2211 uint n_fields = table->s->fields;
2212 ulint num_v = 0;
2213
2214 ut_ad(n_fields == dict_table_get_n_user_cols(index->table)
2215 + dict_table_get_n_v_cols(index->table)
2216 - !!(DICT_TF2_FLAG_IS_SET(index->table,
2217 DICT_TF2_FTS_HAS_DOC_ID)));
2218
2219 for (uint i = 0; i < n_fields; i++) {
2220 Field* field = table->field[i];
2221 ulint ipos;
2222 ulint col_n;
2223 ulint prefix_col;
2224
2225 field->reset();
2226
2227 if (innobase_is_v_fld(field)) {
2228 col_n = num_v;
2229 num_v++;
2230 } else {
2231 col_n = i - num_v;
2232 }
2233
2234 ipos = dict_index_get_nth_col_or_prefix_pos(
2235 index, col_n, true, innobase_is_v_fld(field),
2236 &prefix_col);
2237
2238 if (ipos == ULINT_UNDEFINED
2239 || dfield_is_ext(&fields[ipos])
2240 || dfield_is_null(&fields[ipos])) {
2241
2242 field->set_null();
2243 } else {
2244 field->set_notnull();
2245
2246 const dfield_t* df = &fields[ipos];
2247
2248 innobase_col_to_mysql(
2249 dict_field_get_col(
2250 dict_index_get_nth_field(index, ipos)),
2251 static_cast<const uchar*>(dfield_get_data(df)),
2252 dfield_get_len(df), field);
2253 }
2254 }
2255}
2256
2257/*************************************************************//**
2258Copies an InnoDB row to table->record[0].
2259This is used in preparation for print_keydup_error() from
2260row_log_table_apply() */
2261void
2262innobase_row_to_mysql(
2263/*==================*/
2264 struct TABLE* table, /*!< in/out: MySQL table */
2265 const dict_table_t* itab, /*!< in: InnoDB table */
2266 const dtuple_t* row) /*!< in: InnoDB row */
2267{
2268 uint n_fields = table->s->fields;
2269 ulint num_v = 0;
2270
2271 /* The InnoDB row may contain an extra FTS_DOC_ID column at the end. */
2272 ut_ad(row->n_fields == dict_table_get_n_cols(itab));
2273 ut_ad(n_fields == row->n_fields - DATA_N_SYS_COLS
2274 + dict_table_get_n_v_cols(itab)
2275 - !!(DICT_TF2_FLAG_IS_SET(itab, DICT_TF2_FTS_HAS_DOC_ID)));
2276
2277 for (uint i = 0; i < n_fields; i++) {
2278 Field* field = table->field[i];
2279
2280 field->reset();
2281
2282 if (innobase_is_v_fld(field)) {
2283 /* Virtual column are not stored in InnoDB table, so
2284 skip it */
2285 num_v++;
2286 continue;
2287 }
2288
2289 const dfield_t* df = dtuple_get_nth_field(row, i - num_v);
2290
2291 if (dfield_is_ext(df) || dfield_is_null(df)) {
2292 field->set_null();
2293 } else {
2294 field->set_notnull();
2295
2296 innobase_col_to_mysql(
2297 dict_table_get_nth_col(itab, i - num_v),
2298 static_cast<const uchar*>(dfield_get_data(df)),
2299 dfield_get_len(df), field);
2300 }
2301 }
2302 if (table->vfield) {
2303 my_bitmap_map* old_vcol_set = tmp_use_all_columns(table, table->vcol_set);
2304 table->update_virtual_fields(table->file, VCOL_UPDATE_FOR_READ);
2305 tmp_restore_column_map(table->vcol_set, old_vcol_set);
2306 }
2307}
2308
2309/*******************************************************************//**
2310This function checks that index keys are sensible.
2311@return 0 or error number */
2312static MY_ATTRIBUTE((nonnull, warn_unused_result))
2313int
2314innobase_check_index_keys(
2315/*======================*/
2316 const Alter_inplace_info* info,
2317 /*!< in: indexes to be created or dropped */
2318 const dict_table_t* innodb_table)
2319 /*!< in: Existing indexes */
2320{
2321 for (uint key_num = 0; key_num < info->index_add_count;
2322 key_num++) {
2323 const KEY& key = info->key_info_buffer[
2324 info->index_add_buffer[key_num]];
2325
2326 /* Check that the same index name does not appear
2327 twice in indexes to be created. */
2328
2329 for (ulint i = 0; i < key_num; i++) {
2330 const KEY& key2 = info->key_info_buffer[
2331 info->index_add_buffer[i]];
2332
2333 if (0 == strcmp(key.name.str, key2.name.str)) {
2334 my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0),
2335 key.name.str);
2336
2337 return(ER_WRONG_NAME_FOR_INDEX);
2338 }
2339 }
2340
2341 /* Check that the same index name does not already exist. */
2342
2343 const dict_index_t* index;
2344
2345 for (index = dict_table_get_first_index(innodb_table);
2346 index; index = dict_table_get_next_index(index)) {
2347
2348 if (index->is_committed()
2349 && !strcmp(key.name.str, index->name)) {
2350 break;
2351 }
2352 }
2353
2354 /* Now we are in a situation where we have "ADD INDEX x"
2355 and an index by the same name already exists. We have 4
2356 possible cases:
2357 1. No further clauses for an index x are given. Should reject
2358 the operation.
2359 2. "DROP INDEX x" is given. Should allow the operation.
2360 3. "RENAME INDEX x TO y" is given. Should allow the operation.
2361 4. "DROP INDEX x, RENAME INDEX x TO y" is given. Should allow
2362 the operation, since no name clash occurs. In this particular
2363 case MySQL cancels the operation without calling InnoDB
2364 methods. */
2365
2366 if (index) {
2367 /* If a key by the same name is being created and
2368 dropped, the name clash is OK. */
2369 for (uint i = 0; i < info->index_drop_count;
2370 i++) {
2371 const KEY* drop_key
2372 = info->index_drop_buffer[i];
2373
2374 if (0 == strcmp(key.name.str,
2375 drop_key->name.str)) {
2376 goto name_ok;
2377 }
2378 }
2379
2380#ifdef MYSQL_RENAME_INDEX
2381 /* If a key by the same name is being created and
2382 renamed, the name clash is OK. E.g.
2383 ALTER TABLE t ADD INDEX i (col), RENAME INDEX i TO x
2384 where the index "i" exists prior to the ALTER command.
2385 In this case we:
2386 1. rename the existing index from "i" to "x"
2387 2. add the new index "i" */
2388 for (uint i = 0; i < info->index_rename_count; i++) {
2389 const KEY_PAIR* pair
2390 = &info->index_rename_buffer[i];
2391
2392 if (0 == strcmp(key.name, pair->old_key->name)) {
2393 goto name_ok;
2394 }
2395 }
2396#endif /* MYSQL_RENAME_INDEX */
2397
2398 my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0),
2399 key.name.str);
2400 return(ER_WRONG_NAME_FOR_INDEX);
2401 }
2402
2403name_ok:
2404 for (ulint i = 0; i < key.user_defined_key_parts; i++) {
2405 const KEY_PART_INFO& key_part1
2406 = key.key_part[i];
2407 const Field* field
2408 = key_part1.field;
2409 ibool is_unsigned;
2410
2411 switch (get_innobase_type_from_mysql_type(
2412 &is_unsigned, field)) {
2413 default:
2414 break;
2415 case DATA_INT:
2416 case DATA_FLOAT:
2417 case DATA_DOUBLE:
2418 case DATA_DECIMAL:
2419 /* Check that MySQL does not try to
2420 create a column prefix index field on
2421 an inappropriate data type. */
2422
2423 if (field->type() == MYSQL_TYPE_VARCHAR) {
2424 if (key_part1.length
2425 >= field->pack_length()
2426 - ((Field_varstring*) field)
2427 ->length_bytes) {
2428 break;
2429 }
2430 } else {
2431 if (key_part1.length
2432 >= field->pack_length()) {
2433 break;
2434 }
2435 }
2436
2437 my_error(ER_WRONG_KEY_COLUMN, MYF(0), "InnoDB",
2438 field->field_name.str);
2439 return(ER_WRONG_KEY_COLUMN);
2440 }
2441
2442 /* Check that the same column does not appear
2443 twice in the index. */
2444
2445 for (ulint j = 0; j < i; j++) {
2446 const KEY_PART_INFO& key_part2
2447 = key.key_part[j];
2448
2449 if (key_part1.fieldnr != key_part2.fieldnr) {
2450 continue;
2451 }
2452
2453 my_error(ER_WRONG_KEY_COLUMN, MYF(0), "InnoDB",
2454 field->field_name.str);
2455 return(ER_WRONG_KEY_COLUMN);
2456 }
2457 }
2458 }
2459
2460 return(0);
2461}
2462
2463/** Create index field definition for key part
2464@param[in] new_clustered true if alter is generating a new clustered
2465index
2466@param[in] altered_table MySQL table that is being altered
2467@param[in] key_part MySQL key definition
2468@param[out] index_field index field defition for key_part */
2469static MY_ATTRIBUTE((nonnull(2,3)))
2470void
2471innobase_create_index_field_def(
2472 bool new_clustered,
2473 const TABLE* altered_table,
2474 const KEY_PART_INFO* key_part,
2475 index_field_t* index_field)
2476{
2477 const Field* field;
2478 ibool is_unsigned;
2479 ulint col_type;
2480 ulint num_v = 0;
2481
2482 DBUG_ENTER("innobase_create_index_field_def");
2483
2484 ut_ad(key_part);
2485 ut_ad(index_field);
2486 ut_ad(altered_table);
2487
2488 field = new_clustered
2489 ? altered_table->field[key_part->fieldnr]
2490 : key_part->field;
2491
2492 for (ulint i = 0; i < key_part->fieldnr; i++) {
2493 if (innobase_is_v_fld(altered_table->field[i])) {
2494 num_v++;
2495 }
2496 }
2497
2498 col_type = get_innobase_type_from_mysql_type(
2499 &is_unsigned, field);
2500
2501 if (innobase_is_v_fld(field)) {
2502 index_field->is_v_col = true;
2503 index_field->col_no = num_v;
2504 } else {
2505 index_field->is_v_col = false;
2506 index_field->col_no = key_part->fieldnr - num_v;
2507 }
2508
2509 if (DATA_LARGE_MTYPE(col_type)
2510 || (key_part->length < field->pack_length()
2511 && field->type() != MYSQL_TYPE_VARCHAR)
2512 || (field->type() == MYSQL_TYPE_VARCHAR
2513 && key_part->length < field->pack_length()
2514 - ((Field_varstring*) field)->length_bytes)) {
2515
2516 index_field->prefix_len = key_part->length;
2517 } else {
2518 index_field->prefix_len = 0;
2519 }
2520
2521 DBUG_VOID_RETURN;
2522}
2523
2524/** Create index definition for key
2525@param[in] altered_table MySQL table that is being altered
2526@param[in] keys key definitions
2527@param[in] key_number MySQL key number
2528@param[in] new_clustered true if generating a new clustered
2529index on the table
2530@param[in] key_clustered true if this is the new clustered index
2531@param[out] index index definition
2532@param[in] heap heap where memory is allocated */
2533static MY_ATTRIBUTE((nonnull))
2534void
2535innobase_create_index_def(
2536 const TABLE* altered_table,
2537 const KEY* keys,
2538 ulint key_number,
2539 bool new_clustered,
2540 bool key_clustered,
2541 index_def_t* index,
2542 mem_heap_t* heap)
2543{
2544 const KEY* key = &keys[key_number];
2545 ulint i;
2546 ulint n_fields = key->user_defined_key_parts;
2547
2548 DBUG_ENTER("innobase_create_index_def");
2549 DBUG_ASSERT(!key_clustered || new_clustered);
2550
2551 ut_ad(altered_table);
2552
2553 index->fields = static_cast<index_field_t*>(
2554 mem_heap_alloc(heap, n_fields * sizeof *index->fields));
2555
2556 index->parser = NULL;
2557 index->key_number = key_number;
2558 index->n_fields = n_fields;
2559 index->name = mem_heap_strdup(heap, key->name.str);
2560 index->rebuild = new_clustered;
2561
2562 if (key_clustered) {
2563 DBUG_ASSERT(!(key->flags & (HA_FULLTEXT | HA_SPATIAL)));
2564 DBUG_ASSERT(key->flags & HA_NOSAME);
2565 index->ind_type = DICT_CLUSTERED | DICT_UNIQUE;
2566 } else if (key->flags & HA_FULLTEXT) {
2567 DBUG_ASSERT(!(key->flags & (HA_SPATIAL | HA_NOSAME)));
2568 DBUG_ASSERT(!(key->flags & HA_KEYFLAG_MASK
2569 & ~(HA_FULLTEXT
2570 | HA_PACK_KEY
2571 | HA_BINARY_PACK_KEY)));
2572 index->ind_type = DICT_FTS;
2573
2574 /* Note: key->parser is only parser name,
2575 we need to get parser from altered_table instead */
2576
2577 if (key->flags & HA_USES_PARSER) {
2578 for (ulint j = 0; j < altered_table->s->keys; j++) {
2579 if (ut_strcmp(altered_table->key_info[j].name.str,
2580 key->name.str) == 0) {
2581 ut_ad(altered_table->key_info[j].flags
2582 & HA_USES_PARSER);
2583
2584 plugin_ref parser =
2585 altered_table->key_info[j].parser;
2586 index->parser =
2587 static_cast<st_mysql_ftparser*>(
2588 plugin_decl(parser)->info);
2589
2590 break;
2591 }
2592 }
2593
2594 DBUG_EXECUTE_IF("fts_instrument_use_default_parser",
2595 index->parser = &fts_default_parser;);
2596 ut_ad(index->parser);
2597 }
2598 } else if (key->flags & HA_SPATIAL) {
2599 DBUG_ASSERT(!(key->flags & HA_NOSAME));
2600 index->ind_type = DICT_SPATIAL;
2601 ut_ad(n_fields == 1);
2602 ulint num_v = 0;
2603
2604 /* Need to count the virtual fields before this spatial
2605 indexed field */
2606 for (ulint i = 0; i < key->key_part->fieldnr; i++) {
2607 if (innobase_is_v_fld(altered_table->field[i])) {
2608 num_v++;
2609 }
2610 }
2611 index->fields[0].col_no = key->key_part[0].fieldnr - num_v;
2612 index->fields[0].prefix_len = 0;
2613 index->fields[0].is_v_col = false;
2614
2615 if (innobase_is_v_fld(key->key_part[0].field)) {
2616
2617 /* Currently, the spatial index cannot be created
2618 on virtual columns. It is blocked in server
2619 layer */
2620 ut_ad(0);
2621 index->fields[0].is_v_col = true;
2622 } else {
2623 index->fields[0].is_v_col = false;
2624 }
2625 } else {
2626 index->ind_type = (key->flags & HA_NOSAME) ? DICT_UNIQUE : 0;
2627 }
2628
2629 if (!(key->flags & HA_SPATIAL)) {
2630 for (i = 0; i < n_fields; i++) {
2631 innobase_create_index_field_def(
2632 new_clustered, altered_table,
2633 &key->key_part[i], &index->fields[i]);
2634
2635 if (index->fields[i].is_v_col) {
2636 index->ind_type |= DICT_VIRTUAL;
2637 }
2638 }
2639 }
2640
2641 DBUG_VOID_RETURN;
2642}
2643
2644/*******************************************************************//**
2645Check whether the table has a unique index with FTS_DOC_ID_INDEX_NAME
2646on the Doc ID column.
2647@return the status of the FTS_DOC_ID index */
2648enum fts_doc_id_index_enum
2649innobase_fts_check_doc_id_index(
2650/*============================*/
2651 const dict_table_t* table, /*!< in: table definition */
2652 const TABLE* altered_table, /*!< in: MySQL table
2653 that is being altered */
2654 ulint* fts_doc_col_no) /*!< out: The column number for
2655 Doc ID, or ULINT_UNDEFINED
2656 if it is being created in
2657 ha_alter_info */
2658{
2659 const dict_index_t* index;
2660 const dict_field_t* field;
2661
2662 if (altered_table) {
2663 /* Check if a unique index with the name of
2664 FTS_DOC_ID_INDEX_NAME is being created. */
2665
2666 for (uint i = 0; i < altered_table->s->keys; i++) {
2667 const KEY& key = altered_table->key_info[i];
2668
2669 if (innobase_strcasecmp(
2670 key.name.str, FTS_DOC_ID_INDEX_NAME)) {
2671 continue;
2672 }
2673
2674 if ((key.flags & HA_NOSAME)
2675 && key.user_defined_key_parts == 1
2676 && !strcmp(key.name.str, FTS_DOC_ID_INDEX_NAME)
2677 && !strcmp(key.key_part[0].field->field_name.str,
2678 FTS_DOC_ID_COL_NAME)) {
2679 if (fts_doc_col_no) {
2680 *fts_doc_col_no = ULINT_UNDEFINED;
2681 }
2682 return(FTS_EXIST_DOC_ID_INDEX);
2683 } else {
2684 return(FTS_INCORRECT_DOC_ID_INDEX);
2685 }
2686 }
2687 }
2688
2689 if (!table) {
2690 return(FTS_NOT_EXIST_DOC_ID_INDEX);
2691 }
2692
2693 for (index = dict_table_get_first_index(table);
2694 index; index = dict_table_get_next_index(index)) {
2695
2696 /* Check if there exists a unique index with the name of
2697 FTS_DOC_ID_INDEX_NAME */
2698 if (innobase_strcasecmp(index->name, FTS_DOC_ID_INDEX_NAME)) {
2699 continue;
2700 }
2701
2702 if (!dict_index_is_unique(index)
2703 || dict_index_get_n_unique(index) > 1
2704 || strcmp(index->name, FTS_DOC_ID_INDEX_NAME)) {
2705 return(FTS_INCORRECT_DOC_ID_INDEX);
2706 }
2707
2708 /* Check whether the index has FTS_DOC_ID as its
2709 first column */
2710 field = dict_index_get_nth_field(index, 0);
2711
2712 /* The column would be of a BIGINT data type */
2713 if (strcmp(field->name, FTS_DOC_ID_COL_NAME) == 0
2714 && field->col->mtype == DATA_INT
2715 && field->col->len == 8
2716 && field->col->prtype & DATA_NOT_NULL
2717 && !field->col->is_virtual()) {
2718 if (fts_doc_col_no) {
2719 *fts_doc_col_no = dict_col_get_no(field->col);
2720 }
2721 return(FTS_EXIST_DOC_ID_INDEX);
2722 } else {
2723 return(FTS_INCORRECT_DOC_ID_INDEX);
2724 }
2725 }
2726
2727
2728 /* Not found */
2729 return(FTS_NOT_EXIST_DOC_ID_INDEX);
2730}
2731/*******************************************************************//**
2732Check whether the table has a unique index with FTS_DOC_ID_INDEX_NAME
2733on the Doc ID column in MySQL create index definition.
2734@return FTS_EXIST_DOC_ID_INDEX if there exists the FTS_DOC_ID index,
2735FTS_INCORRECT_DOC_ID_INDEX if the FTS_DOC_ID index is of wrong format */
2736enum fts_doc_id_index_enum
2737innobase_fts_check_doc_id_index_in_def(
2738/*===================================*/
2739 ulint n_key, /*!< in: Number of keys */
2740 const KEY* key_info) /*!< in: Key definition */
2741{
2742 /* Check whether there is a "FTS_DOC_ID_INDEX" in the to be built index
2743 list */
2744 for (ulint j = 0; j < n_key; j++) {
2745 const KEY* key = &key_info[j];
2746
2747 if (innobase_strcasecmp(key->name.str, FTS_DOC_ID_INDEX_NAME)) {
2748 continue;
2749 }
2750
2751 /* Do a check on FTS DOC ID_INDEX, it must be unique,
2752 named as "FTS_DOC_ID_INDEX" and on column "FTS_DOC_ID" */
2753 if (!(key->flags & HA_NOSAME)
2754 || key->user_defined_key_parts != 1
2755 || strcmp(key->name.str, FTS_DOC_ID_INDEX_NAME)
2756 || strcmp(key->key_part[0].field->field_name.str,
2757 FTS_DOC_ID_COL_NAME)) {
2758 return(FTS_INCORRECT_DOC_ID_INDEX);
2759 }
2760
2761 return(FTS_EXIST_DOC_ID_INDEX);
2762 }
2763
2764 return(FTS_NOT_EXIST_DOC_ID_INDEX);
2765}
2766
2767/*******************************************************************//**
2768Create an index table where indexes are ordered as follows:
2769
2770IF a new primary key is defined for the table THEN
2771
2772 1) New primary key
2773 2) The remaining keys in key_info
2774
2775ELSE
2776
2777 1) All new indexes in the order they arrive from MySQL
2778
2779ENDIF
2780
2781@return key definitions */
2782static MY_ATTRIBUTE((nonnull, warn_unused_result, malloc))
2783index_def_t*
2784innobase_create_key_defs(
2785/*=====================*/
2786 mem_heap_t* heap,
2787 /*!< in/out: memory heap where space for key
2788 definitions are allocated */
2789 const Alter_inplace_info* ha_alter_info,
2790 /*!< in: alter operation */
2791 const TABLE* altered_table,
2792 /*!< in: MySQL table that is being altered */
2793 ulint& n_add,
2794 /*!< in/out: number of indexes to be created */
2795 ulint& n_fts_add,
2796 /*!< out: number of FTS indexes to be created */
2797 bool got_default_clust,
2798 /*!< in: whether the table lacks a primary key */
2799 ulint& fts_doc_id_col,
2800 /*!< in: The column number for Doc ID */
2801 bool& add_fts_doc_id,
2802 /*!< in: whether we need to add new DOC ID
2803 column for FTS index */
2804 bool& add_fts_doc_idx,
2805 /*!< in: whether we need to add new DOC ID
2806 index for FTS index */
2807 const TABLE* table)
2808 /*!< in: MySQL table that is being altered */
2809{
2810 index_def_t* indexdef;
2811 index_def_t* indexdefs;
2812 bool new_primary;
2813 const uint*const add
2814 = ha_alter_info->index_add_buffer;
2815 const KEY*const key_info
2816 = ha_alter_info->key_info_buffer;
2817
2818 DBUG_ENTER("innobase_create_key_defs");
2819 DBUG_ASSERT(!add_fts_doc_id || add_fts_doc_idx);
2820 DBUG_ASSERT(ha_alter_info->index_add_count == n_add);
2821
2822 /* If there is a primary key, it is always the first index
2823 defined for the innodb_table. */
2824
2825 new_primary = n_add > 0
2826 && !my_strcasecmp(system_charset_info,
2827 key_info[*add].name.str, "PRIMARY");
2828 n_fts_add = 0;
2829
2830 /* If there is a UNIQUE INDEX consisting entirely of NOT NULL
2831 columns and if the index does not contain column prefix(es)
2832 (only prefix/part of the column is indexed), MySQL will treat the
2833 index as a PRIMARY KEY unless the table already has one. */
2834
2835 ut_ad(altered_table->s->primary_key == 0
2836 || altered_table->s->primary_key == MAX_KEY);
2837
2838 if (got_default_clust && !new_primary) {
2839 new_primary = (altered_table->s->primary_key != MAX_KEY);
2840 }
2841
2842 const bool rebuild = new_primary || add_fts_doc_id
2843 || innobase_need_rebuild(ha_alter_info, table);
2844
2845 /* Reserve one more space if new_primary is true, and we might
2846 need to add the FTS_DOC_ID_INDEX */
2847 indexdef = indexdefs = static_cast<index_def_t*>(
2848 mem_heap_alloc(
2849 heap, sizeof *indexdef
2850 * (ha_alter_info->key_count
2851 + rebuild
2852 + got_default_clust)));
2853
2854 if (rebuild) {
2855 ulint primary_key_number;
2856
2857 if (new_primary) {
2858 DBUG_ASSERT(n_add || got_default_clust);
2859 DBUG_ASSERT(n_add || !altered_table->s->primary_key);
2860 primary_key_number = altered_table->s->primary_key;
2861 } else if (got_default_clust) {
2862 /* Create the GEN_CLUST_INDEX */
2863 index_def_t* index = indexdef++;
2864
2865 index->fields = NULL;
2866 index->n_fields = 0;
2867 index->ind_type = DICT_CLUSTERED;
2868 index->name = innobase_index_reserve_name;
2869 index->rebuild = true;
2870 index->key_number = ~0U;
2871 primary_key_number = ULINT_UNDEFINED;
2872 goto created_clustered;
2873 } else {
2874 primary_key_number = 0;
2875 }
2876
2877 /* Create the PRIMARY key index definition */
2878 innobase_create_index_def(
2879 altered_table, key_info, primary_key_number,
2880 true, true, indexdef++, heap);
2881
2882created_clustered:
2883 n_add = 1;
2884
2885 for (ulint i = 0; i < ha_alter_info->key_count; i++) {
2886 if (i == primary_key_number) {
2887 continue;
2888 }
2889 /* Copy the index definitions. */
2890 innobase_create_index_def(
2891 altered_table, key_info, i, true,
2892 false, indexdef, heap);
2893
2894 if (indexdef->ind_type & DICT_FTS) {
2895 n_fts_add++;
2896 }
2897
2898 indexdef++;
2899 n_add++;
2900 }
2901
2902 if (n_fts_add > 0) {
2903 ulint num_v = 0;
2904
2905 if (!add_fts_doc_id
2906 && !innobase_fts_check_doc_id_col(
2907 NULL, altered_table,
2908 &fts_doc_id_col, &num_v)) {
2909 fts_doc_id_col = altered_table->s->fields - num_v;
2910 add_fts_doc_id = true;
2911 }
2912
2913 if (!add_fts_doc_idx) {
2914 fts_doc_id_index_enum ret;
2915 ulint doc_col_no;
2916
2917 ret = innobase_fts_check_doc_id_index(
2918 NULL, altered_table, &doc_col_no);
2919
2920 /* This should have been checked before */
2921 ut_ad(ret != FTS_INCORRECT_DOC_ID_INDEX);
2922
2923 if (ret == FTS_NOT_EXIST_DOC_ID_INDEX) {
2924 add_fts_doc_idx = true;
2925 } else {
2926 ut_ad(ret == FTS_EXIST_DOC_ID_INDEX);
2927 ut_ad(doc_col_no == ULINT_UNDEFINED
2928 || doc_col_no == fts_doc_id_col);
2929 }
2930 }
2931 }
2932 } else {
2933 /* Create definitions for added secondary indexes. */
2934
2935 for (ulint i = 0; i < n_add; i++) {
2936 innobase_create_index_def(
2937 altered_table, key_info, add[i],
2938 false, false, indexdef, heap);
2939
2940 if (indexdef->ind_type & DICT_FTS) {
2941 n_fts_add++;
2942 }
2943
2944 indexdef++;
2945 }
2946 }
2947
2948 DBUG_ASSERT(indexdefs + n_add == indexdef);
2949
2950 if (add_fts_doc_idx) {
2951 index_def_t* index = indexdef++;
2952
2953 index->fields = static_cast<index_field_t*>(
2954 mem_heap_alloc(heap, sizeof *index->fields));
2955 index->n_fields = 1;
2956 index->fields->col_no = fts_doc_id_col;
2957 index->fields->prefix_len = 0;
2958 index->fields->is_v_col = false;
2959 index->ind_type = DICT_UNIQUE;
2960 ut_ad(!rebuild
2961 || !add_fts_doc_id
2962 || fts_doc_id_col <= altered_table->s->fields);
2963
2964 index->name = FTS_DOC_ID_INDEX_NAME;
2965 index->rebuild = rebuild;
2966
2967 /* TODO: assign a real MySQL key number for this */
2968 index->key_number = ULINT_UNDEFINED;
2969 n_add++;
2970 }
2971
2972 DBUG_ASSERT(indexdef > indexdefs);
2973 DBUG_ASSERT((ulint) (indexdef - indexdefs)
2974 <= ha_alter_info->key_count
2975 + add_fts_doc_idx + got_default_clust);
2976 DBUG_ASSERT(ha_alter_info->index_add_count <= n_add);
2977 DBUG_RETURN(indexdefs);
2978}
2979
2980/*******************************************************************//**
2981Check each index column size, make sure they do not exceed the max limit
2982@return true if index column size exceeds limit */
2983static MY_ATTRIBUTE((nonnull, warn_unused_result))
2984bool
2985innobase_check_column_length(
2986/*=========================*/
2987 ulint max_col_len, /*!< in: maximum column length */
2988 const KEY* key_info) /*!< in: Indexes to be created */
2989{
2990 for (ulint key_part = 0; key_part < key_info->user_defined_key_parts; key_part++) {
2991 if (key_info->key_part[key_part].length > max_col_len) {
2992 return(true);
2993 }
2994 }
2995 return(false);
2996}
2997
2998/********************************************************************//**
2999Drop any indexes that we were not able to free previously due to
3000open table handles. */
3001static
3002void
3003online_retry_drop_indexes_low(
3004/*==========================*/
3005 dict_table_t* table, /*!< in/out: table */
3006 trx_t* trx) /*!< in/out: transaction */
3007{
3008 ut_ad(mutex_own(&dict_sys->mutex));
3009 ut_ad(trx->dict_operation_lock_mode == RW_X_LATCH);
3010 ut_ad(trx_get_dict_operation(trx) == TRX_DICT_OP_INDEX);
3011
3012 /* We can have table->n_ref_count > 1, because other threads
3013 may have prebuilt->table pointing to the table. However, these
3014 other threads should be between statements, waiting for the
3015 next statement to execute, or for a meta-data lock. */
3016 ut_ad(table->get_ref_count() >= 1);
3017
3018 if (table->drop_aborted) {
3019 row_merge_drop_indexes(trx, table, TRUE);
3020 }
3021}
3022
3023/********************************************************************//**
3024Drop any indexes that we were not able to free previously due to
3025open table handles. */
3026static MY_ATTRIBUTE((nonnull))
3027void
3028online_retry_drop_indexes(
3029/*======================*/
3030 dict_table_t* table, /*!< in/out: table */
3031 THD* user_thd) /*!< in/out: MySQL connection */
3032{
3033 if (table->drop_aborted) {
3034 trx_t* trx = innobase_trx_allocate(user_thd);
3035
3036 trx_start_for_ddl(trx, TRX_DICT_OP_INDEX);
3037
3038 row_mysql_lock_data_dictionary(trx);
3039 online_retry_drop_indexes_low(table, trx);
3040 trx_commit_for_mysql(trx);
3041 row_mysql_unlock_data_dictionary(trx);
3042 trx_free(trx);
3043 }
3044
3045 ut_d(mutex_enter(&dict_sys->mutex));
3046 ut_d(dict_table_check_for_dup_indexes(table, CHECK_ALL_COMPLETE));
3047 ut_d(mutex_exit(&dict_sys->mutex));
3048 ut_ad(!table->drop_aborted);
3049}
3050
3051/********************************************************************//**
3052Commit a dictionary transaction and drop any indexes that we were not
3053able to free previously due to open table handles. */
3054static MY_ATTRIBUTE((nonnull))
3055void
3056online_retry_drop_indexes_with_trx(
3057/*===============================*/
3058 dict_table_t* table, /*!< in/out: table */
3059 trx_t* trx) /*!< in/out: transaction */
3060{
3061 ut_ad(trx_state_eq(trx, TRX_STATE_NOT_STARTED));
3062
3063 ut_ad(trx->dict_operation_lock_mode == RW_X_LATCH);
3064
3065 /* Now that the dictionary is being locked, check if we can
3066 drop any incompletely created indexes that may have been left
3067 behind in rollback_inplace_alter_table() earlier. */
3068 if (table->drop_aborted) {
3069
3070 trx->table_id = 0;
3071
3072 trx_start_for_ddl(trx, TRX_DICT_OP_INDEX);
3073
3074 online_retry_drop_indexes_low(table, trx);
3075 trx_commit_for_mysql(trx);
3076 }
3077}
3078
3079/** Determines if InnoDB is dropping a foreign key constraint.
3080@param foreign the constraint
3081@param drop_fk constraints being dropped
3082@param n_drop_fk number of constraints that are being dropped
3083@return whether the constraint is being dropped */
3084MY_ATTRIBUTE((pure, nonnull(1), warn_unused_result))
3085inline
3086bool
3087innobase_dropping_foreign(
3088 const dict_foreign_t* foreign,
3089 dict_foreign_t** drop_fk,
3090 ulint n_drop_fk)
3091{
3092 while (n_drop_fk--) {
3093 if (*drop_fk++ == foreign) {
3094 return(true);
3095 }
3096 }
3097
3098 return(false);
3099}
3100
3101/** Determines if an InnoDB FOREIGN KEY constraint depends on a
3102column that is being dropped or modified to NOT NULL.
3103@param user_table InnoDB table as it is before the ALTER operation
3104@param col_name Name of the column being altered
3105@param drop_fk constraints being dropped
3106@param n_drop_fk number of constraints that are being dropped
3107@param drop true=drop column, false=set NOT NULL
3108@retval true Not allowed (will call my_error())
3109@retval false Allowed
3110*/
3111MY_ATTRIBUTE((pure, nonnull(1,4), warn_unused_result))
3112static
3113bool
3114innobase_check_foreigns_low(
3115 const dict_table_t* user_table,
3116 dict_foreign_t** drop_fk,
3117 ulint n_drop_fk,
3118 const char* col_name,
3119 bool drop)
3120{
3121 dict_foreign_t* foreign;
3122 ut_ad(mutex_own(&dict_sys->mutex));
3123
3124 /* Check if any FOREIGN KEY constraints are defined on this
3125 column. */
3126
3127 for (dict_foreign_set::const_iterator it = user_table->foreign_set.begin();
3128 it != user_table->foreign_set.end();
3129 ++it) {
3130
3131 foreign = *it;
3132
3133 if (!drop && !(foreign->type
3134 & (DICT_FOREIGN_ON_DELETE_SET_NULL
3135 | DICT_FOREIGN_ON_UPDATE_SET_NULL))) {
3136 continue;
3137 }
3138
3139 if (innobase_dropping_foreign(foreign, drop_fk, n_drop_fk)) {
3140 continue;
3141 }
3142
3143 for (unsigned f = 0; f < foreign->n_fields; f++) {
3144 if (!strcmp(foreign->foreign_col_names[f],
3145 col_name)) {
3146 my_error(drop
3147 ? ER_FK_COLUMN_CANNOT_DROP
3148 : ER_FK_COLUMN_NOT_NULL, MYF(0),
3149 col_name, foreign->id);
3150 return(true);
3151 }
3152 }
3153 }
3154
3155 if (!drop) {
3156 /* SET NULL clauses on foreign key constraints of
3157 child tables affect the child tables, not the parent table.
3158 The column can be NOT NULL in the parent table. */
3159 return(false);
3160 }
3161
3162 /* Check if any FOREIGN KEY constraints in other tables are
3163 referring to the column that is being dropped. */
3164 for (dict_foreign_set::const_iterator it
3165 = user_table->referenced_set.begin();
3166 it != user_table->referenced_set.end();
3167 ++it) {
3168
3169 foreign = *it;
3170
3171 if (innobase_dropping_foreign(foreign, drop_fk, n_drop_fk)) {
3172 continue;
3173 }
3174
3175 for (unsigned f = 0; f < foreign->n_fields; f++) {
3176 char display_name[FN_REFLEN];
3177
3178 if (strcmp(foreign->referenced_col_names[f],
3179 col_name)) {
3180 continue;
3181 }
3182
3183 char* buf_end = innobase_convert_name(
3184 display_name, (sizeof display_name) - 1,
3185 foreign->foreign_table_name,
3186 strlen(foreign->foreign_table_name),
3187 NULL);
3188 *buf_end = '\0';
3189 my_error(ER_FK_COLUMN_CANNOT_DROP_CHILD,
3190 MYF(0), col_name, foreign->id,
3191 display_name);
3192
3193 return(true);
3194 }
3195 }
3196
3197 return(false);
3198}
3199
3200/** Determines if an InnoDB FOREIGN KEY constraint depends on a
3201column that is being dropped or modified to NOT NULL.
3202@param ha_alter_info Data used during in-place alter
3203@param altered_table MySQL table that is being altered
3204@param old_table MySQL table as it is before the ALTER operation
3205@param user_table InnoDB table as it is before the ALTER operation
3206@param drop_fk constraints being dropped
3207@param n_drop_fk number of constraints that are being dropped
3208@retval true Not allowed (will call my_error())
3209@retval false Allowed
3210*/
3211MY_ATTRIBUTE((pure, nonnull(1,2,3), warn_unused_result))
3212static
3213bool
3214innobase_check_foreigns(
3215 Alter_inplace_info* ha_alter_info,
3216 const TABLE* old_table,
3217 const dict_table_t* user_table,
3218 dict_foreign_t** drop_fk,
3219 ulint n_drop_fk)
3220{
3221 List_iterator_fast<Create_field> cf_it(
3222 ha_alter_info->alter_info->create_list);
3223
3224 for (Field** fp = old_table->field; *fp; fp++) {
3225 cf_it.rewind();
3226 const Create_field* new_field;
3227
3228 ut_ad(!(*fp)->real_maybe_null()
3229 == !!((*fp)->flags & NOT_NULL_FLAG));
3230
3231 while ((new_field = cf_it++)) {
3232 if (new_field->field == *fp) {
3233 break;
3234 }
3235 }
3236
3237 if (!new_field || (new_field->flags & NOT_NULL_FLAG)) {
3238 if (innobase_check_foreigns_low(
3239 user_table, drop_fk, n_drop_fk,
3240 (*fp)->field_name.str, !new_field)) {
3241 return(true);
3242 }
3243 }
3244 }
3245
3246 return(false);
3247}
3248
3249/** Convert a default value for ADD COLUMN.
3250
3251@param heap Memory heap where allocated
3252@param dfield InnoDB data field to copy to
3253@param field MySQL value for the column
3254@param comp nonzero if in compact format */
3255static MY_ATTRIBUTE((nonnull))
3256void
3257innobase_build_col_map_add(
3258/*=======================*/
3259 mem_heap_t* heap,
3260 dfield_t* dfield,
3261 const Field* field,
3262 ulint comp)
3263{
3264 if (field->is_real_null()) {
3265 dfield_set_null(dfield);
3266 return;
3267 }
3268
3269 ulint size = field->pack_length();
3270
3271 byte* buf = static_cast<byte*>(mem_heap_alloc(heap, size));
3272
3273 const byte* mysql_data = field->ptr;
3274
3275 row_mysql_store_col_in_innobase_format(
3276 dfield, buf, true, mysql_data, size, comp);
3277}
3278
3279/** Construct the translation table for reordering, dropping or
3280adding columns.
3281
3282@param ha_alter_info Data used during in-place alter
3283@param altered_table MySQL table that is being altered
3284@param table MySQL table as it is before the ALTER operation
3285@param new_table InnoDB table corresponding to MySQL altered_table
3286@param old_table InnoDB table corresponding to MYSQL table
3287@param defaults Default values for ADD COLUMN, or NULL if no ADD COLUMN
3288@param heap Memory heap where allocated
3289@return array of integers, mapping column numbers in the table
3290to column numbers in altered_table */
3291static MY_ATTRIBUTE((nonnull(1,2,3,4,5,7), warn_unused_result))
3292const ulint*
3293innobase_build_col_map(
3294/*===================*/
3295 Alter_inplace_info* ha_alter_info,
3296 const TABLE* altered_table,
3297 const TABLE* table,
3298 const dict_table_t* new_table,
3299 const dict_table_t* old_table,
3300 dtuple_t* defaults,
3301 mem_heap_t* heap)
3302{
3303 DBUG_ENTER("innobase_build_col_map");
3304 DBUG_ASSERT(altered_table != table);
3305 DBUG_ASSERT(new_table != old_table);
3306 DBUG_ASSERT(dict_table_get_n_cols(new_table)
3307 + dict_table_get_n_v_cols(new_table)
3308 >= altered_table->s->fields + DATA_N_SYS_COLS);
3309 DBUG_ASSERT(dict_table_get_n_cols(old_table)
3310 + dict_table_get_n_v_cols(old_table)
3311 >= table->s->fields + DATA_N_SYS_COLS);
3312 DBUG_ASSERT(!!defaults == !!(ha_alter_info->handler_flags
3313 & (ALTER_ADD_COLUMN
3314 | ALTER_COLUMN_NOT_NULLABLE)));
3315 DBUG_ASSERT(!defaults || dtuple_get_n_fields(defaults)
3316 == dict_table_get_n_cols(new_table));
3317
3318 ulint* col_map = static_cast<ulint*>(
3319 mem_heap_alloc(
3320 heap, unsigned(old_table->n_cols + old_table->n_v_cols)
3321 * sizeof *col_map));
3322
3323 List_iterator_fast<Create_field> cf_it(
3324 ha_alter_info->alter_info->create_list);
3325 uint i = 0;
3326 uint num_v = 0;
3327
3328 /* Any dropped columns will map to ULINT_UNDEFINED. */
3329 for (uint old_i = 0; old_i + DATA_N_SYS_COLS < old_table->n_cols;
3330 old_i++) {
3331 col_map[old_i] = ULINT_UNDEFINED;
3332 }
3333
3334 for (uint old_i = 0; old_i < old_table->n_v_cols; old_i++) {
3335 col_map[old_i + old_table->n_cols] = ULINT_UNDEFINED;
3336 }
3337
3338 while (const Create_field* new_field = cf_it++) {
3339 bool is_v = innobase_is_v_fld(new_field);
3340
3341 ulint num_old_v = 0;
3342
3343 for (uint old_i = 0; table->field[old_i]; old_i++) {
3344 const Field* field = table->field[old_i];
3345 if (innobase_is_v_fld(field)) {
3346 if (is_v && new_field->field == field) {
3347 col_map[old_table->n_cols + num_v]
3348 = num_old_v;
3349 num_old_v++;
3350 goto found_col;
3351 }
3352 num_old_v++;
3353 continue;
3354 }
3355
3356 if (new_field->field == field) {
3357
3358 const Field* altered_field =
3359 altered_table->field[i + num_v];
3360
3361 if (field->real_maybe_null()
3362 && !altered_field->real_maybe_null()) {
3363 /* Don't consider virtual column.
3364 NULL to NOT NULL is not applicable
3365 for virtual column. */
3366 innobase_build_col_map_add(
3367 heap, dtuple_get_nth_field(
3368 defaults, i),
3369 altered_field,
3370 dict_table_is_comp(new_table));
3371 }
3372
3373 col_map[old_i - num_old_v] = i;
3374 goto found_col;
3375 }
3376 }
3377
3378 ut_ad(!is_v);
3379 innobase_build_col_map_add(
3380 heap, dtuple_get_nth_field(defaults, i),
3381 altered_table->field[i + num_v],
3382 dict_table_is_comp(new_table));
3383found_col:
3384 if (is_v) {
3385 num_v++;
3386 } else {
3387 i++;
3388 }
3389 }
3390
3391 DBUG_ASSERT(i == altered_table->s->fields - num_v);
3392
3393 i = table->s->fields - old_table->n_v_cols;
3394
3395 /* Add the InnoDB hidden FTS_DOC_ID column, if any. */
3396 if (i + DATA_N_SYS_COLS < old_table->n_cols) {
3397 /* There should be exactly one extra field,
3398 the FTS_DOC_ID. */
3399 DBUG_ASSERT(DICT_TF2_FLAG_IS_SET(old_table,
3400 DICT_TF2_FTS_HAS_DOC_ID));
3401 DBUG_ASSERT(i + DATA_N_SYS_COLS + 1 == old_table->n_cols);
3402 DBUG_ASSERT(!strcmp(dict_table_get_col_name(
3403 old_table, i),
3404 FTS_DOC_ID_COL_NAME));
3405 if (altered_table->s->fields + DATA_N_SYS_COLS
3406 - new_table->n_v_cols
3407 < new_table->n_cols) {
3408 DBUG_ASSERT(DICT_TF2_FLAG_IS_SET(
3409 new_table,
3410 DICT_TF2_FTS_HAS_DOC_ID));
3411 DBUG_ASSERT(altered_table->s->fields
3412 + DATA_N_SYS_COLS + 1
3413 == static_cast<ulint>(
3414 new_table->n_cols
3415 + new_table->n_v_cols));
3416 col_map[i] = altered_table->s->fields
3417 - new_table->n_v_cols;
3418 } else {
3419 DBUG_ASSERT(!DICT_TF2_FLAG_IS_SET(
3420 new_table,
3421 DICT_TF2_FTS_HAS_DOC_ID));
3422 col_map[i] = ULINT_UNDEFINED;
3423 }
3424
3425 i++;
3426 } else {
3427 DBUG_ASSERT(!DICT_TF2_FLAG_IS_SET(
3428 old_table,
3429 DICT_TF2_FTS_HAS_DOC_ID));
3430 }
3431
3432 for (; i < old_table->n_cols; i++) {
3433 col_map[i] = i + new_table->n_cols - old_table->n_cols;
3434 }
3435
3436 DBUG_RETURN(col_map);
3437}
3438
3439/** Drop newly create FTS index related auxiliary table during
3440FIC create index process, before fts_add_index is called
3441@param table table that was being rebuilt online
3442@param trx transaction
3443@return DB_SUCCESS if successful, otherwise last error code
3444*/
3445static
3446dberr_t
3447innobase_drop_fts_index_table(
3448/*==========================*/
3449 dict_table_t* table,
3450 trx_t* trx)
3451{
3452 dberr_t ret_err = DB_SUCCESS;
3453
3454 for (dict_index_t* index = dict_table_get_first_index(table);
3455 index != NULL;
3456 index = dict_table_get_next_index(index)) {
3457 if (index->type & DICT_FTS) {
3458 dberr_t err;
3459
3460 err = fts_drop_index_tables(trx, index);
3461
3462 if (err != DB_SUCCESS) {
3463 ret_err = err;
3464 }
3465 }
3466 }
3467
3468 return(ret_err);
3469}
3470
3471/** Get the new non-virtual column names if any columns were renamed
3472@param ha_alter_info Data used during in-place alter
3473@param altered_table MySQL table that is being altered
3474@param table MySQL table as it is before the ALTER operation
3475@param user_table InnoDB table as it is before the ALTER operation
3476@param heap Memory heap for the allocation
3477@return array of new column names in rebuilt_table, or NULL if not renamed */
3478static MY_ATTRIBUTE((nonnull, warn_unused_result))
3479const char**
3480innobase_get_col_names(
3481 Alter_inplace_info* ha_alter_info,
3482 const TABLE* altered_table,
3483 const TABLE* table,
3484 const dict_table_t* user_table,
3485 mem_heap_t* heap)
3486{
3487 const char** cols;
3488 uint i;
3489
3490 DBUG_ENTER("innobase_get_col_names");
3491 DBUG_ASSERT(user_table->n_t_def > table->s->fields);
3492 DBUG_ASSERT(ha_alter_info->handler_flags
3493 & ALTER_COLUMN_NAME);
3494
3495 cols = static_cast<const char**>(
3496 mem_heap_zalloc(heap, user_table->n_def * sizeof *cols));
3497
3498 i = 0;
3499 List_iterator_fast<Create_field> cf_it(
3500 ha_alter_info->alter_info->create_list);
3501 while (const Create_field* new_field = cf_it++) {
3502 ulint num_v = 0;
3503 DBUG_ASSERT(i < altered_table->s->fields);
3504
3505 if (innobase_is_v_fld(new_field)) {
3506 continue;
3507 }
3508
3509 for (uint old_i = 0; table->field[old_i]; old_i++) {
3510 if (innobase_is_v_fld(table->field[old_i])) {
3511 num_v++;
3512 }
3513
3514 if (new_field->field == table->field[old_i]) {
3515 cols[old_i - num_v] = new_field->field_name.str;
3516 break;
3517 }
3518 }
3519
3520 i++;
3521 }
3522
3523 /* Copy the internal column names. */
3524 i = table->s->fields - user_table->n_v_def;
3525 cols[i] = dict_table_get_col_name(user_table, i);
3526
3527 while (++i < user_table->n_def) {
3528 cols[i] = cols[i - 1] + strlen(cols[i - 1]) + 1;
3529 }
3530
3531 DBUG_RETURN(cols);
3532}
3533
3534/** Check whether the column prefix is increased, decreased, or unchanged.
3535@param[in] new_prefix_len new prefix length
3536@param[in] old_prefix_len new prefix length
3537@retval 1 prefix is increased
3538@retval 0 prefix is unchanged
3539@retval -1 prefix is decreased */
3540static inline
3541lint
3542innobase_pk_col_prefix_compare(
3543 ulint new_prefix_len,
3544 ulint old_prefix_len)
3545{
3546 ut_ad(new_prefix_len < COMPRESSED_REC_MAX_DATA_SIZE);
3547 ut_ad(old_prefix_len < COMPRESSED_REC_MAX_DATA_SIZE);
3548
3549 if (new_prefix_len == old_prefix_len) {
3550 return(0);
3551 }
3552
3553 if (new_prefix_len == 0) {
3554 new_prefix_len = ULINT_MAX;
3555 }
3556
3557 if (old_prefix_len == 0) {
3558 old_prefix_len = ULINT_MAX;
3559 }
3560
3561 if (new_prefix_len > old_prefix_len) {
3562 return(1);
3563 } else {
3564 return(-1);
3565 }
3566}
3567
3568/** Check whether the column is existing in old table.
3569@param[in] new_col_no new column no
3570@param[in] col_map mapping of old column numbers to new ones
3571@param[in] col_map_size the column map size
3572@return true if the column is existing, otherwise false. */
3573static inline
3574bool
3575innobase_pk_col_is_existing(
3576 const ulint new_col_no,
3577 const ulint* col_map,
3578 const ulint col_map_size)
3579{
3580 for (ulint i = 0; i < col_map_size; i++) {
3581 if (col_map[i] == new_col_no) {
3582 return(true);
3583 }
3584 }
3585
3586 return(false);
3587}
3588
3589/** Determine whether both the indexes have same set of primary key
3590fields arranged in the same order.
3591
3592Rules when we cannot skip sorting:
3593(1) Removing existing PK columns somewhere else than at the end of the PK;
3594(2) Adding existing columns to the PK, except at the end of the PK when no
3595columns are removed from the PK;
3596(3) Changing the order of existing PK columns;
3597(4) Decreasing the prefix length just like removing existing PK columns
3598follows rule(1), Increasing the prefix length just like adding existing
3599PK columns follows rule(2).
3600@param[in] col_map mapping of old column numbers to new ones
3601@param[in] ha_alter_info Data used during in-place alter
3602@param[in] old_clust_index index to be compared
3603@param[in] new_clust_index index to be compared
3604@retval true if both indexes have same order.
3605@retval false. */
3606static MY_ATTRIBUTE((warn_unused_result))
3607bool
3608innobase_pk_order_preserved(
3609 const ulint* col_map,
3610 const dict_index_t* old_clust_index,
3611 const dict_index_t* new_clust_index)
3612{
3613 ulint old_n_uniq
3614 = dict_index_get_n_ordering_defined_by_user(
3615 old_clust_index);
3616 ulint new_n_uniq
3617 = dict_index_get_n_ordering_defined_by_user(
3618 new_clust_index);
3619
3620 ut_ad(dict_index_is_clust(old_clust_index));
3621 ut_ad(dict_index_is_clust(new_clust_index));
3622 ut_ad(old_clust_index->table != new_clust_index->table);
3623 ut_ad(col_map != NULL);
3624
3625 if (old_n_uniq == 0) {
3626 /* There was no PRIMARY KEY in the table.
3627 If there is no PRIMARY KEY after the ALTER either,
3628 no sorting is needed. */
3629 return(new_n_uniq == old_n_uniq);
3630 }
3631
3632 /* DROP PRIMARY KEY is only allowed in combination with
3633 ADD PRIMARY KEY. */
3634 ut_ad(new_n_uniq > 0);
3635
3636 /* The order of the last processed new_clust_index key field,
3637 not counting ADD COLUMN, which are constant. */
3638 lint last_field_order = -1;
3639 ulint existing_field_count = 0;
3640 ulint old_n_cols = dict_table_get_n_cols(old_clust_index->table);
3641 for (ulint new_field = 0; new_field < new_n_uniq; new_field++) {
3642 ulint new_col_no =
3643 new_clust_index->fields[new_field].col->ind;
3644
3645 /* Check if there is a match in old primary key. */
3646 ulint old_field = 0;
3647 while (old_field < old_n_uniq) {
3648 ulint old_col_no =
3649 old_clust_index->fields[old_field].col->ind;
3650
3651 if (col_map[old_col_no] == new_col_no) {
3652 break;
3653 }
3654
3655 old_field++;
3656 }
3657
3658 /* The order of key field in the new primary key.
3659 1. old PK column: idx in old primary key
3660 2. existing column: old_n_uniq + sequence no
3661 3. newly added column: no order */
3662 lint new_field_order;
3663 const bool old_pk_column = old_field < old_n_uniq;
3664
3665 if (old_pk_column) {
3666 new_field_order = lint(old_field);
3667 } else if (innobase_pk_col_is_existing(new_col_no, col_map,
3668 old_n_cols)) {
3669 new_field_order = lint(old_n_uniq
3670 + existing_field_count++);
3671 } else {
3672 /* Skip newly added column. */
3673 continue;
3674 }
3675
3676 if (last_field_order + 1 != new_field_order) {
3677 /* Old PK order is not kept, or existing column
3678 is not added at the end of old PK. */
3679 return(false);
3680 }
3681
3682 last_field_order = new_field_order;
3683
3684 if (!old_pk_column) {
3685 continue;
3686 }
3687
3688 /* Check prefix length change. */
3689 const lint prefix_change = innobase_pk_col_prefix_compare(
3690 new_clust_index->fields[new_field].prefix_len,
3691 old_clust_index->fields[old_field].prefix_len);
3692
3693 if (prefix_change < 0) {
3694 /* If a column's prefix length is decreased, it should
3695 be the last old PK column in new PK.
3696 Note: we set last_field_order to -2, so that if there
3697 are any old PK colmns or existing columns after it in
3698 new PK, the comparison to new_field_order will fail in
3699 the next round.*/
3700 last_field_order = -2;
3701 } else if (prefix_change > 0) {
3702 /* If a column's prefix length is increased, it should
3703 be the last PK column in old PK. */
3704 if (old_field != old_n_uniq - 1) {
3705 return(false);
3706 }
3707 }
3708 }
3709
3710 return(true);
3711}
3712
3713/** Update the mtype from DATA_BLOB to DATA_GEOMETRY for a specified
3714GIS column of a table. This is used when we want to create spatial index
3715on legacy GIS columns coming from 5.6, where we store GIS data as DATA_BLOB
3716in innodb layer.
3717@param[in] table_id table id
3718@param[in] col_name column name
3719@param[in] trx data dictionary transaction
3720@retval true Failure
3721@retval false Success */
3722static
3723bool
3724innobase_update_gis_column_type(
3725 table_id_t table_id,
3726 const char* col_name,
3727 trx_t* trx)
3728{
3729 pars_info_t* info;
3730 dberr_t error;
3731
3732 DBUG_ENTER("innobase_update_gis_column_type");
3733
3734 DBUG_ASSERT(trx_get_dict_operation(trx) == TRX_DICT_OP_INDEX);
3735 ut_ad(trx->dict_operation_lock_mode == RW_X_LATCH);
3736 ut_ad(mutex_own(&dict_sys->mutex));
3737 ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X));
3738
3739 info = pars_info_create();
3740
3741 pars_info_add_ull_literal(info, "tableid", table_id);
3742 pars_info_add_str_literal(info, "name", col_name);
3743 pars_info_add_int4_literal(info, "mtype", DATA_GEOMETRY);
3744
3745 trx->op_info = "update column type to DATA_GEOMETRY";
3746
3747 error = que_eval_sql(
3748 info,
3749 "PROCEDURE UPDATE_SYS_COLUMNS_PROC () IS\n"
3750 "BEGIN\n"
3751 "UPDATE SYS_COLUMNS SET MTYPE=:mtype\n"
3752 "WHERE TABLE_ID=:tableid AND NAME=:name;\n"
3753 "END;\n",
3754 false, trx);
3755
3756 trx->error_state = DB_SUCCESS;
3757 trx->op_info = "";
3758
3759 DBUG_RETURN(error != DB_SUCCESS);
3760}
3761
3762/** Check if we are creating spatial indexes on GIS columns, which are
3763legacy columns from earlier MySQL, such as 5.6. If so, we have to update
3764the mtypes of the old GIS columns to DATA_GEOMETRY.
3765In 5.6, we store GIS columns as DATA_BLOB in InnoDB layer, it will introduce
3766confusion when we run latest server on older data. That's why we need to
3767do the upgrade.
3768@param[in] ha_alter_info Data used during in-place alter
3769@param[in] table Table on which we want to add indexes
3770@param[in] trx Transaction
3771@return DB_SUCCESS if update successfully or no columns need to be updated,
3772otherwise DB_ERROR, which means we can't update the mtype for some
3773column, and creating spatial index on it should be dangerous */
3774static
3775dberr_t
3776innobase_check_gis_columns(
3777 Alter_inplace_info* ha_alter_info,
3778 dict_table_t* table,
3779 trx_t* trx)
3780{
3781 DBUG_ENTER("innobase_check_gis_columns");
3782
3783 for (uint key_num = 0;
3784 key_num < ha_alter_info->index_add_count;
3785 key_num++) {
3786
3787 const KEY& key = ha_alter_info->key_info_buffer[
3788 ha_alter_info->index_add_buffer[key_num]];
3789
3790 if (!(key.flags & HA_SPATIAL)) {
3791 continue;
3792 }
3793
3794 ut_ad(key.user_defined_key_parts == 1);
3795 const KEY_PART_INFO& key_part = key.key_part[0];
3796
3797 /* Does not support spatial index on virtual columns */
3798 if (innobase_is_v_fld(key_part.field)) {
3799 DBUG_RETURN(DB_UNSUPPORTED);
3800 }
3801
3802 ulint col_nr = dict_table_has_column(
3803 table,
3804 key_part.field->field_name.str,
3805 key_part.fieldnr);
3806 ut_ad(col_nr != table->n_def);
3807 dict_col_t* col = &table->cols[col_nr];
3808
3809 if (col->mtype != DATA_BLOB) {
3810 ut_ad(DATA_GEOMETRY_MTYPE(col->mtype));
3811 continue;
3812 }
3813
3814 const char* col_name = dict_table_get_col_name(
3815 table, col_nr);
3816
3817 if (innobase_update_gis_column_type(
3818 table->id, col_name, trx)) {
3819
3820 DBUG_RETURN(DB_ERROR);
3821 } else {
3822 col->mtype = DATA_GEOMETRY;
3823
3824 ib::info() << "Updated mtype of column" << col_name
3825 << " in table " << table->name
3826 << ", whose id is " << table->id
3827 << " to DATA_GEOMETRY";
3828 }
3829 }
3830
3831 DBUG_RETURN(DB_SUCCESS);
3832}
3833
3834/** Collect virtual column info for its addition
3835@param[in] ha_alter_info Data used during in-place alter
3836@param[in] altered_table MySQL table that is being altered to
3837@param[in] table MySQL table as it is before the ALTER operation
3838@retval true Failure
3839@retval false Success */
3840static
3841bool
3842prepare_inplace_add_virtual(
3843 Alter_inplace_info* ha_alter_info,
3844 const TABLE* altered_table,
3845 const TABLE* table)
3846{
3847 ha_innobase_inplace_ctx* ctx;
3848 ulint i = 0;
3849 ulint j = 0;
3850 const Create_field* new_field;
3851
3852 ctx = static_cast<ha_innobase_inplace_ctx*>
3853 (ha_alter_info->handler_ctx);
3854
3855 ctx->num_to_add_vcol = altered_table->s->fields
3856 + ctx->num_to_drop_vcol - table->s->fields;
3857
3858 ctx->add_vcol = static_cast<dict_v_col_t*>(
3859 mem_heap_zalloc(ctx->heap, ctx->num_to_add_vcol
3860 * sizeof *ctx->add_vcol));
3861 ctx->add_vcol_name = static_cast<const char**>(
3862 mem_heap_alloc(ctx->heap, ctx->num_to_add_vcol
3863 * sizeof *ctx->add_vcol_name));
3864
3865 List_iterator_fast<Create_field> cf_it(
3866 ha_alter_info->alter_info->create_list);
3867
3868 while ((new_field = (cf_it++)) != NULL) {
3869 const Field* field = new_field->field;
3870 ulint old_i;
3871
3872 for (old_i = 0; table->field[old_i]; old_i++) {
3873 const Field* n_field = table->field[old_i];
3874 if (field == n_field) {
3875 break;
3876 }
3877 }
3878
3879 i++;
3880
3881 if (table->field[old_i]) {
3882 continue;
3883 }
3884
3885 ut_ad(!field);
3886
3887 ulint col_len;
3888 ulint is_unsigned;
3889 ulint field_type;
3890 ulint charset_no;
3891
3892 field = altered_table->field[i - 1];
3893
3894 ulint col_type
3895 = get_innobase_type_from_mysql_type(
3896 &is_unsigned, field);
3897
3898
3899 if (!innobase_is_v_fld(field)) {
3900 continue;
3901 }
3902
3903 col_len = field->pack_length();
3904 field_type = (ulint) field->type();
3905
3906 if (!field->real_maybe_null()) {
3907 field_type |= DATA_NOT_NULL;
3908 }
3909
3910 if (field->binary()) {
3911 field_type |= DATA_BINARY_TYPE;
3912 }
3913
3914 if (is_unsigned) {
3915 field_type |= DATA_UNSIGNED;
3916 }
3917
3918 if (dtype_is_string_type(col_type)) {
3919 charset_no = (ulint) field->charset()->number;
3920
3921 DBUG_EXECUTE_IF(
3922 "ib_alter_add_virtual_fail",
3923 charset_no += MAX_CHAR_COLL_NUM;);
3924
3925 if (charset_no > MAX_CHAR_COLL_NUM) {
3926 my_error(ER_WRONG_KEY_COLUMN, MYF(0), "InnoDB",
3927 field->field_name.str);
3928 return(true);
3929 }
3930 } else {
3931 charset_no = 0;
3932 }
3933
3934 if (field->type() == MYSQL_TYPE_VARCHAR) {
3935 uint32 length_bytes
3936 = static_cast<const Field_varstring*>(
3937 field)->length_bytes;
3938
3939 col_len -= length_bytes;
3940
3941 if (length_bytes == 2) {
3942 field_type |= DATA_LONG_TRUE_VARCHAR;
3943 }
3944 }
3945
3946
3947 ctx->add_vcol[j].m_col.prtype = dtype_form_prtype(
3948 field_type, charset_no);
3949
3950 ctx->add_vcol[j].m_col.prtype |= DATA_VIRTUAL;
3951
3952 ctx->add_vcol[j].m_col.mtype = col_type;
3953
3954 ctx->add_vcol[j].m_col.len = col_len;
3955
3956 ctx->add_vcol[j].m_col.ind = i - 1;
3957 ctx->add_vcol[j].num_base = 0;
3958 ctx->add_vcol_name[j] = field->field_name.str;
3959 ctx->add_vcol[j].base_col = NULL;
3960 ctx->add_vcol[j].v_pos = ctx->old_table->n_v_cols
3961 - ctx->num_to_drop_vcol + j;
3962
3963 /* No need to track the list */
3964 ctx->add_vcol[j].v_indexes = NULL;
3965 innodb_base_col_setup(ctx->old_table, field, &ctx->add_vcol[j]);
3966 j++;
3967 }
3968
3969 return(false);
3970}
3971
3972/** Collect virtual column info for its addition
3973@param[in] ha_alter_info Data used during in-place alter
3974@param[in] table MySQL table as it is before the ALTER operation
3975@retval true Failure
3976@retval false Success */
3977static
3978bool
3979prepare_inplace_drop_virtual(
3980 Alter_inplace_info* ha_alter_info,
3981 const TABLE* table)
3982{
3983 ha_innobase_inplace_ctx* ctx;
3984 ulint i = 0;
3985 ulint j = 0;
3986
3987 ctx = static_cast<ha_innobase_inplace_ctx*>
3988 (ha_alter_info->handler_ctx);
3989
3990 ctx->num_to_drop_vcol = 0;
3991 for (i = 0; table->field[i]; i++) {
3992 const Field* field = table->field[i];
3993 if (field->flags & FIELD_IS_DROPPED && !field->stored_in_db()) {
3994 ctx->num_to_drop_vcol++;
3995 }
3996 }
3997
3998 ctx->drop_vcol = static_cast<dict_v_col_t*>(
3999 mem_heap_alloc(ctx->heap, ctx->num_to_drop_vcol
4000 * sizeof *ctx->drop_vcol));
4001 ctx->drop_vcol_name = static_cast<const char**>(
4002 mem_heap_alloc(ctx->heap, ctx->num_to_drop_vcol
4003 * sizeof *ctx->drop_vcol_name));
4004
4005 for (i = 0; table->field[i]; i++) {
4006 Field *field = table->field[i];
4007 if (!(field->flags & FIELD_IS_DROPPED) || field->stored_in_db()) {
4008 continue;
4009 }
4010
4011 ulint col_len;
4012 ulint is_unsigned;
4013 ulint field_type;
4014 ulint charset_no;
4015
4016 ulint col_type
4017 = get_innobase_type_from_mysql_type(
4018 &is_unsigned, field);
4019
4020 col_len = field->pack_length();
4021 field_type = (ulint) field->type();
4022
4023 if (!field->real_maybe_null()) {
4024 field_type |= DATA_NOT_NULL;
4025 }
4026
4027 if (field->binary()) {
4028 field_type |= DATA_BINARY_TYPE;
4029 }
4030
4031 if (is_unsigned) {
4032 field_type |= DATA_UNSIGNED;
4033 }
4034
4035 if (dtype_is_string_type(col_type)) {
4036 charset_no = (ulint) field->charset()->number;
4037
4038 DBUG_EXECUTE_IF(
4039 "ib_alter_add_virtual_fail",
4040 charset_no += MAX_CHAR_COLL_NUM;);
4041
4042 if (charset_no > MAX_CHAR_COLL_NUM) {
4043 my_error(ER_WRONG_KEY_COLUMN, MYF(0), "InnoDB",
4044 field->field_name.str);
4045 return(true);
4046 }
4047 } else {
4048 charset_no = 0;
4049 }
4050
4051 if (field->type() == MYSQL_TYPE_VARCHAR) {
4052 uint32 length_bytes
4053 = static_cast<const Field_varstring*>(
4054 field)->length_bytes;
4055
4056 col_len -= length_bytes;
4057
4058 if (length_bytes == 2) {
4059 field_type |= DATA_LONG_TRUE_VARCHAR;
4060 }
4061 }
4062
4063
4064 ctx->drop_vcol[j].m_col.prtype = dtype_form_prtype(
4065 field_type, charset_no);
4066
4067 ctx->drop_vcol[j].m_col.prtype |= DATA_VIRTUAL;
4068
4069 ctx->drop_vcol[j].m_col.mtype = col_type;
4070
4071 ctx->drop_vcol[j].m_col.len = col_len;
4072
4073 ctx->drop_vcol[j].m_col.ind = i;
4074
4075 ctx->drop_vcol_name[j] = field->field_name.str;
4076
4077 dict_v_col_t* v_col = dict_table_get_nth_v_col_mysql(
4078 ctx->old_table, i);
4079 ctx->drop_vcol[j].v_pos = v_col->v_pos;
4080 j++;
4081 }
4082
4083 return(false);
4084}
4085
4086/** Insert a new record to INNODB SYS_VIRTUAL
4087@param[in] table InnoDB table
4088@param[in] pos virtual column column no
4089@param[in] base_pos base column pos
4090@param[in] trx transaction
4091@return DB_SUCCESS if successful, otherwise error code */
4092static
4093dberr_t
4094innobase_insert_sys_virtual(
4095 const dict_table_t* table,
4096 ulint pos,
4097 ulint base_pos,
4098 trx_t* trx)
4099{
4100 pars_info_t* info = pars_info_create();
4101
4102 pars_info_add_ull_literal(info, "id", table->id);
4103
4104 pars_info_add_int4_literal(info, "pos", pos);
4105
4106 pars_info_add_int4_literal(info, "base_pos", base_pos);
4107
4108 dberr_t error = que_eval_sql(
4109 info,
4110 "PROCEDURE P () IS\n"
4111 "BEGIN\n"
4112 "INSERT INTO SYS_VIRTUAL VALUES"
4113 "(:id, :pos, :base_pos);\n"
4114 "END;\n",
4115 FALSE, trx);
4116
4117 return(error);
4118}
4119
4120/** Update INNODB SYS_COLUMNS on new virtual columns
4121@param[in] table InnoDB table
4122@param[in] col_name column name
4123@param[in] vcol virtual column
4124@param[in] trx transaction
4125@return DB_SUCCESS if successful, otherwise error code */
4126static
4127dberr_t
4128innobase_add_one_virtual(
4129 const dict_table_t* table,
4130 const char* col_name,
4131 dict_v_col_t* vcol,
4132 trx_t* trx)
4133{
4134 ulint pos = dict_create_v_col_pos(vcol->v_pos,
4135 vcol->m_col.ind);
4136 ulint mtype = vcol->m_col.mtype;
4137 ulint prtype = vcol->m_col.prtype;
4138 ulint len = vcol->m_col.len;
4139 pars_info_t* info = pars_info_create();
4140
4141 pars_info_add_ull_literal(info, "id", table->id);
4142
4143 pars_info_add_int4_literal(info, "pos", pos);
4144
4145 pars_info_add_str_literal(info, "name", col_name);
4146 pars_info_add_int4_literal(info, "mtype", mtype);
4147 pars_info_add_int4_literal(info, "prtype", prtype);
4148 pars_info_add_int4_literal(info, "len", len);
4149 pars_info_add_int4_literal(info, "prec", vcol->num_base);
4150
4151 dberr_t error = que_eval_sql(
4152 info,
4153 "PROCEDURE P () IS\n"
4154 "BEGIN\n"
4155 "INSERT INTO SYS_COLUMNS VALUES"
4156 "(:id, :pos, :name, :mtype, :prtype, :len, :prec);\n"
4157 "END;\n",
4158 FALSE, trx);
4159
4160 if (error != DB_SUCCESS) {
4161 return(error);
4162 }
4163
4164 for (ulint i = 0; i < vcol->num_base; i++) {
4165 error = innobase_insert_sys_virtual(
4166 table, pos, vcol->base_col[i]->ind, trx);
4167 if (error != DB_SUCCESS) {
4168 return(error);
4169 }
4170 }
4171
4172 return(error);
4173}
4174
4175/** Update SYS_TABLES.N_COLS in the data dictionary.
4176@param[in] user_table InnoDB table
4177@param[in] n_cols the new value of SYS_TABLES.N_COLS
4178@param[in] trx transaction
4179@return whether the operation failed */
4180static
4181bool
4182innodb_update_n_cols(const dict_table_t* table, ulint n_cols, trx_t* trx)
4183{
4184 pars_info_t* info = pars_info_create();
4185
4186 pars_info_add_int4_literal(info, "n", n_cols);
4187 pars_info_add_ull_literal(info, "id", table->id);
4188
4189 dberr_t err = que_eval_sql(info,
4190 "PROCEDURE UPDATE_N_COLS () IS\n"
4191 "BEGIN\n"
4192 "UPDATE SYS_TABLES SET N_COLS = :n"
4193 " WHERE ID = :id;\n"
4194 "END;\n", FALSE, trx);
4195
4196 if (err != DB_SUCCESS) {
4197 my_error(ER_INTERNAL_ERROR, MYF(0),
4198 "InnoDB: Updating SYS_TABLES.N_COLS failed");
4199 return true;
4200 }
4201
4202 return false;
4203}
4204
4205/** Update system table for adding virtual column(s)
4206@param[in] ha_alter_info Data used during in-place alter
4207@param[in] user_table InnoDB table
4208@param[in] trx transaction
4209@retval true Failure
4210@retval false Success */
4211static
4212bool
4213innobase_add_virtual_try(
4214 Alter_inplace_info* ha_alter_info,
4215 const dict_table_t* user_table,
4216 trx_t* trx)
4217{
4218 ha_innobase_inplace_ctx* ctx;
4219 dberr_t err = DB_SUCCESS;
4220
4221 ctx = static_cast<ha_innobase_inplace_ctx*>(
4222 ha_alter_info->handler_ctx);
4223
4224 for (ulint i = 0; i < ctx->num_to_add_vcol; i++) {
4225
4226 err = innobase_add_one_virtual(
4227 user_table, ctx->add_vcol_name[i],
4228 &ctx->add_vcol[i], trx);
4229
4230 if (err != DB_SUCCESS) {
4231 my_error(ER_INTERNAL_ERROR, MYF(0),
4232 "InnoDB: ADD COLUMN...VIRTUAL");
4233 return(true);
4234 }
4235 }
4236
4237
4238 ulint n_col = unsigned(user_table->n_cols) - DATA_N_SYS_COLS;
4239 ulint n_v_col = unsigned(user_table->n_v_cols)
4240 + ctx->num_to_add_vcol - ctx->num_to_drop_vcol;
4241 ulint new_n = dict_table_encode_n_col(n_col, n_v_col)
4242 + (unsigned(user_table->flags & DICT_TF_COMPACT) << 31);
4243
4244 return innodb_update_n_cols(user_table, new_n, trx);
4245}
4246
4247/** Insert into SYS_COLUMNS and insert/update the 'default row'
4248for instant ADD COLUMN.
4249@param[in,out] ctx ALTER TABLE context for the current partition
4250@param[in] altered_table MySQL table that is being altered
4251@param[in] table MySQL table as it is before the ALTER operation
4252@param[in,out] trx dictionary transaction
4253@retval true failure
4254@retval false success */
4255static
4256bool
4257innobase_add_instant_try(
4258 ha_innobase_inplace_ctx*ctx,
4259 const TABLE* altered_table,
4260 const TABLE* table,
4261 trx_t* trx)
4262{
4263 DBUG_ASSERT(!ctx->need_rebuild());
4264
4265 if (!ctx->is_instant()) return false;
4266
4267 DBUG_ASSERT(altered_table->s->fields > table->s->fields);
4268 DBUG_ASSERT(ctx->old_table->n_cols == ctx->old_n_cols);
4269
4270 dict_table_t* user_table = ctx->old_table;
4271 user_table->instant_add_column(*ctx->instant_table);
4272 dict_index_t* index = dict_table_get_first_index(user_table);
4273 /* The table may have been emptied and may have lost its
4274 'instant-add-ness' during this instant ADD COLUMN. */
4275
4276 /* Construct a table row of default values for the stored columns. */
4277 dtuple_t* row = dtuple_create(ctx->heap, user_table->n_cols);
4278 dict_table_copy_types(row, user_table);
4279 Field** af = altered_table->field;
4280 Field** const end = altered_table->field + altered_table->s->fields;
4281
4282 for (uint i = 0; af < end; af++) {
4283 if (!(*af)->stored_in_db()) {
4284 continue;
4285 }
4286
4287 dict_col_t* col = dict_table_get_nth_col(user_table, i);
4288 DBUG_ASSERT(!strcmp((*af)->field_name.str,
4289 dict_table_get_col_name(user_table, i)));
4290
4291 dfield_t* d = dtuple_get_nth_field(row, i);
4292
4293 if (col->is_instant()) {
4294 dfield_set_data(d, col->def_val.data,
4295 col->def_val.len);
4296 } else if ((*af)->real_maybe_null()) {
4297 /* Store NULL for nullable 'core' columns. */
4298 dfield_set_null(d);
4299 } else {
4300 switch ((*af)->type()) {
4301 case MYSQL_TYPE_VARCHAR:
4302 case MYSQL_TYPE_GEOMETRY:
4303 case MYSQL_TYPE_TINY_BLOB:
4304 case MYSQL_TYPE_MEDIUM_BLOB:
4305 case MYSQL_TYPE_BLOB:
4306 case MYSQL_TYPE_LONG_BLOB:
4307 /* Store the empty string for 'core'
4308 variable-length NOT NULL columns. */
4309 dfield_set_data(d, field_ref_zero, 0);
4310 break;
4311 default:
4312 /* For fixed-length NOT NULL 'core' columns,
4313 get a dummy default value from SQL. Note that
4314 we will preserve the old values of these
4315 columns when updating the 'default row'
4316 record, to avoid unnecessary updates. */
4317 ulint len = (*af)->pack_length();
4318 DBUG_ASSERT(d->type.mtype != DATA_INT
4319 || len <= 8);
4320 row_mysql_store_col_in_innobase_format(
4321 d, d->type.mtype == DATA_INT
4322 ? static_cast<byte*>(
4323 mem_heap_alloc(ctx->heap, len))
4324 : NULL, true, (*af)->ptr, len,
4325 dict_table_is_comp(user_table));
4326 }
4327 }
4328
4329 if (i + DATA_N_SYS_COLS < ctx->old_n_cols) {
4330 i++;
4331 continue;
4332 }
4333
4334 pars_info_t* info = pars_info_create();
4335 pars_info_add_ull_literal(info, "id", user_table->id);
4336 pars_info_add_int4_literal(info, "pos", i);
4337 pars_info_add_str_literal(info, "name", (*af)->field_name.str);
4338 pars_info_add_int4_literal(info, "mtype", d->type.mtype);
4339 pars_info_add_int4_literal(info, "prtype", d->type.prtype);
4340 pars_info_add_int4_literal(info, "len", d->type.len);
4341
4342 dberr_t err = que_eval_sql(
4343 info,
4344 "PROCEDURE ADD_COL () IS\n"
4345 "BEGIN\n"
4346 "INSERT INTO SYS_COLUMNS VALUES"
4347 "(:id,:pos,:name,:mtype,:prtype,:len,0);\n"
4348 "END;\n", FALSE, trx);
4349 if (err != DB_SUCCESS) {
4350 my_error(ER_INTERNAL_ERROR, MYF(0),
4351 "InnoDB: Insert into SYS_COLUMNS failed");
4352 return(true);
4353 }
4354
4355 i++;
4356 }
4357
4358 if (innodb_update_n_cols(user_table, dict_table_encode_n_col(
4359 unsigned(user_table->n_cols)
4360 - DATA_N_SYS_COLS,
4361 user_table->n_v_cols)
4362 | (user_table->flags & DICT_TF_COMPACT) << 31,
4363 trx)) {
4364 return true;
4365 }
4366
4367 unsigned i = unsigned(user_table->n_cols) - DATA_N_SYS_COLS;
4368 byte trx_id[DATA_TRX_ID_LEN], roll_ptr[DATA_ROLL_PTR_LEN];
4369 dfield_set_data(dtuple_get_nth_field(row, i++), field_ref_zero,
4370 DATA_ROW_ID_LEN);
4371 dfield_set_data(dtuple_get_nth_field(row, i++), trx_id, sizeof trx_id);
4372 dfield_set_data(dtuple_get_nth_field(row, i),roll_ptr,sizeof roll_ptr);
4373 DBUG_ASSERT(i + 1 == user_table->n_cols);
4374
4375 trx_write_trx_id(trx_id, trx->id);
4376 /* The DB_ROLL_PTR will be assigned later, when allocating undo log.
4377 Silence a Valgrind warning in dtuple_validate() when
4378 row_ins_clust_index_entry_low() searches for the insert position. */
4379 memset(roll_ptr, 0, sizeof roll_ptr);
4380
4381 dtuple_t* entry = row_build_index_entry(row, NULL, index, ctx->heap);
4382 entry->info_bits = REC_INFO_DEFAULT_ROW;
4383
4384 mtr_t mtr;
4385 mtr.start();
4386 index->set_modified(mtr);
4387 btr_pcur_t pcur;
4388 btr_pcur_open_at_index_side(true, index, BTR_MODIFY_TREE, &pcur, true,
4389 0, &mtr);
4390 ut_ad(btr_pcur_is_before_first_on_page(&pcur));
4391 btr_pcur_move_to_next_on_page(&pcur);
4392
4393 buf_block_t* block = btr_pcur_get_block(&pcur);
4394 ut_ad(page_is_leaf(block->frame));
4395 ut_ad(!page_has_prev(block->frame));
4396 ut_ad(!buf_block_get_page_zip(block));
4397 const rec_t* rec = btr_pcur_get_rec(&pcur);
4398 que_thr_t* thr = pars_complete_graph_for_exec(
4399 NULL, trx, ctx->heap, NULL);
4400
4401 if (rec_is_default_row(rec, index)) {
4402 ut_ad(page_rec_is_user_rec(rec));
4403 if (!page_has_next(block->frame)
4404 && page_rec_is_last(rec, block->frame)) {
4405 goto empty_table;
4406 }
4407 /* Extend the record with the instantly added columns. */
4408 const unsigned n = user_table->n_cols - ctx->old_n_cols;
4409 /* Reserve room for DB_TRX_ID,DB_ROLL_PTR and any
4410 non-updated off-page columns in case they are moved off
4411 page as a result of the update. */
4412 upd_t* update = upd_create(index->n_fields, ctx->heap);
4413 update->n_fields = n;
4414 update->info_bits = REC_INFO_DEFAULT_ROW;
4415 /* Add the default values for instantly added columns */
4416 for (unsigned i = 0; i < n; i++) {
4417 upd_field_t* uf = upd_get_nth_field(update, i);
4418 unsigned f = index->n_fields - n + i;
4419 uf->field_no = f;
4420 uf->new_val = entry->fields[f];
4421 }
4422 ulint* offsets = NULL;
4423 mem_heap_t* offsets_heap = NULL;
4424 big_rec_t* big_rec;
4425 dberr_t error = btr_cur_pessimistic_update(
4426 BTR_NO_LOCKING_FLAG, btr_pcur_get_btr_cur(&pcur),
4427 &offsets, &offsets_heap, ctx->heap,
4428 &big_rec, update, UPD_NODE_NO_ORD_CHANGE,
4429 thr, trx->id, &mtr);
4430 if (big_rec) {
4431 if (error == DB_SUCCESS) {
4432 error = btr_store_big_rec_extern_fields(
4433 &pcur, offsets, big_rec, &mtr,
4434 BTR_STORE_UPDATE);
4435 }
4436
4437 dtuple_big_rec_free(big_rec);
4438 }
4439 if (offsets_heap) {
4440 mem_heap_free(offsets_heap);
4441 }
4442 btr_pcur_close(&pcur);
4443 mtr.commit();
4444 return error != DB_SUCCESS;
4445 } else if (page_rec_is_supremum(rec)) {
4446empty_table:
4447 /* The table is empty. */
4448 ut_ad(page_is_root(block->frame));
4449 btr_page_empty(block, NULL, index, 0, &mtr);
4450 index->remove_instant();
4451 mtr.commit();
4452 return false;
4453 }
4454
4455 /* Convert the table to the instant ADD COLUMN format. */
4456 ut_ad(user_table->is_instant());
4457 mtr.commit();
4458 mtr.start();
4459 index->set_modified(mtr);
4460 dberr_t err;
4461 if (page_t* root = btr_root_get(index, &mtr)) {
4462 switch (fil_page_get_type(root)) {
4463 case FIL_PAGE_TYPE_INSTANT:
4464 DBUG_ASSERT(page_get_instant(root)
4465 == index->n_core_fields);
4466 break;
4467 case FIL_PAGE_INDEX:
4468 DBUG_ASSERT(!page_is_comp(root)
4469 || !page_get_instant(root));
4470 break;
4471 default:
4472 DBUG_ASSERT(!"wrong page type");
4473 mtr.commit();
4474 return true;
4475 }
4476
4477 mlog_write_ulint(root + FIL_PAGE_TYPE,
4478 FIL_PAGE_TYPE_INSTANT, MLOG_2BYTES,
4479 &mtr);
4480 page_set_instant(root, index->n_core_fields, &mtr);
4481 mtr.commit();
4482 mtr.start();
4483 index->set_modified(mtr);
4484 err = row_ins_clust_index_entry_low(
4485 BTR_NO_LOCKING_FLAG, BTR_MODIFY_TREE, index,
4486 index->n_uniq, entry, 0, thr, false);
4487 } else {
4488 err = DB_CORRUPTION;
4489 }
4490
4491 mtr.commit();
4492 return err != DB_SUCCESS;
4493}
4494
4495/** Update INNODB SYS_COLUMNS on new virtual column's position
4496@param[in] table InnoDB table
4497@param[in] old_pos old position
4498@param[in] new_pos new position
4499@param[in] trx transaction
4500@return DB_SUCCESS if successful, otherwise error code */
4501static
4502dberr_t
4503innobase_update_v_pos_sys_columns(
4504 const dict_table_t* table,
4505 ulint old_pos,
4506 ulint new_pos,
4507 trx_t* trx)
4508{
4509 pars_info_t* info = pars_info_create();
4510
4511 pars_info_add_int4_literal(info, "pos", old_pos);
4512 pars_info_add_int4_literal(info, "val", new_pos);
4513 pars_info_add_ull_literal(info, "id", table->id);
4514
4515 dberr_t error = que_eval_sql(
4516 info,
4517 "PROCEDURE P () IS\n"
4518 "BEGIN\n"
4519 "UPDATE SYS_COLUMNS\n"
4520 "SET POS = :val\n"
4521 "WHERE POS = :pos\n"
4522 "AND TABLE_ID = :id;\n"
4523 "END;\n",
4524 FALSE, trx);
4525
4526 return(error);
4527}
4528
4529/** Update INNODB SYS_VIRTUAL table with new virtual column position
4530@param[in] table InnoDB table
4531@param[in] old_pos old position
4532@param[in] new_pos new position
4533@param[in] trx transaction
4534@return DB_SUCCESS if successful, otherwise error code */
4535static
4536dberr_t
4537innobase_update_v_pos_sys_virtual(
4538 const dict_table_t* table,
4539 ulint old_pos,
4540 ulint new_pos,
4541 trx_t* trx)
4542{
4543 pars_info_t* info = pars_info_create();
4544
4545 pars_info_add_int4_literal(info, "pos", old_pos);
4546 pars_info_add_int4_literal(info, "val", new_pos);
4547 pars_info_add_ull_literal(info, "id", table->id);
4548
4549 dberr_t error = que_eval_sql(
4550 info,
4551 "PROCEDURE P () IS\n"
4552 "BEGIN\n"
4553 "UPDATE SYS_VIRTUAL\n"
4554 "SET POS = :val\n"
4555 "WHERE POS = :pos\n"
4556 "AND TABLE_ID = :id;\n"
4557 "END;\n",
4558 FALSE, trx);
4559
4560 return(error);
4561}
4562
4563/** Update InnoDB system tables on dropping a virtual column
4564@param[in] table InnoDB table
4565@param[in] col_name column name of the dropping column
4566@param[in] drop_col col information for the dropping column
4567@param[in] n_prev_dropped number of previously dropped columns in the
4568 same alter clause
4569@param[in] trx transaction
4570@return DB_SUCCESS if successful, otherwise error code */
4571static
4572dberr_t
4573innobase_drop_one_virtual_sys_columns(
4574 const dict_table_t* table,
4575 const char* col_name,
4576 dict_col_t* drop_col,
4577 ulint n_prev_dropped,
4578 trx_t* trx)
4579{
4580 pars_info_t* info = pars_info_create();
4581 pars_info_add_ull_literal(info, "id", table->id);
4582
4583 pars_info_add_str_literal(info, "name", col_name);
4584
4585 dberr_t error = que_eval_sql(
4586 info,
4587 "PROCEDURE P () IS\n"
4588 "BEGIN\n"
4589 "DELETE FROM SYS_COLUMNS\n"
4590 "WHERE TABLE_ID = :id\n"
4591 "AND NAME = :name;\n"
4592 "END;\n",
4593 FALSE, trx);
4594
4595 if (error != DB_SUCCESS) {
4596 return(error);
4597 }
4598
4599 dict_v_col_t* v_col = dict_table_get_nth_v_col_mysql(
4600 table, drop_col->ind);
4601
4602 /* Adjust column positions for all subsequent columns */
4603 for (ulint i = v_col->v_pos + 1; i < table->n_v_cols; i++) {
4604 dict_v_col_t* t_col = dict_table_get_nth_v_col(table, i);
4605 ulint old_p = dict_create_v_col_pos(
4606 t_col->v_pos - n_prev_dropped,
4607 t_col->m_col.ind - n_prev_dropped);
4608 ulint new_p = dict_create_v_col_pos(
4609 t_col->v_pos - 1 - n_prev_dropped,
4610 ulint(t_col->m_col.ind) - 1 - n_prev_dropped);
4611
4612 error = innobase_update_v_pos_sys_columns(
4613 table, old_p, new_p, trx);
4614 if (error != DB_SUCCESS) {
4615 return(error);
4616 }
4617 error = innobase_update_v_pos_sys_virtual(
4618 table, old_p, new_p, trx);
4619 if (error != DB_SUCCESS) {
4620 return(error);
4621 }
4622 }
4623
4624 return(error);
4625}
4626
4627/** Delete virtual column's info from INNODB SYS_VIRTUAL
4628@param[in] table InnoDB table
4629@param[in] pos position of the virtual column to be deleted
4630@param[in] trx transaction
4631@return DB_SUCCESS if successful, otherwise error code */
4632static
4633dberr_t
4634innobase_drop_one_virtual_sys_virtual(
4635 const dict_table_t* table,
4636 ulint pos,
4637 trx_t* trx)
4638{
4639 pars_info_t* info = pars_info_create();
4640 pars_info_add_ull_literal(info, "id", table->id);
4641
4642 pars_info_add_int4_literal(info, "pos", pos);
4643
4644 dberr_t error = que_eval_sql(
4645 info,
4646 "PROCEDURE P () IS\n"
4647 "BEGIN\n"
4648 "DELETE FROM SYS_VIRTUAL\n"
4649 "WHERE TABLE_ID = :id\n"
4650 "AND POS = :pos;\n"
4651 "END;\n",
4652 FALSE, trx);
4653
4654 return(error);
4655}
4656
4657/** Update system table for dropping virtual column(s)
4658@param[in] ha_alter_info Data used during in-place alter
4659@param[in] user_table InnoDB table
4660@param[in] trx transaction
4661@retval true Failure
4662@retval false Success */
4663static
4664bool
4665innobase_drop_virtual_try(
4666 Alter_inplace_info* ha_alter_info,
4667 const dict_table_t* user_table,
4668 trx_t* trx)
4669{
4670 ha_innobase_inplace_ctx* ctx;
4671 dberr_t err = DB_SUCCESS;
4672
4673 ctx = static_cast<ha_innobase_inplace_ctx*>
4674 (ha_alter_info->handler_ctx);
4675
4676 for (ulint i = 0; i < ctx->num_to_drop_vcol; i++) {
4677
4678 ulint pos = dict_create_v_col_pos(
4679 ctx->drop_vcol[i].v_pos - i,
4680 ctx->drop_vcol[i].m_col.ind - i);
4681 err = innobase_drop_one_virtual_sys_virtual(
4682 user_table, pos, trx);
4683
4684 if (err != DB_SUCCESS) {
4685 my_error(ER_INTERNAL_ERROR, MYF(0),
4686 "InnoDB: DROP COLUMN...VIRTUAL");
4687 return(true);
4688 }
4689
4690 err = innobase_drop_one_virtual_sys_columns(
4691 user_table, ctx->drop_vcol_name[i],
4692 &(ctx->drop_vcol[i].m_col), i, trx);
4693
4694 if (err != DB_SUCCESS) {
4695 my_error(ER_INTERNAL_ERROR, MYF(0),
4696 "InnoDB: DROP COLUMN...VIRTUAL");
4697 return(true);
4698 }
4699 }
4700
4701
4702 ulint n_col = unsigned(user_table->n_cols) - DATA_N_SYS_COLS;
4703 ulint n_v_col = unsigned(user_table->n_v_cols)
4704 - ctx->num_to_drop_vcol;
4705 ulint new_n = dict_table_encode_n_col(n_col, n_v_col)
4706 | ((user_table->flags & DICT_TF_COMPACT) << 31);
4707
4708 return innodb_update_n_cols(user_table, new_n, trx);
4709}
4710
4711/** Adjust the create index column number from "New table" to
4712"old InnoDB table" while we are doing dropping virtual column. Since we do
4713not create separate new table for the dropping/adding virtual columns.
4714To correctly find the indexed column, we will need to find its col_no
4715in the "Old Table", not the "New table".
4716@param[in] ha_alter_info Data used during in-place alter
4717@param[in] old_table MySQL table as it is before the ALTER operation
4718@param[in] num_v_dropped number of virtual column dropped
4719@param[in,out] index_def index definition */
4720static
4721void
4722innodb_v_adjust_idx_col(
4723 const Alter_inplace_info* ha_alter_info,
4724 const TABLE* old_table,
4725 ulint num_v_dropped,
4726 index_def_t* index_def)
4727{
4728 List_iterator_fast<Create_field> cf_it(
4729 ha_alter_info->alter_info->create_list);
4730 for (ulint i = 0; i < index_def->n_fields; i++) {
4731#ifdef UNIV_DEBUG
4732 bool col_found = false;
4733#endif /* UNIV_DEBUG */
4734 ulint num_v = 0;
4735
4736 index_field_t* index_field = &index_def->fields[i];
4737
4738 /* Only adjust virtual column col_no, since non-virtual
4739 column position (in non-vcol list) won't change unless
4740 table rebuild */
4741 if (!index_field->is_v_col) {
4742 continue;
4743 }
4744
4745 const Field* field = NULL;
4746
4747 cf_it.rewind();
4748
4749 /* Found the field in the new table */
4750 while (const Create_field* new_field = cf_it++) {
4751 if (!innobase_is_v_fld(new_field)) {
4752 continue;
4753 }
4754
4755 field = new_field->field;
4756
4757 if (num_v == index_field->col_no) {
4758 break;
4759 }
4760 num_v++;
4761 }
4762
4763 if (!field) {
4764 /* this means the field is a newly added field, this
4765 should have been blocked when we drop virtual column
4766 at the same time */
4767 ut_ad(num_v_dropped > 0);
4768 ut_a(0);
4769 }
4770
4771 ut_ad(innobase_is_v_fld(field));
4772
4773 num_v = 0;
4774
4775 /* Look for its position in old table */
4776 for (uint old_i = 0; old_table->field[old_i]; old_i++) {
4777 if (old_table->field[old_i] == field) {
4778 /* Found it, adjust its col_no to its position
4779 in old table */
4780 index_def->fields[i].col_no = num_v;
4781 ut_d(col_found = true);
4782 break;
4783 }
4784
4785 if (innobase_is_v_fld(old_table->field[old_i])) {
4786 num_v++;
4787 }
4788 }
4789
4790 ut_ad(col_found);
4791 }
4792}
4793
4794/** Create index metadata in the data dictionary.
4795@param[in,out] trx dictionary transaction
4796@param[in,out] index index being created
4797@param[in] add_v virtual columns that are being added, or NULL
4798@return the created index */
4799MY_ATTRIBUTE((nonnull(1,2), warn_unused_result))
4800static
4801dict_index_t*
4802create_index_dict(
4803 trx_t* trx,
4804 dict_index_t* index,
4805 const dict_add_v_col_t* add_v)
4806{
4807 DBUG_ENTER("create_index_dict");
4808
4809 mem_heap_t* heap = mem_heap_create(512);
4810 ind_node_t* node = ind_create_graph_create(
4811 index, index->table->name.m_name, heap, add_v);
4812 que_thr_t* thr = pars_complete_graph_for_exec(node, trx, heap, NULL);
4813
4814 que_fork_start_command(
4815 static_cast<que_fork_t*>(que_node_get_parent(thr)));
4816
4817 que_run_threads(thr);
4818
4819 index = node->index;
4820
4821 que_graph_free((que_t*) que_node_get_parent(thr));
4822
4823 DBUG_RETURN(trx->error_state == DB_SUCCESS ? index : NULL);
4824}
4825
4826/** Update internal structures with concurrent writes blocked,
4827while preparing ALTER TABLE.
4828
4829@param ha_alter_info Data used during in-place alter
4830@param altered_table MySQL table that is being altered
4831@param old_table MySQL table as it is before the ALTER operation
4832@param table_name Table name in MySQL
4833@param flags Table and tablespace flags
4834@param flags2 Additional table flags
4835@param fts_doc_id_col The column number of FTS_DOC_ID
4836@param add_fts_doc_id Flag: add column FTS_DOC_ID?
4837@param add_fts_doc_id_idx Flag: add index FTS_DOC_ID_INDEX (FTS_DOC_ID)?
4838
4839@retval true Failure
4840@retval false Success
4841*/
4842static MY_ATTRIBUTE((warn_unused_result, nonnull(1,2,3,4)))
4843bool
4844prepare_inplace_alter_table_dict(
4845/*=============================*/
4846 Alter_inplace_info* ha_alter_info,
4847 const TABLE* altered_table,
4848 const TABLE* old_table,
4849 const char* table_name,
4850 ulint flags,
4851 ulint flags2,
4852 ulint fts_doc_id_col,
4853 bool add_fts_doc_id,
4854 bool add_fts_doc_id_idx)
4855{
4856 bool dict_locked = false;
4857 ulint* add_key_nums; /* MySQL key numbers */
4858 index_def_t* index_defs; /* index definitions */
4859 dict_table_t* user_table;
4860 dict_index_t* fts_index = NULL;
4861 bool new_clustered = false;
4862 dberr_t error;
4863 ulint num_fts_index;
4864 dict_add_v_col_t* add_v = NULL;
4865 ha_innobase_inplace_ctx*ctx;
4866
4867 DBUG_ENTER("prepare_inplace_alter_table_dict");
4868
4869 ctx = static_cast<ha_innobase_inplace_ctx*>
4870 (ha_alter_info->handler_ctx);
4871
4872 DBUG_ASSERT((ctx->add_autoinc != ULINT_UNDEFINED)
4873 == (ctx->sequence.m_max_value > 0));
4874 DBUG_ASSERT(!ctx->num_to_drop_index == !ctx->drop_index);
4875 DBUG_ASSERT(!ctx->num_to_drop_fk == !ctx->drop_fk);
4876 DBUG_ASSERT(!add_fts_doc_id || add_fts_doc_id_idx);
4877 DBUG_ASSERT(!add_fts_doc_id_idx
4878 || innobase_fulltext_exist(altered_table));
4879 DBUG_ASSERT(!ctx->defaults);
4880 DBUG_ASSERT(!ctx->add_index);
4881 DBUG_ASSERT(!ctx->add_key_numbers);
4882 DBUG_ASSERT(!ctx->num_to_add_index);
4883
4884 user_table = ctx->new_table;
4885
4886 trx_start_if_not_started_xa(ctx->prebuilt->trx, true);
4887
4888 if (ha_alter_info->handler_flags
4889 & ALTER_DROP_VIRTUAL_COLUMN) {
4890 if (prepare_inplace_drop_virtual(ha_alter_info, old_table)) {
4891 DBUG_RETURN(true);
4892 }
4893 }
4894
4895 if (ha_alter_info->handler_flags
4896 & ALTER_ADD_VIRTUAL_COLUMN) {
4897 if (prepare_inplace_add_virtual(
4898 ha_alter_info, altered_table, old_table)) {
4899 DBUG_RETURN(true);
4900 }
4901
4902 /* Need information for newly added virtual columns
4903 for create index */
4904
4905 if (ha_alter_info->handler_flags
4906 & ALTER_ADD_NON_UNIQUE_NON_PRIM_INDEX) {
4907 for (ulint i = 0; i < ctx->num_to_add_vcol; i++) {
4908 /* Set mbminmax for newly added column */
4909 dict_col_t& col = ctx->add_vcol[i].m_col;
4910 ulint mbminlen, mbmaxlen;
4911 dtype_get_mblen(col.mtype, col.prtype,
4912 &mbminlen, &mbmaxlen);
4913 col.mbminlen = mbminlen;
4914 col.mbmaxlen = mbmaxlen;
4915 }
4916 add_v = static_cast<dict_add_v_col_t*>(
4917 mem_heap_alloc(ctx->heap, sizeof *add_v));
4918 add_v->n_v_col = ctx->num_to_add_vcol;
4919 add_v->v_col = ctx->add_vcol;
4920 add_v->v_col_name = ctx->add_vcol_name;
4921 }
4922 }
4923
4924 /* There should be no order change for virtual columns coming in
4925 here */
4926 ut_ad(check_v_col_in_order(old_table, altered_table, ha_alter_info));
4927
4928 /* Create table containing all indexes to be built in this
4929 ALTER TABLE ADD INDEX so that they are in the correct order
4930 in the table. */
4931
4932 ctx->num_to_add_index = ha_alter_info->index_add_count;
4933
4934 ut_ad(ctx->prebuilt->trx->mysql_thd != NULL);
4935 const char* path = thd_innodb_tmpdir(
4936 ctx->prebuilt->trx->mysql_thd);
4937
4938 index_defs = innobase_create_key_defs(
4939 ctx->heap, ha_alter_info, altered_table, ctx->num_to_add_index,
4940 num_fts_index,
4941 dict_index_is_auto_gen_clust(dict_table_get_first_index(
4942 ctx->new_table)),
4943 fts_doc_id_col, add_fts_doc_id, add_fts_doc_id_idx,
4944 old_table);
4945
4946 new_clustered = (DICT_CLUSTERED & index_defs[0].ind_type) != 0;
4947
4948 /* The primary index would be rebuilt if a FTS Doc ID
4949 column is to be added, and the primary index definition
4950 is just copied from old table and stored in indexdefs[0] */
4951 DBUG_ASSERT(!add_fts_doc_id || new_clustered);
4952 DBUG_ASSERT(!!new_clustered ==
4953 (innobase_need_rebuild(ha_alter_info, old_table)
4954 || add_fts_doc_id));
4955
4956 /* Allocate memory for dictionary index definitions */
4957
4958 ctx->add_index = static_cast<dict_index_t**>(
4959 mem_heap_zalloc(ctx->heap, ctx->num_to_add_index
4960 * sizeof *ctx->add_index));
4961 ctx->add_key_numbers = add_key_nums = static_cast<ulint*>(
4962 mem_heap_alloc(ctx->heap, ctx->num_to_add_index
4963 * sizeof *ctx->add_key_numbers));
4964
4965 /* Acquire a lock on the table before creating any indexes. */
4966
4967 if (ctx->online) {
4968 error = DB_SUCCESS;
4969 } else {
4970 error = row_merge_lock_table(
4971 ctx->prebuilt->trx, ctx->new_table, LOCK_S);
4972
4973 if (error != DB_SUCCESS) {
4974
4975 goto error_handling;
4976 }
4977 }
4978
4979 /* Create a background transaction for the operations on
4980 the data dictionary tables. */
4981 ctx->trx = innobase_trx_allocate(ctx->prebuilt->trx->mysql_thd);
4982
4983 trx_start_for_ddl(ctx->trx, TRX_DICT_OP_INDEX);
4984
4985 /* Latch the InnoDB data dictionary exclusively so that no deadlocks
4986 or lock waits can happen in it during an index create operation. */
4987
4988 row_mysql_lock_data_dictionary(ctx->trx);
4989 dict_locked = true;
4990
4991 /* Wait for background stats processing to stop using the table that
4992 we are going to alter. We know bg stats will not start using it again
4993 until we are holding the data dict locked and we are holding it here
4994 at least until checking ut_ad(user_table->n_ref_count == 1) below.
4995 XXX what may happen if bg stats opens the table after we
4996 have unlocked data dictionary below? */
4997 dict_stats_wait_bg_to_stop_using_table(user_table, ctx->trx);
4998
4999 online_retry_drop_indexes_low(ctx->new_table, ctx->trx);
5000
5001 ut_d(dict_table_check_for_dup_indexes(
5002 ctx->new_table, CHECK_ABORTED_OK));
5003
5004 DBUG_EXECUTE_IF("innodb_OOM_prepare_inplace_alter",
5005 error = DB_OUT_OF_MEMORY;
5006 goto error_handling;);
5007
5008 /* If a new clustered index is defined for the table we need
5009 to rebuild the table with a temporary name. */
5010
5011 if (new_clustered) {
5012 if (innobase_check_foreigns(
5013 ha_alter_info, old_table,
5014 user_table, ctx->drop_fk, ctx->num_to_drop_fk)) {
5015new_clustered_failed:
5016 DBUG_ASSERT(ctx->trx != ctx->prebuilt->trx);
5017 trx_rollback_to_savepoint(ctx->trx, NULL);
5018
5019 ut_ad(user_table->get_ref_count() == 1);
5020
5021 online_retry_drop_indexes_with_trx(
5022 user_table, ctx->trx);
5023
5024 if (ctx->need_rebuild()) {
5025 ut_ad(!ctx->new_table->cached);
5026 dict_mem_table_free(ctx->new_table);
5027 ctx->new_table = ctx->old_table;
5028 }
5029
5030 while (ctx->num_to_add_index--) {
5031 if (dict_index_t*& i = ctx->add_index[
5032 ctx->num_to_add_index]) {
5033 dict_mem_index_free(i);
5034 i = NULL;
5035 }
5036 }
5037
5038 goto err_exit;
5039 }
5040
5041 size_t dblen = ctx->old_table->name.dblen() + 1;
5042 size_t tablen = altered_table->s->table_name.length;
5043 const char* part = ctx->old_table->name.part();
5044 size_t partlen = part ? strlen(part) : 0;
5045 char* new_table_name = static_cast<char*>(
5046 mem_heap_alloc(ctx->heap,
5047 dblen + tablen + partlen + 1));
5048 memcpy(new_table_name, ctx->old_table->name.m_name, dblen);
5049 memcpy(new_table_name + dblen,
5050 altered_table->s->table_name.str, tablen);
5051 memcpy(new_table_name + dblen + tablen,
5052 part ? part : "", partlen + 1);
5053 ulint n_cols = 0;
5054 ulint n_v_cols = 0;
5055 dtuple_t* defaults;
5056 ulint z = 0;
5057
5058 for (uint i = 0; i < altered_table->s->fields; i++) {
5059 const Field* field = altered_table->field[i];
5060
5061 if (innobase_is_v_fld(field)) {
5062 n_v_cols++;
5063 } else {
5064 n_cols++;
5065 }
5066 }
5067
5068 ut_ad(n_cols + n_v_cols == altered_table->s->fields);
5069
5070 if (add_fts_doc_id) {
5071 n_cols++;
5072 DBUG_ASSERT(flags2 & DICT_TF2_FTS);
5073 DBUG_ASSERT(add_fts_doc_id_idx);
5074 flags2 |= DICT_TF2_FTS_ADD_DOC_ID
5075 | DICT_TF2_FTS_HAS_DOC_ID
5076 | DICT_TF2_FTS;
5077 }
5078
5079 DBUG_ASSERT(!add_fts_doc_id_idx || (flags2 & DICT_TF2_FTS));
5080
5081 ctx->new_table = dict_mem_table_create(
5082 new_table_name, NULL, n_cols + n_v_cols, n_v_cols,
5083 flags, flags2);
5084
5085 /* The rebuilt indexed_table will use the renamed
5086 column names. */
5087 ctx->col_names = NULL;
5088
5089 if (DICT_TF_HAS_DATA_DIR(flags)) {
5090 ctx->new_table->data_dir_path =
5091 mem_heap_strdup(ctx->new_table->heap,
5092 user_table->data_dir_path);
5093 }
5094
5095 for (uint i = 0; i < altered_table->s->fields; i++) {
5096 const Field* field = altered_table->field[i];
5097 ulint is_unsigned;
5098 ulint field_type
5099 = (ulint) field->type();
5100 ulint col_type
5101 = get_innobase_type_from_mysql_type(
5102 &is_unsigned, field);
5103 ulint charset_no;
5104 ulint col_len;
5105 bool is_virtual = innobase_is_v_fld(field);
5106
5107 /* we assume in dtype_form_prtype() that this
5108 fits in two bytes */
5109 ut_a(field_type <= MAX_CHAR_COLL_NUM);
5110
5111 if (!field->real_maybe_null()) {
5112 field_type |= DATA_NOT_NULL;
5113 }
5114
5115 if (field->binary()) {
5116 field_type |= DATA_BINARY_TYPE;
5117 }
5118
5119 if (is_unsigned) {
5120 field_type |= DATA_UNSIGNED;
5121 }
5122
5123 if (altered_table->versioned()) {
5124 if (i == altered_table->s->row_start_field) {
5125 field_type |= DATA_VERS_START;
5126 } else if (i ==
5127 altered_table->s->row_end_field) {
5128 field_type |= DATA_VERS_END;
5129 } else if (!(field->flags
5130 & VERS_UPDATE_UNVERSIONED_FLAG)) {
5131 field_type |= DATA_VERSIONED;
5132 }
5133 }
5134
5135 if (dtype_is_string_type(col_type)) {
5136 charset_no = (ulint) field->charset()->number;
5137
5138 if (charset_no > MAX_CHAR_COLL_NUM) {
5139 my_error(ER_WRONG_KEY_COLUMN, MYF(0), "InnoDB",
5140 field->field_name.str);
5141 goto new_clustered_failed;
5142 }
5143 } else {
5144 charset_no = 0;
5145 }
5146
5147 col_len = field->pack_length();
5148
5149 /* The MySQL pack length contains 1 or 2 bytes
5150 length field for a true VARCHAR. Let us
5151 subtract that, so that the InnoDB column
5152 length in the InnoDB data dictionary is the
5153 real maximum byte length of the actual data. */
5154
5155 if (field->type() == MYSQL_TYPE_VARCHAR) {
5156 uint32 length_bytes
5157 = static_cast<const Field_varstring*>(
5158 field)->length_bytes;
5159
5160 col_len -= length_bytes;
5161
5162 if (length_bytes == 2) {
5163 field_type |= DATA_LONG_TRUE_VARCHAR;
5164 }
5165
5166 }
5167
5168 if (dict_col_name_is_reserved(field->field_name.str)) {
5169 dict_mem_table_free(ctx->new_table);
5170 ctx->new_table = ctx->old_table;
5171 my_error(ER_WRONG_COLUMN_NAME, MYF(0),
5172 field->field_name.str);
5173 goto new_clustered_failed;
5174 }
5175
5176 if (is_virtual) {
5177 dict_mem_table_add_v_col(
5178 ctx->new_table, ctx->heap,
5179 field->field_name.str,
5180 col_type,
5181 dtype_form_prtype(
5182 field_type, charset_no)
5183 | DATA_VIRTUAL,
5184 col_len, i, 0);
5185 } else {
5186 dict_mem_table_add_col(
5187 ctx->new_table, ctx->heap,
5188 field->field_name.str,
5189 col_type,
5190 dtype_form_prtype(
5191 field_type, charset_no),
5192 col_len);
5193 }
5194 }
5195
5196 if (n_v_cols) {
5197 for (uint i = 0; i < altered_table->s->fields; i++) {
5198 dict_v_col_t* v_col;
5199 const Field* field = altered_table->field[i];
5200
5201 if (!innobase_is_v_fld(field)) {
5202 continue;
5203 }
5204 v_col = dict_table_get_nth_v_col(
5205 ctx->new_table, z);
5206 z++;
5207 innodb_base_col_setup(
5208 ctx->new_table, field, v_col);
5209 }
5210 }
5211
5212 if (add_fts_doc_id) {
5213 fts_add_doc_id_column(ctx->new_table, ctx->heap);
5214 ctx->new_table->fts->doc_col = fts_doc_id_col;
5215 ut_ad(fts_doc_id_col
5216 == altered_table->s->fields - n_v_cols);
5217 } else if (ctx->new_table->fts) {
5218 ctx->new_table->fts->doc_col = fts_doc_id_col;
5219 }
5220
5221 dict_table_add_system_columns(ctx->new_table, ctx->heap);
5222
5223 if (ha_alter_info->handler_flags & INNOBASE_DEFAULTS) {
5224 defaults = dtuple_create_with_vcol(
5225 ctx->heap,
5226 dict_table_get_n_cols(ctx->new_table),
5227 dict_table_get_n_v_cols(ctx->new_table));
5228
5229 dict_table_copy_types(defaults, ctx->new_table);
5230 } else {
5231 defaults = NULL;
5232 }
5233
5234 ctx->col_map = innobase_build_col_map(
5235 ha_alter_info, altered_table, old_table,
5236 ctx->new_table, user_table, defaults, ctx->heap);
5237 ctx->defaults = defaults;
5238 } else {
5239 DBUG_ASSERT(!innobase_need_rebuild(ha_alter_info, old_table));
5240 DBUG_ASSERT(old_table->s->primary_key
5241 == altered_table->s->primary_key);
5242
5243 for (dict_index_t* index
5244 = dict_table_get_first_index(user_table);
5245 index != NULL;
5246 index = dict_table_get_next_index(index)) {
5247 if (!index->to_be_dropped && index->is_corrupted()) {
5248 my_error(ER_CHECK_NO_SUCH_TABLE, MYF(0));
5249 goto error_handled;
5250 }
5251 }
5252
5253 for (dict_index_t* index
5254 = dict_table_get_first_index(user_table);
5255 index != NULL;
5256 index = dict_table_get_next_index(index)) {
5257 if (!index->to_be_dropped && index->is_corrupted()) {
5258 my_error(ER_CHECK_NO_SUCH_TABLE, MYF(0));
5259 goto error_handled;
5260 }
5261 }
5262
5263 if (!ctx->new_table->fts
5264 && innobase_fulltext_exist(altered_table)) {
5265 ctx->new_table->fts = fts_create(
5266 ctx->new_table);
5267 ctx->new_table->fts->doc_col = fts_doc_id_col;
5268 }
5269
5270 /* Check if we need to update mtypes of legacy GIS columns.
5271 This check is only needed when we don't have to rebuild
5272 the table, since rebuild would update all mtypes for GIS
5273 columns */
5274 error = innobase_check_gis_columns(
5275 ha_alter_info, ctx->new_table, ctx->trx);
5276 if (error != DB_SUCCESS) {
5277 ut_ad(error == DB_ERROR);
5278 error = DB_UNSUPPORTED;
5279 goto error_handling;
5280 }
5281 }
5282
5283 ut_ad(new_clustered == ctx->need_rebuild());
5284
5285 /* Create the index metadata. */
5286 for (ulint a = 0; a < ctx->num_to_add_index; a++) {
5287 if (index_defs[a].ind_type & DICT_VIRTUAL
5288 && ctx->num_to_drop_vcol > 0 && !new_clustered) {
5289 innodb_v_adjust_idx_col(ha_alter_info, old_table,
5290 ctx->num_to_drop_vcol,
5291 &index_defs[a]);
5292 }
5293
5294 ctx->add_index[a] = row_merge_create_index(
5295 ctx->new_table, &index_defs[a], add_v);
5296
5297 add_key_nums[a] = index_defs[a].key_number;
5298
5299 DBUG_ASSERT(ctx->add_index[a]->is_committed()
5300 == !!new_clustered);
5301 }
5302
5303 if (ctx->need_rebuild() && user_table->supports_instant()) {
5304 if (!instant_alter_column_possible(ha_alter_info, old_table)) {
5305 goto not_instant_add_column;
5306 }
5307
5308 for (uint i = uint(ctx->old_table->n_cols) - DATA_N_SYS_COLS;
5309 i--; ) {
5310 if (ctx->col_map[i] != i) {
5311 goto not_instant_add_column;
5312 }
5313 }
5314
5315 DBUG_ASSERT(ctx->new_table->n_cols > ctx->old_table->n_cols);
5316
5317 for (uint a = 0; a < ctx->num_to_add_index; a++) {
5318 ctx->add_index[a]->table = ctx->new_table;
5319 ctx->add_index[a] = dict_index_add_to_cache(
5320 ctx->add_index[a], FIL_NULL, false,
5321 &error, add_v);
5322 ut_a(error == DB_SUCCESS);
5323 }
5324 DBUG_ASSERT(ha_alter_info->key_count
5325 /* hidden GEN_CLUST_INDEX in InnoDB */
5326 + dict_index_is_auto_gen_clust(
5327 dict_table_get_first_index(ctx->new_table))
5328 /* hidden FTS_DOC_ID_INDEX in InnoDB */
5329 + (ctx->old_table->fts_doc_id_index
5330 && innobase_fts_check_doc_id_index_in_def(
5331 altered_table->s->keys,
5332 altered_table->key_info)
5333 != FTS_EXIST_DOC_ID_INDEX)
5334 == ctx->num_to_add_index);
5335 ctx->num_to_add_index = 0;
5336 ctx->add_index = NULL;
5337
5338 uint i = 0; // index of stored columns ctx->new_table->cols[]
5339 Field **af = altered_table->field;
5340
5341 List_iterator_fast<Create_field> cf_it(
5342 ha_alter_info->alter_info->create_list);
5343
5344 while (const Create_field* new_field = cf_it++) {
5345 DBUG_ASSERT(!new_field->field
5346 || std::find(old_table->field,
5347 old_table->field
5348 + old_table->s->fields,
5349 new_field->field) !=
5350 old_table->field + old_table->s->fields);
5351 DBUG_ASSERT(new_field->field
5352 || !strcmp(new_field->field_name.str,
5353 (*af)->field_name.str));
5354
5355 if (!(*af)->stored_in_db()) {
5356 af++;
5357 continue;
5358 }
5359
5360 dict_col_t* col = dict_table_get_nth_col(
5361 ctx->new_table, i);
5362 DBUG_ASSERT(!strcmp((*af)->field_name.str,
5363 dict_table_get_col_name(ctx->new_table,
5364 i)));
5365 DBUG_ASSERT(!col->is_instant());
5366
5367 if (new_field->field) {
5368 ut_d(const dict_col_t* old_col
5369 = dict_table_get_nth_col(user_table, i));
5370 ut_d(const dict_index_t* index
5371 = user_table->indexes.start);
5372 DBUG_ASSERT(col->mtype == old_col->mtype);
5373 DBUG_ASSERT(col->prtype == old_col->prtype);
5374 DBUG_ASSERT(col->mbminlen
5375 == old_col->mbminlen);
5376 DBUG_ASSERT(col->mbmaxlen
5377 == old_col->mbmaxlen);
5378 DBUG_ASSERT(col->len >= old_col->len);
5379 DBUG_ASSERT(old_col->is_instant()
5380 == (dict_col_get_clust_pos(
5381 old_col, index)
5382 >= index->n_core_fields));
5383 } else if ((*af)->is_real_null()) {
5384 /* DEFAULT NULL */
5385 col->def_val.len = UNIV_SQL_NULL;
5386 } else {
5387 switch ((*af)->type()) {
5388 case MYSQL_TYPE_VARCHAR:
5389 col->def_val.len = reinterpret_cast
5390 <const Field_varstring*>
5391 ((*af))->get_length();
5392 col->def_val.data = reinterpret_cast
5393 <const Field_varstring*>
5394 ((*af))->get_data();
5395 break;
5396 case MYSQL_TYPE_GEOMETRY:
5397 case MYSQL_TYPE_TINY_BLOB:
5398 case MYSQL_TYPE_MEDIUM_BLOB:
5399 case MYSQL_TYPE_BLOB:
5400 case MYSQL_TYPE_LONG_BLOB:
5401 col->def_val.len = reinterpret_cast
5402 <const Field_blob*>
5403 ((*af))->get_length();
5404 col->def_val.data = reinterpret_cast
5405 <const Field_blob*>
5406 ((*af))->get_ptr();
5407 break;
5408 default:
5409 dfield_t d;
5410 dict_col_copy_type(col, &d.type);
5411 ulint len = (*af)->pack_length();
5412 DBUG_ASSERT(len <= 8
5413 || d.type.mtype
5414 != DATA_INT);
5415 row_mysql_store_col_in_innobase_format(
5416 &d,
5417 d.type.mtype == DATA_INT
5418 ? static_cast<byte*>(
5419 mem_heap_alloc(
5420 ctx->heap,
5421 len))
5422 : NULL,
5423 true, (*af)->ptr, len,
5424 dict_table_is_comp(
5425 user_table));
5426 col->def_val.len = d.len;
5427 col->def_val.data = d.data;
5428 }
5429 }
5430
5431 i++;
5432 af++;
5433 }
5434
5435 DBUG_ASSERT(af == altered_table->field
5436 + altered_table->s->fields);
5437 /* There might exist a hidden FTS_DOC_ID column for
5438 FULLTEXT INDEX. If it exists, the columns should have
5439 been implicitly added by ADD FULLTEXT INDEX together
5440 with instant ADD COLUMN. (If a hidden FTS_DOC_ID pre-existed,
5441 then the ctx->col_map[] check should have prevented
5442 adding visible user columns after that.) */
5443 DBUG_ASSERT(DATA_N_SYS_COLS + i == ctx->new_table->n_cols
5444 || (1 + DATA_N_SYS_COLS + i
5445 == ctx->new_table->n_cols
5446 && !strcmp(dict_table_get_col_name(
5447 ctx->new_table, i),
5448 FTS_DOC_ID_COL_NAME)));
5449
5450 ctx->prepare_instant();
5451 }
5452
5453 if (ctx->need_rebuild()) {
5454not_instant_add_column:
5455 DBUG_ASSERT(ctx->need_rebuild());
5456 DBUG_ASSERT(!ctx->is_instant());
5457 DBUG_ASSERT(num_fts_index <= 1);
5458 DBUG_ASSERT(!ctx->online || num_fts_index == 0);
5459 DBUG_ASSERT(!ctx->online
5460 || ctx->add_autoinc == ULINT_UNDEFINED);
5461 DBUG_ASSERT(!ctx->online
5462 || !innobase_need_rebuild(ha_alter_info, old_table)
5463 || !innobase_fulltext_exist(altered_table));
5464
5465 uint32_t key_id = FIL_DEFAULT_ENCRYPTION_KEY;
5466 fil_encryption_t mode = FIL_ENCRYPTION_DEFAULT;
5467
5468 if (fil_space_t* s = user_table->space) {
5469 if (const fil_space_crypt_t* c = s->crypt_data) {
5470 key_id = c->key_id;
5471 mode = c->encryption;
5472 }
5473 }
5474
5475 if (ha_alter_info->handler_flags
5476 & ALTER_CHANGE_CREATE_OPTION) {
5477 const ha_table_option_struct& alt_opt=
5478 *ha_alter_info->create_info->option_struct;
5479 const ha_table_option_struct& opt=
5480 *old_table->s->option_struct;
5481 if (alt_opt.encryption != opt.encryption
5482 || alt_opt.encryption_key_id
5483 != opt.encryption_key_id) {
5484 key_id = uint32_t(alt_opt.encryption_key_id);
5485 mode = fil_encryption_t(alt_opt.encryption);
5486 }
5487 }
5488
5489 if (dict_table_get_low(ctx->new_table->name.m_name)) {
5490 my_error(ER_TABLE_EXISTS_ERROR, MYF(0),
5491 ctx->new_table->name.m_name);
5492 goto new_clustered_failed;
5493 }
5494
5495 /* Create the table. */
5496 trx_set_dict_operation(ctx->trx, TRX_DICT_OP_TABLE);
5497
5498 error = row_create_table_for_mysql(
5499 ctx->new_table, ctx->trx, mode, key_id);
5500
5501 switch (error) {
5502 dict_table_t* temp_table;
5503 case DB_SUCCESS:
5504 /* We need to bump up the table ref count and
5505 before we can use it we need to open the
5506 table. The new_table must be in the data
5507 dictionary cache, because we are still holding
5508 the dict_sys->mutex. */
5509 ut_ad(mutex_own(&dict_sys->mutex));
5510 temp_table = dict_table_open_on_name(
5511 ctx->new_table->name.m_name, TRUE, FALSE,
5512 DICT_ERR_IGNORE_NONE);
5513 ut_a(ctx->new_table == temp_table);
5514 /* n_ref_count must be 1, because purge cannot
5515 be executing on this very table as we are
5516 holding dict_operation_lock X-latch. */
5517 DBUG_ASSERT(ctx->new_table->get_ref_count() == 1);
5518 DBUG_ASSERT(ctx->new_table->id != 0);
5519 DBUG_ASSERT(ctx->new_table->id == ctx->trx->table_id);
5520 break;
5521 case DB_TABLESPACE_EXISTS:
5522 my_error(ER_TABLESPACE_EXISTS, MYF(0),
5523 ctx->new_table->name.m_name);
5524 goto new_table_failed;
5525 case DB_DUPLICATE_KEY:
5526 my_error(HA_ERR_TABLE_EXIST, MYF(0),
5527 altered_table->s->table_name.str);
5528 goto new_table_failed;
5529 case DB_UNSUPPORTED:
5530 my_error(ER_UNSUPPORTED_EXTENSION, MYF(0),
5531 ctx->new_table->name.m_name);
5532 goto new_table_failed;
5533 default:
5534 my_error_innodb(error, table_name, flags);
5535new_table_failed:
5536 DBUG_ASSERT(ctx->trx != ctx->prebuilt->trx);
5537 goto new_clustered_failed;
5538 }
5539
5540 for (ulint a = 0; a < ctx->num_to_add_index; a++) {
5541 dict_index_t*& index = ctx->add_index[a];
5542 const bool has_new_v_col = index->has_new_v_col;
5543 index = create_index_dict(ctx->trx, index, add_v);
5544 if (!index) {
5545 error = ctx->trx->error_state;
5546 ut_ad(error != DB_SUCCESS);
5547 while (++a < ctx->num_to_add_index) {
5548 dict_mem_index_free(ctx->add_index[a]);
5549 }
5550 goto error_handling;
5551 }
5552
5553 index->parser = index_defs[a].parser;
5554 index->has_new_v_col = has_new_v_col;
5555 /* Note the id of the transaction that created this
5556 index, we use it to restrict readers from accessing
5557 this index, to ensure read consistency. */
5558 ut_ad(index->trx_id == ctx->trx->id);
5559
5560 if (index->type & DICT_FTS) {
5561 DBUG_ASSERT(num_fts_index == 1);
5562 DBUG_ASSERT(!fts_index);
5563 DBUG_ASSERT(index->type == DICT_FTS);
5564 fts_index = ctx->add_index[a];
5565 }
5566 }
5567
5568 dict_index_t* clust_index = dict_table_get_first_index(
5569 user_table);
5570 dict_index_t* new_clust_index = dict_table_get_first_index(
5571 ctx->new_table);
5572 ctx->skip_pk_sort = innobase_pk_order_preserved(
5573 ctx->col_map, clust_index, new_clust_index);
5574
5575 DBUG_EXECUTE_IF("innodb_alter_table_pk_assert_no_sort",
5576 DBUG_ASSERT(ctx->skip_pk_sort););
5577
5578 ut_ad(!new_clust_index->is_instant());
5579 /* row_merge_build_index() depends on the correct value */
5580 ut_ad(new_clust_index->n_core_null_bytes
5581 == UT_BITS_IN_BYTES(new_clust_index->n_nullable));
5582
5583 DBUG_ASSERT(!ctx->new_table->persistent_autoinc);
5584 if (const Field* ai = altered_table->found_next_number_field) {
5585 const unsigned col_no = innodb_col_no(ai);
5586
5587 ctx->new_table->persistent_autoinc = 1
5588 + dict_table_get_nth_col_pos(
5589 ctx->new_table, col_no, NULL);
5590
5591 /* Initialize the AUTO_INCREMENT sequence
5592 to the rebuilt table from the old one. */
5593 if (!old_table->found_next_number_field
5594 || !user_table->space) {
5595 } else if (ib_uint64_t autoinc
5596 = btr_read_autoinc(clust_index)) {
5597 btr_write_autoinc(new_clust_index, autoinc);
5598 }
5599 }
5600
5601 if (ctx->online) {
5602 /* Allocate a log for online table rebuild. */
5603 rw_lock_x_lock(&clust_index->lock);
5604 bool ok = row_log_allocate(
5605 ctx->prebuilt->trx,
5606 clust_index, ctx->new_table,
5607 !(ha_alter_info->handler_flags
5608 & ALTER_ADD_PK_INDEX),
5609 ctx->defaults, ctx->col_map, path,
5610 ctx->ignore);
5611 rw_lock_x_unlock(&clust_index->lock);
5612
5613 if (!ok) {
5614 error = DB_OUT_OF_MEMORY;
5615 goto error_handling;
5616 }
5617 }
5618 } else if (ctx->num_to_add_index) {
5619 ut_ad(!ctx->is_instant());
5620 ctx->trx->table_id = user_table->id;
5621
5622 for (ulint a = 0; a < ctx->num_to_add_index; a++) {
5623 dict_index_t*& index = ctx->add_index[a];
5624 const bool has_new_v_col = index->has_new_v_col;
5625 index = create_index_dict(ctx->trx, index, add_v);
5626 if (!index) {
5627 error = ctx->trx->error_state;
5628 ut_ad(error != DB_SUCCESS);
5629error_handling_drop_uncached:
5630 while (++a < ctx->num_to_add_index) {
5631 dict_mem_index_free(ctx->add_index[a]);
5632 }
5633 goto error_handling;
5634 }
5635
5636 index->parser = index_defs[a].parser;
5637 index->has_new_v_col = has_new_v_col;
5638 /* Note the id of the transaction that created this
5639 index, we use it to restrict readers from accessing
5640 this index, to ensure read consistency. */
5641 ut_ad(index->trx_id == ctx->trx->id);
5642
5643 /* If ADD INDEX with LOCK=NONE has been
5644 requested, allocate a modification log. */
5645 if (index->type & DICT_FTS) {
5646 DBUG_ASSERT(num_fts_index == 1);
5647 DBUG_ASSERT(!fts_index);
5648 DBUG_ASSERT(index->type == DICT_FTS);
5649 fts_index = ctx->add_index[a];
5650 /* Fulltext indexes are not covered
5651 by a modification log. */
5652 } else if (!ctx->online
5653 || !user_table->is_readable()
5654 || !user_table->space) {
5655 /* No need to allocate a modification log. */
5656 DBUG_ASSERT(!index->online_log);
5657 } else {
5658 DBUG_EXECUTE_IF(
5659 "innodb_OOM_prepare_inplace_alter",
5660 error = DB_OUT_OF_MEMORY;
5661 goto error_handling_drop_uncached;);
5662 rw_lock_x_lock(&ctx->add_index[a]->lock);
5663
5664 bool ok = row_log_allocate(
5665 ctx->prebuilt->trx,
5666 index,
5667 NULL, true, NULL, NULL,
5668 path, ctx->ignore);
5669 rw_lock_x_unlock(&index->lock);
5670
5671 if (!ok) {
5672 error = DB_OUT_OF_MEMORY;
5673 goto error_handling_drop_uncached;
5674 }
5675 }
5676 }
5677 }
5678
5679 if (ctx->online && ctx->num_to_add_index) {
5680 /* Assign a consistent read view for
5681 row_merge_read_clustered_index(). */
5682 ctx->prebuilt->trx->read_view.open(ctx->prebuilt->trx);
5683 }
5684
5685 if (fts_index) {
5686 /* Ensure that the dictionary operation mode will
5687 not change while creating the auxiliary tables. */
5688 trx_dict_op_t op = trx_get_dict_operation(ctx->trx);
5689
5690#ifdef UNIV_DEBUG
5691 switch (op) {
5692 case TRX_DICT_OP_NONE:
5693 break;
5694 case TRX_DICT_OP_TABLE:
5695 case TRX_DICT_OP_INDEX:
5696 goto op_ok;
5697 }
5698 ut_error;
5699op_ok:
5700#endif /* UNIV_DEBUG */
5701 ut_ad(ctx->trx->dict_operation_lock_mode == RW_X_LATCH);
5702 ut_ad(mutex_own(&dict_sys->mutex));
5703 ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X));
5704
5705 DICT_TF2_FLAG_SET(ctx->new_table, DICT_TF2_FTS);
5706 if (ctx->need_rebuild()) {
5707 /* For !ctx->need_rebuild(), this will be set at
5708 commit_cache_norebuild(). */
5709 ctx->new_table->fts_doc_id_index
5710 = dict_table_get_index_on_name(
5711 ctx->new_table, FTS_DOC_ID_INDEX_NAME);
5712 DBUG_ASSERT(ctx->new_table->fts_doc_id_index != NULL);
5713 }
5714
5715 error = fts_create_index_tables(ctx->trx, fts_index,
5716 ctx->new_table->id);
5717
5718 DBUG_EXECUTE_IF("innodb_test_fail_after_fts_index_table",
5719 error = DB_LOCK_WAIT_TIMEOUT;
5720 goto error_handling;);
5721
5722 if (error != DB_SUCCESS) {
5723 goto error_handling;
5724 }
5725
5726 trx_commit(ctx->trx);
5727 trx_start_for_ddl(ctx->trx, op);
5728
5729 if (!ctx->new_table->fts
5730 || ib_vector_size(ctx->new_table->fts->indexes) == 0) {
5731 error = fts_create_common_tables(
5732 ctx->trx, ctx->new_table, true);
5733
5734 DBUG_EXECUTE_IF(
5735 "innodb_test_fail_after_fts_common_table",
5736 error = DB_LOCK_WAIT_TIMEOUT;);
5737
5738 if (error != DB_SUCCESS) {
5739 goto error_handling;
5740 }
5741
5742 ctx->new_table->fts->fts_status
5743 |= TABLE_DICT_LOCKED;
5744
5745 error = innobase_fts_load_stopword(
5746 ctx->new_table, ctx->trx,
5747 ctx->prebuilt->trx->mysql_thd)
5748 ? DB_SUCCESS : DB_ERROR;
5749 ctx->new_table->fts->fts_status
5750 &= ulint(~TABLE_DICT_LOCKED);
5751
5752 if (error != DB_SUCCESS) {
5753 goto error_handling;
5754 }
5755 }
5756
5757 ut_ad(trx_get_dict_operation(ctx->trx) == op);
5758 }
5759
5760 DBUG_ASSERT(error == DB_SUCCESS);
5761
5762 /* Commit the data dictionary transaction in order to release
5763 the table locks on the system tables. This means that if
5764 MySQL crashes while creating a new primary key inside
5765 row_merge_build_indexes(), ctx->new_table will not be dropped
5766 by trx_rollback_active(). It will have to be recovered or
5767 dropped by the database administrator. */
5768 trx_commit_for_mysql(ctx->trx);
5769
5770 row_mysql_unlock_data_dictionary(ctx->trx);
5771 dict_locked = false;
5772
5773 ut_a(ctx->trx->lock.n_active_thrs == 0);
5774
5775error_handling:
5776 /* After an error, remove all those index definitions from the
5777 dictionary which were defined. */
5778
5779 switch (error) {
5780 case DB_SUCCESS:
5781 ut_a(!dict_locked);
5782
5783 ut_d(mutex_enter(&dict_sys->mutex));
5784 ut_d(dict_table_check_for_dup_indexes(
5785 user_table, CHECK_PARTIAL_OK));
5786 ut_d(mutex_exit(&dict_sys->mutex));
5787 DBUG_RETURN(false);
5788 case DB_TABLESPACE_EXISTS:
5789 my_error(ER_TABLESPACE_EXISTS, MYF(0), "(unknown)");
5790 break;
5791 case DB_DUPLICATE_KEY:
5792 my_error(ER_DUP_KEY, MYF(0), "SYS_INDEXES");
5793 break;
5794 case DB_UNSUPPORTED:
5795 my_error(ER_TABLE_CANT_HANDLE_SPKEYS, MYF(0), "SYS_COLUMNS");
5796 break;
5797 default:
5798 my_error_innodb(error, table_name, user_table->flags);
5799 }
5800
5801error_handled:
5802
5803 ctx->prebuilt->trx->error_info = NULL;
5804
5805 if (!ctx->trx) {
5806 goto err_exit;
5807 }
5808
5809 ctx->trx->error_state = DB_SUCCESS;
5810
5811 if (!dict_locked) {
5812 row_mysql_lock_data_dictionary(ctx->trx);
5813 }
5814
5815 if (new_clustered) {
5816 if (ctx->need_rebuild()) {
5817
5818 if (DICT_TF2_FLAG_IS_SET(
5819 ctx->new_table, DICT_TF2_FTS)) {
5820 innobase_drop_fts_index_table(
5821 ctx->new_table, ctx->trx);
5822 }
5823
5824 dict_table_close_and_drop(ctx->trx, ctx->new_table);
5825
5826 /* Free the log for online table rebuild, if
5827 one was allocated. */
5828
5829 dict_index_t* clust_index = dict_table_get_first_index(
5830 user_table);
5831
5832 rw_lock_x_lock(&clust_index->lock);
5833
5834 if (clust_index->online_log) {
5835 ut_ad(ctx->online);
5836 row_log_abort_sec(clust_index);
5837 clust_index->online_status
5838 = ONLINE_INDEX_COMPLETE;
5839 }
5840
5841 rw_lock_x_unlock(&clust_index->lock);
5842 }
5843
5844 trx_commit_for_mysql(ctx->trx);
5845 /* n_ref_count must be 1, because purge cannot
5846 be executing on this very table as we are
5847 holding dict_operation_lock X-latch. */
5848 DBUG_ASSERT(user_table->get_ref_count() == 1 || ctx->online);
5849
5850 online_retry_drop_indexes_with_trx(user_table, ctx->trx);
5851 } else {
5852 ut_ad(!ctx->need_rebuild());
5853 row_merge_drop_indexes(ctx->trx, user_table, TRUE);
5854 trx_commit_for_mysql(ctx->trx);
5855 }
5856
5857 ut_d(dict_table_check_for_dup_indexes(user_table, CHECK_ALL_COMPLETE));
5858 ut_ad(!user_table->drop_aborted);
5859
5860err_exit:
5861#ifdef UNIV_DEBUG
5862 /* Clear the to_be_dropped flag in the data dictionary cache. */
5863 for (ulint i = 0; i < ctx->num_to_drop_index; i++) {
5864 DBUG_ASSERT(ctx->drop_index[i]->is_committed());
5865 DBUG_ASSERT(ctx->drop_index[i]->to_be_dropped);
5866 ctx->drop_index[i]->to_be_dropped = 0;
5867 }
5868#endif /* UNIV_DEBUG */
5869
5870 if (ctx->trx) {
5871 row_mysql_unlock_data_dictionary(ctx->trx);
5872
5873 trx_free(ctx->trx);
5874 }
5875 trx_commit_for_mysql(ctx->prebuilt->trx);
5876
5877 delete ctx;
5878 ha_alter_info->handler_ctx = NULL;
5879
5880 DBUG_RETURN(true);
5881}
5882
5883/* Check whether an index is needed for the foreign key constraint.
5884If so, if it is dropped, is there an equivalent index can play its role.
5885@return true if the index is needed and can't be dropped */
5886static MY_ATTRIBUTE((nonnull(1,2,3,5), warn_unused_result))
5887bool
5888innobase_check_foreign_key_index(
5889/*=============================*/
5890 Alter_inplace_info* ha_alter_info, /*!< in: Structure describing
5891 changes to be done by ALTER
5892 TABLE */
5893 dict_index_t* index, /*!< in: index to check */
5894 dict_table_t* indexed_table, /*!< in: table that owns the
5895 foreign keys */
5896 const char** col_names, /*!< in: column names, or NULL
5897 for indexed_table->col_names */
5898 trx_t* trx, /*!< in/out: transaction */
5899 dict_foreign_t** drop_fk, /*!< in: Foreign key constraints
5900 to drop */
5901 ulint n_drop_fk) /*!< in: Number of foreign keys
5902 to drop */
5903{
5904 const dict_foreign_set* fks = &indexed_table->referenced_set;
5905
5906 /* Check for all FK references from other tables to the index. */
5907 for (dict_foreign_set::const_iterator it = fks->begin();
5908 it != fks->end(); ++it) {
5909
5910 dict_foreign_t* foreign = *it;
5911 if (foreign->referenced_index != index) {
5912 continue;
5913 }
5914 ut_ad(indexed_table == foreign->referenced_table);
5915
5916 if (NULL == dict_foreign_find_index(
5917 indexed_table, col_names,
5918 foreign->referenced_col_names,
5919 foreign->n_fields, index,
5920 /*check_charsets=*/TRUE,
5921 /*check_null=*/FALSE,
5922 NULL, NULL, NULL)
5923 && NULL == innobase_find_equiv_index(
5924 foreign->referenced_col_names,
5925 foreign->n_fields,
5926 ha_alter_info->key_info_buffer,
5927 ha_alter_info->index_add_buffer,
5928 ha_alter_info->index_add_count)) {
5929
5930 /* Index cannot be dropped. */
5931 trx->error_info = index;
5932 return(true);
5933 }
5934 }
5935
5936 fks = &indexed_table->foreign_set;
5937
5938 /* Check for all FK references in current table using the index. */
5939 for (dict_foreign_set::const_iterator it = fks->begin();
5940 it != fks->end(); ++it) {
5941
5942 dict_foreign_t* foreign = *it;
5943 if (foreign->foreign_index != index) {
5944 continue;
5945 }
5946
5947 ut_ad(indexed_table == foreign->foreign_table);
5948
5949 if (!innobase_dropping_foreign(
5950 foreign, drop_fk, n_drop_fk)
5951 && NULL == dict_foreign_find_index(
5952 indexed_table, col_names,
5953 foreign->foreign_col_names,
5954 foreign->n_fields, index,
5955 /*check_charsets=*/TRUE,
5956 /*check_null=*/FALSE,
5957 NULL, NULL, NULL)
5958 && NULL == innobase_find_equiv_index(
5959 foreign->foreign_col_names,
5960 foreign->n_fields,
5961 ha_alter_info->key_info_buffer,
5962 ha_alter_info->index_add_buffer,
5963 ha_alter_info->index_add_count)) {
5964
5965 /* Index cannot be dropped. */
5966 trx->error_info = index;
5967 return(true);
5968 }
5969 }
5970
5971 return(false);
5972}
5973
5974#ifdef MYSQL_RENAME_INDEX
5975/**
5976Rename a given index in the InnoDB data dictionary.
5977
5978@param index index to rename
5979@param new_name new name of the index
5980@param[in,out] trx dict transaction to use, not going to be committed here
5981
5982@retval true Failure
5983@retval false Success */
5984static MY_ATTRIBUTE((warn_unused_result))
5985bool
5986rename_index_in_data_dictionary(
5987/*============================*/
5988 const dict_index_t* index,
5989 const char* new_name,
5990 trx_t* trx)
5991{
5992 DBUG_ENTER("rename_index_in_data_dictionary");
5993
5994 ut_ad(mutex_own(&dict_sys->mutex));
5995 ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X));
5996 ut_ad(trx->dict_operation_lock_mode == RW_X_LATCH);
5997
5998 pars_info_t* pinfo;
5999 dberr_t err;
6000
6001 pinfo = pars_info_create();
6002
6003 pars_info_add_ull_literal(pinfo, "table_id", index->table->id);
6004 pars_info_add_ull_literal(pinfo, "index_id", index->id);
6005 pars_info_add_str_literal(pinfo, "new_name", new_name);
6006
6007 trx->op_info = "Renaming an index in SYS_INDEXES";
6008
6009 DBUG_EXECUTE_IF(
6010 "ib_rename_index_fail1",
6011 DBUG_SET("+d,innodb_report_deadlock");
6012 );
6013
6014 err = que_eval_sql(
6015 pinfo,
6016 "PROCEDURE RENAME_INDEX_IN_SYS_INDEXES () IS\n"
6017 "BEGIN\n"
6018 "UPDATE SYS_INDEXES SET\n"
6019 "NAME = :new_name\n"
6020 "WHERE\n"
6021 "ID = :index_id AND\n"
6022 "TABLE_ID = :table_id;\n"
6023 "END;\n",
6024 FALSE, trx); /* pinfo is freed by que_eval_sql() */
6025
6026 DBUG_EXECUTE_IF(
6027 "ib_rename_index_fail1",
6028 DBUG_SET("-d,innodb_report_deadlock");
6029 );
6030
6031 trx->op_info = "";
6032
6033 if (err != DB_SUCCESS) {
6034 my_error_innodb(err, index->table->name.m_name, 0);
6035 DBUG_RETURN(true);
6036 }
6037
6038 DBUG_RETURN(false);
6039}
6040
6041/**
6042Rename all indexes in data dictionary of a given table that are
6043specified in ha_alter_info.
6044
6045@param ctx alter context, used to fetch the list of indexes to
6046rename
6047@param ha_alter_info fetch the new names from here
6048@param[in,out] trx dict transaction to use, not going to be committed here
6049
6050@retval true Failure
6051@retval false Success */
6052static MY_ATTRIBUTE((warn_unused_result))
6053bool
6054rename_indexes_in_data_dictionary(
6055/*==============================*/
6056 const ha_innobase_inplace_ctx* ctx,
6057 const Alter_inplace_info* ha_alter_info,
6058 trx_t* trx)
6059{
6060 DBUG_ENTER("rename_indexes_in_data_dictionary");
6061
6062 ut_ad(ctx->num_to_rename == ha_alter_info->index_rename_count);
6063
6064 for (ulint i = 0; i < ctx->num_to_rename; i++) {
6065
6066 KEY_PAIR* pair = &ha_alter_info->index_rename_buffer[i];
6067 dict_index_t* index;
6068
6069 index = ctx->rename[i];
6070
6071 ut_ad(strcmp(index->name, pair->old_key->name) == 0);
6072
6073 if (rename_index_in_data_dictionary(index,
6074 pair->new_key->name,
6075 trx)) {
6076 /* failed */
6077 DBUG_RETURN(true);
6078 }
6079 }
6080
6081 DBUG_RETURN(false);
6082}
6083
6084/**
6085Rename a given index in the InnoDB data dictionary cache.
6086
6087@param[in,out] index index to rename
6088@param new_name new index name
6089*/
6090static
6091void
6092rename_index_in_cache(
6093/*==================*/
6094 dict_index_t* index,
6095 const char* new_name)
6096{
6097 DBUG_ENTER("rename_index_in_cache");
6098
6099 ut_ad(mutex_own(&dict_sys->mutex));
6100 ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X));
6101
6102 size_t old_name_len = strlen(index->name);
6103 size_t new_name_len = strlen(new_name);
6104
6105 if (old_name_len >= new_name_len) {
6106 /* reuse the old buffer for the name if it is large enough */
6107 memcpy(const_cast<char*>(index->name()), new_name,
6108 new_name_len + 1);
6109 } else {
6110 /* Free the old chunk of memory if it is at the topmost
6111 place in the heap, otherwise the old chunk will be freed
6112 when the index is evicted from the cache. This code will
6113 kick-in in a repeated ALTER sequences where the old name is
6114 alternately longer/shorter than the new name:
6115 1. ALTER TABLE t RENAME INDEX a TO aa;
6116 2. ALTER TABLE t RENAME INDEX aa TO a;
6117 3. go to 1. */
6118 index->name = mem_heap_strdup_replace(
6119 index->heap,
6120 /* Presumed topmost element of the heap: */
6121 index->name, old_name_len + 1,
6122 new_name);
6123 }
6124
6125 DBUG_VOID_RETURN;
6126}
6127
6128/**
6129Rename all indexes in data dictionary cache of a given table that are
6130specified in ha_alter_info.
6131
6132@param ctx alter context, used to fetch the list of indexes to rename
6133@param ha_alter_info fetch the new names from here
6134*/
6135static
6136void
6137rename_indexes_in_cache(
6138/*====================*/
6139 const ha_innobase_inplace_ctx* ctx,
6140 const Alter_inplace_info* ha_alter_info)
6141{
6142 DBUG_ENTER("rename_indexes_in_cache");
6143
6144 ut_ad(ctx->num_to_rename == ha_alter_info->index_rename_count);
6145
6146 for (ulint i = 0; i < ctx->num_to_rename; i++) {
6147 KEY_PAIR* pair = &ha_alter_info->index_rename_buffer[i];
6148 dict_index_t* index;
6149
6150 index = ctx->rename[i];
6151
6152 ut_ad(strcmp(index->name, pair->old_key->name) == 0);
6153
6154 rename_index_in_cache(index, pair->new_key->name);
6155 }
6156
6157 DBUG_VOID_RETURN;
6158}
6159#endif /* MYSQL_RENAME_INDEX */
6160
6161/** Fill the stored column information in s_cols list.
6162@param[in] altered_table mysql table object
6163@param[in] table innodb table object
6164@param[out] s_cols list of stored column
6165@param[out] s_heap heap for storing stored
6166column information. */
6167static
6168void
6169alter_fill_stored_column(
6170 const TABLE* altered_table,
6171 dict_table_t* table,
6172 dict_s_col_list** s_cols,
6173 mem_heap_t** s_heap)
6174{
6175 ulint n_cols = altered_table->s->fields;
6176 ulint stored_col_no = 0;
6177
6178 for (ulint i = 0; i < n_cols; i++) {
6179 Field* field = altered_table->field[i];
6180 dict_s_col_t s_col;
6181
6182 if (!innobase_is_v_fld(field)) {
6183 stored_col_no++;
6184 }
6185
6186 if (!innobase_is_s_fld(field)) {
6187 continue;
6188 }
6189
6190 ulint num_base = 0;
6191 dict_col_t* col = dict_table_get_nth_col(table,
6192 stored_col_no);
6193
6194 s_col.m_col = col;
6195 s_col.s_pos = i;
6196
6197 if (*s_cols == NULL) {
6198 *s_cols = UT_NEW_NOKEY(dict_s_col_list());
6199 *s_heap = mem_heap_create(1000);
6200 }
6201
6202 if (num_base != 0) {
6203 s_col.base_col = static_cast<dict_col_t**>(mem_heap_zalloc(
6204 *s_heap, num_base * sizeof(dict_col_t*)));
6205 } else {
6206 s_col.base_col = NULL;
6207 }
6208
6209 s_col.num_base = num_base;
6210 innodb_base_col_setup_for_stored(table, field, &s_col);
6211 (*s_cols)->push_back(s_col);
6212 }
6213}
6214
6215
6216/** Allows InnoDB to update internal structures with concurrent
6217writes blocked (provided that check_if_supported_inplace_alter()
6218did not return HA_ALTER_INPLACE_NO_LOCK).
6219This will be invoked before inplace_alter_table().
6220
6221@param altered_table TABLE object for new version of table.
6222@param ha_alter_info Structure describing changes to be done
6223by ALTER TABLE and holding data used during in-place alter.
6224
6225@retval true Failure
6226@retval false Success
6227*/
6228
6229bool
6230ha_innobase::prepare_inplace_alter_table(
6231/*=====================================*/
6232 TABLE* altered_table,
6233 Alter_inplace_info* ha_alter_info)
6234{
6235 dict_index_t** drop_index; /*!< Index to be dropped */
6236 ulint n_drop_index; /*!< Number of indexes to drop */
6237 dict_index_t** rename_index; /*!< Indexes to be dropped */
6238 ulint n_rename_index; /*!< Number of indexes to rename */
6239 dict_foreign_t**drop_fk; /*!< Foreign key constraints to drop */
6240 ulint n_drop_fk; /*!< Number of foreign keys to drop */
6241 dict_foreign_t**add_fk = NULL; /*!< Foreign key constraints to drop */
6242 ulint n_add_fk; /*!< Number of foreign keys to drop */
6243 dict_table_t* indexed_table; /*!< Table where indexes are created */
6244 mem_heap_t* heap;
6245 const char** col_names;
6246 int error;
6247 ulint max_col_len;
6248 ulint add_autoinc_col_no = ULINT_UNDEFINED;
6249 ulonglong autoinc_col_max_value = 0;
6250 ulint fts_doc_col_no = ULINT_UNDEFINED;
6251 bool add_fts_doc_id = false;
6252 bool add_fts_doc_id_idx = false;
6253 bool add_fts_idx = false;
6254 dict_s_col_list*s_cols = NULL;
6255 mem_heap_t* s_heap = NULL;
6256
6257 DBUG_ENTER("prepare_inplace_alter_table");
6258 DBUG_ASSERT(!ha_alter_info->handler_ctx);
6259 DBUG_ASSERT(ha_alter_info->create_info);
6260 DBUG_ASSERT(!srv_read_only_mode);
6261
6262 /* Init online ddl status variables */
6263 onlineddl_rowlog_rows = 0;
6264 onlineddl_rowlog_pct_used = 0;
6265 onlineddl_pct_progress = 0;
6266
6267 MONITOR_ATOMIC_INC(MONITOR_PENDING_ALTER_TABLE);
6268
6269#ifdef UNIV_DEBUG
6270 for (dict_index_t* index = dict_table_get_first_index(m_prebuilt->table);
6271 index;
6272 index = dict_table_get_next_index(index)) {
6273 ut_ad(!index->to_be_dropped);
6274 }
6275#endif /* UNIV_DEBUG */
6276
6277 ut_d(mutex_enter(&dict_sys->mutex));
6278 ut_d(dict_table_check_for_dup_indexes(
6279 m_prebuilt->table, CHECK_ABORTED_OK));
6280 ut_d(mutex_exit(&dict_sys->mutex));
6281
6282 if (!(ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE)) {
6283 /* Nothing to do */
6284 DBUG_ASSERT(m_prebuilt->trx->dict_operation_lock_mode == 0);
6285 if (ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE) {
6286
6287 online_retry_drop_indexes(
6288 m_prebuilt->table, m_user_thd);
6289
6290 }
6291 DBUG_RETURN(false);
6292 }
6293
6294 indexed_table = m_prebuilt->table;
6295
6296 /* ALTER TABLE will not implicitly move a table from a single-table
6297 tablespace to the system tablespace when innodb_file_per_table=OFF.
6298 But it will implicitly move a table from the system tablespace to a
6299 single-table tablespace if innodb_file_per_table = ON. */
6300
6301 create_table_info_t info(m_user_thd,
6302 altered_table,
6303 ha_alter_info->create_info,
6304 NULL,
6305 NULL);
6306
6307 info.set_tablespace_type(indexed_table->space != fil_system.sys_space);
6308
6309 if (ha_alter_info->handler_flags & ALTER_ADD_NON_UNIQUE_NON_PRIM_INDEX) {
6310 if (info.gcols_in_fulltext_or_spatial()) {
6311 goto err_exit_no_heap;
6312 }
6313 }
6314
6315 if (indexed_table->is_readable()) {
6316 } else {
6317 if (indexed_table->corrupted) {
6318 /* Handled below */
6319 } else {
6320 if (const fil_space_t* space = indexed_table->space) {
6321 String str;
6322 const char* engine= table_type();
6323
6324 push_warning_printf(
6325 m_user_thd,
6326 Sql_condition::WARN_LEVEL_WARN,
6327 HA_ERR_DECRYPTION_FAILED,
6328 "Table %s in file %s is encrypted but encryption service or"
6329 " used key_id is not available. "
6330 " Can't continue reading table.",
6331 table_share->table_name.str,
6332 space->chain.start->name);
6333
6334 my_error(ER_GET_ERRMSG, MYF(0), HA_ERR_DECRYPTION_FAILED, str.c_ptr(), engine);
6335 DBUG_RETURN(true);
6336 }
6337 }
6338 }
6339
6340 if (indexed_table->corrupted
6341 || dict_table_get_first_index(indexed_table) == NULL
6342 || dict_table_get_first_index(indexed_table)->is_corrupted()) {
6343 /* The clustered index is corrupted. */
6344 my_error(ER_CHECK_NO_SUCH_TABLE, MYF(0));
6345 DBUG_RETURN(true);
6346 } else {
6347 const char* invalid_opt = info.create_options_are_invalid();
6348
6349 /* Check engine specific table options */
6350 if (const char* invalid_tbopt = info.check_table_options()) {
6351 my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0),
6352 table_type(), invalid_tbopt);
6353 goto err_exit_no_heap;
6354 }
6355
6356 if (invalid_opt) {
6357 my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0),
6358 table_type(), invalid_opt);
6359 goto err_exit_no_heap;
6360 }
6361 }
6362
6363 /* Check if any index name is reserved. */
6364 if (innobase_index_name_is_reserved(
6365 m_user_thd,
6366 ha_alter_info->key_info_buffer,
6367 ha_alter_info->key_count)) {
6368err_exit_no_heap:
6369 DBUG_ASSERT(m_prebuilt->trx->dict_operation_lock_mode == 0);
6370 if (ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE) {
6371
6372 online_retry_drop_indexes(
6373 m_prebuilt->table, m_user_thd);
6374 }
6375 DBUG_RETURN(true);
6376 }
6377
6378 indexed_table = m_prebuilt->table;
6379
6380 /* Check that index keys are sensible */
6381 error = innobase_check_index_keys(ha_alter_info, indexed_table);
6382
6383 if (error) {
6384 goto err_exit_no_heap;
6385 }
6386
6387 /* Prohibit renaming a column to something that the table
6388 already contains. */
6389 if (ha_alter_info->handler_flags
6390 & ALTER_COLUMN_NAME) {
6391 List_iterator_fast<Create_field> cf_it(
6392 ha_alter_info->alter_info->create_list);
6393
6394 for (Field** fp = table->field; *fp; fp++) {
6395 if (!((*fp)->flags & FIELD_IS_RENAMED)) {
6396 continue;
6397 }
6398
6399 const char* name = 0;
6400
6401 cf_it.rewind();
6402 while (Create_field* cf = cf_it++) {
6403 if (cf->field == *fp) {
6404 name = cf->field_name.str;
6405 goto check_if_ok_to_rename;
6406 }
6407 }
6408
6409 ut_error;
6410check_if_ok_to_rename:
6411 /* Prohibit renaming a column from FTS_DOC_ID
6412 if full-text indexes exist. */
6413 if (!my_strcasecmp(system_charset_info,
6414 (*fp)->field_name.str,
6415 FTS_DOC_ID_COL_NAME)
6416 && innobase_fulltext_exist(altered_table)) {
6417 my_error(ER_INNODB_FT_WRONG_DOCID_COLUMN,
6418 MYF(0), name);
6419 goto err_exit_no_heap;
6420 }
6421
6422 /* Prohibit renaming a column to an internal column. */
6423 const char* s = m_prebuilt->table->col_names;
6424 unsigned j;
6425 /* Skip user columns.
6426 MySQL should have checked these already.
6427 We want to allow renaming of c1 to c2, c2 to c1. */
6428 for (j = 0; j < table->s->fields; j++) {
6429 if (!innobase_is_v_fld(table->field[j])) {
6430 s += strlen(s) + 1;
6431 }
6432 }
6433
6434 for (; j < m_prebuilt->table->n_def; j++) {
6435 if (!my_strcasecmp(
6436 system_charset_info, name, s)) {
6437 my_error(ER_WRONG_COLUMN_NAME, MYF(0),
6438 s);
6439 goto err_exit_no_heap;
6440 }
6441
6442 s += strlen(s) + 1;
6443 }
6444 }
6445 }
6446
6447 if (!info.innobase_table_flags()) {
6448 goto err_exit_no_heap;
6449 }
6450
6451 max_col_len = DICT_MAX_FIELD_LEN_BY_FORMAT_FLAG(info.flags());
6452
6453 /* Check each index's column length to make sure they do not
6454 exceed limit */
6455 for (ulint i = 0; i < ha_alter_info->index_add_count; i++) {
6456 const KEY* key = &ha_alter_info->key_info_buffer[
6457 ha_alter_info->index_add_buffer[i]];
6458
6459 if (key->flags & HA_FULLTEXT) {
6460 /* The column length does not matter for
6461 fulltext search indexes. But, UNIQUE
6462 fulltext indexes are not supported. */
6463 DBUG_ASSERT(!(key->flags & HA_NOSAME));
6464 DBUG_ASSERT(!(key->flags & HA_KEYFLAG_MASK
6465 & ~(HA_FULLTEXT
6466 | HA_PACK_KEY
6467 | HA_BINARY_PACK_KEY)));
6468 add_fts_idx = true;
6469 continue;
6470 }
6471
6472 if (innobase_check_column_length(max_col_len, key)) {
6473 my_error(ER_INDEX_COLUMN_TOO_LONG, MYF(0),
6474 max_col_len);
6475 goto err_exit_no_heap;
6476 }
6477 }
6478
6479 /* We won't be allowed to add fts index to a table with
6480 fts indexes already but without AUX_HEX_NAME set.
6481 This means the aux tables of the table failed to
6482 rename to hex format but new created aux tables
6483 shall be in hex format, which is contradictory. */
6484 if (!DICT_TF2_FLAG_IS_SET(indexed_table, DICT_TF2_FTS_AUX_HEX_NAME)
6485 && indexed_table->fts != NULL && add_fts_idx) {
6486 my_error(ER_INNODB_FT_AUX_NOT_HEX_ID, MYF(0));
6487 goto err_exit_no_heap;
6488 }
6489
6490 /* Check existing index definitions for too-long column
6491 prefixes as well, in case max_col_len shrunk. */
6492 for (const dict_index_t* index
6493 = dict_table_get_first_index(indexed_table);
6494 index;
6495 index = dict_table_get_next_index(index)) {
6496 if (index->type & DICT_FTS) {
6497 DBUG_ASSERT(index->type == DICT_FTS
6498 || (index->type & DICT_CORRUPT));
6499
6500 /* We need to drop any corrupted fts indexes
6501 before we add a new fts index. */
6502 if (add_fts_idx && index->type & DICT_CORRUPT) {
6503 ib_errf(m_user_thd, IB_LOG_LEVEL_ERROR,
6504 ER_INNODB_INDEX_CORRUPT,
6505 "Fulltext index '%s' is corrupt. "
6506 "you should drop this index first.",
6507 index->name());
6508
6509 goto err_exit_no_heap;
6510 }
6511
6512 continue;
6513 }
6514
6515 for (ulint i = 0; i < dict_index_get_n_fields(index); i++) {
6516 const dict_field_t* field
6517 = dict_index_get_nth_field(index, i);
6518 if (field->prefix_len > max_col_len) {
6519 my_error(ER_INDEX_COLUMN_TOO_LONG, MYF(0),
6520 max_col_len);
6521 goto err_exit_no_heap;
6522 }
6523 }
6524 }
6525
6526 n_drop_index = 0;
6527 n_drop_fk = 0;
6528
6529 if (ha_alter_info->handler_flags
6530 & (INNOBASE_ALTER_NOREBUILD | INNOBASE_ALTER_REBUILD
6531 | INNOBASE_ALTER_INSTANT)) {
6532 heap = mem_heap_create(1024);
6533
6534 if (ha_alter_info->handler_flags
6535 & ALTER_COLUMN_NAME) {
6536 col_names = innobase_get_col_names(
6537 ha_alter_info, altered_table, table,
6538 indexed_table, heap);
6539 } else {
6540 col_names = NULL;
6541 }
6542 } else {
6543 heap = NULL;
6544 col_names = NULL;
6545 }
6546
6547 if (ha_alter_info->handler_flags
6548 & ALTER_DROP_FOREIGN_KEY) {
6549 DBUG_ASSERT(ha_alter_info->alter_info->drop_list.elements > 0);
6550
6551 drop_fk = static_cast<dict_foreign_t**>(
6552 mem_heap_alloc(
6553 heap,
6554 ha_alter_info->alter_info->drop_list.elements
6555 * sizeof(dict_foreign_t*)));
6556
6557 List_iterator<Alter_drop> drop_it(
6558 ha_alter_info->alter_info->drop_list);
6559
6560 while (Alter_drop* drop = drop_it++) {
6561 if (drop->type != Alter_drop::FOREIGN_KEY) {
6562 continue;
6563 }
6564
6565 for (dict_foreign_set::iterator it
6566 = m_prebuilt->table->foreign_set.begin();
6567 it != m_prebuilt->table->foreign_set.end();
6568 ++it) {
6569
6570 dict_foreign_t* foreign = *it;
6571 const char* fid = strchr(foreign->id, '/');
6572
6573 DBUG_ASSERT(fid);
6574 /* If no database/ prefix was present in
6575 the FOREIGN KEY constraint name, compare
6576 to the full constraint name. */
6577 fid = fid ? fid + 1 : foreign->id;
6578
6579 if (!my_strcasecmp(system_charset_info,
6580 fid, drop->name)) {
6581 drop_fk[n_drop_fk++] = foreign;
6582 goto found_fk;
6583 }
6584 }
6585
6586 my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0),
6587 drop->type_name(), drop->name);
6588 goto err_exit;
6589found_fk:
6590 continue;
6591 }
6592
6593 DBUG_ASSERT(n_drop_fk > 0);
6594
6595 DBUG_ASSERT(n_drop_fk
6596 == ha_alter_info->alter_info->drop_list.elements);
6597 } else {
6598 drop_fk = NULL;
6599 }
6600
6601 if (ha_alter_info->index_drop_count) {
6602 dict_index_t* drop_primary = NULL;
6603
6604 DBUG_ASSERT(ha_alter_info->handler_flags
6605 & (ALTER_DROP_NON_UNIQUE_NON_PRIM_INDEX
6606 | ALTER_DROP_UNIQUE_INDEX
6607 | ALTER_DROP_PK_INDEX));
6608 /* Check which indexes to drop. */
6609 drop_index = static_cast<dict_index_t**>(
6610 mem_heap_alloc(
6611 heap, (ha_alter_info->index_drop_count + 1)
6612 * sizeof *drop_index));
6613
6614 for (uint i = 0; i < ha_alter_info->index_drop_count; i++) {
6615 const KEY* key
6616 = ha_alter_info->index_drop_buffer[i];
6617 dict_index_t* index
6618 = dict_table_get_index_on_name(
6619 indexed_table, key->name.str);
6620
6621 if (!index) {
6622 push_warning_printf(
6623 m_user_thd,
6624 Sql_condition::WARN_LEVEL_WARN,
6625 HA_ERR_WRONG_INDEX,
6626 "InnoDB could not find key"
6627 " with name %s", key->name);
6628 } else {
6629 ut_ad(!index->to_be_dropped);
6630 if (!index->is_primary()) {
6631 drop_index[n_drop_index++] = index;
6632 } else {
6633 drop_primary = index;
6634 }
6635 }
6636 }
6637
6638 /* If all FULLTEXT indexes were removed, drop an
6639 internal FTS_DOC_ID_INDEX as well, unless it exists in
6640 the table. */
6641
6642 if (innobase_fulltext_exist(table)
6643 && !innobase_fulltext_exist(altered_table)
6644 && !DICT_TF2_FLAG_IS_SET(
6645 indexed_table, DICT_TF2_FTS_HAS_DOC_ID)) {
6646 dict_index_t* fts_doc_index
6647 = indexed_table->fts_doc_id_index;
6648 ut_ad(fts_doc_index);
6649
6650 // Add some fault tolerance for non-debug builds.
6651 if (fts_doc_index == NULL) {
6652 goto check_if_can_drop_indexes;
6653 }
6654
6655 DBUG_ASSERT(!fts_doc_index->to_be_dropped);
6656
6657 for (uint i = 0; i < table->s->keys; i++) {
6658 if (!my_strcasecmp(
6659 system_charset_info,
6660 FTS_DOC_ID_INDEX_NAME,
6661 table->key_info[i].name.str)) {
6662 /* The index exists in the MySQL
6663 data dictionary. Do not drop it,
6664 even though it is no longer needed
6665 by InnoDB fulltext search. */
6666 goto check_if_can_drop_indexes;
6667 }
6668 }
6669
6670 drop_index[n_drop_index++] = fts_doc_index;
6671 }
6672
6673check_if_can_drop_indexes:
6674 /* Check if the indexes can be dropped. */
6675
6676 /* Prevent a race condition between DROP INDEX and
6677 CREATE TABLE adding FOREIGN KEY constraints. */
6678 row_mysql_lock_data_dictionary(m_prebuilt->trx);
6679
6680 if (!n_drop_index) {
6681 drop_index = NULL;
6682 } else {
6683 /* Flag all indexes that are to be dropped. */
6684 for (ulint i = 0; i < n_drop_index; i++) {
6685 ut_ad(!drop_index[i]->to_be_dropped);
6686 drop_index[i]->to_be_dropped = 1;
6687 }
6688 }
6689
6690 if (m_prebuilt->trx->check_foreigns) {
6691 for (uint i = 0; i < n_drop_index; i++) {
6692 dict_index_t* index = drop_index[i];
6693
6694 if (innobase_check_foreign_key_index(
6695 ha_alter_info, index,
6696 indexed_table, col_names,
6697 m_prebuilt->trx, drop_fk, n_drop_fk)) {
6698 row_mysql_unlock_data_dictionary(
6699 m_prebuilt->trx);
6700 m_prebuilt->trx->error_info = index;
6701 print_error(HA_ERR_DROP_INDEX_FK,
6702 MYF(0));
6703 goto err_exit;
6704 }
6705 }
6706
6707 /* If a primary index is dropped, need to check
6708 any depending foreign constraints get affected */
6709 if (drop_primary
6710 && innobase_check_foreign_key_index(
6711 ha_alter_info, drop_primary,
6712 indexed_table, col_names,
6713 m_prebuilt->trx, drop_fk, n_drop_fk)) {
6714 row_mysql_unlock_data_dictionary(m_prebuilt->trx);
6715 print_error(HA_ERR_DROP_INDEX_FK, MYF(0));
6716 goto err_exit;
6717 }
6718 }
6719
6720 row_mysql_unlock_data_dictionary(m_prebuilt->trx);
6721 } else {
6722 drop_index = NULL;
6723 }
6724
6725 /* Check if any of the existing indexes are marked as corruption
6726 and if they are, refuse adding more indexes. */
6727 if (ha_alter_info->handler_flags & ALTER_ADD_NON_UNIQUE_NON_PRIM_INDEX) {
6728 for (dict_index_t* index = dict_table_get_first_index(indexed_table);
6729 index != NULL; index = dict_table_get_next_index(index)) {
6730
6731 if (!index->to_be_dropped && index->is_corrupted()) {
6732 my_error(ER_INDEX_CORRUPT, MYF(0), index->name());
6733 goto err_exit;
6734 }
6735 }
6736 }
6737
6738 n_rename_index = 0;
6739 rename_index = NULL;
6740
6741#ifdef MYSQL_RENAME_INDEX
6742
6743 n_rename_index = ha_alter_info->index_rename_count;
6744
6745 /* Create a list of dict_index_t objects that are to be renamed,
6746 also checking for requests to rename nonexistent indexes. If
6747 the table is going to be rebuilt (new_clustered == true in
6748 prepare_inplace_alter_table_dict()), then this can be skipped,
6749 but we don't for simplicity (we have not determined the value of
6750 new_clustered yet). */
6751 if (n_rename_index > 0) {
6752 rename_index = static_cast<dict_index_t**>(
6753 mem_heap_alloc(
6754 heap,
6755 n_rename_index * sizeof(*rename_index)));
6756 for (ulint i = 0; i < n_rename_index; i++) {
6757 dict_index_t* index = NULL;
6758 const char* old_name = NULL;
6759
6760 const char* old_name = ha_alter_info
6761 ->index_rename_buffer[i].old_key->name;
6762
6763 index = dict_table_get_index_on_name(indexed_table,
6764 old_name);
6765
6766 if (index == NULL) {
6767 my_error(ER_KEY_DOES_NOT_EXITS, MYF(0),
6768 old_name,
6769 m_prebuilt->table->name.m_name);
6770 goto err_exit;
6771 }
6772
6773 rename_index[i] = index;
6774 }
6775 }
6776#endif /* MYSQL_RENAME_INDEX */
6777
6778 n_add_fk = 0;
6779
6780 if (ha_alter_info->handler_flags
6781 & ALTER_ADD_FOREIGN_KEY) {
6782 ut_ad(!m_prebuilt->trx->check_foreigns);
6783
6784 alter_fill_stored_column(altered_table, m_prebuilt->table,
6785 &s_cols, &s_heap);
6786
6787 add_fk = static_cast<dict_foreign_t**>(
6788 mem_heap_zalloc(
6789 heap,
6790 ha_alter_info->alter_info->key_list.elements
6791 * sizeof(dict_foreign_t*)));
6792
6793 if (!innobase_get_foreign_key_info(
6794 ha_alter_info, table_share,
6795 m_prebuilt->table, col_names,
6796 drop_index, n_drop_index,
6797 add_fk, &n_add_fk, m_prebuilt->trx, s_cols)) {
6798err_exit:
6799 if (n_drop_index) {
6800 row_mysql_lock_data_dictionary(m_prebuilt->trx);
6801
6802 /* Clear the to_be_dropped flags, which might
6803 have been set at this point. */
6804 for (ulint i = 0; i < n_drop_index; i++) {
6805 ut_ad(drop_index[i]->is_committed());
6806 drop_index[i]->to_be_dropped = 0;
6807 }
6808
6809 row_mysql_unlock_data_dictionary(
6810 m_prebuilt->trx);
6811 }
6812
6813 if (heap) {
6814 mem_heap_free(heap);
6815 }
6816
6817 if (s_cols != NULL) {
6818 UT_DELETE(s_cols);
6819 mem_heap_free(s_heap);
6820 }
6821
6822 goto err_exit_no_heap;
6823 }
6824
6825 if (s_cols != NULL) {
6826 UT_DELETE(s_cols);
6827 mem_heap_free(s_heap);
6828 }
6829 }
6830
6831 if (!(ha_alter_info->handler_flags & INNOBASE_ALTER_DATA)
6832 || ((ha_alter_info->handler_flags & ~(INNOBASE_INPLACE_IGNORE
6833 | INNOBASE_ALTER_INSTANT))
6834 == ALTER_CHANGE_CREATE_OPTION
6835 && !create_option_need_rebuild(ha_alter_info, table))) {
6836
6837 if (heap) {
6838 ha_alter_info->handler_ctx
6839 = new ha_innobase_inplace_ctx(
6840 (*m_prebuilt_ptr),
6841 drop_index, n_drop_index,
6842 rename_index, n_rename_index,
6843 drop_fk, n_drop_fk,
6844 add_fk, n_add_fk,
6845 ha_alter_info->online,
6846 heap, indexed_table,
6847 col_names, ULINT_UNDEFINED, 0, 0,
6848 ha_alter_info->ignore);
6849 }
6850
6851 DBUG_ASSERT(m_prebuilt->trx->dict_operation_lock_mode == 0);
6852 if (ha_alter_info->handler_flags & ~(INNOBASE_INPLACE_IGNORE)) {
6853
6854 online_retry_drop_indexes(
6855 m_prebuilt->table, m_user_thd);
6856
6857 }
6858
6859 if ((ha_alter_info->handler_flags
6860 & ALTER_DROP_VIRTUAL_COLUMN)
6861 && prepare_inplace_drop_virtual(ha_alter_info, table)) {
6862 DBUG_RETURN(true);
6863 }
6864
6865 if ((ha_alter_info->handler_flags
6866 & ALTER_ADD_VIRTUAL_COLUMN)
6867 && prepare_inplace_add_virtual(
6868 ha_alter_info, altered_table, table)) {
6869 DBUG_RETURN(true);
6870 }
6871
6872 DBUG_RETURN(false);
6873 }
6874
6875 /* If we are to build a full-text search index, check whether
6876 the table already has a DOC ID column. If not, we will need to
6877 add a Doc ID hidden column and rebuild the primary index */
6878 if (innobase_fulltext_exist(altered_table)) {
6879 ulint doc_col_no;
6880 ulint num_v = 0;
6881
6882 if (!innobase_fts_check_doc_id_col(
6883 m_prebuilt->table,
6884 altered_table, &fts_doc_col_no, &num_v)) {
6885
6886 fts_doc_col_no = altered_table->s->fields - num_v;
6887 add_fts_doc_id = true;
6888 add_fts_doc_id_idx = true;
6889
6890 } else if (fts_doc_col_no == ULINT_UNDEFINED) {
6891 goto err_exit;
6892 }
6893
6894 switch (innobase_fts_check_doc_id_index(
6895 m_prebuilt->table, altered_table,
6896 &doc_col_no)) {
6897 case FTS_NOT_EXIST_DOC_ID_INDEX:
6898 add_fts_doc_id_idx = true;
6899 break;
6900 case FTS_INCORRECT_DOC_ID_INDEX:
6901 my_error(ER_INNODB_FT_WRONG_DOCID_INDEX, MYF(0),
6902 FTS_DOC_ID_INDEX_NAME);
6903 goto err_exit;
6904 case FTS_EXIST_DOC_ID_INDEX:
6905 DBUG_ASSERT(
6906 doc_col_no == fts_doc_col_no
6907 || doc_col_no == ULINT_UNDEFINED
6908 || (ha_alter_info->handler_flags
6909 & (ALTER_STORED_COLUMN_ORDER
6910 | ALTER_DROP_STORED_COLUMN
6911 | ALTER_ADD_STORED_BASE_COLUMN)));
6912 }
6913 }
6914
6915 /* See if an AUTO_INCREMENT column was added. */
6916 uint i = 0;
6917 ulint num_v = 0;
6918 List_iterator_fast<Create_field> cf_it(
6919 ha_alter_info->alter_info->create_list);
6920 while (const Create_field* new_field = cf_it++) {
6921 const Field* field;
6922
6923 DBUG_ASSERT(i < altered_table->s->fields);
6924
6925 for (uint old_i = 0; table->field[old_i]; old_i++) {
6926 if (new_field->field == table->field[old_i]) {
6927 goto found_col;
6928 }
6929 }
6930
6931 /* This is an added column. */
6932 DBUG_ASSERT(!new_field->field);
6933 DBUG_ASSERT(ha_alter_info->handler_flags
6934 & ALTER_ADD_COLUMN);
6935
6936 field = altered_table->field[i];
6937
6938 DBUG_ASSERT((MTYP_TYPENR(field->unireg_check)
6939 == Field::NEXT_NUMBER)
6940 == !!(field->flags & AUTO_INCREMENT_FLAG));
6941
6942 if (field->flags & AUTO_INCREMENT_FLAG) {
6943 if (add_autoinc_col_no != ULINT_UNDEFINED) {
6944 /* This should have been blocked earlier. */
6945 ut_ad(0);
6946 my_error(ER_WRONG_AUTO_KEY, MYF(0));
6947 goto err_exit;
6948 }
6949
6950 /* Get the col no of the old table non-virtual column array */
6951 add_autoinc_col_no = i - num_v;
6952
6953 autoinc_col_max_value = innobase_get_int_col_max_value(field);
6954 }
6955found_col:
6956 if (innobase_is_v_fld(new_field)) {
6957 ++num_v;
6958 }
6959
6960 i++;
6961 }
6962
6963 DBUG_ASSERT(heap);
6964 DBUG_ASSERT(m_user_thd == m_prebuilt->trx->mysql_thd);
6965 DBUG_ASSERT(!ha_alter_info->handler_ctx);
6966
6967 ha_alter_info->handler_ctx = new ha_innobase_inplace_ctx(
6968 (*m_prebuilt_ptr),
6969 drop_index, n_drop_index,
6970 rename_index, n_rename_index,
6971 drop_fk, n_drop_fk, add_fk, n_add_fk,
6972 ha_alter_info->online,
6973 heap, m_prebuilt->table, col_names,
6974 add_autoinc_col_no,
6975 ha_alter_info->create_info->auto_increment_value,
6976 autoinc_col_max_value, ha_alter_info->ignore);
6977
6978 DBUG_RETURN(prepare_inplace_alter_table_dict(
6979 ha_alter_info, altered_table, table,
6980 table_share->table_name.str,
6981 info.flags(), info.flags2(),
6982 fts_doc_col_no, add_fts_doc_id,
6983 add_fts_doc_id_idx));
6984}
6985
6986/** Check that the column is part of a virtual index(index contains
6987virtual column) in the table
6988@param[in] table Table containing column
6989@param[in] col column to be checked
6990@return true if this column is indexed with other virtual columns */
6991static
6992bool
6993dict_col_in_v_indexes(
6994 dict_table_t* table,
6995 dict_col_t* col)
6996{
6997 for (dict_index_t* index = dict_table_get_next_index(
6998 dict_table_get_first_index(table)); index != NULL;
6999 index = dict_table_get_next_index(index)) {
7000 if (!dict_index_has_virtual(index)) {
7001 continue;
7002 }
7003 for (ulint k = 0; k < index->n_fields; k++) {
7004 dict_field_t* field
7005 = dict_index_get_nth_field(index, k);
7006 if (field->col->ind == col->ind) {
7007 return(true);
7008 }
7009 }
7010 }
7011
7012 return(false);
7013}
7014
7015/* Check whether a columnn length change alter operation requires
7016to rebuild the template.
7017@param[in] altered_table TABLE object for new version of table.
7018@param[in] ha_alter_info Structure describing changes to be done
7019 by ALTER TABLE and holding data used
7020 during in-place alter.
7021@param[in] table table being altered
7022@return TRUE if needs rebuild. */
7023static
7024bool
7025alter_templ_needs_rebuild(
7026 TABLE* altered_table,
7027 Alter_inplace_info* ha_alter_info,
7028 dict_table_t* table)
7029{
7030 ulint i = 0;
7031 List_iterator_fast<Create_field> cf_it(
7032 ha_alter_info->alter_info->create_list);
7033
7034 for (Field** fp = altered_table->field; *fp; fp++, i++) {
7035 cf_it.rewind();
7036 while (const Create_field* cf = cf_it++) {
7037 for (ulint j=0; j < table->n_cols; j++) {
7038 dict_col_t* cols
7039 = dict_table_get_nth_col(table, j);
7040 if (cf->length > cols->len
7041 && dict_col_in_v_indexes(table, cols)) {
7042 return(true);
7043 }
7044 }
7045 }
7046 }
7047
7048 return(false);
7049}
7050
7051/** Get the name of an erroneous key.
7052@param[in] error_key_num InnoDB number of the erroneus key
7053@param[in] ha_alter_info changes that were being performed
7054@param[in] table InnoDB table
7055@return the name of the erroneous key */
7056static
7057const char*
7058get_error_key_name(
7059 ulint error_key_num,
7060 const Alter_inplace_info* ha_alter_info,
7061 const dict_table_t* table)
7062{
7063 if (error_key_num == ULINT_UNDEFINED) {
7064 return(FTS_DOC_ID_INDEX_NAME);
7065 } else if (ha_alter_info->key_count == 0) {
7066 return(dict_table_get_first_index(table)->name);
7067 } else {
7068 return(ha_alter_info->key_info_buffer[error_key_num].name.str);
7069 }
7070}
7071
7072/** Alter the table structure in-place with operations
7073specified using Alter_inplace_info.
7074The level of concurrency allowed during this operation depends
7075on the return value from check_if_supported_inplace_alter().
7076
7077@param altered_table TABLE object for new version of table.
7078@param ha_alter_info Structure describing changes to be done
7079by ALTER TABLE and holding data used during in-place alter.
7080
7081@retval true Failure
7082@retval false Success
7083*/
7084
7085bool
7086ha_innobase::inplace_alter_table(
7087/*=============================*/
7088 TABLE* altered_table,
7089 Alter_inplace_info* ha_alter_info)
7090{
7091 dberr_t error;
7092 dict_add_v_col_t* add_v = NULL;
7093 dict_vcol_templ_t* s_templ = NULL;
7094 dict_vcol_templ_t* old_templ = NULL;
7095 struct TABLE* eval_table = altered_table;
7096 bool rebuild_templ = false;
7097 DBUG_ENTER("inplace_alter_table");
7098 DBUG_ASSERT(!srv_read_only_mode);
7099
7100 ut_ad(!sync_check_iterate(sync_check()));
7101 ut_ad(!rw_lock_own(dict_operation_lock, RW_LOCK_X));
7102 ut_ad(!rw_lock_own(dict_operation_lock, RW_LOCK_S));
7103
7104 DEBUG_SYNC(m_user_thd, "innodb_inplace_alter_table_enter");
7105
7106 if (!(ha_alter_info->handler_flags & INNOBASE_ALTER_DATA)) {
7107ok_exit:
7108 DEBUG_SYNC(m_user_thd, "innodb_after_inplace_alter_table");
7109 DBUG_RETURN(false);
7110 }
7111
7112 if ((ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE)
7113 == ALTER_CHANGE_CREATE_OPTION
7114 && !create_option_need_rebuild(ha_alter_info, table)) {
7115 goto ok_exit;
7116 }
7117
7118 ha_innobase_inplace_ctx* ctx
7119 = static_cast<ha_innobase_inplace_ctx*>
7120 (ha_alter_info->handler_ctx);
7121
7122 DBUG_ASSERT(ctx);
7123 DBUG_ASSERT(ctx->trx);
7124 DBUG_ASSERT(ctx->prebuilt == m_prebuilt);
7125
7126 if (ctx->is_instant()) goto ok_exit;
7127
7128 dict_index_t* pk = dict_table_get_first_index(m_prebuilt->table);
7129 ut_ad(pk != NULL);
7130
7131 /* For partitioned tables this could be already allocated from a
7132 previous partition invocation. For normal tables this is NULL. */
7133 UT_DELETE(ctx->m_stage);
7134
7135 ctx->m_stage = UT_NEW_NOKEY(ut_stage_alter_t(pk));
7136
7137 if (!m_prebuilt->table->is_readable()) {
7138 goto all_done;
7139 }
7140
7141 /* If we are doing a table rebuilding or having added virtual
7142 columns in the same clause, we will need to build a table template
7143 that carries translation information between MySQL TABLE and InnoDB
7144 table, which indicates the virtual columns and their base columns
7145 info. This is used to do the computation callback, so that the
7146 data in base columns can be extracted send to server.
7147 If the Column length changes and it is a part of virtual
7148 index then we need to rebuild the template. */
7149 rebuild_templ
7150 = ctx->need_rebuild()
7151 || ((ha_alter_info->handler_flags
7152 & ALTER_COLUMN_EQUAL_PACK_LENGTH)
7153 && alter_templ_needs_rebuild(
7154 altered_table, ha_alter_info, ctx->new_table));
7155
7156 if ((ctx->new_table->n_v_cols > 0) && rebuild_templ) {
7157 /* Save the templ if isn't NULL so as to restore the
7158 original state in case of alter operation failures. */
7159 if (ctx->new_table->vc_templ != NULL && !ctx->need_rebuild()) {
7160 old_templ = ctx->new_table->vc_templ;
7161 }
7162 s_templ = UT_NEW_NOKEY(dict_vcol_templ_t());
7163
7164 innobase_build_v_templ(
7165 altered_table, ctx->new_table, s_templ, NULL, false);
7166
7167 ctx->new_table->vc_templ = s_templ;
7168 } else if (ctx->num_to_add_vcol > 0 && ctx->num_to_drop_vcol == 0) {
7169 /* if there is ongoing drop virtual column, then we disallow
7170 inplace add index on newly added virtual column, so it does
7171 not need to come in here to rebuild template with add_v.
7172 Please also see the assertion in innodb_v_adjust_idx_col() */
7173
7174 s_templ = UT_NEW_NOKEY(dict_vcol_templ_t());
7175
7176 add_v = static_cast<dict_add_v_col_t*>(
7177 mem_heap_alloc(ctx->heap, sizeof *add_v));
7178 add_v->n_v_col = ctx->num_to_add_vcol;
7179 add_v->v_col = ctx->add_vcol;
7180 add_v->v_col_name = ctx->add_vcol_name;
7181
7182 innobase_build_v_templ(
7183 altered_table, ctx->new_table, s_templ, add_v, false);
7184 old_templ = ctx->new_table->vc_templ;
7185 ctx->new_table->vc_templ = s_templ;
7186 }
7187
7188 /* Drop virtual column without rebuild will keep dict table
7189 unchanged, we use old table to evaluate virtual column value
7190 in innobase_get_computed_value(). */
7191 if (!ctx->need_rebuild() && ctx->num_to_drop_vcol > 0) {
7192 eval_table = table;
7193 }
7194
7195 /* Read the clustered index of the table and build
7196 indexes based on this information using temporary
7197 files and merge sort. */
7198 DBUG_EXECUTE_IF("innodb_OOM_inplace_alter",
7199 error = DB_OUT_OF_MEMORY; goto oom;);
7200
7201 error = row_merge_build_indexes(
7202 m_prebuilt->trx,
7203 m_prebuilt->table, ctx->new_table,
7204 ctx->online,
7205 ctx->add_index, ctx->add_key_numbers, ctx->num_to_add_index,
7206 altered_table, ctx->defaults, ctx->col_map,
7207 ctx->add_autoinc, ctx->sequence, ctx->skip_pk_sort,
7208 ctx->m_stage, add_v, eval_table,
7209 ha_alter_info->handler_flags & ALTER_DROP_HISTORICAL);
7210
7211#ifndef DBUG_OFF
7212oom:
7213#endif /* !DBUG_OFF */
7214 if (error == DB_SUCCESS && ctx->online && ctx->need_rebuild()) {
7215 DEBUG_SYNC_C("row_log_table_apply1_before");
7216 error = row_log_table_apply(
7217 ctx->thr, m_prebuilt->table, altered_table,
7218 ctx->m_stage);
7219 }
7220
7221 /* Init online ddl status variables */
7222 onlineddl_rowlog_rows = 0;
7223 onlineddl_rowlog_pct_used = 0;
7224 onlineddl_pct_progress = 0;
7225
7226 if (s_templ) {
7227 ut_ad(ctx->need_rebuild() || ctx->num_to_add_vcol > 0
7228 || rebuild_templ);
7229 dict_free_vc_templ(s_templ);
7230 UT_DELETE(s_templ);
7231
7232 ctx->new_table->vc_templ = old_templ;
7233 }
7234
7235 DEBUG_SYNC_C("inplace_after_index_build");
7236
7237 DBUG_EXECUTE_IF("create_index_fail",
7238 error = DB_DUPLICATE_KEY;
7239 m_prebuilt->trx->error_key_num = ULINT_UNDEFINED;);
7240
7241 /* After an error, remove all those index definitions
7242 from the dictionary which were defined. */
7243
7244 switch (error) {
7245 KEY* dup_key;
7246 all_done:
7247 case DB_SUCCESS:
7248 ut_d(mutex_enter(&dict_sys->mutex));
7249 ut_d(dict_table_check_for_dup_indexes(
7250 m_prebuilt->table, CHECK_PARTIAL_OK));
7251 ut_d(mutex_exit(&dict_sys->mutex));
7252 /* prebuilt->table->n_ref_count can be anything here,
7253 given that we hold at most a shared lock on the table. */
7254 goto ok_exit;
7255 case DB_DUPLICATE_KEY:
7256 if (m_prebuilt->trx->error_key_num == ULINT_UNDEFINED
7257 || ha_alter_info->key_count == 0) {
7258 /* This should be the hidden index on
7259 FTS_DOC_ID, or there is no PRIMARY KEY in the
7260 table. Either way, we should be seeing and
7261 reporting a bogus duplicate key error. */
7262 dup_key = NULL;
7263 } else {
7264 DBUG_ASSERT(m_prebuilt->trx->error_key_num
7265 < ha_alter_info->key_count);
7266 dup_key = &ha_alter_info->key_info_buffer[
7267 m_prebuilt->trx->error_key_num];
7268 }
7269 print_keydup_error(altered_table, dup_key, MYF(0));
7270 break;
7271 case DB_ONLINE_LOG_TOO_BIG:
7272 DBUG_ASSERT(ctx->online);
7273 my_error(ER_INNODB_ONLINE_LOG_TOO_BIG, MYF(0),
7274 get_error_key_name(m_prebuilt->trx->error_key_num,
7275 ha_alter_info, m_prebuilt->table));
7276 break;
7277 case DB_INDEX_CORRUPT:
7278 my_error(ER_INDEX_CORRUPT, MYF(0),
7279 get_error_key_name(m_prebuilt->trx->error_key_num,
7280 ha_alter_info, m_prebuilt->table));
7281 break;
7282 case DB_DECRYPTION_FAILED: {
7283 String str;
7284 const char* engine= table_type();
7285 get_error_message(HA_ERR_DECRYPTION_FAILED, &str);
7286 my_error(ER_GET_ERRMSG, MYF(0), HA_ERR_DECRYPTION_FAILED, str.c_ptr(), engine);
7287 break;
7288 }
7289 default:
7290 my_error_innodb(error,
7291 table_share->table_name.str,
7292 m_prebuilt->table->flags);
7293 }
7294
7295 /* prebuilt->table->n_ref_count can be anything here, given
7296 that we hold at most a shared lock on the table. */
7297 m_prebuilt->trx->error_info = NULL;
7298 ctx->trx->error_state = DB_SUCCESS;
7299
7300 DBUG_RETURN(true);
7301}
7302
7303/** Free the modification log for online table rebuild.
7304@param table table that was being rebuilt online */
7305static
7306void
7307innobase_online_rebuild_log_free(
7308/*=============================*/
7309 dict_table_t* table)
7310{
7311 dict_index_t* clust_index = dict_table_get_first_index(table);
7312
7313 ut_ad(mutex_own(&dict_sys->mutex));
7314 ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X));
7315
7316 rw_lock_x_lock(&clust_index->lock);
7317
7318 if (clust_index->online_log) {
7319 ut_ad(dict_index_get_online_status(clust_index)
7320 == ONLINE_INDEX_CREATION);
7321 clust_index->online_status = ONLINE_INDEX_COMPLETE;
7322 row_log_free(clust_index->online_log);
7323 DEBUG_SYNC_C("innodb_online_rebuild_log_free_aborted");
7324 }
7325
7326 DBUG_ASSERT(dict_index_get_online_status(clust_index)
7327 == ONLINE_INDEX_COMPLETE);
7328 rw_lock_x_unlock(&clust_index->lock);
7329}
7330
7331/** For each user column, which is part of an index which is not going to be
7332dropped, it checks if the column number of the column is same as col_no
7333argument passed.
7334@param[in] table table
7335@param[in] col_no column number
7336@param[in] is_v if this is a virtual column
7337@param[in] only_committed whether to consider only committed indexes
7338@retval true column exists
7339@retval false column does not exist, true if column is system column or
7340it is in the index. */
7341static
7342bool
7343check_col_exists_in_indexes(
7344 const dict_table_t* table,
7345 ulint col_no,
7346 bool is_v,
7347 bool only_committed = false)
7348{
7349 /* This function does not check system columns */
7350 if (!is_v && dict_table_get_nth_col(table, col_no)->mtype == DATA_SYS) {
7351 return(true);
7352 }
7353
7354 for (const dict_index_t* index = dict_table_get_first_index(table);
7355 index;
7356 index = dict_table_get_next_index(index)) {
7357
7358 if (only_committed
7359 ? !index->is_committed()
7360 : index->to_be_dropped) {
7361 continue;
7362 }
7363
7364 for (ulint i = 0; i < index->n_user_defined_cols; i++) {
7365 const dict_col_t* idx_col
7366 = dict_index_get_nth_col(index, i);
7367
7368 if (is_v && idx_col->is_virtual()) {
7369 const dict_v_col_t* v_col = reinterpret_cast<
7370 const dict_v_col_t*>(idx_col);
7371 if (v_col->v_pos == col_no) {
7372 return(true);
7373 }
7374 }
7375
7376 if (!is_v && !idx_col->is_virtual()
7377 && dict_col_get_no(idx_col) == col_no) {
7378 return(true);
7379 }
7380 }
7381 }
7382
7383 return(false);
7384}
7385
7386/** Rollback a secondary index creation, drop the indexes with
7387temparary index prefix
7388@param user_table InnoDB table
7389@param table the TABLE
7390@param locked TRUE=table locked, FALSE=may need to do a lazy drop
7391@param trx the transaction
7392*/
7393static MY_ATTRIBUTE((nonnull))
7394void
7395innobase_rollback_sec_index(
7396/*========================*/
7397 dict_table_t* user_table,
7398 const TABLE* table,
7399 ibool locked,
7400 trx_t* trx)
7401{
7402 row_merge_drop_indexes(trx, user_table, locked);
7403
7404 /* Free the table->fts only if there is no FTS_DOC_ID
7405 in the table */
7406 if (user_table->fts
7407 && !DICT_TF2_FLAG_IS_SET(user_table,
7408 DICT_TF2_FTS_HAS_DOC_ID)
7409 && !innobase_fulltext_exist(table)) {
7410 fts_free(user_table);
7411 }
7412}
7413
7414/** Roll back the changes made during prepare_inplace_alter_table()
7415and inplace_alter_table() inside the storage engine. Note that the
7416allowed level of concurrency during this operation will be the same as
7417for inplace_alter_table() and thus might be higher than during
7418prepare_inplace_alter_table(). (E.g concurrent writes were blocked
7419during prepare, but might not be during commit).
7420
7421@param ha_alter_info Data used during in-place alter.
7422@param table the TABLE
7423@param prebuilt the prebuilt struct
7424@retval true Failure
7425@retval false Success
7426*/
7427inline MY_ATTRIBUTE((nonnull, warn_unused_result))
7428bool
7429rollback_inplace_alter_table(
7430/*=========================*/
7431 Alter_inplace_info* ha_alter_info,
7432 const TABLE* table,
7433 row_prebuilt_t* prebuilt)
7434{
7435 bool fail = false;
7436
7437 ha_innobase_inplace_ctx* ctx
7438 = static_cast<ha_innobase_inplace_ctx*>
7439 (ha_alter_info->handler_ctx);
7440
7441 DBUG_ENTER("rollback_inplace_alter_table");
7442
7443 if (!ctx || !ctx->trx) {
7444 /* If we have not started a transaction yet,
7445 (almost) nothing has been or needs to be done. */
7446 goto func_exit;
7447 }
7448
7449 trx_start_for_ddl(ctx->trx, ctx->need_rebuild()
7450 ? TRX_DICT_OP_TABLE : TRX_DICT_OP_INDEX);
7451 row_mysql_lock_data_dictionary(ctx->trx);
7452
7453 if (ctx->need_rebuild()) {
7454 /* DML threads can access ctx->new_table via the
7455 online rebuild log. Free it first. */
7456 innobase_online_rebuild_log_free(prebuilt->table);
7457 }
7458
7459 if (!ctx->new_table) {
7460 ut_ad(ctx->need_rebuild());
7461 } else if (ctx->need_rebuild()) {
7462 dberr_t err= DB_SUCCESS;
7463 ulint flags = ctx->new_table->flags;
7464
7465 /* Since the FTS index specific auxiliary tables has
7466 not yet registered with "table->fts" by fts_add_index(),
7467 we will need explicitly delete them here */
7468 if (dict_table_has_fts_index(ctx->new_table)) {
7469
7470 err = innobase_drop_fts_index_table(
7471 ctx->new_table, ctx->trx);
7472
7473 if (err != DB_SUCCESS) {
7474 my_error_innodb(
7475 err, table->s->table_name.str,
7476 flags);
7477 fail = true;
7478 }
7479 }
7480
7481 dict_table_close_and_drop(ctx->trx, ctx->new_table);
7482
7483 switch (err) {
7484 case DB_SUCCESS:
7485 break;
7486 default:
7487 my_error_innodb(err, table->s->table_name.str,
7488 flags);
7489 fail = true;
7490 }
7491 } else {
7492 DBUG_ASSERT(!(ha_alter_info->handler_flags
7493 & ALTER_ADD_PK_INDEX));
7494 DBUG_ASSERT(ctx->new_table == prebuilt->table);
7495
7496 innobase_rollback_sec_index(
7497 prebuilt->table, table, FALSE, ctx->trx);
7498 }
7499
7500 trx_commit_for_mysql(ctx->trx);
7501 row_mysql_unlock_data_dictionary(ctx->trx);
7502 trx_free(ctx->trx);
7503
7504func_exit:
7505#ifndef DBUG_OFF
7506 dict_index_t* clust_index = dict_table_get_first_index(
7507 prebuilt->table);
7508 DBUG_ASSERT(!clust_index->online_log);
7509 DBUG_ASSERT(dict_index_get_online_status(clust_index)
7510 == ONLINE_INDEX_COMPLETE);
7511#endif /* !DBUG_OFF */
7512
7513 if (ctx) {
7514 DBUG_ASSERT(ctx->prebuilt == prebuilt);
7515
7516 if (ctx->num_to_add_fk) {
7517 for (ulint i = 0; i < ctx->num_to_add_fk; i++) {
7518 dict_foreign_free(ctx->add_fk[i]);
7519 }
7520 }
7521
7522 if (ctx->num_to_drop_index) {
7523 row_mysql_lock_data_dictionary(prebuilt->trx);
7524
7525 /* Clear the to_be_dropped flags
7526 in the data dictionary cache.
7527 The flags may already have been cleared,
7528 in case an error was detected in
7529 commit_inplace_alter_table(). */
7530 for (ulint i = 0; i < ctx->num_to_drop_index; i++) {
7531 dict_index_t* index = ctx->drop_index[i];
7532 DBUG_ASSERT(index->is_committed());
7533 index->to_be_dropped = 0;
7534 }
7535
7536 row_mysql_unlock_data_dictionary(prebuilt->trx);
7537 }
7538 }
7539
7540 /* Reset dict_col_t::ord_part for those columns fail to be indexed,
7541 we do this by checking every existing column, if any current
7542 index would index them */
7543 for (ulint i = 0; i < dict_table_get_n_cols(prebuilt->table); i++) {
7544 dict_col_t& col = prebuilt->table->cols[i];
7545 if (!col.ord_part) {
7546 continue;
7547 }
7548 if (!check_col_exists_in_indexes(prebuilt->table, i, false,
7549 true)) {
7550 col.ord_part = 0;
7551 }
7552 }
7553
7554 for (ulint i = 0; i < dict_table_get_n_v_cols(prebuilt->table); i++) {
7555 dict_col_t& col = prebuilt->table->v_cols[i].m_col;
7556 if (!col.ord_part) {
7557 continue;
7558 }
7559 if (!check_col_exists_in_indexes(prebuilt->table, i, true,
7560 true)) {
7561 col.ord_part = 0;
7562 }
7563 }
7564
7565 trx_commit_for_mysql(prebuilt->trx);
7566 MONITOR_ATOMIC_DEC(MONITOR_PENDING_ALTER_TABLE);
7567 DBUG_RETURN(fail);
7568}
7569
7570/** Drop a FOREIGN KEY constraint from the data dictionary tables.
7571@param trx data dictionary transaction
7572@param table_name Table name in MySQL
7573@param foreign_id Foreign key constraint identifier
7574@retval true Failure
7575@retval false Success */
7576static MY_ATTRIBUTE((nonnull, warn_unused_result))
7577bool
7578innobase_drop_foreign_try(
7579/*======================*/
7580 trx_t* trx,
7581 const char* table_name,
7582 const char* foreign_id)
7583{
7584 DBUG_ENTER("innobase_drop_foreign_try");
7585
7586 DBUG_ASSERT(trx_get_dict_operation(trx) == TRX_DICT_OP_INDEX);
7587 ut_ad(trx->dict_operation_lock_mode == RW_X_LATCH);
7588 ut_ad(mutex_own(&dict_sys->mutex));
7589 ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X));
7590
7591 /* Drop the constraint from the data dictionary. */
7592 static const char sql[] =
7593 "PROCEDURE DROP_FOREIGN_PROC () IS\n"
7594 "BEGIN\n"
7595 "DELETE FROM SYS_FOREIGN WHERE ID=:id;\n"
7596 "DELETE FROM SYS_FOREIGN_COLS WHERE ID=:id;\n"
7597 "END;\n";
7598
7599 dberr_t error;
7600 pars_info_t* info;
7601
7602 info = pars_info_create();
7603 pars_info_add_str_literal(info, "id", foreign_id);
7604
7605 trx->op_info = "dropping foreign key constraint from dictionary";
7606 error = que_eval_sql(info, sql, FALSE, trx);
7607 trx->op_info = "";
7608
7609 DBUG_EXECUTE_IF("ib_drop_foreign_error",
7610 error = DB_OUT_OF_FILE_SPACE;);
7611
7612 if (error != DB_SUCCESS) {
7613 my_error_innodb(error, table_name, 0);
7614 trx->error_state = DB_SUCCESS;
7615 DBUG_RETURN(true);
7616 }
7617
7618 DBUG_RETURN(false);
7619}
7620
7621/** Rename a column in the data dictionary tables.
7622@param[in] user_table InnoDB table that was being altered
7623@param[in] trx data dictionary transaction
7624@param[in] table_name Table name in MySQL
7625@param[in] nth_col 0-based index of the column
7626@param[in] from old column name
7627@param[in] to new column name
7628@param[in] new_clustered whether the table has been rebuilt
7629@param[in] is_virtual whether it is a virtual column
7630@retval true Failure
7631@retval false Success */
7632static MY_ATTRIBUTE((nonnull, warn_unused_result))
7633bool
7634innobase_rename_column_try(
7635 const dict_table_t* user_table,
7636 trx_t* trx,
7637 const char* table_name,
7638 ulint nth_col,
7639 const char* from,
7640 const char* to,
7641 bool new_clustered)
7642{
7643 pars_info_t* info;
7644 dberr_t error;
7645
7646 DBUG_ENTER("innobase_rename_column_try");
7647
7648 DBUG_ASSERT(trx_get_dict_operation(trx) == TRX_DICT_OP_INDEX);
7649 ut_ad(trx->dict_operation_lock_mode == RW_X_LATCH);
7650 ut_ad(mutex_own(&dict_sys->mutex));
7651 ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X));
7652
7653 if (new_clustered) {
7654 goto rename_foreign;
7655 }
7656
7657 info = pars_info_create();
7658
7659 pars_info_add_ull_literal(info, "tableid", user_table->id);
7660 pars_info_add_int4_literal(info, "nth", nth_col);
7661 pars_info_add_str_literal(info, "old", from);
7662 pars_info_add_str_literal(info, "new", to);
7663
7664 trx->op_info = "renaming column in SYS_COLUMNS";
7665
7666 error = que_eval_sql(
7667 info,
7668 "PROCEDURE RENAME_SYS_COLUMNS_PROC () IS\n"
7669 "BEGIN\n"
7670 "UPDATE SYS_COLUMNS SET NAME=:new\n"
7671 "WHERE TABLE_ID=:tableid AND NAME=:old\n"
7672 "AND POS=:nth;\n"
7673 "END;\n",
7674 FALSE, trx);
7675
7676 DBUG_EXECUTE_IF("ib_rename_column_error",
7677 error = DB_OUT_OF_FILE_SPACE;);
7678
7679 if (error != DB_SUCCESS) {
7680err_exit:
7681 my_error_innodb(error, table_name, 0);
7682 trx->error_state = DB_SUCCESS;
7683 trx->op_info = "";
7684 DBUG_RETURN(true);
7685 }
7686
7687 trx->op_info = "renaming column in SYS_FIELDS";
7688
7689 for (const dict_index_t* index = dict_table_get_first_index(
7690 user_table);
7691 index != NULL;
7692 index = dict_table_get_next_index(index)) {
7693
7694 for (ulint i = 0; i < dict_index_get_n_fields(index); i++) {
7695 if (strcmp(dict_index_get_nth_field(index, i)->name,
7696 from)) {
7697 continue;
7698 }
7699
7700 info = pars_info_create();
7701
7702 pars_info_add_ull_literal(info, "indexid", index->id);
7703 pars_info_add_int4_literal(info, "nth", i);
7704 pars_info_add_str_literal(info, "old", from);
7705 pars_info_add_str_literal(info, "new", to);
7706
7707 error = que_eval_sql(
7708 info,
7709 "PROCEDURE RENAME_SYS_FIELDS_PROC () IS\n"
7710 "BEGIN\n"
7711
7712 "UPDATE SYS_FIELDS SET COL_NAME=:new\n"
7713 "WHERE INDEX_ID=:indexid AND COL_NAME=:old\n"
7714 "AND POS=:nth;\n"
7715
7716 /* Try again, in case there is a prefix_len
7717 encoded in SYS_FIELDS.POS */
7718
7719 "UPDATE SYS_FIELDS SET COL_NAME=:new\n"
7720 "WHERE INDEX_ID=:indexid AND COL_NAME=:old\n"
7721 "AND POS>=65536*:nth AND POS<65536*(:nth+1);\n"
7722
7723 "END;\n",
7724 FALSE, trx);
7725
7726 if (error != DB_SUCCESS) {
7727 goto err_exit;
7728 }
7729 }
7730 }
7731
7732rename_foreign:
7733 trx->op_info = "renaming column in SYS_FOREIGN_COLS";
7734
7735 std::list<dict_foreign_t*> fk_evict;
7736 bool foreign_modified;
7737
7738 for (dict_foreign_set::const_iterator it = user_table->foreign_set.begin();
7739 it != user_table->foreign_set.end();
7740 ++it) {
7741
7742 dict_foreign_t* foreign = *it;
7743 foreign_modified = false;
7744
7745 for (unsigned i = 0; i < foreign->n_fields; i++) {
7746 if (strcmp(foreign->foreign_col_names[i], from)) {
7747 continue;
7748 }
7749
7750 info = pars_info_create();
7751
7752 pars_info_add_str_literal(info, "id", foreign->id);
7753 pars_info_add_int4_literal(info, "nth", i);
7754 pars_info_add_str_literal(info, "old", from);
7755 pars_info_add_str_literal(info, "new", to);
7756
7757 error = que_eval_sql(
7758 info,
7759 "PROCEDURE RENAME_SYS_FOREIGN_F_PROC () IS\n"
7760 "BEGIN\n"
7761 "UPDATE SYS_FOREIGN_COLS\n"
7762 "SET FOR_COL_NAME=:new\n"
7763 "WHERE ID=:id AND POS=:nth\n"
7764 "AND FOR_COL_NAME=:old;\n"
7765 "END;\n",
7766 FALSE, trx);
7767
7768 if (error != DB_SUCCESS) {
7769 goto err_exit;
7770 }
7771 foreign_modified = true;
7772 }
7773
7774 if (foreign_modified) {
7775 fk_evict.push_back(foreign);
7776 }
7777 }
7778
7779 for (dict_foreign_set::const_iterator it
7780 = user_table->referenced_set.begin();
7781 it != user_table->referenced_set.end();
7782 ++it) {
7783
7784 foreign_modified = false;
7785 dict_foreign_t* foreign = *it;
7786
7787 for (unsigned i = 0; i < foreign->n_fields; i++) {
7788 if (strcmp(foreign->referenced_col_names[i], from)) {
7789 continue;
7790 }
7791
7792 info = pars_info_create();
7793
7794 pars_info_add_str_literal(info, "id", foreign->id);
7795 pars_info_add_int4_literal(info, "nth", i);
7796 pars_info_add_str_literal(info, "old", from);
7797 pars_info_add_str_literal(info, "new", to);
7798
7799 error = que_eval_sql(
7800 info,
7801 "PROCEDURE RENAME_SYS_FOREIGN_R_PROC () IS\n"
7802 "BEGIN\n"
7803 "UPDATE SYS_FOREIGN_COLS\n"
7804 "SET REF_COL_NAME=:new\n"
7805 "WHERE ID=:id AND POS=:nth\n"
7806 "AND REF_COL_NAME=:old;\n"
7807 "END;\n",
7808 FALSE, trx);
7809
7810 if (error != DB_SUCCESS) {
7811 goto err_exit;
7812 }
7813 foreign_modified = true;
7814 }
7815
7816 if (foreign_modified) {
7817 fk_evict.push_back(foreign);
7818 }
7819 }
7820
7821 if (new_clustered) {
7822 std::for_each(fk_evict.begin(), fk_evict.end(),
7823 dict_foreign_remove_from_cache);
7824 }
7825
7826 trx->op_info = "";
7827 DBUG_RETURN(false);
7828}
7829
7830/** Rename columns in the data dictionary tables.
7831@param ha_alter_info Data used during in-place alter.
7832@param ctx In-place ALTER TABLE context
7833@param table the TABLE
7834@param trx data dictionary transaction
7835@param table_name Table name in MySQL
7836@retval true Failure
7837@retval false Success */
7838static MY_ATTRIBUTE((nonnull, warn_unused_result))
7839bool
7840innobase_rename_columns_try(
7841/*========================*/
7842 Alter_inplace_info* ha_alter_info,
7843 ha_innobase_inplace_ctx*ctx,
7844 const TABLE* table,
7845 trx_t* trx,
7846 const char* table_name)
7847{
7848 List_iterator_fast<Create_field> cf_it(
7849 ha_alter_info->alter_info->create_list);
7850 uint i = 0;
7851 ulint num_v = 0;
7852
7853 DBUG_ASSERT(ctx);
7854 DBUG_ASSERT(ha_alter_info->handler_flags
7855 & ALTER_COLUMN_NAME);
7856
7857 for (Field** fp = table->field; *fp; fp++, i++) {
7858 bool is_virtual = innobase_is_v_fld(*fp);
7859
7860 if (!((*fp)->flags & FIELD_IS_RENAMED)) {
7861 goto processed_field;
7862 }
7863
7864 cf_it.rewind();
7865 while (Create_field* cf = cf_it++) {
7866 if (cf->field == *fp) {
7867 ulint col_n = is_virtual
7868 ? dict_create_v_col_pos(
7869 num_v, i)
7870 : i - num_v;
7871
7872 if (innobase_rename_column_try(
7873 ctx->old_table, trx, table_name,
7874 col_n,
7875 cf->field->field_name.str,
7876 cf->field_name.str,
7877 ctx->need_rebuild())) {
7878 return(true);
7879 }
7880 goto processed_field;
7881 }
7882 }
7883
7884 ut_error;
7885processed_field:
7886 if (is_virtual) {
7887 num_v++;
7888 }
7889
7890 continue;
7891 }
7892
7893 return(false);
7894}
7895
7896/** Enlarge a column in the data dictionary tables.
7897@param user_table InnoDB table that was being altered
7898@param trx data dictionary transaction
7899@param table_name Table name in MySQL
7900@param nth_col 0-based index of the column
7901@param new_len new column length, in bytes
7902@param is_v if it's a virtual column
7903@retval true Failure
7904@retval false Success */
7905static MY_ATTRIBUTE((nonnull, warn_unused_result))
7906bool
7907innobase_enlarge_column_try(
7908/*========================*/
7909 const dict_table_t* user_table,
7910 trx_t* trx,
7911 const char* table_name,
7912 ulint nth_col,
7913 ulint new_len,
7914 bool is_v)
7915{
7916 pars_info_t* info;
7917 dberr_t error;
7918#ifdef UNIV_DEBUG
7919 dict_col_t* col;
7920#endif /* UNIV_DEBUG */
7921 dict_v_col_t* v_col;
7922 ulint pos;
7923
7924 DBUG_ENTER("innobase_enlarge_column_try");
7925
7926 DBUG_ASSERT(trx_get_dict_operation(trx) == TRX_DICT_OP_INDEX);
7927 ut_ad(trx->dict_operation_lock_mode == RW_X_LATCH);
7928 ut_ad(mutex_own(&dict_sys->mutex));
7929 ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X));
7930
7931 if (is_v) {
7932 v_col = dict_table_get_nth_v_col(user_table, nth_col);
7933 pos = dict_create_v_col_pos(v_col->v_pos, v_col->m_col.ind);
7934#ifdef UNIV_DEBUG
7935 col = &v_col->m_col;
7936#endif /* UNIV_DEBUG */
7937 } else {
7938#ifdef UNIV_DEBUG
7939 col = dict_table_get_nth_col(user_table, nth_col);
7940#endif /* UNIV_DEBUG */
7941 pos = nth_col;
7942 }
7943
7944#ifdef UNIV_DEBUG
7945 ut_ad(col->len < new_len);
7946 switch (col->mtype) {
7947 case DATA_MYSQL:
7948 /* NOTE: we could allow this when !(prtype & DATA_BINARY_TYPE)
7949 and ROW_FORMAT is not REDUNDANT and mbminlen<mbmaxlen.
7950 That is, we treat a UTF-8 CHAR(n) column somewhat like
7951 a VARCHAR. */
7952 ut_error;
7953 case DATA_BINARY:
7954 case DATA_VARCHAR:
7955 case DATA_VARMYSQL:
7956 case DATA_DECIMAL:
7957 case DATA_BLOB:
7958 break;
7959 default:
7960 ut_error;
7961 }
7962#endif /* UNIV_DEBUG */
7963 info = pars_info_create();
7964
7965 pars_info_add_ull_literal(info, "tableid", user_table->id);
7966 pars_info_add_int4_literal(info, "nth", pos);
7967 pars_info_add_int4_literal(info, "new", new_len);
7968
7969 trx->op_info = "resizing column in SYS_COLUMNS";
7970
7971 error = que_eval_sql(
7972 info,
7973 "PROCEDURE RESIZE_SYS_COLUMNS_PROC () IS\n"
7974 "BEGIN\n"
7975 "UPDATE SYS_COLUMNS SET LEN=:new\n"
7976 "WHERE TABLE_ID=:tableid AND POS=:nth;\n"
7977 "END;\n",
7978 FALSE, trx);
7979
7980 DBUG_EXECUTE_IF("ib_resize_column_error",
7981 error = DB_OUT_OF_FILE_SPACE;);
7982
7983 trx->op_info = "";
7984 trx->error_state = DB_SUCCESS;
7985
7986 if (error != DB_SUCCESS) {
7987 my_error_innodb(error, table_name, 0);
7988 DBUG_RETURN(true);
7989 }
7990
7991 DBUG_RETURN(false);
7992}
7993
7994/** Enlarge columns in the data dictionary tables.
7995@param ha_alter_info Data used during in-place alter.
7996@param table the TABLE
7997@param user_table InnoDB table that was being altered
7998@param trx data dictionary transaction
7999@param table_name Table name in MySQL
8000@retval true Failure
8001@retval false Success */
8002static MY_ATTRIBUTE((nonnull, warn_unused_result))
8003bool
8004innobase_enlarge_columns_try(
8005/*=========================*/
8006 Alter_inplace_info* ha_alter_info,
8007 const TABLE* table,
8008 const dict_table_t* user_table,
8009 trx_t* trx,
8010 const char* table_name)
8011{
8012 List_iterator_fast<Create_field> cf_it(
8013 ha_alter_info->alter_info->create_list);
8014 ulint i = 0;
8015 ulint num_v = 0;
8016 bool is_v;
8017
8018 for (Field** fp = table->field; *fp; fp++, i++) {
8019 ulint idx;
8020
8021 if (innobase_is_v_fld(*fp)) {
8022 is_v = true;
8023 idx = num_v;
8024 num_v++;
8025 } else {
8026 idx = i - num_v;
8027 is_v = false;
8028 }
8029
8030 cf_it.rewind();
8031 while (Create_field* cf = cf_it++) {
8032 if (cf->field == *fp) {
8033 if ((*fp)->is_equal(cf)
8034 == IS_EQUAL_PACK_LENGTH
8035 && innobase_enlarge_column_try(
8036 user_table, trx, table_name,
8037 idx, static_cast<ulint>(cf->length), is_v)) {
8038 return(true);
8039 }
8040
8041 break;
8042 }
8043 }
8044 }
8045
8046 return(false);
8047}
8048
8049/** Rename or enlarge columns in the data dictionary cache
8050as part of commit_cache_norebuild().
8051@param ha_alter_info Data used during in-place alter.
8052@param table the TABLE
8053@param user_table InnoDB table that was being altered */
8054static MY_ATTRIBUTE((nonnull))
8055void
8056innobase_rename_or_enlarge_columns_cache(
8057/*=====================================*/
8058 Alter_inplace_info* ha_alter_info,
8059 const TABLE* table,
8060 dict_table_t* user_table)
8061{
8062 if (!(ha_alter_info->handler_flags
8063 & (ALTER_COLUMN_EQUAL_PACK_LENGTH
8064 | ALTER_COLUMN_NAME))) {
8065 return;
8066 }
8067
8068 List_iterator_fast<Create_field> cf_it(
8069 ha_alter_info->alter_info->create_list);
8070 uint i = 0;
8071 ulint num_v = 0;
8072
8073 for (Field** fp = table->field; *fp; fp++, i++) {
8074 bool is_virtual = innobase_is_v_fld(*fp);
8075
8076 cf_it.rewind();
8077 while (Create_field* cf = cf_it++) {
8078 if (cf->field != *fp) {
8079 continue;
8080 }
8081
8082 ulint col_n = is_virtual ? num_v : i - num_v;
8083
8084 if ((*fp)->is_equal(cf) == IS_EQUAL_PACK_LENGTH) {
8085 if (is_virtual) {
8086 dict_table_get_nth_v_col(
8087 user_table, col_n)->m_col.len
8088 = cf->length;
8089 } else {
8090 dict_table_get_nth_col(
8091 user_table, col_n)->len
8092 = cf->length;
8093 }
8094 }
8095
8096 if ((*fp)->flags & FIELD_IS_RENAMED) {
8097 dict_mem_table_col_rename(
8098 user_table, col_n,
8099 cf->field->field_name.str,
8100 cf->field_name.str, is_virtual);
8101 }
8102
8103 break;
8104 }
8105
8106 if (is_virtual) {
8107 num_v++;
8108 }
8109 }
8110}
8111
8112/** Set the auto-increment value of the table on commit.
8113@param ha_alter_info Data used during in-place alter
8114@param ctx In-place ALTER TABLE context
8115@param altered_table MySQL table that is being altered
8116@param old_table MySQL table as it is before the ALTER operation
8117@return whether the operation failed (and my_error() was called) */
8118static MY_ATTRIBUTE((nonnull))
8119bool
8120commit_set_autoinc(
8121 Alter_inplace_info* ha_alter_info,
8122 ha_innobase_inplace_ctx*ctx,
8123 const TABLE* altered_table,
8124 const TABLE* old_table)
8125{
8126 DBUG_ENTER("commit_set_autoinc");
8127
8128 if (!altered_table->found_next_number_field) {
8129 /* There is no AUTO_INCREMENT column in the table
8130 after the ALTER operation. */
8131 } else if (ctx->add_autoinc != ULINT_UNDEFINED) {
8132 ut_ad(ctx->need_rebuild());
8133 /* An AUTO_INCREMENT column was added. Get the last
8134 value from the sequence, which may be based on a
8135 supplied AUTO_INCREMENT value. */
8136 ib_uint64_t autoinc = ctx->sequence.last();
8137 ctx->new_table->autoinc = autoinc;
8138 /* Bulk index creation does not update
8139 PAGE_ROOT_AUTO_INC, so we must persist the "last used"
8140 value here. */
8141 btr_write_autoinc(dict_table_get_first_index(ctx->new_table),
8142 autoinc - 1, true);
8143 } else if ((ha_alter_info->handler_flags
8144 & ALTER_CHANGE_CREATE_OPTION)
8145 && (ha_alter_info->create_info->used_fields
8146 & HA_CREATE_USED_AUTO)) {
8147
8148 if (!ctx->old_table->space) {
8149 my_error(ER_TABLESPACE_DISCARDED, MYF(0),
8150 old_table->s->table_name.str);
8151 DBUG_RETURN(true);
8152 }
8153
8154 /* An AUTO_INCREMENT value was supplied by the user.
8155 It must be persisted to the data file. */
8156 const Field* ai = old_table->found_next_number_field;
8157 ut_ad(!strcmp(dict_table_get_col_name(ctx->old_table,
8158 innodb_col_no(ai)),
8159 ai->field_name.str));
8160
8161 ib_uint64_t autoinc
8162 = ha_alter_info->create_info->auto_increment_value;
8163 if (autoinc == 0) {
8164 autoinc = 1;
8165 }
8166
8167 if (autoinc >= ctx->old_table->autoinc) {
8168 /* Persist the predecessor of the
8169 AUTO_INCREMENT value as the last used one. */
8170 ctx->new_table->autoinc = autoinc--;
8171 } else {
8172 /* Mimic ALGORITHM=COPY in the following scenario:
8173
8174 CREATE TABLE t (a SERIAL);
8175 INSERT INTO t SET a=100;
8176 ALTER TABLE t AUTO_INCREMENT = 1;
8177 INSERT INTO t SET a=NULL;
8178 SELECT * FROM t;
8179
8180 By default, ALGORITHM=INPLACE would reset the
8181 sequence to 1, while after ALGORITHM=COPY, the
8182 last INSERT would use a value larger than 100.
8183
8184 We could only search the tree to know current
8185 max counter in the table and compare. */
8186 const dict_col_t* autoinc_col
8187 = dict_table_get_nth_col(ctx->old_table,
8188 innodb_col_no(ai));
8189 dict_index_t* index
8190 = dict_table_get_first_index(ctx->old_table);
8191 while (index != NULL
8192 && index->fields[0].col != autoinc_col) {
8193 index = dict_table_get_next_index(index);
8194 }
8195
8196 ut_ad(index);
8197
8198 ib_uint64_t max_in_table = index
8199 ? row_search_max_autoinc(index)
8200 : 0;
8201
8202 if (autoinc <= max_in_table) {
8203 ctx->new_table->autoinc = innobase_next_autoinc(
8204 max_in_table, 1,
8205 ctx->prebuilt->autoinc_increment,
8206 ctx->prebuilt->autoinc_offset,
8207 innobase_get_int_col_max_value(ai));
8208 /* Persist the maximum value as the
8209 last used one. */
8210 autoinc = max_in_table;
8211 } else {
8212 /* Persist the predecessor of the
8213 AUTO_INCREMENT value as the last used one. */
8214 ctx->new_table->autoinc = autoinc--;
8215 }
8216 }
8217
8218 btr_write_autoinc(dict_table_get_first_index(ctx->new_table),
8219 autoinc, true);
8220 } else if (ctx->need_rebuild()) {
8221 /* No AUTO_INCREMENT value was specified.
8222 Copy it from the old table. */
8223 ctx->new_table->autoinc = ctx->old_table->autoinc;
8224 /* The persistent value was already copied in
8225 prepare_inplace_alter_table_dict() when ctx->new_table
8226 was created. If this was a LOCK=NONE operation, the
8227 AUTO_INCREMENT values would be updated during
8228 row_log_table_apply(). If this was LOCK!=NONE,
8229 the table contents could not possibly have changed
8230 between prepare_inplace and commit_inplace. */
8231 }
8232
8233 DBUG_RETURN(false);
8234}
8235
8236/** Add or drop foreign key constraints to the data dictionary tables,
8237but do not touch the data dictionary cache.
8238@param ha_alter_info Data used during in-place alter
8239@param ctx In-place ALTER TABLE context
8240@param trx Data dictionary transaction
8241@param table_name Table name in MySQL
8242@retval true Failure
8243@retval false Success
8244*/
8245static MY_ATTRIBUTE((nonnull, warn_unused_result))
8246bool
8247innobase_update_foreign_try(
8248/*========================*/
8249 ha_innobase_inplace_ctx*ctx,
8250 trx_t* trx,
8251 const char* table_name)
8252{
8253 ulint foreign_id;
8254 ulint i;
8255
8256 DBUG_ENTER("innobase_update_foreign_try");
8257 DBUG_ASSERT(ctx);
8258
8259 foreign_id = dict_table_get_highest_foreign_id(ctx->new_table);
8260
8261 foreign_id++;
8262
8263 for (i = 0; i < ctx->num_to_add_fk; i++) {
8264 dict_foreign_t* fk = ctx->add_fk[i];
8265
8266 ut_ad(fk->foreign_table == ctx->new_table
8267 || fk->foreign_table == ctx->old_table);
8268
8269 dberr_t error = dict_create_add_foreign_id(
8270 &foreign_id, ctx->old_table->name.m_name, fk);
8271
8272 if (error != DB_SUCCESS) {
8273 my_error(ER_TOO_LONG_IDENT, MYF(0),
8274 fk->id);
8275 DBUG_RETURN(true);
8276 }
8277
8278 if (!fk->foreign_index) {
8279 fk->foreign_index = dict_foreign_find_index(
8280 ctx->new_table, ctx->col_names,
8281 fk->foreign_col_names,
8282 fk->n_fields, fk->referenced_index, TRUE,
8283 fk->type
8284 & (DICT_FOREIGN_ON_DELETE_SET_NULL
8285 | DICT_FOREIGN_ON_UPDATE_SET_NULL),
8286 NULL, NULL, NULL);
8287 if (!fk->foreign_index) {
8288 my_error(ER_FK_INCORRECT_OPTION,
8289 MYF(0), table_name, fk->id);
8290 DBUG_RETURN(true);
8291 }
8292 }
8293
8294 /* The fk->foreign_col_names[] uses renamed column
8295 names, while the columns in ctx->old_table have not
8296 been renamed yet. */
8297 error = dict_create_add_foreign_to_dictionary(
8298 ctx->old_table->name.m_name, fk, trx);
8299
8300 DBUG_EXECUTE_IF(
8301 "innodb_test_cannot_add_fk_system",
8302 error = DB_ERROR;);
8303
8304 if (error != DB_SUCCESS) {
8305 my_error(ER_FK_FAIL_ADD_SYSTEM, MYF(0),
8306 fk->id);
8307 DBUG_RETURN(true);
8308 }
8309 }
8310
8311 for (i = 0; i < ctx->num_to_drop_fk; i++) {
8312 dict_foreign_t* fk = ctx->drop_fk[i];
8313
8314 DBUG_ASSERT(fk->foreign_table == ctx->old_table);
8315
8316 if (innobase_drop_foreign_try(trx, table_name, fk->id)) {
8317 DBUG_RETURN(true);
8318 }
8319 }
8320
8321 DBUG_RETURN(false);
8322}
8323
8324/** Update the foreign key constraint definitions in the data dictionary cache
8325after the changes to data dictionary tables were committed.
8326@param ctx In-place ALTER TABLE context
8327@param user_thd MySQL connection
8328@return InnoDB error code (should always be DB_SUCCESS) */
8329static MY_ATTRIBUTE((nonnull, warn_unused_result))
8330dberr_t
8331innobase_update_foreign_cache(
8332/*==========================*/
8333 ha_innobase_inplace_ctx* ctx,
8334 THD* user_thd)
8335{
8336 dict_table_t* user_table;
8337 dberr_t err = DB_SUCCESS;
8338
8339 DBUG_ENTER("innobase_update_foreign_cache");
8340
8341 ut_ad(mutex_own(&dict_sys->mutex));
8342
8343 user_table = ctx->old_table;
8344
8345 /* Discard the added foreign keys, because we will
8346 load them from the data dictionary. */
8347 for (ulint i = 0; i < ctx->num_to_add_fk; i++) {
8348 dict_foreign_t* fk = ctx->add_fk[i];
8349 dict_foreign_free(fk);
8350 }
8351
8352 if (ctx->need_rebuild()) {
8353 /* The rebuilt table is already using the renamed
8354 column names. No need to pass col_names or to drop
8355 constraints from the data dictionary cache. */
8356 DBUG_ASSERT(!ctx->col_names);
8357 DBUG_ASSERT(user_table->foreign_set.empty());
8358 DBUG_ASSERT(user_table->referenced_set.empty());
8359 user_table = ctx->new_table;
8360 } else {
8361 /* Drop the foreign key constraints if the
8362 table was not rebuilt. If the table is rebuilt,
8363 there would not be any foreign key contraints for
8364 it yet in the data dictionary cache. */
8365 for (ulint i = 0; i < ctx->num_to_drop_fk; i++) {
8366 dict_foreign_t* fk = ctx->drop_fk[i];
8367 dict_foreign_remove_from_cache(fk);
8368 }
8369 }
8370
8371 /* Load the old or added foreign keys from the data dictionary
8372 and prevent the table from being evicted from the data
8373 dictionary cache (work around the lack of WL#6049). */
8374 dict_names_t fk_tables;
8375
8376 err = dict_load_foreigns(user_table->name.m_name,
8377 ctx->col_names, false, true,
8378 DICT_ERR_IGNORE_NONE,
8379 fk_tables);
8380
8381 if (err == DB_CANNOT_ADD_CONSTRAINT) {
8382 fk_tables.clear();
8383
8384 /* It is possible there are existing foreign key are
8385 loaded with "foreign_key checks" off,
8386 so let's retry the loading with charset_check is off */
8387 err = dict_load_foreigns(user_table->name.m_name,
8388 ctx->col_names, false, false,
8389 DICT_ERR_IGNORE_NONE,
8390 fk_tables);
8391
8392 /* The load with "charset_check" off is successful, warn
8393 the user that the foreign key has loaded with mis-matched
8394 charset */
8395 if (err == DB_SUCCESS) {
8396 push_warning_printf(
8397 user_thd,
8398 Sql_condition::WARN_LEVEL_WARN,
8399 ER_ALTER_INFO,
8400 "Foreign key constraints for table '%s'"
8401 " are loaded with charset check off",
8402 user_table->name.m_name);
8403 }
8404 }
8405
8406 /* For complete loading of foreign keys, all associated tables must
8407 also be loaded. */
8408 while (err == DB_SUCCESS && !fk_tables.empty()) {
8409 dict_table_t* table = dict_load_table(
8410 fk_tables.front(), true, DICT_ERR_IGNORE_NONE);
8411
8412 if (table == NULL) {
8413 table_name_t table_name;
8414 table_name.m_name = const_cast<char*>(
8415 fk_tables.front());
8416
8417 err = DB_TABLE_NOT_FOUND;
8418 ib::error()
8419 << "Failed to load table '" << table_name
8420 << "' which has a foreign key constraint with"
8421 << " table '" << user_table->name << "'.";
8422 break;
8423 }
8424
8425 fk_tables.pop_front();
8426 }
8427
8428 DBUG_RETURN(err);
8429}
8430
8431/** Commit the changes made during prepare_inplace_alter_table()
8432and inplace_alter_table() inside the data dictionary tables,
8433when rebuilding the table.
8434@param ha_alter_info Data used during in-place alter
8435@param ctx In-place ALTER TABLE context
8436@param altered_table MySQL table that is being altered
8437@param old_table MySQL table as it is before the ALTER operation
8438@param trx Data dictionary transaction
8439@param table_name Table name in MySQL
8440@retval true Failure
8441@retval false Success
8442*/
8443inline MY_ATTRIBUTE((nonnull, warn_unused_result))
8444bool
8445commit_try_rebuild(
8446/*===============*/
8447 Alter_inplace_info* ha_alter_info,
8448 ha_innobase_inplace_ctx*ctx,
8449 TABLE* altered_table,
8450 const TABLE* old_table,
8451 trx_t* trx,
8452 const char* table_name)
8453{
8454 dict_table_t* rebuilt_table = ctx->new_table;
8455 dict_table_t* user_table = ctx->old_table;
8456
8457 DBUG_ENTER("commit_try_rebuild");
8458 DBUG_ASSERT(ctx->need_rebuild());
8459 DBUG_ASSERT(trx->dict_operation_lock_mode == RW_X_LATCH);
8460 DBUG_ASSERT(!(ha_alter_info->handler_flags
8461 & ALTER_DROP_FOREIGN_KEY)
8462 || ctx->num_to_drop_fk > 0);
8463
8464 for (dict_index_t* index = dict_table_get_first_index(rebuilt_table);
8465 index;
8466 index = dict_table_get_next_index(index)) {
8467 DBUG_ASSERT(dict_index_get_online_status(index)
8468 == ONLINE_INDEX_COMPLETE);
8469 DBUG_ASSERT(index->is_committed());
8470 if (index->is_corrupted()) {
8471 my_error(ER_INDEX_CORRUPT, MYF(0), index->name());
8472 DBUG_RETURN(true);
8473 }
8474 }
8475
8476 if (innobase_update_foreign_try(ctx, trx, table_name)) {
8477 DBUG_RETURN(true);
8478 }
8479
8480 dberr_t error;
8481
8482 /* Clear the to_be_dropped flag in the data dictionary cache
8483 of user_table. */
8484 for (ulint i = 0; i < ctx->num_to_drop_index; i++) {
8485 dict_index_t* index = ctx->drop_index[i];
8486 DBUG_ASSERT(index->table == user_table);
8487 DBUG_ASSERT(index->is_committed());
8488 DBUG_ASSERT(index->to_be_dropped);
8489 index->to_be_dropped = 0;
8490 }
8491
8492 /* We copied the table. Any indexes that were requested to be
8493 dropped were not created in the copy of the table. Apply any
8494 last bit of the rebuild log and then rename the tables. */
8495
8496 if (ctx->online) {
8497 DEBUG_SYNC_C("row_log_table_apply2_before");
8498
8499 dict_vcol_templ_t* s_templ = NULL;
8500
8501 if (ctx->new_table->n_v_cols > 0) {
8502 s_templ = UT_NEW_NOKEY(
8503 dict_vcol_templ_t());
8504 s_templ->vtempl = NULL;
8505
8506 innobase_build_v_templ(
8507 altered_table, ctx->new_table, s_templ,
8508 NULL, true);
8509 ctx->new_table->vc_templ = s_templ;
8510 }
8511
8512 error = row_log_table_apply(
8513 ctx->thr, user_table, altered_table,
8514 static_cast<ha_innobase_inplace_ctx*>(
8515 ha_alter_info->handler_ctx)->m_stage);
8516
8517 if (s_templ) {
8518 ut_ad(ctx->need_rebuild());
8519 dict_free_vc_templ(s_templ);
8520 UT_DELETE(s_templ);
8521 ctx->new_table->vc_templ = NULL;
8522 }
8523
8524 ulint err_key = thr_get_trx(ctx->thr)->error_key_num;
8525
8526 switch (error) {
8527 KEY* dup_key;
8528 case DB_SUCCESS:
8529 break;
8530 case DB_DUPLICATE_KEY:
8531 if (err_key == ULINT_UNDEFINED) {
8532 /* This should be the hidden index on
8533 FTS_DOC_ID. */
8534 dup_key = NULL;
8535 } else {
8536 DBUG_ASSERT(err_key <
8537 ha_alter_info->key_count);
8538 dup_key = &ha_alter_info
8539 ->key_info_buffer[err_key];
8540 }
8541 print_keydup_error(altered_table, dup_key, MYF(0));
8542 DBUG_RETURN(true);
8543 case DB_ONLINE_LOG_TOO_BIG:
8544 my_error(ER_INNODB_ONLINE_LOG_TOO_BIG, MYF(0),
8545 get_error_key_name(err_key, ha_alter_info,
8546 rebuilt_table));
8547 DBUG_RETURN(true);
8548 case DB_INDEX_CORRUPT:
8549 my_error(ER_INDEX_CORRUPT, MYF(0),
8550 get_error_key_name(err_key, ha_alter_info,
8551 rebuilt_table));
8552 DBUG_RETURN(true);
8553 default:
8554 my_error_innodb(error, table_name, user_table->flags);
8555 DBUG_RETURN(true);
8556 }
8557 }
8558
8559 if ((ha_alter_info->handler_flags
8560 & ALTER_COLUMN_NAME)
8561 && innobase_rename_columns_try(ha_alter_info, ctx, old_table,
8562 trx, table_name)) {
8563 DBUG_RETURN(true);
8564 }
8565
8566 DBUG_EXECUTE_IF("ib_ddl_crash_before_rename", DBUG_SUICIDE(););
8567
8568 /* The new table must inherit the flag from the
8569 "parent" table. */
8570 if (!user_table->space) {
8571 rebuilt_table->file_unreadable = true;
8572 rebuilt_table->flags2 |= DICT_TF2_DISCARDED;
8573 }
8574
8575 /* We can now rename the old table as a temporary table,
8576 rename the new temporary table as the old table and drop the
8577 old table. First, we only do this in the data dictionary
8578 tables. The actual renaming will be performed in
8579 commit_cache_rebuild(), once the data dictionary transaction
8580 has been successfully committed. */
8581
8582 error = row_merge_rename_tables_dict(
8583 user_table, rebuilt_table, ctx->tmp_name, trx);
8584
8585 /* We must be still holding a table handle. */
8586 DBUG_ASSERT(user_table->get_ref_count() == 1);
8587
8588 DBUG_EXECUTE_IF("ib_ddl_crash_after_rename", DBUG_SUICIDE(););
8589 DBUG_EXECUTE_IF("ib_rebuild_cannot_rename", error = DB_ERROR;);
8590
8591 switch (error) {
8592 case DB_SUCCESS:
8593 DBUG_RETURN(false);
8594 case DB_TABLESPACE_EXISTS:
8595 ut_a(rebuilt_table->get_ref_count() == 1);
8596 my_error(ER_TABLESPACE_EXISTS, MYF(0), ctx->tmp_name);
8597 DBUG_RETURN(true);
8598 case DB_DUPLICATE_KEY:
8599 ut_a(rebuilt_table->get_ref_count() == 1);
8600 my_error(ER_TABLE_EXISTS_ERROR, MYF(0), ctx->tmp_name);
8601 DBUG_RETURN(true);
8602 default:
8603 my_error_innodb(error, table_name, user_table->flags);
8604 DBUG_RETURN(true);
8605 }
8606}
8607
8608/** Apply the changes made during commit_try_rebuild(),
8609to the data dictionary cache and the file system.
8610@param ctx In-place ALTER TABLE context */
8611inline MY_ATTRIBUTE((nonnull))
8612void
8613commit_cache_rebuild(
8614/*=================*/
8615 ha_innobase_inplace_ctx* ctx)
8616{
8617 dberr_t error;
8618
8619 DBUG_ENTER("commit_cache_rebuild");
8620 DEBUG_SYNC_C("commit_cache_rebuild");
8621 DBUG_ASSERT(ctx->need_rebuild());
8622 DBUG_ASSERT(!ctx->old_table->space == !ctx->new_table->space);
8623
8624 const char* old_name = mem_heap_strdup(
8625 ctx->heap, ctx->old_table->name.m_name);
8626
8627 /* We already committed and redo logged the renames,
8628 so this must succeed. */
8629 error = dict_table_rename_in_cache(
8630 ctx->old_table, ctx->tmp_name, FALSE);
8631 ut_a(error == DB_SUCCESS);
8632
8633 error = dict_table_rename_in_cache(
8634 ctx->new_table, old_name, FALSE);
8635 ut_a(error == DB_SUCCESS);
8636
8637 DBUG_VOID_RETURN;
8638}
8639
8640/** Set of column numbers */
8641typedef std::set<ulint, std::less<ulint>, ut_allocator<ulint> > col_set;
8642
8643/** Store the column number of the columns in a list belonging
8644to indexes which are not being dropped.
8645@param[in] ctx In-place ALTER TABLE context
8646@param[in, out] drop_col_list list which will be set, containing columns
8647 which is part of index being dropped
8648@param[in, out] drop_v_col_list list which will be set, containing
8649 virtual columns which is part of index
8650 being dropped */
8651static
8652void
8653get_col_list_to_be_dropped(
8654 const ha_innobase_inplace_ctx* ctx,
8655 col_set& drop_col_list,
8656 col_set& drop_v_col_list)
8657{
8658 for (ulint index_count = 0; index_count < ctx->num_to_drop_index;
8659 index_count++) {
8660 const dict_index_t* index = ctx->drop_index[index_count];
8661
8662 for (ulint col = 0; col < index->n_user_defined_cols; col++) {
8663 const dict_col_t* idx_col
8664 = dict_index_get_nth_col(index, col);
8665
8666 if (idx_col->is_virtual()) {
8667 const dict_v_col_t* v_col
8668 = reinterpret_cast<
8669 const dict_v_col_t*>(idx_col);
8670 drop_v_col_list.insert(v_col->v_pos);
8671
8672 } else {
8673 ulint col_no = dict_col_get_no(idx_col);
8674 drop_col_list.insert(col_no);
8675 }
8676 }
8677 }
8678}
8679
8680/** Commit the changes made during prepare_inplace_alter_table()
8681and inplace_alter_table() inside the data dictionary tables,
8682when not rebuilding the table.
8683@param ha_alter_info Data used during in-place alter
8684@param ctx In-place ALTER TABLE context
8685@param old_table MySQL table as it is before the ALTER operation
8686@param trx Data dictionary transaction
8687@param table_name Table name in MySQL
8688@retval true Failure
8689@retval false Success
8690*/
8691inline MY_ATTRIBUTE((nonnull, warn_unused_result))
8692bool
8693commit_try_norebuild(
8694/*=================*/
8695 Alter_inplace_info* ha_alter_info,
8696 ha_innobase_inplace_ctx*ctx,
8697 TABLE* altered_table,
8698 const TABLE* old_table,
8699 trx_t* trx,
8700 const char* table_name)
8701{
8702 DBUG_ENTER("commit_try_norebuild");
8703 DBUG_ASSERT(!ctx->need_rebuild());
8704 DBUG_ASSERT(trx->dict_operation_lock_mode == RW_X_LATCH);
8705 DBUG_ASSERT(!(ha_alter_info->handler_flags
8706 & ALTER_DROP_FOREIGN_KEY)
8707 || ctx->num_to_drop_fk > 0);
8708 DBUG_ASSERT(ctx->num_to_drop_fk
8709 == ha_alter_info->alter_info->drop_list.elements
8710 || ctx->num_to_drop_vcol
8711 == ha_alter_info->alter_info->drop_list.elements);
8712
8713 for (ulint i = 0; i < ctx->num_to_add_index; i++) {
8714 dict_index_t* index = ctx->add_index[i];
8715 DBUG_ASSERT(dict_index_get_online_status(index)
8716 == ONLINE_INDEX_COMPLETE);
8717 DBUG_ASSERT(!index->is_committed());
8718 if (index->is_corrupted()) {
8719 /* Report a duplicate key
8720 error for the index that was
8721 flagged corrupted, most likely
8722 because a duplicate value was
8723 inserted (directly or by
8724 rollback) after
8725 ha_innobase::inplace_alter_table()
8726 completed.
8727 TODO: report this as a corruption
8728 with a detailed reason once
8729 WL#6379 has been implemented. */
8730 my_error(ER_DUP_UNKNOWN_IN_INDEX,
8731 MYF(0), index->name());
8732 DBUG_RETURN(true);
8733 }
8734 }
8735
8736 if (innobase_update_foreign_try(ctx, trx, table_name)) {
8737 DBUG_RETURN(true);
8738 }
8739
8740 dberr_t error;
8741
8742 /* We altered the table in place. Mark the indexes as committed. */
8743 for (ulint i = 0; i < ctx->num_to_add_index; i++) {
8744 dict_index_t* index = ctx->add_index[i];
8745 DBUG_ASSERT(dict_index_get_online_status(index)
8746 == ONLINE_INDEX_COMPLETE);
8747 DBUG_ASSERT(!index->is_committed());
8748 error = row_merge_rename_index_to_add(
8749 trx, ctx->new_table->id, index->id);
8750 switch (error) {
8751 case DB_SUCCESS:
8752 break;
8753 case DB_TOO_MANY_CONCURRENT_TRXS:
8754 /* If we wrote some undo log here, then the
8755 persistent data dictionary for this table may
8756 probably be corrupted. This is because a
8757 'trigger' on SYS_INDEXES could already have invoked
8758 btr_free_if_exists(), which cannot be rolled back. */
8759 DBUG_ASSERT(trx->undo_no == 0);
8760 my_error(ER_TOO_MANY_CONCURRENT_TRXS, MYF(0));
8761 DBUG_RETURN(true);
8762 default:
8763 sql_print_error(
8764 "InnoDB: rename index to add: %lu\n",
8765 (ulong) error);
8766 DBUG_ASSERT(0);
8767 my_error(ER_INTERNAL_ERROR, MYF(0),
8768 "rename index to add");
8769 DBUG_RETURN(true);
8770 }
8771 }
8772
8773 /* Drop any indexes that were requested to be dropped.
8774 Flag them in the data dictionary first. */
8775
8776 for (ulint i = 0; i < ctx->num_to_drop_index; i++) {
8777 dict_index_t* index = ctx->drop_index[i];
8778 DBUG_ASSERT(index->is_committed());
8779 DBUG_ASSERT(index->table == ctx->new_table);
8780 DBUG_ASSERT(index->to_be_dropped);
8781
8782 error = row_merge_rename_index_to_drop(
8783 trx, index->table->id, index->id);
8784 if (error != DB_SUCCESS) {
8785 sql_print_error(
8786 "InnoDB: rename index to drop: %lu\n",
8787 (ulong) error);
8788 DBUG_ASSERT(0);
8789 my_error(ER_INTERNAL_ERROR, MYF(0),
8790 "rename index to drop");
8791 DBUG_RETURN(true);
8792 }
8793 }
8794
8795 if ((ha_alter_info->handler_flags
8796 & ALTER_COLUMN_NAME)
8797 && innobase_rename_columns_try(ha_alter_info, ctx, old_table,
8798 trx, table_name)) {
8799 DBUG_RETURN(true);
8800 }
8801
8802 if ((ha_alter_info->handler_flags
8803 & ALTER_COLUMN_EQUAL_PACK_LENGTH)
8804 && innobase_enlarge_columns_try(ha_alter_info, old_table,
8805 ctx->old_table, trx, table_name)) {
8806 DBUG_RETURN(true);
8807 }
8808
8809#ifdef MYSQL_RENAME_INDEX
8810 if ((ha_alter_info->handler_flags
8811 & ALTER_RENAME_INDEX)
8812 && rename_indexes_in_data_dictionary(ctx, ha_alter_info, trx)) {
8813 DBUG_RETURN(true);
8814 }
8815#endif /* MYSQL_RENAME_INDEX */
8816
8817 if ((ha_alter_info->handler_flags
8818 & ALTER_DROP_VIRTUAL_COLUMN)
8819 && innobase_drop_virtual_try(ha_alter_info, ctx->old_table, trx)) {
8820 DBUG_RETURN(true);
8821 }
8822
8823 if ((ha_alter_info->handler_flags
8824 & ALTER_ADD_VIRTUAL_COLUMN)
8825 && innobase_add_virtual_try(ha_alter_info, ctx->old_table, trx)) {
8826 DBUG_RETURN(true);
8827 }
8828
8829 if (innobase_add_instant_try(ctx, altered_table, old_table, trx)) {
8830 DBUG_RETURN(true);
8831 }
8832
8833 DBUG_RETURN(false);
8834}
8835
8836/** Commit the changes to the data dictionary cache
8837after a successful commit_try_norebuild() call.
8838@param ha_alter_info algorithm=inplace context
8839@param ctx In-place ALTER TABLE context for the current partition
8840@param table the TABLE before the ALTER
8841@param trx Data dictionary transaction
8842(will be started and committed, for DROP INDEX) */
8843inline MY_ATTRIBUTE((nonnull))
8844void
8845commit_cache_norebuild(
8846/*===================*/
8847 Alter_inplace_info* ha_alter_info,
8848 ha_innobase_inplace_ctx*ctx,
8849 const TABLE* table,
8850 trx_t* trx)
8851{
8852 DBUG_ENTER("commit_cache_norebuild");
8853 DBUG_ASSERT(!ctx->need_rebuild());
8854
8855 col_set drop_list;
8856 col_set v_drop_list;
8857 col_set::const_iterator col_it;
8858
8859 /* Check if the column, part of an index to be dropped is part of any
8860 other index which is not being dropped. If it so, then set the ord_part
8861 of the column to 0. */
8862 get_col_list_to_be_dropped(ctx, drop_list, v_drop_list);
8863
8864 for (col_it = drop_list.begin(); col_it != drop_list.end(); ++col_it) {
8865 if (!check_col_exists_in_indexes(ctx->new_table,
8866 *col_it, false)) {
8867 ctx->new_table->cols[*col_it].ord_part = 0;
8868 }
8869 }
8870
8871 for (col_it = v_drop_list.begin();
8872 col_it != v_drop_list.end(); ++col_it) {
8873 if (!check_col_exists_in_indexes(ctx->new_table,
8874 *col_it, true)) {
8875 ctx->new_table->v_cols[*col_it].m_col.ord_part = 0;
8876 }
8877 }
8878
8879 for (ulint i = 0; i < ctx->num_to_add_index; i++) {
8880 dict_index_t* index = ctx->add_index[i];
8881 DBUG_ASSERT(dict_index_get_online_status(index)
8882 == ONLINE_INDEX_COMPLETE);
8883 DBUG_ASSERT(!index->is_committed());
8884 index->set_committed(true);
8885 }
8886
8887 if (ctx->num_to_drop_index) {
8888 /* Really drop the indexes that were dropped.
8889 The transaction had to be committed first
8890 (after renaming the indexes), so that in the
8891 event of a crash, crash recovery will drop the
8892 indexes, because it drops all indexes whose
8893 names start with TEMP_INDEX_PREFIX. Once we
8894 have started dropping an index tree, there is
8895 no way to roll it back. */
8896
8897 for (ulint i = 0; i < ctx->num_to_drop_index; i++) {
8898 dict_index_t* index = ctx->drop_index[i];
8899 DBUG_ASSERT(index->is_committed());
8900 DBUG_ASSERT(index->table == ctx->new_table);
8901 DBUG_ASSERT(index->to_be_dropped);
8902
8903 /* Replace the indexes in foreign key
8904 constraints if needed. */
8905
8906 if (!dict_foreign_replace_index(
8907 index->table, ctx->col_names, index)) {
8908 ut_a(!ctx->prebuilt->trx->check_foreigns);
8909 }
8910
8911 /* Mark the index dropped
8912 in the data dictionary cache. */
8913 rw_lock_x_lock(dict_index_get_lock(index));
8914 index->page = FIL_NULL;
8915 rw_lock_x_unlock(dict_index_get_lock(index));
8916 }
8917
8918 trx_start_for_ddl(trx, TRX_DICT_OP_INDEX);
8919 row_merge_drop_indexes_dict(trx, ctx->new_table->id);
8920
8921 for (ulint i = 0; i < ctx->num_to_drop_index; i++) {
8922 dict_index_t* index = ctx->drop_index[i];
8923 DBUG_ASSERT(index->is_committed());
8924 DBUG_ASSERT(index->table == ctx->new_table);
8925
8926 if (index->type & DICT_FTS) {
8927 DBUG_ASSERT(index->type == DICT_FTS
8928 || (index->type
8929 & DICT_CORRUPT));
8930 DBUG_ASSERT(index->table->fts);
8931 fts_drop_index(index->table, index, trx);
8932 }
8933
8934 dict_index_remove_from_cache(index->table, index);
8935 }
8936
8937 trx_commit_for_mysql(trx);
8938 }
8939
8940 if (!ctx->is_instant()) {
8941 innobase_rename_or_enlarge_columns_cache(
8942 ha_alter_info, table, ctx->new_table);
8943 }
8944
8945#ifdef MYSQL_RENAME_INDEX
8946 rename_indexes_in_cache(ctx, ha_alter_info);
8947#endif
8948
8949 ctx->new_table->fts_doc_id_index
8950 = ctx->new_table->fts
8951 ? dict_table_get_index_on_name(
8952 ctx->new_table, FTS_DOC_ID_INDEX_NAME)
8953 : NULL;
8954 DBUG_ASSERT((ctx->new_table->fts == NULL)
8955 == (ctx->new_table->fts_doc_id_index == NULL));
8956 DBUG_VOID_RETURN;
8957}
8958
8959/** Adjust the persistent statistics after non-rebuilding ALTER TABLE.
8960Remove statistics for dropped indexes, add statistics for created indexes
8961and rename statistics for renamed indexes.
8962@param ha_alter_info Data used during in-place alter
8963@param ctx In-place ALTER TABLE context
8964@param thd MySQL connection
8965*/
8966static
8967void
8968alter_stats_norebuild(
8969/*==================*/
8970 Alter_inplace_info* ha_alter_info,
8971 ha_innobase_inplace_ctx* ctx,
8972 THD* thd)
8973{
8974 ulint i;
8975
8976 DBUG_ENTER("alter_stats_norebuild");
8977 DBUG_ASSERT(!ctx->need_rebuild());
8978
8979 if (!dict_stats_is_persistent_enabled(ctx->new_table)) {
8980 DBUG_VOID_RETURN;
8981 }
8982
8983 /* Delete corresponding rows from the stats table. We do this
8984 in a separate transaction from trx, because lock waits are not
8985 allowed in a data dictionary transaction. (Lock waits are possible
8986 on the statistics table, because it is directly accessible by users,
8987 not covered by the dict_operation_lock.)
8988
8989 Because the data dictionary changes were already committed, orphaned
8990 rows may be left in the statistics table if the system crashes.
8991
8992 FIXME: each change to the statistics tables is being committed in a
8993 separate transaction, meaning that the operation is not atomic
8994
8995 FIXME: This will not drop the (unused) statistics for
8996 FTS_DOC_ID_INDEX if it was a hidden index, dropped together
8997 with the last renamining FULLTEXT index. */
8998 for (i = 0; i < ha_alter_info->index_drop_count; i++) {
8999 const KEY* key = ha_alter_info->index_drop_buffer[i];
9000
9001 if (key->flags & HA_FULLTEXT) {
9002 /* There are no index cardinality
9003 statistics for FULLTEXT indexes. */
9004 continue;
9005 }
9006
9007 char errstr[1024];
9008
9009 if (dict_stats_drop_index(
9010 ctx->new_table->name.m_name, key->name.str,
9011 errstr, sizeof errstr) != DB_SUCCESS) {
9012 push_warning(thd,
9013 Sql_condition::WARN_LEVEL_WARN,
9014 ER_LOCK_WAIT_TIMEOUT, errstr);
9015 }
9016 }
9017
9018#ifdef MYSQL_RENAME_INDEX
9019 for (i = 0; i < ha_alter_info->index_rename_count; i++) {
9020 KEY_PAIR* pair = &ha_alter_info->index_rename_buffer[i];
9021 dberr_t err;
9022
9023 err = dict_stats_rename_index(ctx->new_table,
9024 pair->old_key->name,
9025 pair->new_key->name);
9026
9027 if (err != DB_SUCCESS) {
9028 push_warning_printf(
9029 thd,
9030 Sql_condition::WARN_LEVEL_WARN,
9031 ER_ERROR_ON_RENAME,
9032 "Error renaming an index of table '%s'"
9033 " from '%s' to '%s' in InnoDB persistent"
9034 " statistics storage: %s",
9035 table_name,
9036 pair->old_key->name,
9037 pair->new_key->name,
9038 ut_strerr(err));
9039 }
9040 }
9041#endif /* MYSQL_RENAME_INDEX */
9042
9043 for (i = 0; i < ctx->num_to_add_index; i++) {
9044 dict_index_t* index = ctx->add_index[i];
9045 DBUG_ASSERT(index->table == ctx->new_table);
9046
9047 if (!(index->type & DICT_FTS)) {
9048 dict_stats_init(ctx->new_table);
9049 dict_stats_update_for_index(index);
9050 }
9051 }
9052
9053 DBUG_VOID_RETURN;
9054}
9055
9056/** Adjust the persistent statistics after rebuilding ALTER TABLE.
9057Remove statistics for dropped indexes, add statistics for created indexes
9058and rename statistics for renamed indexes.
9059@param table InnoDB table that was rebuilt by ALTER TABLE
9060@param table_name Table name in MySQL
9061@param thd MySQL connection
9062*/
9063static
9064void
9065alter_stats_rebuild(
9066/*================*/
9067 dict_table_t* table,
9068 const char* table_name,
9069 THD* thd)
9070{
9071 DBUG_ENTER("alter_stats_rebuild");
9072
9073 if (!table->space
9074 || !dict_stats_is_persistent_enabled(table)) {
9075 DBUG_VOID_RETURN;
9076 }
9077
9078#ifndef DBUG_OFF
9079 bool file_unreadable_orig = false;
9080#endif /* DBUG_OFF */
9081
9082 DBUG_EXECUTE_IF(
9083 "ib_rename_index_fail2",
9084 file_unreadable_orig = table->file_unreadable;
9085 table->file_unreadable = true;
9086 );
9087
9088 dberr_t ret = dict_stats_update(table, DICT_STATS_RECALC_PERSISTENT);
9089
9090 DBUG_EXECUTE_IF(
9091 "ib_rename_index_fail2",
9092 table->file_unreadable = file_unreadable_orig;
9093 );
9094
9095 if (ret != DB_SUCCESS) {
9096 push_warning_printf(
9097 thd,
9098 Sql_condition::WARN_LEVEL_WARN,
9099 ER_ALTER_INFO,
9100 "Error updating stats for table '%s'"
9101 " after table rebuild: %s",
9102 table_name, ut_strerr(ret));
9103 }
9104
9105 DBUG_VOID_RETURN;
9106}
9107
9108#ifndef DBUG_OFF
9109# define DBUG_INJECT_CRASH(prefix, count) \
9110do { \
9111 char buf[32]; \
9112 snprintf(buf, sizeof buf, prefix "_%u", count); \
9113 DBUG_EXECUTE_IF(buf, DBUG_SUICIDE();); \
9114} while (0)
9115#else
9116# define DBUG_INJECT_CRASH(prefix, count)
9117#endif
9118
9119/** Commit or rollback the changes made during
9120prepare_inplace_alter_table() and inplace_alter_table() inside
9121the storage engine. Note that the allowed level of concurrency
9122during this operation will be the same as for
9123inplace_alter_table() and thus might be higher than during
9124prepare_inplace_alter_table(). (E.g concurrent writes were
9125blocked during prepare, but might not be during commit).
9126@param altered_table TABLE object for new version of table.
9127@param ha_alter_info Structure describing changes to be done
9128by ALTER TABLE and holding data used during in-place alter.
9129@param commit true => Commit, false => Rollback.
9130@retval true Failure
9131@retval false Success
9132*/
9133
9134bool
9135ha_innobase::commit_inplace_alter_table(
9136/*====================================*/
9137 TABLE* altered_table,
9138 Alter_inplace_info* ha_alter_info,
9139 bool commit)
9140{
9141 dberr_t error;
9142 ha_innobase_inplace_ctx*ctx0;
9143 struct mtr_buf_copy_t logs;
9144
9145 ctx0 = static_cast<ha_innobase_inplace_ctx*>
9146 (ha_alter_info->handler_ctx);
9147
9148#ifndef DBUG_OFF
9149 uint crash_inject_count = 1;
9150 uint crash_fail_inject_count = 1;
9151 uint failure_inject_count = 1;
9152#endif /* DBUG_OFF */
9153
9154 DBUG_ENTER("commit_inplace_alter_table");
9155 DBUG_ASSERT(!srv_read_only_mode);
9156 DBUG_ASSERT(!ctx0 || ctx0->prebuilt == m_prebuilt);
9157 DBUG_ASSERT(!ctx0 || ctx0->old_table == m_prebuilt->table);
9158
9159 DEBUG_SYNC_C("innodb_commit_inplace_alter_table_enter");
9160
9161 DEBUG_SYNC_C("innodb_commit_inplace_alter_table_wait");
9162
9163 if (ctx0 != NULL && ctx0->m_stage != NULL) {
9164 ctx0->m_stage->begin_phase_end();
9165 }
9166
9167 if (!commit) {
9168 /* A rollback is being requested. So far we may at
9169 most have created some indexes. If any indexes were to
9170 be dropped, they would actually be dropped in this
9171 method if commit=true. */
9172 const bool ret = rollback_inplace_alter_table(
9173 ha_alter_info, table, m_prebuilt);
9174 DBUG_RETURN(ret);
9175 }
9176
9177 if (!(ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE)) {
9178 DBUG_ASSERT(!ctx0);
9179 MONITOR_ATOMIC_DEC(MONITOR_PENDING_ALTER_TABLE);
9180 ha_alter_info->group_commit_ctx = NULL;
9181 DBUG_RETURN(false);
9182 }
9183
9184 DBUG_ASSERT(ctx0);
9185
9186 inplace_alter_handler_ctx** ctx_array;
9187 inplace_alter_handler_ctx* ctx_single[2];
9188
9189 if (ha_alter_info->group_commit_ctx) {
9190 ctx_array = ha_alter_info->group_commit_ctx;
9191 } else {
9192 ctx_single[0] = ctx0;
9193 ctx_single[1] = NULL;
9194 ctx_array = ctx_single;
9195 }
9196
9197 DBUG_ASSERT(ctx0 == ctx_array[0]);
9198 ut_ad(m_prebuilt->table == ctx0->old_table);
9199 ha_alter_info->group_commit_ctx = NULL;
9200
9201 trx_start_if_not_started_xa(m_prebuilt->trx, true);
9202
9203 for (inplace_alter_handler_ctx** pctx = ctx_array; *pctx; pctx++) {
9204 ha_innobase_inplace_ctx* ctx
9205 = static_cast<ha_innobase_inplace_ctx*>(*pctx);
9206 DBUG_ASSERT(ctx->prebuilt->trx == m_prebuilt->trx);
9207
9208 /* If decryption failed for old table or new table
9209 fail here. */
9210 if ((!ctx->old_table->is_readable()
9211 && ctx->old_table->space)
9212 || (!ctx->new_table->is_readable()
9213 && ctx->new_table->space)) {
9214 String str;
9215 const char* engine= table_type();
9216 get_error_message(HA_ERR_DECRYPTION_FAILED, &str);
9217 my_error(ER_GET_ERRMSG, MYF(0), HA_ERR_DECRYPTION_FAILED, str.c_ptr(), engine);
9218 DBUG_RETURN(true);
9219 }
9220
9221 /* Exclusively lock the table, to ensure that no other
9222 transaction is holding locks on the table while we
9223 change the table definition. The MySQL meta-data lock
9224 should normally guarantee that no conflicting locks
9225 exist. However, FOREIGN KEY constraints checks and any
9226 transactions collected during crash recovery could be
9227 holding InnoDB locks only, not MySQL locks. */
9228
9229 error = row_merge_lock_table(
9230 m_prebuilt->trx, ctx->old_table, LOCK_X);
9231
9232 if (error != DB_SUCCESS) {
9233 my_error_innodb(
9234 error, table_share->table_name.str, 0);
9235 DBUG_RETURN(true);
9236 }
9237 }
9238
9239 DEBUG_SYNC(m_user_thd, "innodb_alter_commit_after_lock_table");
9240
9241 const bool new_clustered = ctx0->need_rebuild();
9242 trx_t* trx = ctx0->trx;
9243 bool fail = false;
9244
9245 if (new_clustered) {
9246 for (inplace_alter_handler_ctx** pctx = ctx_array;
9247 *pctx; pctx++) {
9248 ha_innobase_inplace_ctx* ctx
9249 = static_cast<ha_innobase_inplace_ctx*>(*pctx);
9250 DBUG_ASSERT(ctx->need_rebuild());
9251
9252 if (ctx->old_table->fts) {
9253 ut_ad(!ctx->old_table->fts->add_wq);
9254 fts_optimize_remove_table(
9255 ctx->old_table);
9256 }
9257
9258 if (ctx->new_table->fts) {
9259 ut_ad(!ctx->new_table->fts->add_wq);
9260 fts_optimize_remove_table(
9261 ctx->new_table);
9262 }
9263 }
9264 }
9265
9266 if (!trx) {
9267 DBUG_ASSERT(!new_clustered);
9268 trx = innobase_trx_allocate(m_user_thd);
9269 }
9270
9271 trx_start_for_ddl(trx, TRX_DICT_OP_INDEX);
9272 /* Latch the InnoDB data dictionary exclusively so that no deadlocks
9273 or lock waits can happen in it during the data dictionary operation. */
9274 row_mysql_lock_data_dictionary(trx);
9275
9276 ut_ad(log_append_on_checkpoint(NULL) == NULL);
9277
9278 /* Prevent the background statistics collection from accessing
9279 the tables. */
9280 for (;;) {
9281 bool retry = false;
9282
9283 for (inplace_alter_handler_ctx** pctx = ctx_array;
9284 *pctx; pctx++) {
9285 ha_innobase_inplace_ctx* ctx
9286 = static_cast<ha_innobase_inplace_ctx*>(*pctx);
9287
9288 DBUG_ASSERT(new_clustered == ctx->need_rebuild());
9289
9290 if (new_clustered
9291 && !dict_stats_stop_bg(ctx->old_table)) {
9292 retry = true;
9293 }
9294
9295 if (!dict_stats_stop_bg(ctx->new_table)) {
9296 retry = true;
9297 }
9298 }
9299
9300 if (!retry) {
9301 break;
9302 }
9303
9304 DICT_BG_YIELD(trx);
9305 }
9306
9307 /* Make a concurrent Drop fts Index to wait until sync of that
9308 fts index is happening in the background */
9309 for (;;) {
9310 bool retry = false;
9311
9312 for (inplace_alter_handler_ctx** pctx = ctx_array;
9313 *pctx; pctx++) {
9314 int count =0;
9315 ha_innobase_inplace_ctx* ctx
9316 = static_cast<ha_innobase_inplace_ctx*>(*pctx);
9317 DBUG_ASSERT(new_clustered == ctx->need_rebuild());
9318
9319 if (dict_fts_index_syncing(ctx->old_table)) {
9320 count++;
9321 if (count == 100) {
9322 fprintf(stderr,
9323 "Drop index waiting for background sync"
9324 "to finish\n");
9325 }
9326 retry = true;
9327 }
9328
9329 if (new_clustered && dict_fts_index_syncing(ctx->new_table)) {
9330 count++;
9331 if (count == 100) {
9332 fprintf(stderr,
9333 "Drop index waiting for background sync"
9334 "to finish\n");
9335 }
9336 retry = true;
9337 }
9338 }
9339
9340 if (!retry) {
9341 break;
9342 }
9343
9344 DICT_BG_YIELD(trx);
9345 }
9346
9347 /* Apply the changes to the data dictionary tables, for all
9348 partitions. */
9349
9350 for (inplace_alter_handler_ctx** pctx = ctx_array;
9351 *pctx && !fail; pctx++) {
9352 ha_innobase_inplace_ctx* ctx
9353 = static_cast<ha_innobase_inplace_ctx*>(*pctx);
9354
9355 DBUG_ASSERT(new_clustered == ctx->need_rebuild());
9356
9357 fail = commit_set_autoinc(ha_alter_info, ctx, altered_table,
9358 table);
9359
9360 if (fail) {
9361 } else if (ctx->need_rebuild()) {
9362 ctx->tmp_name = dict_mem_create_temporary_tablename(
9363 ctx->heap, ctx->new_table->name.m_name,
9364 ctx->new_table->id);
9365
9366 fail = commit_try_rebuild(
9367 ha_alter_info, ctx, altered_table, table,
9368 trx, table_share->table_name.str);
9369 } else {
9370 fail = commit_try_norebuild(
9371 ha_alter_info, ctx, altered_table, table, trx,
9372 table_share->table_name.str);
9373 }
9374 DBUG_INJECT_CRASH("ib_commit_inplace_crash",
9375 crash_inject_count++);
9376#ifndef DBUG_OFF
9377 {
9378 /* Generate a dynamic dbug text. */
9379 char buf[32];
9380
9381 snprintf(buf, sizeof buf,
9382 "ib_commit_inplace_fail_%u",
9383 failure_inject_count++);
9384
9385 DBUG_EXECUTE_IF(buf,
9386 my_error(ER_INTERNAL_ERROR, MYF(0),
9387 "Injected error!");
9388 fail = true;
9389 );
9390 }
9391#endif
9392 }
9393
9394 /* Commit or roll back the changes to the data dictionary. */
9395 DEBUG_SYNC(m_user_thd, "innodb_alter_inplace_before_commit");
9396
9397 if (fail) {
9398 trx_rollback_for_mysql(trx);
9399 for (inplace_alter_handler_ctx** pctx = ctx_array;
9400 *pctx; pctx++) {
9401 ha_innobase_inplace_ctx* ctx
9402 = static_cast<ha_innobase_inplace_ctx*>(*pctx);
9403 ctx->rollback_instant();
9404 }
9405 } else if (!new_clustered) {
9406 trx_commit_for_mysql(trx);
9407 } else {
9408 mtr_t mtr;
9409 mtr_start(&mtr);
9410
9411 for (inplace_alter_handler_ctx** pctx = ctx_array;
9412 *pctx; pctx++) {
9413 ha_innobase_inplace_ctx* ctx
9414 = static_cast<ha_innobase_inplace_ctx*>(*pctx);
9415
9416 DBUG_ASSERT(ctx->need_rebuild());
9417 /* Check for any possible problems for any
9418 file operations that will be performed in
9419 commit_cache_rebuild(), and if none, generate
9420 the redo log for these operations. */
9421 error = fil_mtr_rename_log(ctx->old_table,
9422 ctx->new_table,
9423 ctx->tmp_name, &mtr);
9424 if (error != DB_SUCCESS) {
9425 /* Out of memory or a problem will occur
9426 when renaming files. */
9427 fail = true;
9428 my_error_innodb(error, ctx->old_table->name.m_name,
9429 ctx->old_table->flags);
9430 }
9431 DBUG_INJECT_CRASH("ib_commit_inplace_crash",
9432 crash_inject_count++);
9433 }
9434
9435 /* Test what happens on crash if the redo logs
9436 are flushed to disk here. The log records
9437 about the rename should not be committed, and
9438 the data dictionary transaction should be
9439 rolled back, restoring the old table. */
9440 DBUG_EXECUTE_IF("innodb_alter_commit_crash_before_commit",
9441 log_buffer_flush_to_disk();
9442 DBUG_SUICIDE(););
9443 ut_ad(!trx->fts_trx);
9444
9445 if (fail) {
9446 mtr.set_log_mode(MTR_LOG_NO_REDO);
9447 mtr_commit(&mtr);
9448 trx_rollback_for_mysql(trx);
9449 } else {
9450 ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE));
9451 ut_ad(trx->has_logged());
9452
9453 if (mtr.get_log()->size() > 0) {
9454 ut_ad(*mtr.get_log()->front()->begin()
9455 == MLOG_FILE_RENAME2);
9456
9457 /* Append the MLOG_FILE_RENAME2
9458 records on checkpoint, as a separate
9459 mini-transaction before the one that
9460 contains the MLOG_CHECKPOINT marker. */
9461 static const byte multi
9462 = MLOG_MULTI_REC_END;
9463
9464 mtr.get_log()->for_each_block(logs);
9465 logs.m_buf.push(&multi, sizeof multi);
9466
9467 log_append_on_checkpoint(&logs.m_buf);
9468 }
9469
9470 /* The following call commits the
9471 mini-transaction, making the data dictionary
9472 transaction committed at mtr.end_lsn. The
9473 transaction becomes 'durable' by the time when
9474 log_buffer_flush_to_disk() returns. In the
9475 logical sense the commit in the file-based
9476 data structures happens here. */
9477
9478 trx_commit_low(trx, &mtr);
9479 }
9480
9481 /* If server crashes here, the dictionary in
9482 InnoDB and MySQL will differ. The .ibd files
9483 and the .frm files must be swapped manually by
9484 the administrator. No loss of data. */
9485 DBUG_EXECUTE_IF("innodb_alter_commit_crash_after_commit",
9486 log_make_checkpoint_at(LSN_MAX, TRUE);
9487 log_buffer_flush_to_disk();
9488 DBUG_SUICIDE(););
9489 }
9490
9491 /* Flush the log to reduce probability that the .frm files and
9492 the InnoDB data dictionary get out-of-sync if the user runs
9493 with innodb_flush_log_at_trx_commit = 0 */
9494
9495 log_buffer_flush_to_disk();
9496
9497 /* At this point, the changes to the persistent storage have
9498 been committed or rolled back. What remains to be done is to
9499 update the in-memory structures, close some handles, release
9500 temporary files, and (unless we rolled back) update persistent
9501 statistics. */
9502 for (inplace_alter_handler_ctx** pctx = ctx_array;
9503 *pctx; pctx++) {
9504 ha_innobase_inplace_ctx* ctx
9505 = static_cast<ha_innobase_inplace_ctx*>(*pctx);
9506
9507 DBUG_ASSERT(ctx->need_rebuild() == new_clustered);
9508
9509 if (new_clustered) {
9510 innobase_online_rebuild_log_free(ctx->old_table);
9511 }
9512
9513 if (fail) {
9514 if (new_clustered) {
9515 trx_start_for_ddl(trx, TRX_DICT_OP_TABLE);
9516
9517 dict_table_close_and_drop(trx, ctx->new_table);
9518
9519 trx_commit_for_mysql(trx);
9520 ctx->new_table = NULL;
9521 } else {
9522 /* We failed, but did not rebuild the table.
9523 Roll back any ADD INDEX, or get rid of garbage
9524 ADD INDEX that was left over from a previous
9525 ALTER TABLE statement. */
9526 trx_start_for_ddl(trx, TRX_DICT_OP_INDEX);
9527 innobase_rollback_sec_index(
9528 ctx->new_table, table, TRUE, trx);
9529 trx_commit_for_mysql(trx);
9530 }
9531 DBUG_INJECT_CRASH("ib_commit_inplace_crash_fail",
9532 crash_fail_inject_count++);
9533
9534 continue;
9535 }
9536
9537 innobase_copy_frm_flags_from_table_share(
9538 ctx->new_table, altered_table->s);
9539
9540 if (new_clustered) {
9541 /* We will reload and refresh the
9542 in-memory foreign key constraint
9543 metadata. This is a rename operation
9544 in preparing for dropping the old
9545 table. Set the table to_be_dropped bit
9546 here, so to make sure DML foreign key
9547 constraint check does not use the
9548 stale dict_foreign_t. This is done
9549 because WL#6049 (FK MDL) has not been
9550 implemented yet. */
9551 ctx->old_table->to_be_dropped = true;
9552
9553 DBUG_PRINT("to_be_dropped",
9554 ("table: %s", ctx->old_table->name.m_name));
9555
9556 /* Rename the tablespace files. */
9557 commit_cache_rebuild(ctx);
9558
9559 error = innobase_update_foreign_cache(ctx, m_user_thd);
9560 if (error != DB_SUCCESS) {
9561 goto foreign_fail;
9562 }
9563 } else {
9564 error = innobase_update_foreign_cache(ctx, m_user_thd);
9565
9566 if (error != DB_SUCCESS) {
9567foreign_fail:
9568 /* The data dictionary cache
9569 should be corrupted now. The
9570 best solution should be to
9571 kill and restart the server,
9572 but the *.frm file has not
9573 been replaced yet. */
9574 push_warning_printf(
9575 m_user_thd,
9576 Sql_condition::WARN_LEVEL_WARN,
9577 ER_ALTER_INFO,
9578 "InnoDB: Could not add foreign"
9579 " key constraints.");
9580 } else {
9581 commit_cache_norebuild(ha_alter_info, ctx,
9582 table, trx);
9583 }
9584 }
9585
9586 dict_mem_table_free_foreign_vcol_set(ctx->new_table);
9587 dict_mem_table_fill_foreign_vcol_set(ctx->new_table);
9588
9589 DBUG_INJECT_CRASH("ib_commit_inplace_crash",
9590 crash_inject_count++);
9591 }
9592
9593 log_append_on_checkpoint(NULL);
9594
9595 /* Invalidate the index translation table. In partitioned
9596 tables, there is no share. */
9597 if (m_share) {
9598 m_share->idx_trans_tbl.index_count = 0;
9599 }
9600
9601 /* Tell the InnoDB server that there might be work for
9602 utility threads: */
9603
9604 srv_active_wake_master_thread();
9605
9606 if (fail) {
9607 for (inplace_alter_handler_ctx** pctx = ctx_array;
9608 *pctx; pctx++) {
9609 ha_innobase_inplace_ctx* ctx
9610 = static_cast<ha_innobase_inplace_ctx*>
9611 (*pctx);
9612 DBUG_ASSERT(ctx->need_rebuild() == new_clustered);
9613
9614 ut_d(dict_table_check_for_dup_indexes(
9615 ctx->old_table,
9616 CHECK_ABORTED_OK));
9617 ut_a(fts_check_cached_index(ctx->old_table));
9618 DBUG_INJECT_CRASH("ib_commit_inplace_crash_fail",
9619 crash_fail_inject_count++);
9620 }
9621
9622 row_mysql_unlock_data_dictionary(trx);
9623 if (trx != ctx0->trx) {
9624 trx_free(trx);
9625 }
9626 DBUG_RETURN(true);
9627 }
9628
9629 if (trx == ctx0->trx) {
9630 ctx0->trx = NULL;
9631 }
9632
9633 /* Free the ctx->trx of other partitions, if any. We will only
9634 use the ctx0->trx here. Others may have been allocated in
9635 the prepare stage. */
9636
9637 for (inplace_alter_handler_ctx** pctx = &ctx_array[1]; *pctx;
9638 pctx++) {
9639 ha_innobase_inplace_ctx* ctx
9640 = static_cast<ha_innobase_inplace_ctx*>(*pctx);
9641
9642 if (ctx->trx) {
9643 trx_free(ctx->trx);
9644 }
9645 }
9646
9647 if (ctx0->num_to_drop_vcol || ctx0->num_to_add_vcol) {
9648 DBUG_ASSERT(ctx0->old_table->get_ref_count() == 1);
9649
9650 trx_commit_for_mysql(m_prebuilt->trx);
9651#ifdef BTR_CUR_HASH_ADAPT
9652 if (btr_search_enabled) {
9653 btr_search_disable(false);
9654 btr_search_enable();
9655 }
9656#endif /* BTR_CUR_HASH_ADAPT */
9657
9658 char tb_name[FN_REFLEN];
9659 ut_strcpy(tb_name, m_prebuilt->table->name.m_name);
9660
9661 tb_name[strlen(m_prebuilt->table->name.m_name)] = 0;
9662
9663 dict_table_close(m_prebuilt->table, true, false);
9664 dict_table_remove_from_cache(m_prebuilt->table);
9665 m_prebuilt->table = dict_table_open_on_name(
9666 tb_name, TRUE, TRUE, DICT_ERR_IGNORE_NONE);
9667
9668 /* Drop outdated table stats. */
9669 char errstr[1024];
9670 if (dict_stats_drop_table(
9671 m_prebuilt->table->name.m_name,
9672 errstr, sizeof(errstr))
9673 != DB_SUCCESS) {
9674 push_warning_printf(
9675 m_user_thd,
9676 Sql_condition::WARN_LEVEL_WARN,
9677 ER_ALTER_INFO,
9678 "Deleting persistent statistics"
9679 " for table '%s' in"
9680 " InnoDB failed: %s",
9681 table->s->table_name.str,
9682 errstr);
9683 }
9684
9685 row_mysql_unlock_data_dictionary(trx);
9686 trx_free(trx);
9687 MONITOR_ATOMIC_DEC(MONITOR_PENDING_ALTER_TABLE);
9688 DBUG_RETURN(false);
9689 }
9690
9691 /* Release the table locks. */
9692 trx_commit_for_mysql(m_prebuilt->trx);
9693
9694 DBUG_EXECUTE_IF("ib_ddl_crash_after_user_trx_commit", DBUG_SUICIDE(););
9695
9696 for (inplace_alter_handler_ctx** pctx = ctx_array;
9697 *pctx; pctx++) {
9698 ha_innobase_inplace_ctx* ctx
9699 = static_cast<ha_innobase_inplace_ctx*>
9700 (*pctx);
9701 DBUG_ASSERT(ctx->need_rebuild() == new_clustered);
9702
9703 bool add_fts = false;
9704
9705 /* Publish the created fulltext index, if any.
9706 Note that a fulltext index can be created without
9707 creating the clustered index, if there already exists
9708 a suitable FTS_DOC_ID column. If not, one will be
9709 created, implying new_clustered */
9710 for (ulint i = 0; i < ctx->num_to_add_index; i++) {
9711 dict_index_t* index = ctx->add_index[i];
9712
9713 if (index->type & DICT_FTS) {
9714 DBUG_ASSERT(index->type == DICT_FTS);
9715 /* We reset DICT_TF2_FTS here because the bit
9716 is left unset when a drop proceeds the add. */
9717 DICT_TF2_FLAG_SET(ctx->new_table, DICT_TF2_FTS);
9718 fts_add_index(index, ctx->new_table);
9719 add_fts = true;
9720 }
9721 }
9722
9723 ut_d(dict_table_check_for_dup_indexes(
9724 ctx->new_table, CHECK_ALL_COMPLETE));
9725
9726 if (add_fts) {
9727 fts_optimize_add_table(ctx->new_table);
9728 }
9729
9730 ut_d(dict_table_check_for_dup_indexes(
9731 ctx->new_table, CHECK_ABORTED_OK));
9732
9733#ifdef UNIV_DEBUG
9734 if (!(ctx->new_table->fts != NULL
9735 && ctx->new_table->fts->cache->sync->in_progress)) {
9736 ut_a(fts_check_cached_index(ctx->new_table));
9737 }
9738#endif
9739 if (new_clustered) {
9740 /* Since the table has been rebuilt, we remove
9741 all persistent statistics corresponding to the
9742 old copy of the table (which was renamed to
9743 ctx->tmp_name). */
9744
9745 char errstr[1024];
9746
9747 DBUG_ASSERT(0 == strcmp(ctx->old_table->name.m_name,
9748 ctx->tmp_name));
9749
9750 DBUG_EXECUTE_IF(
9751 "ib_rename_index_fail3",
9752 DBUG_SET("+d,innodb_report_deadlock");
9753 );
9754
9755 if (dict_stats_drop_table(
9756 ctx->new_table->name.m_name,
9757 errstr, sizeof(errstr))
9758 != DB_SUCCESS) {
9759 push_warning_printf(
9760 m_user_thd,
9761 Sql_condition::WARN_LEVEL_WARN,
9762 ER_ALTER_INFO,
9763 "Deleting persistent statistics"
9764 " for rebuilt table '%s' in"
9765 " InnoDB failed: %s",
9766 table->s->table_name.str,
9767 errstr);
9768 }
9769
9770 DBUG_EXECUTE_IF(
9771 "ib_rename_index_fail3",
9772 DBUG_SET("-d,innodb_report_deadlock");
9773 );
9774
9775 DBUG_EXECUTE_IF("ib_ddl_crash_before_commit",
9776 DBUG_SUICIDE(););
9777
9778 ut_ad(m_prebuilt != ctx->prebuilt
9779 || ctx == ctx0);
9780 bool update_own_prebuilt =
9781 (m_prebuilt == ctx->prebuilt);
9782 trx_t* const user_trx = m_prebuilt->trx;
9783
9784 row_prebuilt_free(ctx->prebuilt, TRUE);
9785
9786 /* Drop the copy of the old table, which was
9787 renamed to ctx->tmp_name at the atomic DDL
9788 transaction commit. If the system crashes
9789 before this is completed, some orphan tables
9790 with ctx->tmp_name may be recovered. */
9791 trx_start_for_ddl(trx, TRX_DICT_OP_TABLE);
9792 error = row_merge_drop_table(trx, ctx->old_table);
9793
9794 if (error != DB_SUCCESS) {
9795 ib::error() << "Inplace alter table " << ctx->old_table->name.m_name
9796 << " dropping copy of the old table failed error "
9797 << error
9798 << ". tmp_name " << (ctx->tmp_name ? ctx->tmp_name : "N/A")
9799 << " new_table " << (ctx->new_table ? ctx->new_table->name.m_name
9800 : "N/A");
9801 }
9802
9803 trx_commit_for_mysql(trx);
9804
9805 /* Rebuild the prebuilt object. */
9806 ctx->prebuilt = row_create_prebuilt(
9807 ctx->new_table, altered_table->s->reclength);
9808 if (update_own_prebuilt) {
9809 m_prebuilt = ctx->prebuilt;
9810 }
9811 trx_start_if_not_started(user_trx, true);
9812 user_trx->will_lock++;
9813 m_prebuilt->trx = user_trx;
9814 }
9815 DBUG_INJECT_CRASH("ib_commit_inplace_crash",
9816 crash_inject_count++);
9817 }
9818
9819 row_mysql_unlock_data_dictionary(trx);
9820 trx_free(trx);
9821
9822 /* TODO: The following code could be executed
9823 while allowing concurrent access to the table
9824 (MDL downgrade). */
9825
9826 if (new_clustered) {
9827 for (inplace_alter_handler_ctx** pctx = ctx_array;
9828 *pctx; pctx++) {
9829 ha_innobase_inplace_ctx* ctx
9830 = static_cast<ha_innobase_inplace_ctx*>
9831 (*pctx);
9832 DBUG_ASSERT(ctx->need_rebuild());
9833
9834 alter_stats_rebuild(
9835 ctx->new_table, table->s->table_name.str,
9836 m_user_thd);
9837 DBUG_INJECT_CRASH("ib_commit_inplace_crash",
9838 crash_inject_count++);
9839 }
9840 } else {
9841 for (inplace_alter_handler_ctx** pctx = ctx_array;
9842 *pctx; pctx++) {
9843 ha_innobase_inplace_ctx* ctx
9844 = static_cast<ha_innobase_inplace_ctx*>
9845 (*pctx);
9846 DBUG_ASSERT(!ctx->need_rebuild());
9847
9848 alter_stats_norebuild(ha_alter_info, ctx, m_user_thd);
9849 DBUG_INJECT_CRASH("ib_commit_inplace_crash",
9850 crash_inject_count++);
9851 }
9852 }
9853
9854 innobase_parse_hint_from_comment(
9855 m_user_thd, m_prebuilt->table, altered_table->s);
9856
9857 /* TODO: Also perform DROP TABLE and DROP INDEX after
9858 the MDL downgrade. */
9859
9860#ifndef DBUG_OFF
9861 dict_index_t* clust_index = dict_table_get_first_index(
9862 ctx0->prebuilt->table);
9863 DBUG_ASSERT(!clust_index->online_log);
9864 DBUG_ASSERT(dict_index_get_online_status(clust_index)
9865 == ONLINE_INDEX_COMPLETE);
9866
9867 for (dict_index_t* index = clust_index;
9868 index;
9869 index = dict_table_get_next_index(index)) {
9870 DBUG_ASSERT(!index->to_be_dropped);
9871 }
9872#endif /* DBUG_OFF */
9873 MONITOR_ATOMIC_DEC(MONITOR_PENDING_ALTER_TABLE);
9874 DBUG_RETURN(false);
9875}
9876
9877/**
9878@param thd the session
9879@param start_value the lower bound
9880@param max_value the upper bound (inclusive) */
9881
9882ib_sequence_t::ib_sequence_t(
9883 THD* thd,
9884 ulonglong start_value,
9885 ulonglong max_value)
9886 :
9887 m_max_value(max_value),
9888 m_increment(0),
9889 m_offset(0),
9890 m_next_value(start_value),
9891 m_eof(false)
9892{
9893 if (thd != 0 && m_max_value > 0) {
9894
9895 thd_get_autoinc(thd, &m_offset, &m_increment);
9896
9897 if (m_increment > 1 || m_offset > 1) {
9898
9899 /* If there is an offset or increment specified
9900 then we need to work out the exact next value. */
9901
9902 m_next_value = innobase_next_autoinc(
9903 start_value, 1,
9904 m_increment, m_offset, m_max_value);
9905
9906 } else if (start_value == 0) {
9907 /* The next value can never be 0. */
9908 m_next_value = 1;
9909 }
9910 } else {
9911 m_eof = true;
9912 }
9913}
9914
9915/**
9916Postfix increment
9917@return the next value to insert */
9918
9919ulonglong
9920ib_sequence_t::operator++(int) UNIV_NOTHROW
9921{
9922 ulonglong current = m_next_value;
9923
9924 ut_ad(!m_eof);
9925 ut_ad(m_max_value > 0);
9926
9927 m_next_value = innobase_next_autoinc(
9928 current, 1, m_increment, m_offset, m_max_value);
9929
9930 if (m_next_value == m_max_value && current == m_next_value) {
9931 m_eof = true;
9932 }
9933
9934 return(current);
9935}
9936