1 /*****************************************************************************
2
3 Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
4 Copyright (c) 2012, Facebook Inc.
5 Copyright (c) 2013, 2021, MariaDB Corporation.
6
7 This program is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free Software
9 Foundation; version 2 of the License.
10
11 This program is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along with
16 this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
18
19 *****************************************************************************/
20
21 /******************************************************************//**
22 @file dict/dict0dict.cc
23 Data dictionary system
24
25 Created 1/8/1996 Heikki Tuuri
26 ***********************************************************************/
27
28 #include <my_config.h>
29 #include <string>
30
31 #include "ha_prototypes.h"
32 #include <mysqld.h>
33 #include <strfunc.h>
34
35 #include "dict0dict.h"
36 #include "fts0fts.h"
37 #include "fil0fil.h"
38 #include <algorithm>
39
40 /** dummy index for ROW_FORMAT=REDUNDANT supremum and infimum records */
41 dict_index_t* dict_ind_redundant;
42
43 #if defined UNIV_DEBUG || defined UNIV_IBUF_DEBUG
44 /** Flag to control insert buffer debugging. */
45 extern uint ibuf_debug;
46 #endif /* UNIV_DEBUG || UNIV_IBUF_DEBUG */
47
48 #include "btr0btr.h"
49 #include "btr0cur.h"
50 #include "btr0sea.h"
51 #include "buf0buf.h"
52 #include "data0type.h"
53 #include "dict0boot.h"
54 #include "dict0crea.h"
55 #include "dict0mem.h"
56 #include "dict0priv.h"
57 #include "dict0stats.h"
58 #include "fts0fts.h"
59 #include "fts0types.h"
60 #include "lock0lock.h"
61 #include "mach0data.h"
62 #include "mem0mem.h"
63 #include "page0page.h"
64 #include "page0zip.h"
65 #include "pars0pars.h"
66 #include "pars0sym.h"
67 #include "que0que.h"
68 #include "rem0cmp.h"
69 #include "row0log.h"
70 #include "row0merge.h"
71 #include "row0mysql.h"
72 #include "row0upd.h"
73 #include "srv0mon.h"
74 #include "srv0start.h"
75 #include "sync0sync.h"
76 #include "trx0undo.h"
77
78 #include <vector>
79 #include <algorithm>
80
81 /** the dictionary system */
82 dict_sys_t* dict_sys = NULL;
83
84 /** @brief the data dictionary rw-latch protecting dict_sys
85
86 table create, drop, etc. reserve this in X-mode; implicit or
87 backround operations purge, rollback, foreign key checks reserve this
88 in S-mode; we cannot trust that MySQL protects implicit or background
89 operations a table drop since MySQL does not know of them; therefore
90 we need this; NOTE: a transaction which reserves this must keep book
91 on the mode in trx_t::dict_operation_lock_mode */
92 rw_lock_t dict_operation_lock;
93
94 /** Percentage of compression failures that are allowed in a single
95 round */
96 ulong zip_failure_threshold_pct = 5;
97
98 /** Maximum percentage of a page that can be allowed as a pad to avoid
99 compression failures */
100 ulong zip_pad_max = 50;
101
102 #define DICT_HEAP_SIZE 100 /*!< initial memory heap size when
103 creating a table or index object */
104 #define DICT_POOL_PER_TABLE_HASH 512 /*!< buffer pool max size per table
105 hash table fixed size in bytes */
106 #define DICT_POOL_PER_VARYING 4 /*!< buffer pool max size per data
107 dictionary varying size in bytes */
108
109 /** Identifies generated InnoDB foreign key names */
110 static char dict_ibfk[] = "_ibfk_";
111
112 bool innodb_table_stats_not_found = false;
113 bool innodb_index_stats_not_found = false;
114 static bool innodb_table_stats_not_found_reported = false;
115 static bool innodb_index_stats_not_found_reported = false;
116
117 /*******************************************************************//**
118 Tries to find column names for the index and sets the col field of the
119 index.
120 @param[in] index index
121 @param[in] add_v new virtual columns added along with an add index call
122 @return whether the column names were found */
123 static
124 bool
125 dict_index_find_cols(
126 dict_index_t* index,
127 const dict_add_v_col_t* add_v);
128 /*******************************************************************//**
129 Builds the internal dictionary cache representation for a clustered
130 index, containing also system fields not defined by the user.
131 @return own: the internal representation of the clustered index */
132 static
133 dict_index_t*
134 dict_index_build_internal_clust(
135 /*============================*/
136 dict_index_t* index); /*!< in: user representation of
137 a clustered index */
138 /*******************************************************************//**
139 Builds the internal dictionary cache representation for a non-clustered
140 index, containing also system fields not defined by the user.
141 @return own: the internal representation of the non-clustered index */
142 static
143 dict_index_t*
144 dict_index_build_internal_non_clust(
145 /*================================*/
146 dict_index_t* index); /*!< in: user representation of
147 a non-clustered index */
148 /**********************************************************************//**
149 Builds the internal dictionary cache representation for an FTS index.
150 @return own: the internal representation of the FTS index */
151 static
152 dict_index_t*
153 dict_index_build_internal_fts(
154 /*==========================*/
155 dict_index_t* index); /*!< in: user representation of an FTS index */
156
157 /**********************************************************************//**
158 Removes an index from the dictionary cache. */
159 static
160 void
161 dict_index_remove_from_cache_low(
162 /*=============================*/
163 dict_table_t* table, /*!< in/out: table */
164 dict_index_t* index, /*!< in, own: index */
165 ibool lru_evict); /*!< in: TRUE if page being evicted
166 to make room in the table LRU list */
167 #ifdef UNIV_DEBUG
168 /**********************************************************************//**
169 Validate the dictionary table LRU list.
170 @return TRUE if validate OK */
171 static
172 ibool
173 dict_lru_validate(void);
174 /*===================*/
175 /**********************************************************************//**
176 Check if table is in the dictionary table LRU list.
177 @return TRUE if table found */
178 static
179 ibool
180 dict_lru_find_table(
181 /*================*/
182 const dict_table_t* find_table); /*!< in: table to find */
183 /**********************************************************************//**
184 Check if a table exists in the dict table non-LRU list.
185 @return TRUE if table found */
186 static
187 ibool
188 dict_non_lru_find_table(
189 /*====================*/
190 const dict_table_t* find_table); /*!< in: table to find */
191 #endif /* UNIV_DEBUG */
192
193 /* Stream for storing detailed information about the latest foreign key
194 and unique key errors. Only created if !srv_read_only_mode */
195 FILE* dict_foreign_err_file = NULL;
196 /* mutex protecting the foreign and unique error buffers */
197 ib_mutex_t dict_foreign_err_mutex;
198
199 /********************************************************************//**
200 Checks if the database name in two table names is the same.
201 @return TRUE if same db name */
202 ibool
dict_tables_have_same_db(const char * name1,const char * name2)203 dict_tables_have_same_db(
204 /*=====================*/
205 const char* name1, /*!< in: table name in the form
206 dbname '/' tablename */
207 const char* name2) /*!< in: table name in the form
208 dbname '/' tablename */
209 {
210 for (; *name1 == *name2; name1++, name2++) {
211 if (*name1 == '/') {
212 return(TRUE);
213 }
214 ut_a(*name1); /* the names must contain '/' */
215 }
216 return(FALSE);
217 }
218
219 /********************************************************************//**
220 Return the end of table name where we have removed dbname and '/'.
221 @return table name */
222 const char*
dict_remove_db_name(const char * name)223 dict_remove_db_name(
224 /*================*/
225 const char* name) /*!< in: table name in the form
226 dbname '/' tablename */
227 {
228 const char* s = strchr(name, '/');
229 ut_a(s);
230
231 return(s + 1);
232 }
233
234 /********************************************************************//**
235 Get the database name length in a table name.
236 @return database name length */
237 ulint
dict_get_db_name_len(const char * name)238 dict_get_db_name_len(
239 /*=================*/
240 const char* name) /*!< in: table name in the form
241 dbname '/' tablename */
242 {
243 const char* s;
244 s = strchr(name, '/');
245 ut_a(s);
246 return ulint(s - name);
247 }
248
249 /** Reserve the dictionary system mutex. */
250 void
dict_mutex_enter_for_mysql_func(const char * file,unsigned line)251 dict_mutex_enter_for_mysql_func(const char *file, unsigned line)
252 {
253 mutex_enter_loc(&dict_sys->mutex, file, line);
254 }
255
256 /********************************************************************//**
257 Releases the dictionary system mutex for MySQL. */
258 void
dict_mutex_exit_for_mysql(void)259 dict_mutex_exit_for_mysql(void)
260 /*===========================*/
261 {
262 mutex_exit(&dict_sys->mutex);
263 }
264
265 /**********************************************************************//**
266 Try to drop any indexes after an aborted index creation.
267 This can also be after a server kill during DROP INDEX. */
268 static
269 void
dict_table_try_drop_aborted(dict_table_t * table,table_id_t table_id,int32 ref_count)270 dict_table_try_drop_aborted(
271 /*========================*/
272 dict_table_t* table, /*!< in: table, or NULL if it
273 needs to be looked up again */
274 table_id_t table_id, /*!< in: table identifier */
275 int32 ref_count) /*!< in: expected table->n_ref_count */
276 {
277 trx_t* trx;
278
279 trx = trx_create();
280 trx->op_info = "try to drop any indexes after an aborted index creation";
281 row_mysql_lock_data_dictionary(trx);
282 trx_set_dict_operation(trx, TRX_DICT_OP_INDEX);
283
284 if (table == NULL) {
285 table = dict_table_open_on_id_low(
286 table_id, DICT_ERR_IGNORE_FK_NOKEY, FALSE);
287 } else {
288 ut_ad(table->id == table_id);
289 }
290
291 if (table && table->get_ref_count() == ref_count && table->drop_aborted
292 && !UT_LIST_GET_FIRST(table->locks)) {
293 /* Silence a debug assertion in row_merge_drop_indexes(). */
294 ut_d(table->acquire());
295 row_merge_drop_indexes(trx, table, true);
296 ut_d(table->release());
297 ut_ad(table->get_ref_count() == ref_count);
298 trx_commit_for_mysql(trx);
299 }
300
301 row_mysql_unlock_data_dictionary(trx);
302 trx->free();
303 }
304
305 /**********************************************************************//**
306 When opening a table,
307 try to drop any indexes after an aborted index creation.
308 Release the dict_sys->mutex. */
309 static
310 void
dict_table_try_drop_aborted_and_mutex_exit(dict_table_t * table,ibool try_drop)311 dict_table_try_drop_aborted_and_mutex_exit(
312 /*=======================================*/
313 dict_table_t* table, /*!< in: table (may be NULL) */
314 ibool try_drop) /*!< in: FALSE if should try to
315 drop indexes whose online creation
316 was aborted */
317 {
318 if (try_drop
319 && table != NULL
320 && table->drop_aborted
321 && table->get_ref_count() == 1
322 && dict_table_get_first_index(table)) {
323
324 /* Attempt to drop the indexes whose online creation
325 was aborted. */
326 table_id_t table_id = table->id;
327
328 mutex_exit(&dict_sys->mutex);
329
330 dict_table_try_drop_aborted(table, table_id, 1);
331 } else {
332 mutex_exit(&dict_sys->mutex);
333 }
334 }
335
336 /********************************************************************//**
337 Decrements the count of open handles to a table. */
338 void
dict_table_close(dict_table_t * table,ibool dict_locked,ibool try_drop)339 dict_table_close(
340 /*=============*/
341 dict_table_t* table, /*!< in/out: table */
342 ibool dict_locked, /*!< in: TRUE=data dictionary locked */
343 ibool try_drop) /*!< in: TRUE=try to drop any orphan
344 indexes after an aborted online
345 index creation */
346 {
347 if (!dict_locked) {
348 mutex_enter(&dict_sys->mutex);
349 }
350
351 ut_ad(mutex_own(&dict_sys->mutex));
352 ut_a(table->get_ref_count() > 0);
353
354 const bool last_handle = table->release();
355
356 /* Force persistent stats re-read upon next open of the table
357 so that FLUSH TABLE can be used to forcibly fetch stats from disk
358 if they have been manually modified. We reset table->stat_initialized
359 only if table reference count is 0 because we do not want too frequent
360 stats re-reads (e.g. in other cases than FLUSH TABLE). */
361 if (last_handle && strchr(table->name.m_name, '/') != NULL
362 && dict_stats_is_persistent_enabled(table)) {
363
364 dict_stats_deinit(table);
365 }
366
367 MONITOR_DEC(MONITOR_TABLE_REFERENCE);
368
369 ut_ad(dict_lru_validate());
370
371 #ifdef UNIV_DEBUG
372 if (table->can_be_evicted) {
373 ut_ad(dict_lru_find_table(table));
374 } else {
375 ut_ad(dict_non_lru_find_table(table));
376 }
377 #endif /* UNIV_DEBUG */
378
379 if (!dict_locked) {
380 table_id_t table_id = table->id;
381 const bool drop_aborted = last_handle && try_drop
382 && table->drop_aborted
383 && dict_table_get_first_index(table);
384
385 mutex_exit(&dict_sys->mutex);
386
387 /* dict_table_try_drop_aborted() can generate undo logs.
388 So it should be avoided after shutdown of background
389 threads */
390 if (drop_aborted && !srv_undo_sources) {
391 dict_table_try_drop_aborted(NULL, table_id, 0);
392 }
393 }
394 }
395
396 /********************************************************************//**
397 Closes the only open handle to a table and drops a table while assuring
398 that dict_sys->mutex is held the whole time. This assures that the table
399 is not evicted after the close when the count of open handles goes to zero.
400 Because dict_sys->mutex is held, we do not need to call
401 dict_table_prevent_eviction(). */
402 void
dict_table_close_and_drop(trx_t * trx,dict_table_t * table)403 dict_table_close_and_drop(
404 /*======================*/
405 trx_t* trx, /*!< in: data dictionary transaction */
406 dict_table_t* table) /*!< in/out: table */
407 {
408 dberr_t err = DB_SUCCESS;
409
410 ut_ad(mutex_own(&dict_sys->mutex));
411 ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_X));
412 ut_ad(trx->dict_operation != TRX_DICT_OP_NONE);
413 ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE));
414
415 dict_table_close(table, TRUE, FALSE);
416
417 #if defined UNIV_DEBUG || defined UNIV_DDL_DEBUG
418 /* Nobody should have initialized the stats of the newly created
419 table when this is called. So we know that it has not been added
420 for background stats gathering. */
421 ut_a(!table->stat_initialized);
422 #endif /* UNIV_DEBUG || UNIV_DDL_DEBUG */
423
424 err = row_merge_drop_table(trx, table);
425
426 if (err != DB_SUCCESS) {
427 ib::error() << "At " << __FILE__ << ":" << __LINE__
428 << " row_merge_drop_table returned error: " << err
429 << " table: " << table->name;
430 }
431 }
432
433 /** Check if the table has a given (non_virtual) column.
434 @param[in] table table object
435 @param[in] col_name column name
436 @param[in] col_nr column number guessed, 0 as default
437 @return column number if the table has the specified column,
438 otherwise table->n_def */
439 ulint
dict_table_has_column(const dict_table_t * table,const char * col_name,ulint col_nr)440 dict_table_has_column(
441 const dict_table_t* table,
442 const char* col_name,
443 ulint col_nr)
444 {
445 ulint col_max = table->n_def;
446
447 ut_ad(table);
448 ut_ad(col_name);
449 ut_ad(table->magic_n == DICT_TABLE_MAGIC_N);
450
451 if (col_nr < col_max
452 && innobase_strcasecmp(
453 col_name, dict_table_get_col_name(table, col_nr)) == 0) {
454 return(col_nr);
455 }
456
457 /** The order of column may changed, check it with other columns */
458 for (ulint i = 0; i < col_max; i++) {
459 if (i != col_nr
460 && innobase_strcasecmp(
461 col_name, dict_table_get_col_name(table, i)) == 0) {
462
463 return(i);
464 }
465 }
466
467 return(col_max);
468 }
469
470 /** Retrieve the column name.
471 @param[in] table the table of this column */
name(const dict_table_t & table) const472 const char* dict_col_t::name(const dict_table_t& table) const
473 {
474 ut_ad(table.magic_n == DICT_TABLE_MAGIC_N);
475
476 size_t col_nr;
477 const char *s;
478
479 if (is_virtual()) {
480 col_nr = size_t(reinterpret_cast<const dict_v_col_t*>(this)
481 - table.v_cols);
482 ut_ad(col_nr < table.n_v_def);
483 s = table.v_col_names;
484 } else {
485 col_nr = size_t(this - table.cols);
486 ut_ad(col_nr < table.n_def);
487 s = table.col_names;
488 }
489
490 if (s) {
491 for (size_t i = 0; i < col_nr; i++) {
492 s += strlen(s) + 1;
493 }
494 }
495
496 return(s);
497 }
498
499 /** Returns a virtual column's name.
500 @param[in] table target table
501 @param[in] col_nr virtual column number (nth virtual column)
502 @return column name or NULL if column number out of range. */
503 const char*
dict_table_get_v_col_name(const dict_table_t * table,ulint col_nr)504 dict_table_get_v_col_name(
505 const dict_table_t* table,
506 ulint col_nr)
507 {
508 const char* s;
509
510 ut_ad(table);
511 ut_ad(col_nr < table->n_v_def);
512 ut_ad(table->magic_n == DICT_TABLE_MAGIC_N);
513
514 if (col_nr >= table->n_v_def) {
515 return(NULL);
516 }
517
518 s = table->v_col_names;
519
520 if (s != NULL) {
521 for (ulint i = 0; i < col_nr; i++) {
522 s += strlen(s) + 1;
523 }
524 }
525
526 return(s);
527 }
528
529 /** Search virtual column's position in InnoDB according to its position
530 in original table's position
531 @param[in] table target table
532 @param[in] col_nr column number (nth column in the MySQL table)
533 @return virtual column's position in InnoDB, ULINT_UNDEFINED if not find */
534 static
535 ulint
dict_table_get_v_col_pos_for_mysql(const dict_table_t * table,ulint col_nr)536 dict_table_get_v_col_pos_for_mysql(
537 const dict_table_t* table,
538 ulint col_nr)
539 {
540 ulint i;
541
542 ut_ad(table);
543 ut_ad(col_nr < static_cast<ulint>(table->n_t_def));
544 ut_ad(table->magic_n == DICT_TABLE_MAGIC_N);
545
546 for (i = 0; i < table->n_v_def; i++) {
547 if (col_nr == dict_get_v_col_mysql_pos(
548 table->v_cols[i].m_col.ind)) {
549 break;
550 }
551 }
552
553 if (i == table->n_v_def) {
554 return(ULINT_UNDEFINED);
555 }
556
557 return(i);
558 }
559
560 /** Returns a virtual column's name according to its original
561 MySQL table position.
562 @param[in] table target table
563 @param[in] col_nr column number (nth column in the table)
564 @return column name. */
565 static
566 const char*
dict_table_get_v_col_name_mysql(const dict_table_t * table,ulint col_nr)567 dict_table_get_v_col_name_mysql(
568 const dict_table_t* table,
569 ulint col_nr)
570 {
571 ulint i = dict_table_get_v_col_pos_for_mysql(table, col_nr);
572
573 if (i == ULINT_UNDEFINED) {
574 return(NULL);
575 }
576
577 return(dict_table_get_v_col_name(table, i));
578 }
579
580 /** Get nth virtual column according to its original MySQL table position
581 @param[in] table target table
582 @param[in] col_nr column number in MySQL Table definition
583 @return dict_v_col_t ptr */
584 dict_v_col_t*
dict_table_get_nth_v_col_mysql(const dict_table_t * table,ulint col_nr)585 dict_table_get_nth_v_col_mysql(
586 const dict_table_t* table,
587 ulint col_nr)
588 {
589 ulint i = dict_table_get_v_col_pos_for_mysql(table, col_nr);
590
591 if (i == ULINT_UNDEFINED) {
592 return(NULL);
593 }
594
595 return(dict_table_get_nth_v_col(table, i));
596 }
597
598 /********************************************************************//**
599 Acquire the autoinc lock. */
600 void
dict_table_autoinc_lock(dict_table_t * table)601 dict_table_autoinc_lock(
602 /*====================*/
603 dict_table_t* table) /*!< in/out: table */
604 {
605 mysql_mutex_lock(&table->autoinc_mutex);
606 }
607
608 /** Acquire the zip_pad_mutex latch.
609 @param[in,out] index the index whose zip_pad_mutex to acquire.*/
610 static
611 void
dict_index_zip_pad_lock(dict_index_t * index)612 dict_index_zip_pad_lock(
613 dict_index_t* index)
614 {
615 mysql_mutex_lock(&index->zip_pad.mutex);
616 }
617
618 /** Get all the FTS indexes on a table.
619 @param[in] table table
620 @param[out] indexes all FTS indexes on this table
621 @return number of FTS indexes */
622 ulint
dict_table_get_all_fts_indexes(const dict_table_t * table,ib_vector_t * indexes)623 dict_table_get_all_fts_indexes(
624 const dict_table_t* table,
625 ib_vector_t* indexes)
626 {
627 dict_index_t* index;
628
629 ut_a(ib_vector_size(indexes) == 0);
630
631 for (index = dict_table_get_first_index(table);
632 index;
633 index = dict_table_get_next_index(index)) {
634
635 if (index->type == DICT_FTS) {
636 ib_vector_push(indexes, &index);
637 }
638 }
639
640 return(ib_vector_size(indexes));
641 }
642
643 /********************************************************************//**
644 Release the autoinc lock. */
645 void
dict_table_autoinc_unlock(dict_table_t * table)646 dict_table_autoinc_unlock(
647 /*======================*/
648 dict_table_t* table) /*!< in/out: table */
649 {
650 mysql_mutex_unlock(&table->autoinc_mutex);
651 }
652
653 /** Looks for column n in an index.
654 @param[in] index index
655 @param[in] n column number
656 @param[in] inc_prefix true=consider column prefixes too
657 @param[in] is_virtual true==virtual column
658 @param[out] prefix_col_pos col num if prefix
659 @return position in internal representation of the index;
660 ULINT_UNDEFINED if not contained */
661 ulint
dict_index_get_nth_col_or_prefix_pos(const dict_index_t * index,ulint n,bool inc_prefix,bool is_virtual,ulint * prefix_col_pos)662 dict_index_get_nth_col_or_prefix_pos(
663 const dict_index_t* index,
664 ulint n,
665 bool inc_prefix,
666 bool is_virtual,
667 ulint* prefix_col_pos)
668 {
669 const dict_field_t* field;
670 const dict_col_t* col;
671 ulint pos;
672 ulint n_fields;
673
674 ut_ad(index);
675 ut_ad(index->magic_n == DICT_INDEX_MAGIC_N);
676
677 if (prefix_col_pos) {
678 *prefix_col_pos = ULINT_UNDEFINED;
679 }
680
681 if (is_virtual) {
682 col = &(dict_table_get_nth_v_col(index->table, n)->m_col);
683 } else {
684 col = dict_table_get_nth_col(index->table, n);
685 }
686
687 if (dict_index_is_clust(index)) {
688
689 return(dict_col_get_clust_pos(col, index));
690 }
691
692 n_fields = dict_index_get_n_fields(index);
693
694 for (pos = 0; pos < n_fields; pos++) {
695 field = dict_index_get_nth_field(index, pos);
696
697 if (col == field->col) {
698 if (prefix_col_pos) {
699 *prefix_col_pos = pos;
700 }
701 if (inc_prefix || field->prefix_len == 0) {
702 return(pos);
703 }
704 }
705 }
706
707 return(ULINT_UNDEFINED);
708 }
709
710 /** Returns TRUE if the index contains a column or a prefix of that column.
711 @param[in] index index
712 @param[in] n column number
713 @param[in] is_virtual whether it is a virtual col
714 @return TRUE if contains the column or its prefix */
715 bool
dict_index_contains_col_or_prefix(const dict_index_t * index,ulint n,bool is_virtual)716 dict_index_contains_col_or_prefix(
717 const dict_index_t* index,
718 ulint n,
719 bool is_virtual)
720 {
721 const dict_field_t* field;
722 const dict_col_t* col;
723 ulint pos;
724 ulint n_fields;
725
726 ut_ad(index);
727 ut_ad(index->magic_n == DICT_INDEX_MAGIC_N);
728
729 if (dict_index_is_clust(index)) {
730 return(!is_virtual);
731 }
732
733 if (is_virtual) {
734 col = &dict_table_get_nth_v_col(index->table, n)->m_col;
735 } else {
736 col = dict_table_get_nth_col(index->table, n);
737 }
738
739 n_fields = dict_index_get_n_fields(index);
740
741 for (pos = 0; pos < n_fields; pos++) {
742 field = dict_index_get_nth_field(index, pos);
743
744 if (col == field->col) {
745
746 return(true);
747 }
748 }
749
750 return(false);
751 }
752
753 /********************************************************************//**
754 Looks for a matching field in an index. The column has to be the same. The
755 column in index must be complete, or must contain a prefix longer than the
756 column in index2. That is, we must be able to construct the prefix in index2
757 from the prefix in index.
758 @return position in internal representation of the index;
759 ULINT_UNDEFINED if not contained */
760 ulint
dict_index_get_nth_field_pos(const dict_index_t * index,const dict_index_t * index2,ulint n)761 dict_index_get_nth_field_pos(
762 /*=========================*/
763 const dict_index_t* index, /*!< in: index from which to search */
764 const dict_index_t* index2, /*!< in: index */
765 ulint n) /*!< in: field number in index2 */
766 {
767 const dict_field_t* field;
768 const dict_field_t* field2;
769 ulint n_fields;
770 ulint pos;
771
772 ut_ad(index->magic_n == DICT_INDEX_MAGIC_N);
773
774 field2 = dict_index_get_nth_field(index2, n);
775
776 n_fields = dict_index_get_n_fields(index);
777
778 /* Are we looking for a MBR (Minimum Bound Box) field of
779 a spatial index */
780 bool is_mbr_fld = (n == 0 && dict_index_is_spatial(index2));
781
782 for (pos = 0; pos < n_fields; pos++) {
783 field = dict_index_get_nth_field(index, pos);
784
785 /* The first field of a spatial index is a transformed
786 MBR (Minimum Bound Box) field made out of original column,
787 so its field->col still points to original cluster index
788 col, but the actual content is different. So we cannot
789 consider them equal if neither of them is MBR field */
790 if (pos == 0 && dict_index_is_spatial(index) && !is_mbr_fld) {
791 continue;
792 }
793
794 if (field->col == field2->col
795 && (field->prefix_len == 0
796 || (field->prefix_len >= field2->prefix_len
797 && field2->prefix_len != 0))) {
798
799 return(pos);
800 }
801 }
802
803 return(ULINT_UNDEFINED);
804 }
805
806 /**********************************************************************//**
807 Returns a table object based on table id.
808 @return table, NULL if does not exist */
809 dict_table_t*
dict_table_open_on_id(table_id_t table_id,ibool dict_locked,dict_table_op_t table_op)810 dict_table_open_on_id(
811 /*==================*/
812 table_id_t table_id, /*!< in: table id */
813 ibool dict_locked, /*!< in: TRUE=data dictionary locked */
814 dict_table_op_t table_op) /*!< in: operation to perform */
815 {
816 dict_table_t* table;
817
818 if (!dict_locked) {
819 mutex_enter(&dict_sys->mutex);
820 }
821
822 ut_ad(mutex_own(&dict_sys->mutex));
823
824 table = dict_table_open_on_id_low(
825 table_id,
826 table_op == DICT_TABLE_OP_LOAD_TABLESPACE
827 ? DICT_ERR_IGNORE_RECOVER_LOCK
828 : DICT_ERR_IGNORE_FK_NOKEY,
829 table_op == DICT_TABLE_OP_OPEN_ONLY_IF_CACHED);
830
831 if (table != NULL) {
832
833 if (table->can_be_evicted) {
834 dict_move_to_mru(table);
835 }
836
837 table->acquire();
838
839 MONITOR_INC(MONITOR_TABLE_REFERENCE);
840 }
841
842 if (!dict_locked) {
843 dict_table_try_drop_aborted_and_mutex_exit(
844 table, table_op == DICT_TABLE_OP_DROP_ORPHAN);
845 }
846
847 return(table);
848 }
849
850 /********************************************************************//**
851 Looks for column n position in the clustered index.
852 @return position in internal representation of the clustered index */
853 ulint
dict_table_get_nth_col_pos(const dict_table_t * table,ulint n,ulint * prefix_col_pos)854 dict_table_get_nth_col_pos(
855 /*=======================*/
856 const dict_table_t* table, /*!< in: table */
857 ulint n, /*!< in: column number */
858 ulint* prefix_col_pos)
859 {
860 return(dict_index_get_nth_col_pos(dict_table_get_first_index(table),
861 n, prefix_col_pos));
862 }
863
864 /********************************************************************//**
865 Checks if a column is in the ordering columns of the clustered index of a
866 table. Column prefixes are treated like whole columns.
867 @return TRUE if the column, or its prefix, is in the clustered key */
868 ibool
dict_table_col_in_clustered_key(const dict_table_t * table,ulint n)869 dict_table_col_in_clustered_key(
870 /*============================*/
871 const dict_table_t* table, /*!< in: table */
872 ulint n) /*!< in: column number */
873 {
874 const dict_index_t* index;
875 const dict_field_t* field;
876 const dict_col_t* col;
877 ulint pos;
878 ulint n_fields;
879
880 col = dict_table_get_nth_col(table, n);
881
882 index = dict_table_get_first_index(table);
883
884 n_fields = dict_index_get_n_unique(index);
885
886 for (pos = 0; pos < n_fields; pos++) {
887 field = dict_index_get_nth_field(index, pos);
888
889 if (col == field->col) {
890
891 return(TRUE);
892 }
893 }
894
895 return(FALSE);
896 }
897
898 /**********************************************************************//**
899 Inits the data dictionary module. */
900 void
dict_init(void)901 dict_init(void)
902 /*===========*/
903 {
904 dict_sys = static_cast<dict_sys_t*>(ut_zalloc_nokey(sizeof(*dict_sys)));
905
906 UT_LIST_INIT(dict_sys->table_LRU, &dict_table_t::table_LRU);
907 UT_LIST_INIT(dict_sys->table_non_LRU, &dict_table_t::table_LRU);
908
909 mutex_create(LATCH_ID_DICT_SYS, &dict_sys->mutex);
910
911 dict_sys->table_hash = hash_create(
912 buf_pool_get_curr_size()
913 / (DICT_POOL_PER_TABLE_HASH * UNIV_WORD_SIZE));
914
915 dict_sys->table_id_hash = hash_create(
916 buf_pool_get_curr_size()
917 / (DICT_POOL_PER_TABLE_HASH * UNIV_WORD_SIZE));
918
919 rw_lock_create(dict_operation_lock_key,
920 &dict_operation_lock, SYNC_DICT_OPERATION);
921
922 if (!srv_read_only_mode) {
923 dict_foreign_err_file = os_file_create_tmpfile();
924 ut_a(dict_foreign_err_file);
925 }
926
927 mutex_create(LATCH_ID_DICT_FOREIGN_ERR, &dict_foreign_err_mutex);
928 }
929
930 /**********************************************************************//**
931 Move to the most recently used segment of the LRU list. */
932 void
dict_move_to_mru(dict_table_t * table)933 dict_move_to_mru(
934 /*=============*/
935 dict_table_t* table) /*!< in: table to move to MRU */
936 {
937 ut_ad(mutex_own(&dict_sys->mutex));
938 ut_ad(dict_lru_validate());
939 ut_ad(dict_lru_find_table(table));
940
941 ut_a(table->can_be_evicted);
942
943 UT_LIST_REMOVE(dict_sys->table_LRU, table);
944
945 UT_LIST_ADD_FIRST(dict_sys->table_LRU, table);
946
947 ut_ad(dict_lru_validate());
948 }
949
950 /**********************************************************************//**
951 Returns a table object and increment its open handle count.
952 NOTE! This is a high-level function to be used mainly from outside the
953 'dict' module. Inside this directory dict_table_get_low
954 is usually the appropriate function.
955 @return table, NULL if does not exist */
956 dict_table_t*
dict_table_open_on_name(const char * table_name,ibool dict_locked,ibool try_drop,dict_err_ignore_t ignore_err)957 dict_table_open_on_name(
958 /*====================*/
959 const char* table_name, /*!< in: table name */
960 ibool dict_locked, /*!< in: TRUE=data dictionary locked */
961 ibool try_drop, /*!< in: TRUE=try to drop any orphan
962 indexes after an aborted online
963 index creation */
964 dict_err_ignore_t
965 ignore_err) /*!< in: error to be ignored when
966 loading a table definition */
967 {
968 dict_table_t* table;
969 DBUG_ENTER("dict_table_open_on_name");
970 DBUG_PRINT("dict_table_open_on_name", ("table: '%s'", table_name));
971
972 if (!dict_locked) {
973 mutex_enter(&dict_sys->mutex);
974 }
975
976 ut_ad(table_name);
977 ut_ad(mutex_own(&dict_sys->mutex));
978
979 table = dict_table_check_if_in_cache_low(table_name);
980
981 if (table == NULL) {
982 table = dict_load_table(table_name, ignore_err);
983 }
984
985 ut_ad(!table || table->cached);
986
987 if (table != NULL) {
988
989 /* If table is encrypted or corrupted */
990 if (!(ignore_err & ~DICT_ERR_IGNORE_FK_NOKEY)
991 && !table->is_readable()) {
992 /* Make life easy for drop table. */
993 dict_table_prevent_eviction(table);
994
995 if (table->corrupted) {
996
997 ib::error() << "Table " << table->name
998 << " is corrupted. Please "
999 "drop the table and recreate.";
1000 if (!dict_locked) {
1001 mutex_exit(&dict_sys->mutex);
1002 }
1003
1004 DBUG_RETURN(NULL);
1005 }
1006
1007 if (table->can_be_evicted) {
1008 dict_move_to_mru(table);
1009 }
1010
1011 table->acquire();
1012
1013 if (!dict_locked) {
1014 mutex_exit(&dict_sys->mutex);
1015 }
1016
1017 DBUG_RETURN(table);
1018 }
1019
1020 if (table->can_be_evicted) {
1021 dict_move_to_mru(table);
1022 }
1023
1024 table->acquire();
1025
1026 MONITOR_INC(MONITOR_TABLE_REFERENCE);
1027 }
1028
1029 ut_ad(dict_lru_validate());
1030
1031 if (!dict_locked) {
1032 dict_table_try_drop_aborted_and_mutex_exit(table, try_drop);
1033 }
1034
1035 DBUG_RETURN(table);
1036 }
1037
1038 /**********************************************************************//**
1039 Adds system columns to a table object. */
1040 void
dict_table_add_system_columns(dict_table_t * table,mem_heap_t * heap)1041 dict_table_add_system_columns(
1042 /*==========================*/
1043 dict_table_t* table, /*!< in/out: table */
1044 mem_heap_t* heap) /*!< in: temporary heap */
1045 {
1046 ut_ad(table->n_def == table->n_cols - DATA_N_SYS_COLS);
1047 ut_ad(table->magic_n == DICT_TABLE_MAGIC_N);
1048 ut_ad(!table->cached);
1049
1050 /* NOTE: the system columns MUST be added in the following order
1051 (so that they can be indexed by the numerical value of DATA_ROW_ID,
1052 etc.) and as the last columns of the table memory object.
1053 The clustered index will not always physically contain all system
1054 columns. */
1055
1056 dict_mem_table_add_col(table, heap, "DB_ROW_ID", DATA_SYS,
1057 DATA_ROW_ID | DATA_NOT_NULL,
1058 DATA_ROW_ID_LEN);
1059
1060 compile_time_assert(DATA_ROW_ID == 0);
1061 dict_mem_table_add_col(table, heap, "DB_TRX_ID", DATA_SYS,
1062 DATA_TRX_ID | DATA_NOT_NULL,
1063 DATA_TRX_ID_LEN);
1064 compile_time_assert(DATA_TRX_ID == 1);
1065 dict_mem_table_add_col(table, heap, "DB_ROLL_PTR", DATA_SYS,
1066 DATA_ROLL_PTR | DATA_NOT_NULL,
1067 DATA_ROLL_PTR_LEN);
1068 compile_time_assert(DATA_ROLL_PTR == 2);
1069
1070 /* This check reminds that if a new system column is added to
1071 the program, it should be dealt with here */
1072 compile_time_assert(DATA_N_SYS_COLS == 3);
1073 }
1074
1075 /** Add the table definition to the data dictionary cache */
1076 void
add_to_cache()1077 dict_table_t::add_to_cache()
1078 {
1079 ut_ad(dict_lru_validate());
1080 ut_ad(mutex_own(&dict_sys->mutex));
1081
1082 mysql_mutex_init(0, &autoinc_mutex, NULL);
1083 cached = TRUE;
1084
1085 ulint fold = ut_fold_string(name.m_name);
1086 ulint id_fold = ut_fold_ull(id);
1087
1088 /* Look for a table with the same name: error if such exists */
1089 {
1090 dict_table_t* table2;
1091 HASH_SEARCH(name_hash, dict_sys->table_hash, fold,
1092 dict_table_t*, table2, ut_ad(table2->cached),
1093 !strcmp(table2->name.m_name, name.m_name));
1094 ut_a(table2 == NULL);
1095
1096 #ifdef UNIV_DEBUG
1097 /* Look for the same table pointer with a different name */
1098 HASH_SEARCH_ALL(name_hash, dict_sys->table_hash,
1099 dict_table_t*, table2, ut_ad(table2->cached),
1100 table2 == this);
1101 ut_ad(table2 == NULL);
1102 #endif /* UNIV_DEBUG */
1103 }
1104
1105 /* Look for a table with the same id: error if such exists */
1106 {
1107 dict_table_t* table2;
1108 HASH_SEARCH(id_hash, dict_sys->table_id_hash, id_fold,
1109 dict_table_t*, table2, ut_ad(table2->cached),
1110 table2->id == id);
1111 ut_a(table2 == NULL);
1112
1113 #ifdef UNIV_DEBUG
1114 /* Look for the same table pointer with a different id */
1115 HASH_SEARCH_ALL(id_hash, dict_sys->table_id_hash,
1116 dict_table_t*, table2, ut_ad(table2->cached),
1117 table2 == this);
1118 ut_ad(table2 == NULL);
1119 #endif /* UNIV_DEBUG */
1120 }
1121
1122 /* Add table to hash table of tables */
1123 HASH_INSERT(dict_table_t, name_hash, dict_sys->table_hash, fold,
1124 this);
1125
1126 /* Add table to hash table of tables based on table id */
1127 HASH_INSERT(dict_table_t, id_hash, dict_sys->table_id_hash, id_fold,
1128 this);
1129
1130 if (can_be_evicted) {
1131 UT_LIST_ADD_FIRST(dict_sys->table_LRU, this);
1132 } else {
1133 UT_LIST_ADD_FIRST(dict_sys->table_non_LRU, this);
1134 }
1135
1136 ut_ad(dict_lru_validate());
1137 }
1138
1139 /**********************************************************************//**
1140 Test whether a table can be evicted from the LRU cache.
1141 @return TRUE if table can be evicted. */
1142 static
1143 ibool
dict_table_can_be_evicted(dict_table_t * table)1144 dict_table_can_be_evicted(
1145 /*======================*/
1146 dict_table_t* table) /*!< in: table to test */
1147 {
1148 ut_ad(mutex_own(&dict_sys->mutex));
1149 ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_X));
1150
1151 ut_a(table->can_be_evicted);
1152 ut_a(table->foreign_set.empty());
1153 ut_a(table->referenced_set.empty());
1154
1155 if (table->get_ref_count() == 0) {
1156 /* The transaction commit and rollback are called from
1157 outside the handler interface. This means that there is
1158 a window where the table->n_ref_count can be zero but
1159 the table instance is in "use". */
1160
1161 if (lock_table_has_locks(table)) {
1162 return(FALSE);
1163 }
1164
1165 #ifdef BTR_CUR_HASH_ADAPT
1166 /* We cannot really evict the table if adaptive hash
1167 index entries are pointing to any of its indexes. */
1168 for (dict_index_t* index = dict_table_get_first_index(table);
1169 index != NULL;
1170 index = dict_table_get_next_index(index)) {
1171 if (index->n_ahi_pages()) {
1172 return(FALSE);
1173 }
1174 }
1175 #endif /* BTR_CUR_HASH_ADAPT */
1176
1177 return(TRUE);
1178 }
1179
1180 return(FALSE);
1181 }
1182
1183 #ifdef BTR_CUR_HASH_ADAPT
1184 /** @return a clone of this */
clone() const1185 dict_index_t *dict_index_t::clone() const
1186 {
1187 ut_ad(n_fields);
1188 ut_ad(!(type & (DICT_IBUF | DICT_SPATIAL | DICT_FTS)));
1189 ut_ad(online_status == ONLINE_INDEX_COMPLETE);
1190 ut_ad(is_committed());
1191 ut_ad(!is_dummy);
1192 ut_ad(!parser);
1193 ut_ad(!index_fts_syncing);
1194 ut_ad(!online_log);
1195 ut_ad(!rtr_track);
1196
1197 const size_t size= sizeof *this + n_fields * sizeof(*fields) +
1198 #ifdef BTR_CUR_ADAPT
1199 sizeof *search_info +
1200 #endif
1201 1 + strlen(name) +
1202 n_uniq * (sizeof *stat_n_diff_key_vals +
1203 sizeof *stat_n_sample_sizes +
1204 sizeof *stat_n_non_null_key_vals);
1205
1206 mem_heap_t* heap= mem_heap_create(size);
1207 dict_index_t *index= static_cast<dict_index_t*>(mem_heap_dup(heap, this,
1208 sizeof *this));
1209 *index= *this;
1210 rw_lock_create(index_tree_rw_lock_key, &index->lock, SYNC_INDEX_TREE);
1211 index->heap= heap;
1212 index->name= mem_heap_strdup(heap, name);
1213 index->fields= static_cast<dict_field_t*>
1214 (mem_heap_dup(heap, fields, n_fields * sizeof *fields));
1215 #ifdef BTR_CUR_ADAPT
1216 index->search_info= btr_search_info_create(index->heap);
1217 #endif /* BTR_CUR_ADAPT */
1218 index->stat_n_diff_key_vals= static_cast<ib_uint64_t*>
1219 (mem_heap_zalloc(heap, n_uniq * sizeof *stat_n_diff_key_vals));
1220 index->stat_n_sample_sizes= static_cast<ib_uint64_t*>
1221 (mem_heap_zalloc(heap, n_uniq * sizeof *stat_n_sample_sizes));
1222 index->stat_n_non_null_key_vals= static_cast<ib_uint64_t*>
1223 (mem_heap_zalloc(heap, n_uniq * sizeof *stat_n_non_null_key_vals));
1224 mysql_mutex_init(0, &index->zip_pad.mutex, NULL);
1225 return index;
1226 }
1227
1228 /** Clone this index for lazy dropping of the adaptive hash.
1229 @return this or a clone */
clone_if_needed()1230 dict_index_t *dict_index_t::clone_if_needed()
1231 {
1232 if (!search_info->ref_count)
1233 return this;
1234 dict_index_t *prev= UT_LIST_GET_PREV(indexes, this);
1235
1236 mysql_mutex_lock(&table->autoinc_mutex);
1237 UT_LIST_REMOVE(table->indexes, this);
1238 UT_LIST_ADD_LAST(table->freed_indexes, this);
1239 dict_index_t *index= clone();
1240 set_freed();
1241 if (prev)
1242 UT_LIST_INSERT_AFTER(table->indexes, prev, index);
1243 else
1244 UT_LIST_ADD_FIRST(table->indexes, index);
1245 mysql_mutex_unlock(&table->autoinc_mutex);
1246 return index;
1247 }
1248 #endif /* BTR_CUR_HASH_ADAPT */
1249
1250 /**********************************************************************//**
1251 Make room in the table cache by evicting an unused table. The unused table
1252 should not be part of FK relationship and currently not used in any user
1253 transaction. There is no guarantee that it will remove a table.
1254 @return number of tables evicted. If the number of tables in the dict_LRU
1255 is less than max_tables it will not do anything. */
1256 ulint
dict_make_room_in_cache(ulint max_tables,ulint pct_check)1257 dict_make_room_in_cache(
1258 /*====================*/
1259 ulint max_tables, /*!< in: max tables allowed in cache */
1260 ulint pct_check) /*!< in: max percent to check */
1261 {
1262 ulint i;
1263 ulint len;
1264 dict_table_t* table;
1265 ulint check_up_to;
1266 ulint n_evicted = 0;
1267
1268 ut_a(pct_check > 0);
1269 ut_a(pct_check <= 100);
1270 ut_ad(mutex_own(&dict_sys->mutex));
1271 ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_X));
1272 ut_ad(dict_lru_validate());
1273
1274 i = len = UT_LIST_GET_LEN(dict_sys->table_LRU);
1275
1276 if (len < max_tables) {
1277 return(0);
1278 }
1279
1280 check_up_to = len - ((len * pct_check) / 100);
1281
1282 /* Check for overflow */
1283 ut_a(i == 0 || check_up_to <= i);
1284
1285 /* Find a suitable candidate to evict from the cache. Don't scan the
1286 entire LRU list. Only scan pct_check list entries. */
1287
1288 for (table = UT_LIST_GET_LAST(dict_sys->table_LRU);
1289 table != NULL
1290 && i > check_up_to
1291 && (len - n_evicted) > max_tables;
1292 --i) {
1293
1294 dict_table_t* prev_table;
1295
1296 prev_table = UT_LIST_GET_PREV(table_LRU, table);
1297
1298 if (dict_table_can_be_evicted(table)) {
1299 ut_ad(!table->fts);
1300 dict_table_remove_from_cache_low(table, TRUE);
1301
1302 ++n_evicted;
1303 }
1304
1305 table = prev_table;
1306 }
1307
1308 return(n_evicted);
1309 }
1310
1311 /**********************************************************************//**
1312 Move a table to the non-LRU list from the LRU list. */
1313 void
dict_table_move_from_lru_to_non_lru(dict_table_t * table)1314 dict_table_move_from_lru_to_non_lru(
1315 /*================================*/
1316 dict_table_t* table) /*!< in: table to move from LRU to non-LRU */
1317 {
1318 ut_ad(mutex_own(&dict_sys->mutex));
1319 ut_ad(dict_lru_find_table(table));
1320
1321 ut_a(table->can_be_evicted);
1322
1323 UT_LIST_REMOVE(dict_sys->table_LRU, table);
1324
1325 UT_LIST_ADD_LAST(dict_sys->table_non_LRU, table);
1326
1327 table->can_be_evicted = FALSE;
1328 }
1329
1330 /** Looks for an index with the given id given a table instance.
1331 @param[in] table table instance
1332 @param[in] id index id
1333 @return index or NULL */
1334 dict_index_t*
dict_table_find_index_on_id(const dict_table_t * table,index_id_t id)1335 dict_table_find_index_on_id(
1336 const dict_table_t* table,
1337 index_id_t id)
1338 {
1339 dict_index_t* index;
1340
1341 for (index = dict_table_get_first_index(table);
1342 index != NULL;
1343 index = dict_table_get_next_index(index)) {
1344
1345 if (id == index->id) {
1346 /* Found */
1347
1348 return(index);
1349 }
1350 }
1351
1352 return(NULL);
1353 }
1354
1355 /**********************************************************************//**
1356 Looks for an index with the given id. NOTE that we do not reserve
1357 the dictionary mutex: this function is for emergency purposes like
1358 printing info of a corrupt database page!
1359 @return index or NULL if not found in cache */
1360 dict_index_t*
dict_index_find_on_id_low(index_id_t id)1361 dict_index_find_on_id_low(
1362 /*======================*/
1363 index_id_t id) /*!< in: index id */
1364 {
1365 dict_table_t* table;
1366
1367 /* This can happen if the system tablespace is the wrong page size */
1368 if (dict_sys == NULL) {
1369 return(NULL);
1370 }
1371
1372 for (table = UT_LIST_GET_FIRST(dict_sys->table_LRU);
1373 table != NULL;
1374 table = UT_LIST_GET_NEXT(table_LRU, table)) {
1375
1376 dict_index_t* index = dict_table_find_index_on_id(table, id);
1377
1378 if (index != NULL) {
1379 return(index);
1380 }
1381 }
1382
1383 for (table = UT_LIST_GET_FIRST(dict_sys->table_non_LRU);
1384 table != NULL;
1385 table = UT_LIST_GET_NEXT(table_LRU, table)) {
1386
1387 dict_index_t* index = dict_table_find_index_on_id(table, id);
1388
1389 if (index != NULL) {
1390 return(index);
1391 }
1392 }
1393
1394 return(NULL);
1395 }
1396
1397 /** Function object to remove a foreign key constraint from the
1398 referenced_set of the referenced table. The foreign key object is
1399 also removed from the dictionary cache. The foreign key constraint
1400 is not removed from the foreign_set of the table containing the
1401 constraint. */
1402 struct dict_foreign_remove_partial
1403 {
operator ()dict_foreign_remove_partial1404 void operator()(dict_foreign_t* foreign) {
1405 dict_table_t* table = foreign->referenced_table;
1406 if (table != NULL) {
1407 table->referenced_set.erase(foreign);
1408 }
1409 dict_foreign_free(foreign);
1410 }
1411 };
1412
1413 /**********************************************************************//**
1414 Renames a table object.
1415 @return TRUE if success */
1416 dberr_t
dict_table_rename_in_cache(dict_table_t * table,const char * new_name,bool rename_also_foreigns,bool replace_new_file)1417 dict_table_rename_in_cache(
1418 /*=======================*/
1419 dict_table_t* table, /*!< in/out: table */
1420 const char* new_name, /*!< in: new name */
1421 bool rename_also_foreigns,
1422 /*!< in: in ALTER TABLE we want
1423 to preserve the original table name
1424 in constraints which reference it */
1425 bool replace_new_file)
1426 /*!< in: whether to replace the
1427 file with the new name
1428 (as part of rolling back TRUNCATE) */
1429 {
1430 dberr_t err;
1431 dict_foreign_t* foreign;
1432 ulint fold;
1433 char old_name[MAX_FULL_NAME_LEN + 1];
1434 os_file_type_t ftype;
1435
1436 ut_ad(mutex_own(&dict_sys->mutex));
1437
1438 /* store the old/current name to an automatic variable */
1439 ut_a(strlen(table->name.m_name) < sizeof old_name);
1440 strcpy(old_name, table->name.m_name);
1441
1442 fold = ut_fold_string(new_name);
1443
1444 /* Look for a table with the same name: error if such exists */
1445 dict_table_t* table2;
1446 HASH_SEARCH(name_hash, dict_sys->table_hash, fold,
1447 dict_table_t*, table2, ut_ad(table2->cached),
1448 (ut_strcmp(table2->name.m_name, new_name) == 0));
1449 DBUG_EXECUTE_IF("dict_table_rename_in_cache_failure",
1450 if (table2 == NULL) {
1451 table2 = (dict_table_t*) -1;
1452 } );
1453 if (table2) {
1454 ib::error() << "Cannot rename table '" << old_name
1455 << "' to '" << new_name << "' since the"
1456 " dictionary cache already contains '" << new_name << "'.";
1457 return(DB_ERROR);
1458 }
1459
1460 /* If the table is stored in a single-table tablespace, rename the
1461 .ibd file and rebuild the .isl file if needed. */
1462
1463 if (!table->space) {
1464 bool exists;
1465 char* filepath;
1466
1467 ut_ad(dict_table_is_file_per_table(table));
1468 ut_ad(!table->is_temporary());
1469
1470 /* Make sure the data_dir_path is set. */
1471 dict_get_and_save_data_dir_path(table, true);
1472
1473 if (DICT_TF_HAS_DATA_DIR(table->flags)) {
1474 ut_a(table->data_dir_path);
1475
1476 filepath = fil_make_filepath(
1477 table->data_dir_path, table->name.m_name,
1478 IBD, true);
1479 } else {
1480 filepath = fil_make_filepath(
1481 NULL, table->name.m_name, IBD, false);
1482 }
1483
1484 if (filepath == NULL) {
1485 return(DB_OUT_OF_MEMORY);
1486 }
1487
1488 fil_delete_tablespace(table->space_id, !table->space);
1489
1490 /* Delete any temp file hanging around. */
1491 if (os_file_status(filepath, &exists, &ftype)
1492 && exists
1493 && !os_file_delete_if_exists(innodb_temp_file_key,
1494 filepath, NULL)) {
1495
1496 ib::info() << "Delete of " << filepath << " failed.";
1497 }
1498 ut_free(filepath);
1499
1500 } else if (dict_table_is_file_per_table(table)) {
1501 char* new_path;
1502 const char* old_path = UT_LIST_GET_FIRST(table->space->chain)
1503 ->name;
1504
1505 ut_ad(!table->is_temporary());
1506
1507 if (DICT_TF_HAS_DATA_DIR(table->flags)) {
1508 new_path = os_file_make_new_pathname(
1509 old_path, new_name);
1510 err = RemoteDatafile::create_link_file(
1511 new_name, new_path);
1512
1513 if (err != DB_SUCCESS) {
1514 ut_free(new_path);
1515 return(DB_TABLESPACE_EXISTS);
1516 }
1517 } else {
1518 new_path = fil_make_filepath(
1519 NULL, new_name, IBD, false);
1520 }
1521
1522 /* New filepath must not exist. */
1523 err = table->space->rename(new_name, new_path, true,
1524 replace_new_file);
1525 ut_free(new_path);
1526
1527 /* If the tablespace is remote, a new .isl file was created
1528 If success, delete the old one. If not, delete the new one. */
1529 if (DICT_TF_HAS_DATA_DIR(table->flags)) {
1530 RemoteDatafile::delete_link_file(
1531 err == DB_SUCCESS ? old_name : new_name);
1532 }
1533
1534 if (err != DB_SUCCESS) {
1535 return err;
1536 }
1537 }
1538
1539 /* Remove table from the hash tables of tables */
1540 HASH_DELETE(dict_table_t, name_hash, dict_sys->table_hash,
1541 ut_fold_string(old_name), table);
1542
1543 if (strlen(new_name) > strlen(table->name.m_name)) {
1544 /* We allocate MAX_FULL_NAME_LEN + 1 bytes here to avoid
1545 memory fragmentation, we assume a repeated calls of
1546 ut_realloc() with the same size do not cause fragmentation */
1547 ut_a(strlen(new_name) <= MAX_FULL_NAME_LEN);
1548
1549 table->name.m_name = static_cast<char*>(
1550 ut_realloc(table->name.m_name, MAX_FULL_NAME_LEN + 1));
1551 }
1552 strcpy(table->name.m_name, new_name);
1553
1554 /* Add table to hash table of tables */
1555 HASH_INSERT(dict_table_t, name_hash, dict_sys->table_hash, fold,
1556 table);
1557
1558 if (!rename_also_foreigns) {
1559 /* In ALTER TABLE we think of the rename table operation
1560 in the direction table -> temporary table (#sql...)
1561 as dropping the table with the old name and creating
1562 a new with the new name. Thus we kind of drop the
1563 constraints from the dictionary cache here. The foreign key
1564 constraints will be inherited to the new table from the
1565 system tables through a call of dict_load_foreigns. */
1566
1567 /* Remove the foreign constraints from the cache */
1568 std::for_each(table->foreign_set.begin(),
1569 table->foreign_set.end(),
1570 dict_foreign_remove_partial());
1571 table->foreign_set.clear();
1572
1573 /* Reset table field in referencing constraints */
1574 for (dict_foreign_set::iterator it
1575 = table->referenced_set.begin();
1576 it != table->referenced_set.end();
1577 ++it) {
1578
1579 foreign = *it;
1580 foreign->referenced_table = NULL;
1581 foreign->referenced_index = NULL;
1582
1583 }
1584
1585 /* Make the set of referencing constraints empty */
1586 table->referenced_set.clear();
1587
1588 return(DB_SUCCESS);
1589 }
1590
1591 /* Update the table name fields in foreign constraints, and update also
1592 the constraint id of new format >= 4.0.18 constraints. Note that at
1593 this point we have already changed table->name to the new name. */
1594
1595 dict_foreign_set fk_set;
1596
1597 for (;;) {
1598
1599 dict_foreign_set::iterator it
1600 = table->foreign_set.begin();
1601
1602 if (it == table->foreign_set.end()) {
1603 break;
1604 }
1605
1606 foreign = *it;
1607
1608 if (foreign->referenced_table) {
1609 foreign->referenced_table->referenced_set.erase(foreign);
1610 }
1611
1612 if (ut_strlen(foreign->foreign_table_name)
1613 < ut_strlen(table->name.m_name)) {
1614 /* Allocate a longer name buffer;
1615 TODO: store buf len to save memory */
1616
1617 foreign->foreign_table_name = mem_heap_strdup(
1618 foreign->heap, table->name.m_name);
1619 dict_mem_foreign_table_name_lookup_set(foreign, TRUE);
1620 } else {
1621 strcpy(foreign->foreign_table_name,
1622 table->name.m_name);
1623 dict_mem_foreign_table_name_lookup_set(foreign, FALSE);
1624 }
1625 if (strchr(foreign->id, '/')) {
1626 /* This is a >= 4.0.18 format id */
1627
1628 ulint db_len;
1629 char* old_id;
1630 char old_name_cs_filename[MAX_FULL_NAME_LEN+1];
1631 uint errors = 0;
1632
1633 /* All table names are internally stored in charset
1634 my_charset_filename (except the temp tables and the
1635 partition identifier suffix in partition tables). The
1636 foreign key constraint names are internally stored
1637 in UTF-8 charset. The variable fkid here is used
1638 to store foreign key constraint name in charset
1639 my_charset_filename for comparison further below. */
1640 char fkid[MAX_TABLE_NAME_LEN+20];
1641 ibool on_tmp = FALSE;
1642
1643 /* The old table name in my_charset_filename is stored
1644 in old_name_cs_filename */
1645
1646 strcpy(old_name_cs_filename, old_name);
1647 old_name_cs_filename[MAX_FULL_NAME_LEN] = '\0';
1648 if (strstr(old_name, TEMP_TABLE_PATH_PREFIX) == NULL) {
1649
1650 innobase_convert_to_system_charset(
1651 strchr(old_name_cs_filename, '/') + 1,
1652 strchr(old_name, '/') + 1,
1653 MAX_TABLE_NAME_LEN, &errors);
1654
1655 if (errors) {
1656 /* There has been an error to convert
1657 old table into UTF-8. This probably
1658 means that the old table name is
1659 actually in UTF-8. */
1660 innobase_convert_to_filename_charset(
1661 strchr(old_name_cs_filename,
1662 '/') + 1,
1663 strchr(old_name, '/') + 1,
1664 MAX_TABLE_NAME_LEN);
1665 } else {
1666 /* Old name already in
1667 my_charset_filename */
1668 strcpy(old_name_cs_filename, old_name);
1669 old_name_cs_filename[MAX_FULL_NAME_LEN]
1670 = '\0';
1671 }
1672 }
1673
1674 strncpy(fkid, foreign->id, MAX_TABLE_NAME_LEN);
1675
1676 if (strstr(fkid, TEMP_TABLE_PATH_PREFIX) == NULL) {
1677 innobase_convert_to_filename_charset(
1678 strchr(fkid, '/') + 1,
1679 strchr(foreign->id, '/') + 1,
1680 MAX_TABLE_NAME_LEN+20);
1681 } else {
1682 on_tmp = TRUE;
1683 }
1684
1685 old_id = mem_strdup(foreign->id);
1686
1687 if (ut_strlen(fkid) > ut_strlen(old_name_cs_filename)
1688 + ((sizeof dict_ibfk) - 1)
1689 && !memcmp(fkid, old_name_cs_filename,
1690 ut_strlen(old_name_cs_filename))
1691 && !memcmp(fkid + ut_strlen(old_name_cs_filename),
1692 dict_ibfk, (sizeof dict_ibfk) - 1)) {
1693
1694 /* This is a generated >= 4.0.18 format id */
1695
1696 char table_name[MAX_TABLE_NAME_LEN + 1];
1697 uint errors = 0;
1698
1699 if (strlen(table->name.m_name)
1700 > strlen(old_name)) {
1701 foreign->id = static_cast<char*>(
1702 mem_heap_alloc(
1703 foreign->heap,
1704 strlen(table->name.m_name)
1705 + strlen(old_id) + 1));
1706 }
1707
1708 /* Convert the table name to UTF-8 */
1709 strncpy(table_name, table->name.m_name,
1710 MAX_TABLE_NAME_LEN);
1711 table_name[MAX_TABLE_NAME_LEN] = '\0';
1712 innobase_convert_to_system_charset(
1713 strchr(table_name, '/') + 1,
1714 strchr(table->name.m_name, '/') + 1,
1715 MAX_TABLE_NAME_LEN, &errors);
1716
1717 if (errors) {
1718 /* Table name could not be converted
1719 from charset my_charset_filename to
1720 UTF-8. This means that the table name
1721 is already in UTF-8 (#mysql50#). */
1722 strncpy(table_name, table->name.m_name,
1723 MAX_TABLE_NAME_LEN);
1724 table_name[MAX_TABLE_NAME_LEN] = '\0';
1725 }
1726
1727 /* Replace the prefix 'databasename/tablename'
1728 with the new names */
1729 strcpy(foreign->id, table_name);
1730 if (on_tmp) {
1731 strcat(foreign->id,
1732 old_id + ut_strlen(old_name));
1733 } else {
1734 sprintf(strchr(foreign->id, '/') + 1,
1735 "%s%s",
1736 strchr(table_name, '/') +1,
1737 strstr(old_id, "_ibfk_") );
1738 }
1739
1740 } else {
1741 /* This is a >= 4.0.18 format id where the user
1742 gave the id name */
1743 db_len = dict_get_db_name_len(
1744 table->name.m_name) + 1;
1745
1746 if (db_len - 1
1747 > dict_get_db_name_len(foreign->id)) {
1748
1749 foreign->id = static_cast<char*>(
1750 mem_heap_alloc(
1751 foreign->heap,
1752 db_len + strlen(old_id) + 1));
1753 }
1754
1755 /* Replace the database prefix in id with the
1756 one from table->name */
1757
1758 ut_memcpy(foreign->id,
1759 table->name.m_name, db_len);
1760
1761 strcpy(foreign->id + db_len,
1762 dict_remove_db_name(old_id));
1763 }
1764
1765 ut_free(old_id);
1766 }
1767
1768 table->foreign_set.erase(it);
1769 fk_set.insert(foreign);
1770
1771 if (foreign->referenced_table) {
1772 foreign->referenced_table->referenced_set.insert(foreign);
1773 }
1774 }
1775
1776 ut_a(table->foreign_set.empty());
1777 table->foreign_set.swap(fk_set);
1778
1779 for (dict_foreign_set::iterator it = table->referenced_set.begin();
1780 it != table->referenced_set.end();
1781 ++it) {
1782
1783 foreign = *it;
1784
1785 if (ut_strlen(foreign->referenced_table_name)
1786 < ut_strlen(table->name.m_name)) {
1787 /* Allocate a longer name buffer;
1788 TODO: store buf len to save memory */
1789
1790 foreign->referenced_table_name = mem_heap_strdup(
1791 foreign->heap, table->name.m_name);
1792
1793 dict_mem_referenced_table_name_lookup_set(
1794 foreign, TRUE);
1795 } else {
1796 /* Use the same buffer */
1797 strcpy(foreign->referenced_table_name,
1798 table->name.m_name);
1799
1800 dict_mem_referenced_table_name_lookup_set(
1801 foreign, FALSE);
1802 }
1803 }
1804
1805 return(DB_SUCCESS);
1806 }
1807
1808 /**********************************************************************//**
1809 Change the id of a table object in the dictionary cache. This is used in
1810 DISCARD TABLESPACE. */
1811 void
dict_table_change_id_in_cache(dict_table_t * table,table_id_t new_id)1812 dict_table_change_id_in_cache(
1813 /*==========================*/
1814 dict_table_t* table, /*!< in/out: table object already in cache */
1815 table_id_t new_id) /*!< in: new id to set */
1816 {
1817 ut_ad(mutex_own(&dict_sys->mutex));
1818 ut_ad(table->magic_n == DICT_TABLE_MAGIC_N);
1819
1820 /* Remove the table from the hash table of id's */
1821
1822 HASH_DELETE(dict_table_t, id_hash, dict_sys->table_id_hash,
1823 ut_fold_ull(table->id), table);
1824 table->id = new_id;
1825
1826 /* Add the table back to the hash table */
1827 HASH_INSERT(dict_table_t, id_hash, dict_sys->table_id_hash,
1828 ut_fold_ull(table->id), table);
1829 }
1830
1831 /**********************************************************************//**
1832 Removes a table object from the dictionary cache. */
1833 void
dict_table_remove_from_cache_low(dict_table_t * table,ibool lru_evict)1834 dict_table_remove_from_cache_low(
1835 /*=============================*/
1836 dict_table_t* table, /*!< in, own: table */
1837 ibool lru_evict) /*!< in: TRUE if table being evicted
1838 to make room in the table LRU list */
1839 {
1840 dict_foreign_t* foreign;
1841 dict_index_t* index;
1842
1843 ut_ad(dict_lru_validate());
1844 ut_a(table->get_ref_count() == 0);
1845 ut_a(table->n_rec_locks == 0);
1846 ut_ad(mutex_own(&dict_sys->mutex));
1847 ut_ad(table->magic_n == DICT_TABLE_MAGIC_N);
1848
1849 /* Remove the foreign constraints from the cache */
1850 std::for_each(table->foreign_set.begin(), table->foreign_set.end(),
1851 dict_foreign_remove_partial());
1852 table->foreign_set.clear();
1853
1854 /* Reset table field in referencing constraints */
1855 for (dict_foreign_set::iterator it = table->referenced_set.begin();
1856 it != table->referenced_set.end();
1857 ++it) {
1858
1859 foreign = *it;
1860 foreign->referenced_table = NULL;
1861 foreign->referenced_index = NULL;
1862 }
1863
1864 /* Remove the indexes from the cache */
1865
1866 for (index = UT_LIST_GET_LAST(table->indexes);
1867 index != NULL;
1868 index = UT_LIST_GET_LAST(table->indexes)) {
1869
1870 dict_index_remove_from_cache_low(table, index, lru_evict);
1871 }
1872
1873 /* Remove table from the hash tables of tables */
1874
1875 HASH_DELETE(dict_table_t, name_hash, dict_sys->table_hash,
1876 ut_fold_string(table->name.m_name), table);
1877
1878 HASH_DELETE(dict_table_t, id_hash, dict_sys->table_id_hash,
1879 ut_fold_ull(table->id), table);
1880
1881 /* Remove table from LRU or non-LRU list. */
1882 if (table->can_be_evicted) {
1883 ut_ad(dict_lru_find_table(table));
1884 UT_LIST_REMOVE(dict_sys->table_LRU, table);
1885 } else {
1886 ut_ad(dict_non_lru_find_table(table));
1887 UT_LIST_REMOVE(dict_sys->table_non_LRU, table);
1888 }
1889
1890 ut_ad(dict_lru_validate());
1891
1892 if (lru_evict && table->drop_aborted) {
1893 /* When evicting the table definition,
1894 drop the orphan indexes from the data dictionary
1895 and free the index pages. */
1896 trx_t* trx = trx_create();
1897
1898 ut_ad(mutex_own(&dict_sys->mutex));
1899 ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_X));
1900
1901 /* Mimic row_mysql_lock_data_dictionary(). */
1902 trx->dict_operation_lock_mode = RW_X_LATCH;
1903
1904 trx_set_dict_operation(trx, TRX_DICT_OP_INDEX);
1905 row_merge_drop_indexes_dict(trx, table->id);
1906 trx_commit_for_mysql(trx);
1907 trx->dict_operation_lock_mode = 0;
1908 trx->free();
1909 }
1910
1911 /* Free virtual column template if any */
1912 if (table->vc_templ != NULL) {
1913 dict_free_vc_templ(table->vc_templ);
1914 UT_DELETE(table->vc_templ);
1915 }
1916
1917 #ifdef BTR_CUR_HASH_ADAPT
1918 if (table->fts) {
1919 fts_optimize_remove_table(table);
1920 fts_free(table);
1921 table->fts = NULL;
1922 }
1923
1924 mysql_mutex_lock(&table->autoinc_mutex);
1925
1926 ulint freed = UT_LIST_GET_LEN(table->freed_indexes);
1927
1928 table->vc_templ = NULL;
1929 table->id = 0;
1930 mysql_mutex_unlock(&table->autoinc_mutex);
1931
1932 if (UNIV_UNLIKELY(freed != 0)) {
1933 return;
1934 }
1935 #endif /* BTR_CUR_HASH_ADAPT */
1936
1937 mysql_mutex_destroy(&table->autoinc_mutex);
1938 dict_mem_table_free(table);
1939 }
1940
1941 /**********************************************************************//**
1942 Removes a table object from the dictionary cache. */
1943 void
dict_table_remove_from_cache(dict_table_t * table)1944 dict_table_remove_from_cache(
1945 /*=========================*/
1946 dict_table_t* table) /*!< in, own: table */
1947 {
1948 dict_table_remove_from_cache_low(table, FALSE);
1949 }
1950
1951 /****************************************************************//**
1952 If the given column name is reserved for InnoDB system columns, return
1953 TRUE.
1954 @return TRUE if name is reserved */
1955 ibool
dict_col_name_is_reserved(const char * name)1956 dict_col_name_is_reserved(
1957 /*======================*/
1958 const char* name) /*!< in: column name */
1959 {
1960 static const char* reserved_names[] = {
1961 "DB_ROW_ID", "DB_TRX_ID", "DB_ROLL_PTR"
1962 };
1963
1964 compile_time_assert(UT_ARR_SIZE(reserved_names) == DATA_N_SYS_COLS);
1965
1966 for (ulint i = 0; i < UT_ARR_SIZE(reserved_names); i++) {
1967 if (innobase_strcasecmp(name, reserved_names[i]) == 0) {
1968
1969 return(TRUE);
1970 }
1971 }
1972
1973 return(FALSE);
1974 }
1975
1976 /** Clears the virtual column's index list before index is
1977 being freed.
1978 @param[in] index Index being freed */
dict_index_remove_from_v_col_list(dict_index_t * index)1979 void dict_index_remove_from_v_col_list(dict_index_t* index)
1980 {
1981 /* Index is not completely formed */
1982 if (!index->cached) {
1983 return;
1984 }
1985 if (dict_index_has_virtual(index)) {
1986 const dict_col_t* col;
1987 const dict_v_col_t* vcol;
1988
1989 for (ulint i = 0; i < dict_index_get_n_fields(index); i++) {
1990 col = dict_index_get_nth_col(index, i);
1991 if (col && col->is_virtual()) {
1992 vcol = reinterpret_cast<const dict_v_col_t*>(
1993 col);
1994 /* This could be NULL, when we do add
1995 virtual column, add index together. We do not
1996 need to track this virtual column's index */
1997 if (vcol->v_indexes == NULL) {
1998 continue;
1999 }
2000 dict_v_idx_list::iterator it;
2001 for (it = vcol->v_indexes->begin();
2002 it != vcol->v_indexes->end(); ++it) {
2003 dict_v_idx_t v_index = *it;
2004 if (v_index.index == index) {
2005 vcol->v_indexes->erase(it);
2006 break;
2007 }
2008 }
2009 }
2010 }
2011 }
2012 }
2013
2014 /** Adds an index to the dictionary cache, with possible indexing newly
2015 added column.
2016 @param[in,out] index index; NOTE! The index memory
2017 object is freed in this function!
2018 @param[in] page_no root page number of the index
2019 @param[in] add_v virtual columns being added along with ADD INDEX
2020 @return DB_SUCCESS, or DB_CORRUPTION */
2021 dberr_t
dict_index_add_to_cache(dict_index_t * & index,ulint page_no,const dict_add_v_col_t * add_v)2022 dict_index_add_to_cache(
2023 dict_index_t*& index,
2024 ulint page_no,
2025 const dict_add_v_col_t* add_v)
2026 {
2027 dict_index_t* new_index;
2028 ulint n_ord;
2029 ulint i;
2030
2031 ut_ad(mutex_own(&dict_sys->mutex));
2032 ut_ad(index->n_def == index->n_fields);
2033 ut_ad(index->magic_n == DICT_INDEX_MAGIC_N);
2034 ut_ad(!dict_index_is_online_ddl(index));
2035 ut_ad(!dict_index_is_ibuf(index));
2036
2037 ut_d(mem_heap_validate(index->heap));
2038 ut_a(!dict_index_is_clust(index)
2039 || UT_LIST_GET_LEN(index->table->indexes) == 0);
2040 ut_ad(dict_index_is_clust(index) || !index->table->no_rollback());
2041
2042 if (!dict_index_find_cols(index, add_v)) {
2043
2044 dict_mem_index_free(index);
2045 index = NULL;
2046 return DB_CORRUPTION;
2047 }
2048
2049 /* Build the cache internal representation of the index,
2050 containing also the added system fields */
2051
2052 if (dict_index_is_clust(index)) {
2053 new_index = dict_index_build_internal_clust(index);
2054 } else {
2055 new_index = (index->type & DICT_FTS)
2056 ? dict_index_build_internal_fts(index)
2057 : dict_index_build_internal_non_clust(index);
2058 new_index->n_core_null_bytes = UT_BITS_IN_BYTES(
2059 unsigned(new_index->n_nullable));
2060 }
2061
2062 /* Set the n_fields value in new_index to the actual defined
2063 number of fields in the cache internal representation */
2064
2065 new_index->n_fields = new_index->n_def;
2066 new_index->trx_id = index->trx_id;
2067 new_index->set_committed(index->is_committed());
2068 new_index->nulls_equal = index->nulls_equal;
2069 #ifdef MYSQL_INDEX_DISABLE_AHI
2070 new_index->disable_ahi = index->disable_ahi;
2071 #endif
2072
2073 n_ord = new_index->n_uniq;
2074 /* Flag the ordering columns and also set column max_prefix */
2075
2076 for (i = 0; i < n_ord; i++) {
2077 const dict_field_t* field
2078 = dict_index_get_nth_field(new_index, i);
2079
2080 /* Check the column being added in the index for
2081 the first time and flag the ordering column. */
2082 if (field->col->ord_part == 0 ) {
2083 field->col->max_prefix = field->prefix_len;
2084 field->col->ord_part = 1;
2085 } else if (field->prefix_len == 0) {
2086 /* Set the max_prefix for a column to 0 if
2087 its prefix length is 0 (for this index)
2088 even if it was a part of any other index
2089 with some prefix length. */
2090 field->col->max_prefix = 0;
2091 } else if (field->col->max_prefix != 0
2092 && field->prefix_len
2093 > field->col->max_prefix) {
2094 /* Set the max_prefix value based on the
2095 prefix_len. */
2096 field->col->max_prefix = field->prefix_len;
2097 }
2098 ut_ad(field->col->ord_part == 1);
2099 }
2100
2101 new_index->stat_n_diff_key_vals =
2102 static_cast<ib_uint64_t*>(mem_heap_zalloc(
2103 new_index->heap,
2104 dict_index_get_n_unique(new_index)
2105 * sizeof(*new_index->stat_n_diff_key_vals)));
2106
2107 new_index->stat_n_sample_sizes =
2108 static_cast<ib_uint64_t*>(mem_heap_zalloc(
2109 new_index->heap,
2110 dict_index_get_n_unique(new_index)
2111 * sizeof(*new_index->stat_n_sample_sizes)));
2112
2113 new_index->stat_n_non_null_key_vals =
2114 static_cast<ib_uint64_t*>(mem_heap_zalloc(
2115 new_index->heap,
2116 dict_index_get_n_unique(new_index)
2117 * sizeof(*new_index->stat_n_non_null_key_vals)));
2118
2119 new_index->stat_index_size = 1;
2120 new_index->stat_n_leaf_pages = 1;
2121
2122 new_index->stat_defrag_n_pages_freed = 0;
2123 new_index->stat_defrag_n_page_split = 0;
2124
2125 new_index->stat_defrag_sample_next_slot = 0;
2126 memset(&new_index->stat_defrag_data_size_sample,
2127 0x0, sizeof(ulint) * STAT_DEFRAG_DATA_SIZE_N_SAMPLE);
2128
2129 /* Add the new index as the last index for the table */
2130
2131 UT_LIST_ADD_LAST(new_index->table->indexes, new_index);
2132 #ifdef BTR_CUR_ADAPT
2133 new_index->search_info = btr_search_info_create(new_index->heap);
2134 #endif /* BTR_CUR_ADAPT */
2135
2136 new_index->page = unsigned(page_no);
2137 rw_lock_create(index_tree_rw_lock_key, &new_index->lock,
2138 SYNC_INDEX_TREE);
2139
2140 new_index->n_core_fields = new_index->n_fields;
2141
2142 dict_mem_index_free(index);
2143 index = new_index;
2144 return DB_SUCCESS;
2145 }
2146
2147 /**********************************************************************//**
2148 Removes an index from the dictionary cache. */
2149 static
2150 void
dict_index_remove_from_cache_low(dict_table_t * table,dict_index_t * index,ibool lru_evict)2151 dict_index_remove_from_cache_low(
2152 /*=============================*/
2153 dict_table_t* table, /*!< in/out: table */
2154 dict_index_t* index, /*!< in, own: index */
2155 ibool lru_evict) /*!< in: TRUE if index being evicted
2156 to make room in the table LRU list */
2157 {
2158 ut_ad(table && index);
2159 ut_ad(table->magic_n == DICT_TABLE_MAGIC_N);
2160 ut_ad(index->magic_n == DICT_INDEX_MAGIC_N);
2161 ut_ad(mutex_own(&dict_sys->mutex));
2162 ut_ad(table->id);
2163 #ifdef BTR_CUR_HASH_ADAPT
2164 ut_ad(!index->freed());
2165 #endif /* BTR_CUR_HASH_ADAPT */
2166
2167 /* No need to acquire the dict_index_t::lock here because
2168 there can't be any active operations on this index (or table). */
2169
2170 if (index->online_log) {
2171 ut_ad(index->online_status == ONLINE_INDEX_CREATION);
2172 row_log_free(index->online_log);
2173 index->online_log = NULL;
2174 }
2175
2176 /* Remove the index from the list of indexes of the table */
2177 UT_LIST_REMOVE(table->indexes, index);
2178
2179 /* The index is being dropped, remove any compression stats for it. */
2180 if (!lru_evict && DICT_TF_GET_ZIP_SSIZE(index->table->flags)) {
2181 mutex_enter(&page_zip_stat_per_index_mutex);
2182 page_zip_stat_per_index.erase(index->id);
2183 mutex_exit(&page_zip_stat_per_index_mutex);
2184 }
2185
2186 /* Remove the index from affected virtual column index list */
2187 index->detach_columns();
2188
2189 #ifdef BTR_CUR_HASH_ADAPT
2190 /* We always create search info whether or not adaptive
2191 hash index is enabled or not. */
2192 /* We are not allowed to free the in-memory index struct
2193 dict_index_t until all entries in the adaptive hash index
2194 that point to any of the page belonging to his b-tree index
2195 are dropped. This is so because dropping of these entries
2196 require access to dict_index_t struct. To avoid such scenario
2197 We keep a count of number of such pages in the search_info and
2198 only free the dict_index_t struct when this count drops to
2199 zero. See also: dict_table_can_be_evicted() */
2200
2201 if (index->n_ahi_pages()) {
2202 mysql_mutex_lock(&table->autoinc_mutex);
2203 index->set_freed();
2204 UT_LIST_ADD_LAST(table->freed_indexes, index);
2205 mysql_mutex_unlock(&table->autoinc_mutex);
2206 return;
2207 }
2208 #endif /* BTR_CUR_HASH_ADAPT */
2209
2210 rw_lock_free(&index->lock);
2211
2212 dict_mem_index_free(index);
2213 }
2214
2215 /**********************************************************************//**
2216 Removes an index from the dictionary cache. */
2217 void
dict_index_remove_from_cache(dict_table_t * table,dict_index_t * index)2218 dict_index_remove_from_cache(
2219 /*=========================*/
2220 dict_table_t* table, /*!< in/out: table */
2221 dict_index_t* index) /*!< in, own: index */
2222 {
2223 dict_index_remove_from_cache_low(table, index, FALSE);
2224 }
2225
2226 /** Tries to find column names for the index and sets the col field of the
2227 index.
2228 @param[in] table table
2229 @param[in,out] index index
2230 @param[in] add_v new virtual columns added along with an add index call
2231 @return whether the column names were found */
2232 static
2233 bool
dict_index_find_cols(dict_index_t * index,const dict_add_v_col_t * add_v)2234 dict_index_find_cols(
2235 dict_index_t* index,
2236 const dict_add_v_col_t* add_v)
2237 {
2238 std::vector<ulint, ut_allocator<ulint> > col_added;
2239 std::vector<ulint, ut_allocator<ulint> > v_col_added;
2240
2241 const dict_table_t* table = index->table;
2242 ut_ad(table->magic_n == DICT_TABLE_MAGIC_N);
2243 ut_ad(mutex_own(&dict_sys->mutex));
2244
2245 for (ulint i = 0; i < index->n_fields; i++) {
2246 ulint j;
2247 dict_field_t* field = dict_index_get_nth_field(index, i);
2248
2249 for (j = 0; j < table->n_cols; j++) {
2250 if (!innobase_strcasecmp(dict_table_get_col_name(table, j),
2251 field->name)) {
2252
2253 /* Check if same column is being assigned again
2254 which suggest that column has duplicate name. */
2255 bool exists =
2256 std::find(col_added.begin(),
2257 col_added.end(), j)
2258 != col_added.end();
2259
2260 if (exists) {
2261 /* Duplicate column found. */
2262 goto dup_err;
2263 }
2264
2265 field->col = dict_table_get_nth_col(table, j);
2266
2267 col_added.push_back(j);
2268
2269 goto found;
2270 }
2271 }
2272
2273 /* Let's check if it is a virtual column */
2274 for (j = 0; j < table->n_v_cols; j++) {
2275 if (!strcmp(dict_table_get_v_col_name(table, j),
2276 field->name)) {
2277
2278 /* Check if same column is being assigned again
2279 which suggest that column has duplicate name. */
2280 bool exists =
2281 std::find(v_col_added.begin(),
2282 v_col_added.end(), j)
2283 != v_col_added.end();
2284
2285 if (exists) {
2286 /* Duplicate column found. */
2287 break;
2288 }
2289
2290 field->col = reinterpret_cast<dict_col_t*>(
2291 dict_table_get_nth_v_col(table, j));
2292
2293 v_col_added.push_back(j);
2294
2295 goto found;
2296 }
2297 }
2298
2299 if (add_v) {
2300 for (j = 0; j < add_v->n_v_col; j++) {
2301 if (!strcmp(add_v->v_col_name[j],
2302 field->name)) {
2303 field->col = const_cast<dict_col_t*>(
2304 &add_v->v_col[j].m_col);
2305 goto found;
2306 }
2307 }
2308 }
2309
2310 dup_err:
2311 #ifdef UNIV_DEBUG
2312 /* It is an error not to find a matching column. */
2313 ib::error() << "No matching column for " << field->name
2314 << " in index " << index->name
2315 << " of table " << table->name;
2316 #endif /* UNIV_DEBUG */
2317 return(FALSE);
2318
2319 found:
2320 ;
2321 }
2322
2323 return(TRUE);
2324 }
2325
2326 /*******************************************************************//**
2327 Adds a column to index. */
2328 void
dict_index_add_col(dict_index_t * index,const dict_table_t * table,dict_col_t * col,ulint prefix_len)2329 dict_index_add_col(
2330 /*===============*/
2331 dict_index_t* index, /*!< in/out: index */
2332 const dict_table_t* table, /*!< in: table */
2333 dict_col_t* col, /*!< in: column */
2334 ulint prefix_len) /*!< in: column prefix length */
2335 {
2336 dict_field_t* field;
2337 const char* col_name;
2338
2339 if (col->is_virtual()) {
2340 dict_v_col_t* v_col = reinterpret_cast<dict_v_col_t*>(col);
2341
2342 /* When v_col->v_indexes==NULL,
2343 ha_innobase::commit_inplace_alter_table(commit=true)
2344 will evict and reload the table definition, and
2345 v_col->v_indexes will not be NULL for the new table. */
2346 if (v_col->v_indexes != NULL) {
2347 /* Register the index with the virtual column index
2348 list */
2349 v_col->v_indexes->push_back(
2350 dict_v_idx_t(index, index->n_def));
2351 }
2352
2353 col_name = dict_table_get_v_col_name_mysql(
2354 table, dict_col_get_no(col));
2355 } else {
2356 col_name = dict_table_get_col_name(table, dict_col_get_no(col));
2357 }
2358
2359 dict_mem_index_add_field(index, col_name, prefix_len);
2360
2361 field = dict_index_get_nth_field(index, unsigned(index->n_def) - 1);
2362
2363 field->col = col;
2364 field->fixed_len = static_cast<unsigned int>(
2365 dict_col_get_fixed_size(
2366 col, dict_table_is_comp(table)));
2367
2368 if (prefix_len && field->fixed_len > prefix_len) {
2369 field->fixed_len = (unsigned int) prefix_len;
2370 }
2371
2372 /* Long fixed-length fields that need external storage are treated as
2373 variable-length fields, so that the extern flag can be embedded in
2374 the length word. */
2375
2376 if (field->fixed_len > DICT_MAX_FIXED_COL_LEN) {
2377 field->fixed_len = 0;
2378 }
2379
2380 /* The comparison limit above must be constant. If it were
2381 changed, the disk format of some fixed-length columns would
2382 change, which would be a disaster. */
2383 compile_time_assert(DICT_MAX_FIXED_COL_LEN == 768);
2384
2385 if (!(col->prtype & DATA_NOT_NULL)) {
2386 index->n_nullable++;
2387 }
2388 }
2389
2390 /*******************************************************************//**
2391 Copies fields contained in index2 to index1. */
2392 static
2393 void
dict_index_copy(dict_index_t * index1,const dict_index_t * index2,ulint start,ulint end)2394 dict_index_copy(
2395 /*============*/
2396 dict_index_t* index1, /*!< in: index to copy to */
2397 const dict_index_t* index2, /*!< in: index to copy from */
2398 ulint start, /*!< in: first position to copy */
2399 ulint end) /*!< in: last position to copy */
2400 {
2401 dict_field_t* field;
2402 ulint i;
2403
2404 /* Copy fields contained in index2 */
2405
2406 for (i = start; i < end; i++) {
2407
2408 field = dict_index_get_nth_field(index2, i);
2409
2410 dict_index_add_col(index1, index2->table, field->col,
2411 field->prefix_len);
2412 }
2413 }
2414
2415 /*******************************************************************//**
2416 Copies types of fields contained in index to tuple. */
2417 void
dict_index_copy_types(dtuple_t * tuple,const dict_index_t * index,ulint n_fields)2418 dict_index_copy_types(
2419 /*==================*/
2420 dtuple_t* tuple, /*!< in/out: data tuple */
2421 const dict_index_t* index, /*!< in: index */
2422 ulint n_fields) /*!< in: number of
2423 field types to copy */
2424 {
2425 ulint i;
2426
2427 if (dict_index_is_ibuf(index)) {
2428 dtuple_set_types_binary(tuple, n_fields);
2429
2430 return;
2431 }
2432
2433 for (i = 0; i < n_fields; i++) {
2434 const dict_field_t* ifield;
2435 dtype_t* dfield_type;
2436
2437 ifield = dict_index_get_nth_field(index, i);
2438 dfield_type = dfield_get_type(dtuple_get_nth_field(tuple, i));
2439 dict_col_copy_type(dict_field_get_col(ifield), dfield_type);
2440 if (dict_index_is_spatial(index)
2441 && DATA_GEOMETRY_MTYPE(dfield_type->mtype)) {
2442 dfield_type->prtype |= DATA_GIS_MBR;
2443 }
2444 }
2445 }
2446
2447 /** Copies types of virtual columns contained in table to tuple and sets all
2448 fields of the tuple to the SQL NULL value. This function should
2449 be called right after dtuple_create().
2450 @param[in,out] tuple data tuple
2451 @param[in] table table
2452 */
2453 void
dict_table_copy_v_types(dtuple_t * tuple,const dict_table_t * table)2454 dict_table_copy_v_types(
2455 dtuple_t* tuple,
2456 const dict_table_t* table)
2457 {
2458 /* tuple could have more virtual columns than existing table,
2459 if we are calling this for creating index along with adding
2460 virtual columns */
2461 ulint n_fields = ut_min(dtuple_get_n_v_fields(tuple),
2462 static_cast<ulint>(table->n_v_def));
2463
2464 for (ulint i = 0; i < n_fields; i++) {
2465
2466 dfield_t* dfield = dtuple_get_nth_v_field(tuple, i);
2467 dtype_t* dtype = dfield_get_type(dfield);
2468
2469 dfield_set_null(dfield);
2470 dict_col_copy_type(
2471 &(dict_table_get_nth_v_col(table, i)->m_col),
2472 dtype);
2473 }
2474 }
2475 /*******************************************************************//**
2476 Copies types of columns contained in table to tuple and sets all
2477 fields of the tuple to the SQL NULL value. This function should
2478 be called right after dtuple_create(). */
2479 void
dict_table_copy_types(dtuple_t * tuple,const dict_table_t * table)2480 dict_table_copy_types(
2481 /*==================*/
2482 dtuple_t* tuple, /*!< in/out: data tuple */
2483 const dict_table_t* table) /*!< in: table */
2484 {
2485 ulint i;
2486
2487 for (i = 0; i < dtuple_get_n_fields(tuple); i++) {
2488
2489 dfield_t* dfield = dtuple_get_nth_field(tuple, i);
2490 dtype_t* dtype = dfield_get_type(dfield);
2491
2492 dfield_set_null(dfield);
2493 dict_col_copy_type(dict_table_get_nth_col(table, i), dtype);
2494 }
2495
2496 dict_table_copy_v_types(tuple, table);
2497 }
2498
2499 /*******************************************************************//**
2500 Builds the internal dictionary cache representation for a clustered
2501 index, containing also system fields not defined by the user.
2502 @return own: the internal representation of the clustered index */
2503 static
2504 dict_index_t*
dict_index_build_internal_clust(dict_index_t * index)2505 dict_index_build_internal_clust(
2506 /*============================*/
2507 dict_index_t* index) /*!< in: user representation of
2508 a clustered index */
2509 {
2510 dict_table_t* table = index->table;
2511 dict_index_t* new_index;
2512 dict_field_t* field;
2513 ulint trx_id_pos;
2514 ulint i;
2515 ibool* indexed;
2516
2517 ut_ad(dict_index_is_clust(index));
2518 ut_ad(!dict_index_is_ibuf(index));
2519
2520 ut_ad(mutex_own(&dict_sys->mutex));
2521
2522 /* Create a new index object with certainly enough fields */
2523 new_index = dict_mem_index_create(index->table, index->name,
2524 index->type,
2525 unsigned(index->n_fields
2526 + table->n_cols));
2527
2528 /* Copy other relevant data from the old index struct to the new
2529 struct: it inherits the values */
2530
2531 new_index->n_user_defined_cols = index->n_fields;
2532
2533 new_index->id = index->id;
2534
2535 /* Copy the fields of index */
2536 dict_index_copy(new_index, index, 0, index->n_fields);
2537
2538 if (dict_index_is_unique(index)) {
2539 /* Only the fields defined so far are needed to identify
2540 the index entry uniquely */
2541
2542 new_index->n_uniq = new_index->n_def;
2543 } else {
2544 /* Also the row id is needed to identify the entry */
2545 new_index->n_uniq = 1 + unsigned(new_index->n_def);
2546 }
2547
2548 new_index->trx_id_offset = 0;
2549
2550 /* Add system columns, trx id first */
2551
2552 trx_id_pos = new_index->n_def;
2553
2554 compile_time_assert(DATA_ROW_ID == 0);
2555 compile_time_assert(DATA_TRX_ID == 1);
2556 compile_time_assert(DATA_ROLL_PTR == 2);
2557
2558 if (!dict_index_is_unique(index)) {
2559 dict_index_add_col(new_index, table,
2560 dict_table_get_sys_col(
2561 table, DATA_ROW_ID),
2562 0);
2563 trx_id_pos++;
2564 }
2565
2566 dict_index_add_col(
2567 new_index, table,
2568 dict_table_get_sys_col(table, DATA_TRX_ID), 0);
2569
2570 for (i = 0; i < trx_id_pos; i++) {
2571
2572 ulint fixed_size = dict_col_get_fixed_size(
2573 dict_index_get_nth_col(new_index, i),
2574 dict_table_is_comp(table));
2575
2576 if (fixed_size == 0) {
2577 new_index->trx_id_offset = 0;
2578
2579 break;
2580 }
2581
2582 dict_field_t* field = dict_index_get_nth_field(
2583 new_index, i);
2584 if (field->prefix_len > 0) {
2585 new_index->trx_id_offset = 0;
2586
2587 break;
2588 }
2589
2590 /* Add fixed_size to new_index->trx_id_offset.
2591 Because the latter is a bit-field, an overflow
2592 can theoretically occur. Check for it. */
2593 fixed_size += new_index->trx_id_offset;
2594
2595 new_index->trx_id_offset = unsigned(fixed_size);
2596
2597 if (new_index->trx_id_offset != fixed_size) {
2598 /* Overflow. Pretend that this is a
2599 variable-length PRIMARY KEY. */
2600 ut_ad(0);
2601 new_index->trx_id_offset = 0;
2602 break;
2603 }
2604 }
2605
2606 dict_index_add_col(
2607 new_index, table,
2608 dict_table_get_sys_col(table, DATA_ROLL_PTR), 0);
2609
2610 /* Remember the table columns already contained in new_index */
2611 indexed = static_cast<ibool*>(
2612 ut_zalloc_nokey(table->n_cols * sizeof *indexed));
2613
2614 /* Mark the table columns already contained in new_index */
2615 for (i = 0; i < new_index->n_def; i++) {
2616
2617 field = dict_index_get_nth_field(new_index, i);
2618
2619 /* If there is only a prefix of the column in the index
2620 field, do not mark the column as contained in the index */
2621
2622 if (field->prefix_len == 0) {
2623
2624 indexed[field->col->ind] = TRUE;
2625 }
2626 }
2627
2628 /* Add to new_index non-system columns of table not yet included
2629 there */
2630 for (i = 0; i + DATA_N_SYS_COLS < ulint(table->n_cols); i++) {
2631 dict_col_t* col = dict_table_get_nth_col(table, i);
2632 ut_ad(col->mtype != DATA_SYS);
2633
2634 if (!indexed[col->ind]) {
2635 dict_index_add_col(new_index, table, col, 0);
2636 }
2637 }
2638
2639 ut_free(indexed);
2640
2641 ut_ad(UT_LIST_GET_LEN(table->indexes) == 0);
2642
2643 new_index->n_core_null_bytes = table->supports_instant()
2644 ? dict_index_t::NO_CORE_NULL_BYTES
2645 : UT_BITS_IN_BYTES(unsigned(new_index->n_nullable));
2646 new_index->cached = TRUE;
2647
2648 return(new_index);
2649 }
2650
2651 /*******************************************************************//**
2652 Builds the internal dictionary cache representation for a non-clustered
2653 index, containing also system fields not defined by the user.
2654 @return own: the internal representation of the non-clustered index */
2655 static
2656 dict_index_t*
dict_index_build_internal_non_clust(dict_index_t * index)2657 dict_index_build_internal_non_clust(
2658 /*================================*/
2659 dict_index_t* index) /*!< in: user representation of
2660 a non-clustered index */
2661 {
2662 dict_field_t* field;
2663 dict_index_t* new_index;
2664 dict_index_t* clust_index;
2665 dict_table_t* table = index->table;
2666 ulint i;
2667 ibool* indexed;
2668
2669 ut_ad(table && index);
2670 ut_ad(!dict_index_is_clust(index));
2671 ut_ad(!dict_index_is_ibuf(index));
2672 ut_ad(mutex_own(&dict_sys->mutex));
2673
2674 /* The clustered index should be the first in the list of indexes */
2675 clust_index = UT_LIST_GET_FIRST(table->indexes);
2676
2677 ut_ad(clust_index);
2678 ut_ad(dict_index_is_clust(clust_index));
2679 ut_ad(!dict_index_is_ibuf(clust_index));
2680
2681 /* Create a new index */
2682 new_index = dict_mem_index_create(
2683 index->table, index->name, index->type,
2684 ulint(index->n_fields + 1 + clust_index->n_uniq));
2685
2686 /* Copy other relevant data from the old index
2687 struct to the new struct: it inherits the values */
2688
2689 new_index->n_user_defined_cols = index->n_fields;
2690
2691 new_index->id = index->id;
2692
2693 /* Copy fields from index to new_index */
2694 dict_index_copy(new_index, index, 0, index->n_fields);
2695
2696 /* Remember the table columns already contained in new_index */
2697 indexed = static_cast<ibool*>(
2698 ut_zalloc_nokey(table->n_cols * sizeof *indexed));
2699
2700 /* Mark the table columns already contained in new_index */
2701 for (i = 0; i < new_index->n_def; i++) {
2702
2703 field = dict_index_get_nth_field(new_index, i);
2704
2705 if (field->col->is_virtual()) {
2706 continue;
2707 }
2708
2709 /* If there is only a prefix of the column in the index
2710 field, do not mark the column as contained in the index */
2711
2712 if (field->prefix_len == 0) {
2713
2714 indexed[field->col->ind] = TRUE;
2715 }
2716 }
2717
2718 /* Add to new_index the columns necessary to determine the clustered
2719 index entry uniquely */
2720
2721 for (i = 0; i < clust_index->n_uniq; i++) {
2722
2723 field = dict_index_get_nth_field(clust_index, i);
2724
2725 if (!indexed[field->col->ind]) {
2726 dict_index_add_col(new_index, table, field->col,
2727 field->prefix_len);
2728 } else if (dict_index_is_spatial(index)) {
2729 /*For spatial index, we still need to add the
2730 field to index. */
2731 dict_index_add_col(new_index, table, field->col,
2732 field->prefix_len);
2733 }
2734 }
2735
2736 ut_free(indexed);
2737
2738 if (dict_index_is_unique(index)) {
2739 new_index->n_uniq = index->n_fields;
2740 } else {
2741 new_index->n_uniq = new_index->n_def;
2742 }
2743
2744 /* Set the n_fields value in new_index to the actual defined
2745 number of fields */
2746
2747 new_index->n_fields = new_index->n_def;
2748
2749 new_index->cached = TRUE;
2750
2751 return(new_index);
2752 }
2753
2754 /***********************************************************************
2755 Builds the internal dictionary cache representation for an FTS index.
2756 @return own: the internal representation of the FTS index */
2757 static
2758 dict_index_t*
dict_index_build_internal_fts(dict_index_t * index)2759 dict_index_build_internal_fts(
2760 /*==========================*/
2761 dict_index_t* index) /*!< in: user representation of an FTS index */
2762 {
2763 dict_index_t* new_index;
2764
2765 ut_ad(index->type == DICT_FTS);
2766 ut_ad(mutex_own(&dict_sys->mutex));
2767
2768 /* Create a new index */
2769 new_index = dict_mem_index_create(index->table, index->name,
2770 index->type, index->n_fields);
2771
2772 /* Copy other relevant data from the old index struct to the new
2773 struct: it inherits the values */
2774
2775 new_index->n_user_defined_cols = index->n_fields;
2776
2777 new_index->id = index->id;
2778
2779 /* Copy fields from index to new_index */
2780 dict_index_copy(new_index, index, 0, index->n_fields);
2781
2782 new_index->n_uniq = 0;
2783 new_index->cached = TRUE;
2784
2785 dict_table_t* table = index->table;
2786
2787 if (table->fts->cache == NULL) {
2788 table->fts->cache = fts_cache_create(table);
2789 }
2790
2791 rw_lock_x_lock(&table->fts->cache->init_lock);
2792 /* Notify the FTS cache about this index. */
2793 fts_cache_index_cache_create(table, new_index);
2794 rw_lock_x_unlock(&table->fts->cache->init_lock);
2795
2796 return(new_index);
2797 }
2798 /*====================== FOREIGN KEY PROCESSING ========================*/
2799
2800 /** Check whether the dict_table_t is a partition.
2801 A partitioned table on the SQL level is composed of InnoDB tables,
2802 where each InnoDB table is a [sub]partition including its secondary indexes
2803 which belongs to the partition.
2804 @param[in] table Table to check.
2805 @return true if the dict_table_t is a partition else false. */
2806 UNIV_INLINE
2807 bool
dict_table_is_partition(const dict_table_t * table)2808 dict_table_is_partition(
2809 const dict_table_t* table)
2810 {
2811 /* Check both P and p on all platforms in case it was moved to/from
2812 WIN. */
2813 return(strstr(table->name.m_name, "#p#")
2814 || strstr(table->name.m_name, "#P#"));
2815 }
2816
2817 /*********************************************************************//**
2818 Checks if a table is referenced by foreign keys.
2819 @return TRUE if table is referenced by a foreign key */
2820 ibool
dict_table_is_referenced_by_foreign_key(const dict_table_t * table)2821 dict_table_is_referenced_by_foreign_key(
2822 /*====================================*/
2823 const dict_table_t* table) /*!< in: InnoDB table */
2824 {
2825 return(!table->referenced_set.empty());
2826 }
2827
2828 /**********************************************************************//**
2829 Removes a foreign constraint struct from the dictionary cache. */
2830 void
dict_foreign_remove_from_cache(dict_foreign_t * foreign)2831 dict_foreign_remove_from_cache(
2832 /*===========================*/
2833 dict_foreign_t* foreign) /*!< in, own: foreign constraint */
2834 {
2835 ut_ad(mutex_own(&dict_sys->mutex));
2836 ut_a(foreign);
2837
2838 if (foreign->referenced_table != NULL) {
2839 foreign->referenced_table->referenced_set.erase(foreign);
2840 }
2841
2842 if (foreign->foreign_table != NULL) {
2843 foreign->foreign_table->foreign_set.erase(foreign);
2844 }
2845
2846 dict_foreign_free(foreign);
2847 }
2848
2849 /**********************************************************************//**
2850 Looks for the foreign constraint from the foreign and referenced lists
2851 of a table.
2852 @return foreign constraint */
2853 static
2854 dict_foreign_t*
dict_foreign_find(dict_table_t * table,dict_foreign_t * foreign)2855 dict_foreign_find(
2856 /*==============*/
2857 dict_table_t* table, /*!< in: table object */
2858 dict_foreign_t* foreign) /*!< in: foreign constraint */
2859 {
2860 ut_ad(mutex_own(&dict_sys->mutex));
2861
2862 ut_ad(dict_foreign_set_validate(table->foreign_set));
2863 ut_ad(dict_foreign_set_validate(table->referenced_set));
2864
2865 dict_foreign_set::iterator it = table->foreign_set.find(foreign);
2866
2867 if (it != table->foreign_set.end()) {
2868 return(*it);
2869 }
2870
2871 it = table->referenced_set.find(foreign);
2872
2873 if (it != table->referenced_set.end()) {
2874 return(*it);
2875 }
2876
2877 return(NULL);
2878 }
2879
2880 /*********************************************************************//**
2881 Tries to find an index whose first fields are the columns in the array,
2882 in the same order and is not marked for deletion and is not the same
2883 as types_idx.
2884 @return matching index, NULL if not found */
2885 dict_index_t*
dict_foreign_find_index(const dict_table_t * table,const char ** col_names,const char ** columns,ulint n_cols,const dict_index_t * types_idx,bool check_charsets,ulint check_null,fkerr_t * error,ulint * err_col_no,dict_index_t ** err_index)2886 dict_foreign_find_index(
2887 /*====================*/
2888 const dict_table_t* table, /*!< in: table */
2889 const char** col_names,
2890 /*!< in: column names, or NULL
2891 to use table->col_names */
2892 const char** columns,/*!< in: array of column names */
2893 ulint n_cols, /*!< in: number of columns */
2894 const dict_index_t* types_idx,
2895 /*!< in: NULL or an index
2896 whose types the column types
2897 must match */
2898 bool check_charsets,
2899 /*!< in: whether to check
2900 charsets. only has an effect
2901 if types_idx != NULL */
2902 ulint check_null,
2903 /*!< in: nonzero if none of
2904 the columns must be declared
2905 NOT NULL */
2906 fkerr_t* error, /*!< out: error code */
2907 ulint* err_col_no,
2908 /*!< out: column number where
2909 error happened */
2910 dict_index_t** err_index)
2911 /*!< out: index where error
2912 happened */
2913 {
2914 ut_ad(mutex_own(&dict_sys->mutex));
2915
2916 if (error) {
2917 *error = FK_INDEX_NOT_FOUND;
2918 }
2919
2920 for (dict_index_t* index = dict_table_get_first_index(table);
2921 index;
2922 index = dict_table_get_next_index(index)) {
2923 if (types_idx != index
2924 && !index->to_be_dropped
2925 && !dict_index_is_online_ddl(index)
2926 && dict_foreign_qualify_index(
2927 table, col_names, columns, n_cols,
2928 index, types_idx,
2929 check_charsets, check_null,
2930 error, err_col_no, err_index)) {
2931 if (error) {
2932 *error = FK_SUCCESS;
2933 }
2934
2935 return(index);
2936 }
2937 }
2938
2939 return(NULL);
2940 }
2941 /**********************************************************************//**
2942 Report an error in a foreign key definition. */
2943 static
2944 void
dict_foreign_error_report_low(FILE * file,const char * name)2945 dict_foreign_error_report_low(
2946 /*==========================*/
2947 FILE* file, /*!< in: output stream */
2948 const char* name) /*!< in: table name */
2949 {
2950 rewind(file);
2951 ut_print_timestamp(file);
2952 fprintf(file, " Error in foreign key constraint of table %s:\n",
2953 name);
2954 }
2955
2956 /**********************************************************************//**
2957 Report an error in a foreign key definition. */
2958 static
2959 void
dict_foreign_error_report(FILE * file,dict_foreign_t * fk,const char * msg)2960 dict_foreign_error_report(
2961 /*======================*/
2962 FILE* file, /*!< in: output stream */
2963 dict_foreign_t* fk, /*!< in: foreign key constraint */
2964 const char* msg) /*!< in: the error message */
2965 {
2966 std::string fk_str;
2967 mutex_enter(&dict_foreign_err_mutex);
2968 dict_foreign_error_report_low(file, fk->foreign_table_name);
2969 fputs(msg, file);
2970 fputs(" Constraint:\n", file);
2971 fk_str = dict_print_info_on_foreign_key_in_create_format(NULL, fk, TRUE);
2972 fputs(fk_str.c_str(), file);
2973 putc('\n', file);
2974 if (fk->foreign_index) {
2975 fprintf(file, "The index in the foreign key in table is"
2976 " %s\n%s\n", fk->foreign_index->name(),
2977 FOREIGN_KEY_CONSTRAINTS_MSG);
2978 }
2979 mutex_exit(&dict_foreign_err_mutex);
2980 }
2981
2982 /**********************************************************************//**
2983 Adds a foreign key constraint object to the dictionary cache. May free
2984 the object if there already is an object with the same identifier in.
2985 At least one of the foreign table and the referenced table must already
2986 be in the dictionary cache!
2987 @return DB_SUCCESS or error code */
2988 dberr_t
dict_foreign_add_to_cache(dict_foreign_t * foreign,const char ** col_names,bool check_charsets,dict_err_ignore_t ignore_err)2989 dict_foreign_add_to_cache(
2990 /*======================*/
2991 dict_foreign_t* foreign,
2992 /*!< in, own: foreign key constraint */
2993 const char** col_names,
2994 /*!< in: column names, or NULL to use
2995 foreign->foreign_table->col_names */
2996 bool check_charsets,
2997 /*!< in: whether to check charset
2998 compatibility */
2999 dict_err_ignore_t ignore_err)
3000 /*!< in: error to be ignored */
3001 {
3002 dict_table_t* for_table;
3003 dict_table_t* ref_table;
3004 dict_foreign_t* for_in_cache = NULL;
3005 dict_index_t* index;
3006 ibool added_to_referenced_list= FALSE;
3007 FILE* ef = dict_foreign_err_file;
3008
3009 DBUG_ENTER("dict_foreign_add_to_cache");
3010 DBUG_PRINT("dict_foreign_add_to_cache", ("id: %s", foreign->id));
3011
3012 ut_ad(mutex_own(&dict_sys->mutex));
3013
3014 for_table = dict_table_check_if_in_cache_low(
3015 foreign->foreign_table_name_lookup);
3016
3017 ref_table = dict_table_check_if_in_cache_low(
3018 foreign->referenced_table_name_lookup);
3019 ut_a(for_table || ref_table);
3020
3021 if (for_table) {
3022 for_in_cache = dict_foreign_find(for_table, foreign);
3023 }
3024
3025 if (!for_in_cache && ref_table) {
3026 for_in_cache = dict_foreign_find(ref_table, foreign);
3027 }
3028
3029 if (for_in_cache) {
3030 dict_foreign_free(foreign);
3031 } else {
3032 for_in_cache = foreign;
3033
3034 }
3035
3036 if (ref_table && !for_in_cache->referenced_table) {
3037 index = dict_foreign_find_index(
3038 ref_table, NULL,
3039 for_in_cache->referenced_col_names,
3040 for_in_cache->n_fields, for_in_cache->foreign_index,
3041 check_charsets, false);
3042
3043 if (index == NULL
3044 && !(ignore_err & DICT_ERR_IGNORE_FK_NOKEY)) {
3045 dict_foreign_error_report(
3046 ef, for_in_cache,
3047 "there is no index in referenced table"
3048 " which would contain\n"
3049 "the columns as the first columns,"
3050 " or the data types in the\n"
3051 "referenced table do not match"
3052 " the ones in table.");
3053
3054 if (for_in_cache == foreign) {
3055 dict_foreign_free(foreign);
3056 }
3057
3058 DBUG_RETURN(DB_CANNOT_ADD_CONSTRAINT);
3059 }
3060
3061 for_in_cache->referenced_table = ref_table;
3062 for_in_cache->referenced_index = index;
3063
3064 std::pair<dict_foreign_set::iterator, bool> ret
3065 = ref_table->referenced_set.insert(for_in_cache);
3066
3067 ut_a(ret.second); /* second is true if the insertion
3068 took place */
3069 added_to_referenced_list = TRUE;
3070 }
3071
3072 if (for_table && !for_in_cache->foreign_table) {
3073 index = dict_foreign_find_index(
3074 for_table, col_names,
3075 for_in_cache->foreign_col_names,
3076 for_in_cache->n_fields,
3077 for_in_cache->referenced_index, check_charsets,
3078 for_in_cache->type
3079 & (DICT_FOREIGN_ON_DELETE_SET_NULL
3080 | DICT_FOREIGN_ON_UPDATE_SET_NULL));
3081
3082 if (index == NULL
3083 && !(ignore_err & DICT_ERR_IGNORE_FK_NOKEY)) {
3084 dict_foreign_error_report(
3085 ef, for_in_cache,
3086 "there is no index in the table"
3087 " which would contain\n"
3088 "the columns as the first columns,"
3089 " or the data types in the\n"
3090 "table do not match"
3091 " the ones in the referenced table\n"
3092 "or one of the ON ... SET NULL columns"
3093 " is declared NOT NULL.");
3094
3095 if (for_in_cache == foreign) {
3096 if (added_to_referenced_list) {
3097 const dict_foreign_set::size_type
3098 n = ref_table->referenced_set
3099 .erase(for_in_cache);
3100
3101 ut_a(n == 1); /* the number of
3102 elements removed must
3103 be one */
3104 }
3105
3106 dict_foreign_free(foreign);
3107 }
3108
3109 DBUG_RETURN(DB_CANNOT_ADD_CONSTRAINT);
3110 }
3111
3112 for_in_cache->foreign_table = for_table;
3113 for_in_cache->foreign_index = index;
3114
3115 std::pair<dict_foreign_set::iterator, bool> ret
3116 = for_table->foreign_set.insert(for_in_cache);
3117
3118 ut_a(ret.second); /* second is true if the insertion
3119 took place */
3120 }
3121
3122 /* We need to move the table to the non-LRU end of the table LRU
3123 list. Otherwise it will be evicted from the cache. */
3124
3125 if (ref_table != NULL) {
3126 dict_table_prevent_eviction(ref_table);
3127 }
3128
3129 if (for_table != NULL) {
3130 dict_table_prevent_eviction(for_table);
3131 }
3132
3133 ut_ad(dict_lru_validate());
3134 DBUG_RETURN(DB_SUCCESS);
3135 }
3136
3137 /*********************************************************************//**
3138 Scans from pointer onwards. Stops if is at the start of a copy of
3139 'string' where characters are compared without case sensitivity, and
3140 only outside `` or "" quotes. Stops also at NUL.
3141 @return scanned up to this */
3142 static
3143 const char*
dict_scan_to(const char * ptr,const char * string)3144 dict_scan_to(
3145 /*=========*/
3146 const char* ptr, /*!< in: scan from */
3147 const char* string) /*!< in: look for this */
3148 {
3149 char quote = '\0';
3150 bool escape = false;
3151
3152 for (; *ptr; ptr++) {
3153 if (*ptr == quote) {
3154 /* Closing quote character: do not look for
3155 starting quote or the keyword. */
3156
3157 /* If the quote character is escaped by a
3158 backslash, ignore it. */
3159 if (escape) {
3160 escape = false;
3161 } else {
3162 quote = '\0';
3163 }
3164 } else if (quote) {
3165 /* Within quotes: do nothing. */
3166 if (escape) {
3167 escape = false;
3168 } else if (*ptr == '\\') {
3169 escape = true;
3170 }
3171 } else if (*ptr == '`' || *ptr == '"' || *ptr == '\'') {
3172 /* Starting quote: remember the quote character. */
3173 quote = *ptr;
3174 } else {
3175 /* Outside quotes: look for the keyword. */
3176 ulint i;
3177 for (i = 0; string[i]; i++) {
3178 if (toupper((int)(unsigned char)(ptr[i]))
3179 != toupper((int)(unsigned char)
3180 (string[i]))) {
3181 goto nomatch;
3182 }
3183 }
3184 break;
3185 nomatch:
3186 ;
3187 }
3188 }
3189
3190 return(ptr);
3191 }
3192
3193 /*********************************************************************//**
3194 Accepts a specified string. Comparisons are case-insensitive.
3195 @return if string was accepted, the pointer is moved after that, else
3196 ptr is returned */
3197 static
3198 const char*
dict_accept(CHARSET_INFO * cs,const char * ptr,const char * string,ibool * success)3199 dict_accept(
3200 /*========*/
3201 CHARSET_INFO* cs, /*!< in: the character set of ptr */
3202 const char* ptr, /*!< in: scan from this */
3203 const char* string, /*!< in: accept only this string as the next
3204 non-whitespace string */
3205 ibool* success)/*!< out: TRUE if accepted */
3206 {
3207 const char* old_ptr = ptr;
3208 const char* old_ptr2;
3209
3210 *success = FALSE;
3211
3212 while (my_isspace(cs, *ptr)) {
3213 ptr++;
3214 }
3215
3216 old_ptr2 = ptr;
3217
3218 ptr = dict_scan_to(ptr, string);
3219
3220 if (*ptr == '\0' || old_ptr2 != ptr) {
3221 return(old_ptr);
3222 }
3223
3224 *success = TRUE;
3225
3226 return(ptr + ut_strlen(string));
3227 }
3228
3229 /*********************************************************************//**
3230 Scans an id. For the lexical definition of an 'id', see the code below.
3231 Strips backquotes or double quotes from around the id.
3232 @return scanned to */
3233 static
3234 const char*
dict_scan_id(CHARSET_INFO * cs,const char * ptr,mem_heap_t * heap,const char ** id,ibool table_id,ibool accept_also_dot)3235 dict_scan_id(
3236 /*=========*/
3237 CHARSET_INFO* cs, /*!< in: the character set of ptr */
3238 const char* ptr, /*!< in: scanned to */
3239 mem_heap_t* heap, /*!< in: heap where to allocate the id
3240 (NULL=id will not be allocated, but it
3241 will point to string near ptr) */
3242 const char** id, /*!< out,own: the id; NULL if no id was
3243 scannable */
3244 ibool table_id,/*!< in: TRUE=convert the allocated id
3245 as a table name; FALSE=convert to UTF-8 */
3246 ibool accept_also_dot)
3247 /*!< in: TRUE if also a dot can appear in a
3248 non-quoted id; in a quoted id it can appear
3249 always */
3250 {
3251 char quote = '\0';
3252 ulint len = 0;
3253 const char* s;
3254 char* str;
3255 char* dst;
3256
3257 *id = NULL;
3258
3259 while (my_isspace(cs, *ptr)) {
3260 ptr++;
3261 }
3262
3263 if (*ptr == '\0') {
3264
3265 return(ptr);
3266 }
3267
3268 if (*ptr == '`' || *ptr == '"') {
3269 quote = *ptr++;
3270 }
3271
3272 s = ptr;
3273
3274 if (quote) {
3275 for (;;) {
3276 if (!*ptr) {
3277 /* Syntax error */
3278 return(ptr);
3279 }
3280 if (*ptr == quote) {
3281 ptr++;
3282 if (*ptr != quote) {
3283 break;
3284 }
3285 }
3286 ptr++;
3287 len++;
3288 }
3289 } else {
3290 while (!my_isspace(cs, *ptr) && *ptr != '(' && *ptr != ')'
3291 && (accept_also_dot || *ptr != '.')
3292 && *ptr != ',' && *ptr != '\0') {
3293
3294 ptr++;
3295 }
3296
3297 len = ulint(ptr - s);
3298 }
3299
3300 if (heap == NULL) {
3301 /* no heap given: id will point to source string */
3302 *id = s;
3303 return(ptr);
3304 }
3305
3306 if (quote) {
3307 char* d;
3308
3309 str = d = static_cast<char*>(
3310 mem_heap_alloc(heap, len + 1));
3311
3312 while (len--) {
3313 if ((*d++ = *s++) == quote) {
3314 s++;
3315 }
3316 }
3317 *d++ = 0;
3318 len = ulint(d - str);
3319 ut_ad(*s == quote);
3320 ut_ad(s + 1 == ptr);
3321 } else {
3322 str = mem_heap_strdupl(heap, s, len);
3323 }
3324
3325 if (!table_id) {
3326 convert_id:
3327 /* Convert the identifier from connection character set
3328 to UTF-8. */
3329 len = 3 * len + 1;
3330 *id = dst = static_cast<char*>(mem_heap_alloc(heap, len));
3331
3332 innobase_convert_from_id(cs, dst, str, len);
3333 } else if (!strncmp(str, srv_mysql50_table_name_prefix,
3334 sizeof(srv_mysql50_table_name_prefix) - 1)) {
3335 /* This is a pre-5.1 table name
3336 containing chars other than [A-Za-z0-9].
3337 Discard the prefix and use raw UTF-8 encoding. */
3338 str += sizeof(srv_mysql50_table_name_prefix) - 1;
3339 len -= sizeof(srv_mysql50_table_name_prefix) - 1;
3340 goto convert_id;
3341 } else {
3342 /* Encode using filename-safe characters. */
3343 len = 5 * len + 1;
3344 *id = dst = static_cast<char*>(mem_heap_alloc(heap, len));
3345
3346 innobase_convert_from_table_id(cs, dst, str, len);
3347 }
3348
3349 return(ptr);
3350 }
3351
3352 /*********************************************************************//**
3353 Tries to scan a column name.
3354 @return scanned to */
3355 static
3356 const char*
dict_scan_col(CHARSET_INFO * cs,const char * ptr,ibool * success,dict_table_t * table,const dict_col_t ** column,mem_heap_t * heap,const char ** name)3357 dict_scan_col(
3358 /*==========*/
3359 CHARSET_INFO* cs, /*!< in: the character set of ptr */
3360 const char* ptr, /*!< in: scanned to */
3361 ibool* success,/*!< out: TRUE if success */
3362 dict_table_t* table, /*!< in: table in which the column is */
3363 const dict_col_t** column, /*!< out: pointer to column if success */
3364 mem_heap_t* heap, /*!< in: heap where to allocate */
3365 const char** name) /*!< out,own: the column name;
3366 NULL if no name was scannable */
3367 {
3368 ulint i;
3369
3370 *success = FALSE;
3371
3372 ptr = dict_scan_id(cs, ptr, heap, name, FALSE, TRUE);
3373
3374 if (*name == NULL) {
3375
3376 return(ptr); /* Syntax error */
3377 }
3378
3379 if (table == NULL) {
3380 *success = TRUE;
3381 *column = NULL;
3382 } else {
3383 for (i = 0; i < dict_table_get_n_cols(table); i++) {
3384
3385 const char* col_name = dict_table_get_col_name(
3386 table, i);
3387
3388 if (0 == innobase_strcasecmp(col_name, *name)) {
3389 /* Found */
3390
3391 *success = TRUE;
3392 *column = dict_table_get_nth_col(table, i);
3393 strcpy((char*) *name, col_name);
3394
3395 break;
3396 }
3397 }
3398
3399 for (i = 0; i < dict_table_get_n_v_cols(table); i++) {
3400
3401 const char* col_name = dict_table_get_v_col_name(
3402 table, i);
3403
3404 if (0 == innobase_strcasecmp(col_name, *name)) {
3405 /* Found */
3406 dict_v_col_t * vcol;
3407 *success = TRUE;
3408 vcol = dict_table_get_nth_v_col(table, i);
3409 *column = &vcol->m_col;
3410 strcpy((char*) *name, col_name);
3411
3412 break;
3413 }
3414 }
3415 }
3416
3417 return(ptr);
3418 }
3419
3420 /*********************************************************************//**
3421 Open a table from its database and table name, this is currently used by
3422 foreign constraint parser to get the referenced table.
3423 @return complete table name with database and table name, allocated from
3424 heap memory passed in */
3425 char*
dict_get_referenced_table(const char * name,const char * database_name,ulint database_name_len,const char * table_name,ulint table_name_len,dict_table_t ** table,mem_heap_t * heap)3426 dict_get_referenced_table(
3427 /*======================*/
3428 const char* name, /*!< in: foreign key table name */
3429 const char* database_name, /*!< in: table db name */
3430 ulint database_name_len, /*!< in: db name length */
3431 const char* table_name, /*!< in: table name */
3432 ulint table_name_len, /*!< in: table name length */
3433 dict_table_t** table, /*!< out: table object or NULL */
3434 mem_heap_t* heap) /*!< in/out: heap memory */
3435 {
3436 char* ref;
3437 const char* db_name;
3438
3439 if (!database_name) {
3440 /* Use the database name of the foreign key table */
3441
3442 db_name = name;
3443 database_name_len = dict_get_db_name_len(name);
3444 } else {
3445 db_name = database_name;
3446 }
3447
3448 /* Copy database_name, '/', table_name, '\0' */
3449 ref = static_cast<char*>(
3450 mem_heap_alloc(heap, database_name_len + table_name_len + 2));
3451
3452 memcpy(ref, db_name, database_name_len);
3453 ref[database_name_len] = '/';
3454 memcpy(ref + database_name_len + 1, table_name, table_name_len + 1);
3455
3456 /* Values; 0 = Store and compare as given; case sensitive
3457 1 = Store and compare in lower; case insensitive
3458 2 = Store as given, compare in lower; case semi-sensitive */
3459 if (innobase_get_lower_case_table_names() == 2) {
3460 innobase_casedn_str(ref);
3461 *table = dict_table_get_low(ref);
3462 memcpy(ref, db_name, database_name_len);
3463 ref[database_name_len] = '/';
3464 memcpy(ref + database_name_len + 1, table_name, table_name_len + 1);
3465
3466 } else {
3467 #ifndef _WIN32
3468 if (innobase_get_lower_case_table_names() == 1) {
3469 innobase_casedn_str(ref);
3470 }
3471 #else
3472 innobase_casedn_str(ref);
3473 #endif /* !_WIN32 */
3474 *table = dict_table_get_low(ref);
3475 }
3476
3477 return(ref);
3478 }
3479 /*********************************************************************//**
3480 Scans a table name from an SQL string.
3481 @return scanned to */
3482 static
3483 const char*
dict_scan_table_name(CHARSET_INFO * cs,const char * ptr,dict_table_t ** table,const char * name,ibool * success,mem_heap_t * heap,const char ** ref_name)3484 dict_scan_table_name(
3485 /*=================*/
3486 CHARSET_INFO* cs, /*!< in: the character set of ptr */
3487 const char* ptr, /*!< in: scanned to */
3488 dict_table_t** table, /*!< out: table object or NULL */
3489 const char* name, /*!< in: foreign key table name */
3490 ibool* success,/*!< out: TRUE if ok name found */
3491 mem_heap_t* heap, /*!< in: heap where to allocate the id */
3492 const char** ref_name)/*!< out,own: the table name;
3493 NULL if no name was scannable */
3494 {
3495 const char* database_name = NULL;
3496 ulint database_name_len = 0;
3497 const char* table_name = NULL;
3498 const char* scan_name;
3499
3500 *success = FALSE;
3501 *table = NULL;
3502
3503 ptr = dict_scan_id(cs, ptr, heap, &scan_name, TRUE, FALSE);
3504
3505 if (scan_name == NULL) {
3506
3507 return(ptr); /* Syntax error */
3508 }
3509
3510 if (*ptr == '.') {
3511 /* We scanned the database name; scan also the table name */
3512
3513 ptr++;
3514
3515 database_name = scan_name;
3516 database_name_len = strlen(database_name);
3517
3518 ptr = dict_scan_id(cs, ptr, heap, &table_name, TRUE, FALSE);
3519
3520 if (table_name == NULL) {
3521
3522 return(ptr); /* Syntax error */
3523 }
3524 } else {
3525 /* To be able to read table dumps made with InnoDB-4.0.17 or
3526 earlier, we must allow the dot separator between the database
3527 name and the table name also to appear within a quoted
3528 identifier! InnoDB used to print a constraint as:
3529 ... REFERENCES `databasename.tablename` ...
3530 starting from 4.0.18 it is
3531 ... REFERENCES `databasename`.`tablename` ... */
3532 const char* s;
3533
3534 for (s = scan_name; *s; s++) {
3535 if (*s == '.') {
3536 database_name = scan_name;
3537 database_name_len = ulint(s - scan_name);
3538 scan_name = ++s;
3539 break;/* to do: multiple dots? */
3540 }
3541 }
3542
3543 table_name = scan_name;
3544 }
3545
3546 *ref_name = dict_get_referenced_table(
3547 name, database_name, database_name_len,
3548 table_name, strlen(table_name), table, heap);
3549
3550 *success = TRUE;
3551 return(ptr);
3552 }
3553
3554 /*********************************************************************//**
3555 Skips one id. The id is allowed to contain also '.'.
3556 @return scanned to */
3557 static
3558 const char*
dict_skip_word(CHARSET_INFO * cs,const char * ptr,ibool * success)3559 dict_skip_word(
3560 /*===========*/
3561 CHARSET_INFO* cs, /*!< in: the character set of ptr */
3562 const char* ptr, /*!< in: scanned to */
3563 ibool* success)/*!< out: TRUE if success, FALSE if just spaces
3564 left in string or a syntax error */
3565 {
3566 const char* start;
3567
3568 *success = FALSE;
3569
3570 ptr = dict_scan_id(cs, ptr, NULL, &start, FALSE, TRUE);
3571
3572 if (start) {
3573 *success = TRUE;
3574 }
3575
3576 return(ptr);
3577 }
3578
3579 /*********************************************************************//**
3580 Removes MySQL comments from an SQL string. A comment is either
3581 (a) '#' to the end of the line,
3582 (b) '--[space]' to the end of the line, or
3583 (c) '[slash][asterisk]' till the next '[asterisk][slash]' (like the familiar
3584 C comment syntax).
3585 @return own: SQL string stripped from comments; the caller must free
3586 this with ut_free()! */
3587 static
3588 char*
dict_strip_comments(const char * sql_string,size_t sql_length)3589 dict_strip_comments(
3590 /*================*/
3591 const char* sql_string, /*!< in: SQL string */
3592 size_t sql_length) /*!< in: length of sql_string */
3593 {
3594 char* str;
3595 const char* sptr;
3596 const char* eptr = sql_string + sql_length;
3597 char* ptr;
3598 /* unclosed quote character (0 if none) */
3599 char quote = 0;
3600 bool escape = false;
3601
3602 DBUG_ENTER("dict_strip_comments");
3603
3604 DBUG_PRINT("dict_strip_comments", ("%s", sql_string));
3605
3606 str = static_cast<char*>(ut_malloc_nokey(sql_length + 1));
3607
3608 sptr = sql_string;
3609 ptr = str;
3610
3611 for (;;) {
3612 scan_more:
3613 if (sptr >= eptr || *sptr == '\0') {
3614 end_of_string:
3615 *ptr = '\0';
3616
3617 ut_a(ptr <= str + sql_length);
3618
3619 DBUG_PRINT("dict_strip_comments", ("%s", str));
3620 DBUG_RETURN(str);
3621 }
3622
3623 if (*sptr == quote) {
3624 /* Closing quote character: do not look for
3625 starting quote or comments. */
3626
3627 /* If the quote character is escaped by a
3628 backslash, ignore it. */
3629 if (escape) {
3630 escape = false;
3631 } else {
3632 quote = 0;
3633 }
3634 } else if (quote) {
3635 /* Within quotes: do not look for
3636 starting quotes or comments. */
3637 if (escape) {
3638 escape = false;
3639 } else if (*sptr == '\\') {
3640 escape = true;
3641 }
3642 } else if (*sptr == '"' || *sptr == '`' || *sptr == '\'') {
3643 /* Starting quote: remember the quote character. */
3644 quote = *sptr;
3645 } else if (*sptr == '#'
3646 || (sptr[0] == '-' && sptr[1] == '-'
3647 && sptr[2] == ' ')) {
3648 for (;;) {
3649 if (++sptr >= eptr) {
3650 goto end_of_string;
3651 }
3652
3653 /* In Unix a newline is 0x0A while in Windows
3654 it is 0x0D followed by 0x0A */
3655
3656 switch (*sptr) {
3657 case (char) 0X0A:
3658 case (char) 0x0D:
3659 case '\0':
3660 goto scan_more;
3661 }
3662 }
3663 } else if (!quote && *sptr == '/' && *(sptr + 1) == '*') {
3664 sptr += 2;
3665 for (;;) {
3666 if (sptr >= eptr) {
3667 goto end_of_string;
3668 }
3669
3670 switch (*sptr) {
3671 case '\0':
3672 goto scan_more;
3673 case '*':
3674 if (sptr[1] == '/') {
3675 sptr += 2;
3676 goto scan_more;
3677 }
3678 }
3679
3680 sptr++;
3681 }
3682 }
3683
3684 *ptr = *sptr;
3685
3686 ptr++;
3687 sptr++;
3688 }
3689 }
3690
3691 /*********************************************************************//**
3692 Finds the highest [number] for foreign key constraints of the table. Looks
3693 only at the >= 4.0.18-format id's, which are of the form
3694 databasename/tablename_ibfk_[number].
3695 @return highest number, 0 if table has no new format foreign key constraints */
3696 ulint
dict_table_get_highest_foreign_id(dict_table_t * table)3697 dict_table_get_highest_foreign_id(
3698 /*==============================*/
3699 dict_table_t* table) /*!< in: table in the dictionary memory cache */
3700 {
3701 dict_foreign_t* foreign;
3702 char* endp;
3703 ulint biggest_id = 0;
3704 ulint id;
3705 ulint len;
3706
3707 DBUG_ENTER("dict_table_get_highest_foreign_id");
3708
3709 ut_a(table);
3710
3711 len = ut_strlen(table->name.m_name);
3712
3713 for (dict_foreign_set::iterator it = table->foreign_set.begin();
3714 it != table->foreign_set.end();
3715 ++it) {
3716 char fkid[MAX_TABLE_NAME_LEN+20];
3717 foreign = *it;
3718
3719 strcpy(fkid, foreign->id);
3720 /* Convert foreign key identifier on dictionary memory
3721 cache to filename charset. */
3722 innobase_convert_to_filename_charset(
3723 strchr(fkid, '/') + 1,
3724 strchr(foreign->id, '/') + 1,
3725 MAX_TABLE_NAME_LEN);
3726
3727 if (ut_strlen(fkid) > ((sizeof dict_ibfk) - 1) + len
3728 && 0 == ut_memcmp(fkid, table->name.m_name, len)
3729 && 0 == ut_memcmp(fkid + len,
3730 dict_ibfk, (sizeof dict_ibfk) - 1)
3731 && fkid[len + ((sizeof dict_ibfk) - 1)] != '0') {
3732 /* It is of the >= 4.0.18 format */
3733
3734 id = strtoul(fkid + len
3735 + ((sizeof dict_ibfk) - 1),
3736 &endp, 10);
3737 if (*endp == '\0') {
3738 ut_a(id != biggest_id);
3739
3740 if (id > biggest_id) {
3741 biggest_id = id;
3742 }
3743 }
3744 }
3745 }
3746
3747 DBUG_PRINT("dict_table_get_highest_foreign_id",
3748 ("id: " ULINTPF, biggest_id));
3749
3750 DBUG_RETURN(biggest_id);
3751 }
3752
3753 /*********************************************************************//**
3754 Reports a simple foreign key create clause syntax error. */
3755 static
3756 void
dict_foreign_report_syntax_err(const char * fmt,const char * oper,const char * name,const char * start_of_latest_foreign,const char * ptr)3757 dict_foreign_report_syntax_err(
3758 /*===========================*/
3759 const char* fmt, /*!< in: syntax err msg */
3760 const char* oper, /*!< in: operation */
3761 const char* name, /*!< in: table name */
3762 const char* start_of_latest_foreign,
3763 /*!< in: start of the foreign key clause
3764 in the SQL string */
3765 const char* ptr) /*!< in: place of the syntax error */
3766 {
3767 ut_ad(!srv_read_only_mode);
3768
3769 FILE* ef = dict_foreign_err_file;
3770
3771 mutex_enter(&dict_foreign_err_mutex);
3772 dict_foreign_error_report_low(ef, name);
3773 fprintf(ef, fmt, oper, name, start_of_latest_foreign, ptr);
3774 mutex_exit(&dict_foreign_err_mutex);
3775 }
3776
3777 /*********************************************************************//**
3778 Push warning message to SQL-layer based on foreign key constraint
3779 index match error. */
3780 static
3781 void
dict_foreign_push_index_error(trx_t * trx,const char * operation,const char * create_name,const char * latest_foreign,const char ** columns,fkerr_t index_error,ulint err_col,dict_index_t * err_index,dict_table_t * table,FILE * ef)3782 dict_foreign_push_index_error(
3783 /*==========================*/
3784 trx_t* trx, /*!< in: trx */
3785 const char* operation, /*!< in: operation create or alter
3786 */
3787 const char* create_name, /*!< in: table name in create or
3788 alter table */
3789 const char* latest_foreign, /*!< in: start of latest foreign key
3790 constraint name */
3791 const char** columns, /*!< in: foreign key columns */
3792 fkerr_t index_error, /*!< in: error code */
3793 ulint err_col, /*!< in: column where error happened
3794 */
3795 dict_index_t* err_index, /*!< in: index where error happened
3796 */
3797 dict_table_t* table, /*!< in: table */
3798 FILE* ef) /*!< in: output stream */
3799 {
3800 switch (index_error) {
3801 case FK_SUCCESS:
3802 break;
3803 case FK_INDEX_NOT_FOUND:
3804 fprintf(ef,
3805 "%s table %s with foreign key constraint"
3806 " failed. There is no index in the referenced"
3807 " table where the referenced columns appear"
3808 " as the first columns near '%s'.\n",
3809 operation, create_name, latest_foreign);
3810 ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
3811 "%s table %s with foreign key constraint"
3812 " failed. There is no index in the referenced"
3813 " table where the referenced columns appear"
3814 " as the first columns near '%s'.",
3815 operation, create_name, latest_foreign);
3816 return;
3817 case FK_IS_PREFIX_INDEX:
3818 fprintf(ef,
3819 "%s table %s with foreign key constraint"
3820 " failed. There is only prefix index in the referenced"
3821 " table where the referenced columns appear"
3822 " as the first columns near '%s'.\n",
3823 operation, create_name, latest_foreign);
3824 ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
3825 "%s table %s with foreign key constraint"
3826 " failed. There is only prefix index in the referenced"
3827 " table where the referenced columns appear"
3828 " as the first columns near '%s'.",
3829 operation, create_name, latest_foreign);
3830 return;
3831 case FK_COL_NOT_NULL:
3832 fprintf(ef,
3833 "%s table %s with foreign key constraint"
3834 " failed. You have defined a SET NULL condition but "
3835 "column '%s' on index is defined as NOT NULL near '%s'.\n",
3836 operation, create_name, columns[err_col], latest_foreign);
3837 ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
3838 "%s table %s with foreign key constraint"
3839 " failed. You have defined a SET NULL condition but "
3840 "column '%s' on index is defined as NOT NULL near '%s'.",
3841 operation, create_name, columns[err_col], latest_foreign);
3842 return;
3843 case FK_COLS_NOT_EQUAL:
3844 dict_field_t* field;
3845 const char* col_name;
3846 field = dict_index_get_nth_field(err_index, err_col);
3847
3848 col_name = field->col->is_virtual()
3849 ? "(null)"
3850 : dict_table_get_col_name(
3851 table, dict_col_get_no(field->col));
3852 fprintf(ef,
3853 "%s table %s with foreign key constraint"
3854 " failed. Field type or character set for column '%s' "
3855 "does not mach referenced column '%s' near '%s'.\n",
3856 operation, create_name, columns[err_col], col_name, latest_foreign);
3857 ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
3858 "%s table %s with foreign key constraint"
3859 " failed. Field type or character set for column '%s' "
3860 "does not mach referenced column '%s' near '%s'.",
3861 operation, create_name, columns[err_col], col_name, latest_foreign);
3862 return;
3863 }
3864 DBUG_ASSERT(!"unknown error");
3865 }
3866
3867 /*********************************************************************//**
3868 Scans a table create SQL string and adds to the data dictionary the foreign key
3869 constraints declared in the string. This function should be called after the
3870 indexes for a table have been created. Each foreign key constraint must be
3871 accompanied with indexes in bot participating tables. The indexes are allowed
3872 to contain more fields than mentioned in the constraint.
3873 @return error code or DB_SUCCESS */
3874 static
3875 dberr_t
dict_create_foreign_constraints_low(trx_t * trx,mem_heap_t * heap,CHARSET_INFO * cs,const char * sql_string,const char * name,ibool reject_fks)3876 dict_create_foreign_constraints_low(
3877 trx_t* trx,
3878 mem_heap_t* heap,
3879 CHARSET_INFO* cs,
3880 const char* sql_string,
3881 const char* name,
3882 ibool reject_fks)
3883 {
3884 dict_table_t* table = NULL;
3885 dict_table_t* referenced_table = NULL;
3886 dict_table_t* table_to_alter = NULL;
3887 dict_table_t* table_to_create = NULL;
3888 ulint highest_id_so_far = 0;
3889 ulint number = 1;
3890 dict_index_t* index = NULL;
3891 dict_foreign_t* foreign = NULL;
3892 const char* ptr = sql_string;
3893 const char* start_of_latest_foreign = sql_string;
3894 const char* start_of_latest_set = NULL;
3895 FILE* ef = dict_foreign_err_file;
3896 fkerr_t index_error = FK_SUCCESS;
3897 dict_index_t* err_index = NULL;
3898 ulint err_col;
3899 const char* constraint_name;
3900 ibool success;
3901 dberr_t error;
3902 const char* ptr1;
3903 const char* ptr2;
3904 ulint i;
3905 ulint j;
3906 ibool is_on_delete;
3907 ulint n_on_deletes;
3908 ulint n_on_updates;
3909 const dict_col_t*columns[500];
3910 const char* column_names[500];
3911 const char* ref_column_names[500];
3912 const char* referenced_table_name;
3913 dict_foreign_set local_fk_set;
3914 dict_foreign_set_free local_fk_set_free(local_fk_set);
3915 const char* create_table_name;
3916 const char* orig;
3917 char create_name[MAX_TABLE_NAME_LEN + 1];
3918
3919 ut_ad(!srv_read_only_mode);
3920 ut_ad(mutex_own(&dict_sys->mutex));
3921
3922 table = dict_table_get_low(name);
3923 /* First check if we are actually doing an ALTER TABLE, and in that
3924 case look for the table being altered */
3925 orig = ptr;
3926 ptr = dict_accept(cs, ptr, "ALTER", &success);
3927
3928 const char* const operation = success ? "Alter " : "Create ";
3929
3930 if (!success) {
3931 orig = ptr;
3932 ptr = dict_scan_to(ptr, "CREATE");
3933 ptr = dict_scan_to(ptr, "TABLE");
3934 ptr = dict_accept(cs, ptr, "TABLE", &success);
3935 create_table_name = NULL;
3936
3937 if (success) {
3938 ptr = dict_scan_table_name(cs, ptr, &table_to_create, name,
3939 &success, heap, &create_table_name);
3940 }
3941
3942 ptr = orig;
3943 const char* n = create_table_name ? create_table_name : name;
3944 char *bufend = innobase_convert_name(create_name, MAX_TABLE_NAME_LEN,
3945 n, strlen(n), trx->mysql_thd);
3946 create_name[bufend-create_name] = '\0';
3947 } else {
3948 strncpy(create_name, name, sizeof create_name);
3949 create_name[(sizeof create_name) - 1] = '\0';
3950 }
3951
3952 if (table == NULL) {
3953 mutex_enter(&dict_foreign_err_mutex);
3954 dict_foreign_error_report_low(ef, create_name);
3955 fprintf(ef, "%s table %s with foreign key constraint"
3956 " failed. Table %s not found from data dictionary."
3957 " Error close to %s.\n",
3958 operation, create_name, create_name, start_of_latest_foreign);
3959 mutex_exit(&dict_foreign_err_mutex);
3960 ib_push_warning(trx, DB_ERROR,
3961 "%s table %s with foreign key constraint"
3962 " failed. Table %s not found from data dictionary."
3963 " Error close to %s.",
3964 operation, create_name, create_name, start_of_latest_foreign);
3965
3966 return(DB_ERROR);
3967 }
3968
3969 /* If not alter table jump to loop */
3970 if (!success) {
3971
3972 goto loop;
3973 }
3974
3975 orig = ptr;
3976 for (;;) {
3977 ptr = dict_accept(cs, ptr, "TABLE", &success);
3978 if (success) {
3979 break;
3980 }
3981 ptr = dict_accept(cs, ptr, "ONLINE", &success);
3982 if (success) {
3983 continue;
3984 }
3985 ptr = dict_accept(cs, ptr, "IGNORE", &success);
3986 if (!success) {
3987 goto loop;
3988 }
3989 }
3990
3991 /* We are doing an ALTER TABLE: scan the table name we are altering */
3992
3993 orig = ptr;
3994 ptr = dict_scan_table_name(cs, ptr, &table_to_alter, name,
3995 &success, heap, &referenced_table_name);
3996
3997 {
3998 const char* n = table_to_alter
3999 ? table_to_alter->name.m_name : referenced_table_name;
4000 char* bufend = innobase_convert_name(
4001 create_name, MAX_TABLE_NAME_LEN, n, strlen(n),
4002 trx->mysql_thd);
4003 create_name[bufend-create_name]='\0';
4004 }
4005
4006 if (!success) {
4007 ib::error() << "Could not find the table " << create_name << " being" << operation << " near to "
4008 << orig;
4009
4010 ib_push_warning(trx, DB_ERROR,
4011 "%s table %s with foreign key constraint"
4012 " failed. Table %s not found from data dictionary."
4013 " Error close to %s.",
4014 operation, create_name, create_name, orig);
4015
4016 return(DB_ERROR);
4017 }
4018
4019 /* Starting from 4.0.18 and 4.1.2, we generate foreign key id's in the
4020 format databasename/tablename_ibfk_[number], where [number] is local
4021 to the table; look for the highest [number] for table_to_alter, so
4022 that we can assign to new constraints higher numbers. */
4023
4024 /* If we are altering a temporary table, the table name after ALTER
4025 TABLE does not correspond to the internal table name, and
4026 table_to_alter is NULL. TODO: should we fix this somehow? */
4027
4028 if (table_to_alter == NULL) {
4029 highest_id_so_far = 0;
4030 } else {
4031 highest_id_so_far = dict_table_get_highest_foreign_id(
4032 table_to_alter);
4033 }
4034
4035 number = highest_id_so_far + 1;
4036 /* Scan for foreign key declarations in a loop */
4037 loop:
4038 /* Scan either to "CONSTRAINT" or "FOREIGN", whichever is closer */
4039
4040 ptr1 = dict_scan_to(ptr, "CONSTRAINT");
4041 ptr2 = dict_scan_to(ptr, "FOREIGN");
4042
4043 constraint_name = NULL;
4044
4045 if (ptr1 < ptr2) {
4046 /* The user may have specified a constraint name. Pick it so
4047 that we can store 'databasename/constraintname' as the id of
4048 of the constraint to system tables. */
4049 ptr = ptr1;
4050
4051 orig = ptr;
4052 ptr = dict_accept(cs, ptr, "CONSTRAINT", &success);
4053
4054 ut_a(success);
4055
4056 if (!my_isspace(cs, *ptr) && *ptr != '"' && *ptr != '`') {
4057 goto loop;
4058 }
4059
4060 while (my_isspace(cs, *ptr)) {
4061 ptr++;
4062 }
4063
4064 /* read constraint name unless got "CONSTRAINT FOREIGN" */
4065 if (ptr != ptr2) {
4066 ptr = dict_scan_id(cs, ptr, heap,
4067 &constraint_name, FALSE, FALSE);
4068 }
4069 } else {
4070 ptr = ptr2;
4071 }
4072
4073 if (*ptr == '\0') {
4074 /* The proper way to reject foreign keys for temporary
4075 tables would be to split the lexing and syntactical
4076 analysis of foreign key clauses from the actual adding
4077 of them, so that ha_innodb.cc could first parse the SQL
4078 command, determine if there are any foreign keys, and
4079 if so, immediately reject the command if the table is a
4080 temporary one. For now, this kludge will work. */
4081 if (reject_fks && !local_fk_set.empty()) {
4082 mutex_enter(&dict_foreign_err_mutex);
4083 dict_foreign_error_report_low(ef, create_name);
4084 fprintf(ef, "%s table %s with foreign key constraint"
4085 " failed. Temporary tables can't have foreign key constraints."
4086 " Error close to %s.\n",
4087 operation, create_name, start_of_latest_foreign);
4088 mutex_exit(&dict_foreign_err_mutex);
4089
4090 ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
4091 "%s table %s with foreign key constraint"
4092 " failed. Temporary tables can't have foreign key constraints."
4093 " Error close to %s.",
4094 operation, create_name, start_of_latest_foreign);
4095
4096 return(DB_CANNOT_ADD_CONSTRAINT);
4097 }
4098
4099 if (dict_foreigns_has_s_base_col(local_fk_set, table)) {
4100 return(DB_NO_FK_ON_S_BASE_COL);
4101 }
4102
4103 /**********************************************************/
4104 /* The following call adds the foreign key constraints
4105 to the data dictionary system tables on disk */
4106 trx->op_info = "adding foreign keys";
4107
4108 trx_start_if_not_started_xa(trx, true);
4109
4110 trx_set_dict_operation(trx, TRX_DICT_OP_TABLE);
4111
4112 error = dict_create_add_foreigns_to_dictionary(
4113 local_fk_set, table, trx);
4114
4115 if (error == DB_SUCCESS) {
4116
4117 table->foreign_set.insert(local_fk_set.begin(),
4118 local_fk_set.end());
4119 std::for_each(local_fk_set.begin(),
4120 local_fk_set.end(),
4121 dict_foreign_add_to_referenced_table());
4122 local_fk_set.clear();
4123
4124 dict_mem_table_fill_foreign_vcol_set(table);
4125 }
4126 return(error);
4127 }
4128
4129 start_of_latest_foreign = ptr;
4130
4131 orig = ptr;
4132 ptr = dict_accept(cs, ptr, "FOREIGN", &success);
4133
4134 if (!success) {
4135 goto loop;
4136 }
4137
4138 if (!my_isspace(cs, *ptr)) {
4139 goto loop;
4140 }
4141
4142 orig = ptr;
4143 ptr = dict_accept(cs, ptr, "KEY", &success);
4144
4145 if (!success) {
4146 goto loop;
4147 }
4148
4149 if (my_isspace(cs, *ptr)) {
4150 ptr1 = dict_accept(cs, ptr, "IF", &success);
4151
4152 if (success) {
4153 if (!my_isspace(cs, *ptr1)) {
4154 goto loop;
4155 }
4156 ptr1 = dict_accept(cs, ptr1, "NOT", &success);
4157 if (!success) {
4158 goto loop;
4159 }
4160 ptr1 = dict_accept(cs, ptr1, "EXISTS", &success);
4161 if (!success) {
4162 goto loop;
4163 }
4164 ptr = ptr1;
4165 }
4166 }
4167
4168 orig = ptr;
4169 ptr = dict_accept(cs, ptr, "(", &success);
4170
4171 if (!success) {
4172 if (constraint_name) {
4173 /* MySQL allows also an index id before the '('; we
4174 skip it */
4175 ptr = dict_skip_word(cs, ptr, &success);
4176 if (!success) {
4177 dict_foreign_report_syntax_err(
4178 "%s table %s with foreign key constraint"
4179 " failed. Parse error in '%s'"
4180 " near '%s'.\n",
4181 operation, create_name, start_of_latest_foreign, orig);
4182
4183 ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
4184 "%s table %s with foreign key constraint"
4185 " failed. Parse error in '%s'"
4186 " near '%s'.",
4187 operation, create_name, start_of_latest_foreign, orig);
4188 return(DB_CANNOT_ADD_CONSTRAINT);
4189 }
4190 } else {
4191 while (my_isspace(cs, *ptr)) {
4192 ptr++;
4193 }
4194
4195 ptr = dict_scan_id(cs, ptr, heap,
4196 &constraint_name, FALSE, FALSE);
4197 }
4198
4199 ptr = dict_accept(cs, ptr, "(", &success);
4200
4201 if (!success) {
4202 /* We do not flag a syntax error here because in an
4203 ALTER TABLE we may also have DROP FOREIGN KEY abc */
4204
4205 goto loop;
4206 }
4207 }
4208
4209 i = 0;
4210
4211 /* Scan the columns in the first list */
4212 col_loop1:
4213 ut_a(i < (sizeof column_names) / sizeof *column_names);
4214 orig = ptr;
4215 ptr = dict_scan_col(cs, ptr, &success, table, columns + i,
4216 heap, column_names + i);
4217 if (!success) {
4218 mutex_enter(&dict_foreign_err_mutex);
4219 dict_foreign_error_report_low(ef, create_name);
4220 fprintf(ef,
4221 "%s table %s with foreign key constraint"
4222 " failed. Parse error in '%s'"
4223 " near '%s'.\n",
4224 operation, create_name, start_of_latest_foreign, orig);
4225
4226 mutex_exit(&dict_foreign_err_mutex);
4227
4228 ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
4229 "%s table %s with foreign key constraint"
4230 " failed. Parse error in '%s'"
4231 " near '%s'.",
4232 operation, create_name, start_of_latest_foreign, orig);
4233
4234 return(DB_CANNOT_ADD_CONSTRAINT);
4235 }
4236
4237 i++;
4238
4239 ptr = dict_accept(cs, ptr, ",", &success);
4240
4241 if (success) {
4242 goto col_loop1;
4243 }
4244
4245 orig = ptr;
4246 ptr = dict_accept(cs, ptr, ")", &success);
4247
4248 if (!success) {
4249 dict_foreign_report_syntax_err(
4250 "%s table %s with foreign key constraint"
4251 " failed. Parse error in '%s'"
4252 " near '%s'.\n",
4253 operation, create_name, start_of_latest_foreign, orig);
4254
4255 ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
4256 "%s table %s with foreign key constraint"
4257 " failed. Parse error in '%s'"
4258 " near '%s'.",
4259 operation, create_name, start_of_latest_foreign, orig);
4260
4261 return(DB_CANNOT_ADD_CONSTRAINT);
4262 }
4263
4264 /* Try to find an index which contains the columns
4265 as the first fields and in the right order. There is
4266 no need to check column type match (on types_idx), since
4267 the referenced table can be NULL if foreign_key_checks is
4268 set to 0 */
4269
4270 index = dict_foreign_find_index(
4271 table, NULL, column_names, i,
4272 NULL, TRUE, FALSE, &index_error, &err_col, &err_index);
4273
4274 if (!index) {
4275 mutex_enter(&dict_foreign_err_mutex);
4276 dict_foreign_error_report_low(ef, create_name);
4277 fputs("There is no index in table ", ef);
4278 ut_print_name(ef, NULL, create_name);
4279 fprintf(ef, " where the columns appear\n"
4280 "as the first columns. Constraint:\n%s\n%s",
4281 start_of_latest_foreign,
4282 FOREIGN_KEY_CONSTRAINTS_MSG);
4283 dict_foreign_push_index_error(trx, operation, create_name, start_of_latest_foreign,
4284 column_names, index_error, err_col, err_index, table, ef);
4285
4286 mutex_exit(&dict_foreign_err_mutex);
4287 return(DB_CANNOT_ADD_CONSTRAINT);
4288 }
4289
4290 orig = ptr;
4291 ptr = dict_accept(cs, ptr, "REFERENCES", &success);
4292
4293 if (!success || !my_isspace(cs, *ptr)) {
4294 dict_foreign_report_syntax_err(
4295 "%s table %s with foreign key constraint"
4296 " failed. Parse error in '%s'"
4297 " near '%s'.\n",
4298 operation, create_name, start_of_latest_foreign, orig);
4299
4300 ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
4301 "%s table %s with foreign key constraint"
4302 " failed. Parse error in '%s'"
4303 " near '%s'.",
4304 operation, create_name, start_of_latest_foreign, orig);
4305 return(DB_CANNOT_ADD_CONSTRAINT);
4306 }
4307
4308 /* Don't allow foreign keys on partitioned tables yet. */
4309 ptr1 = dict_scan_to(ptr, "PARTITION");
4310 if (ptr1) {
4311 ptr1 = dict_accept(cs, ptr1, "PARTITION", &success);
4312 if (success && my_isspace(cs, *ptr1)) {
4313 ptr2 = dict_accept(cs, ptr1, "BY", &success);
4314 if (success) {
4315 my_error(ER_FOREIGN_KEY_ON_PARTITIONED,MYF(0));
4316 return(DB_CANNOT_ADD_CONSTRAINT);
4317 }
4318 }
4319 }
4320 if (dict_table_is_partition(table)) {
4321 my_error(ER_FOREIGN_KEY_ON_PARTITIONED,MYF(0));
4322 return(DB_CANNOT_ADD_CONSTRAINT);
4323 }
4324
4325 /* Let us create a constraint struct */
4326
4327 foreign = dict_mem_foreign_create();
4328
4329 if (constraint_name) {
4330 ulint db_len;
4331
4332 /* Catenate 'databasename/' to the constraint name specified
4333 by the user: we conceive the constraint as belonging to the
4334 same MySQL 'database' as the table itself. We store the name
4335 to foreign->id. */
4336
4337 db_len = dict_get_db_name_len(table->name.m_name);
4338
4339 foreign->id = static_cast<char*>(mem_heap_alloc(
4340 foreign->heap, db_len + strlen(constraint_name) + 2));
4341
4342 ut_memcpy(foreign->id, table->name.m_name, db_len);
4343 foreign->id[db_len] = '/';
4344 strcpy(foreign->id + db_len + 1, constraint_name);
4345 }
4346
4347 if (foreign->id == NULL) {
4348 error = dict_create_add_foreign_id(
4349 &number, table->name.m_name, foreign);
4350 if (error != DB_SUCCESS) {
4351 dict_foreign_free(foreign);
4352 return(error);
4353 }
4354 }
4355
4356 std::pair<dict_foreign_set::iterator, bool> ret
4357 = local_fk_set.insert(foreign);
4358
4359 if (!ret.second) {
4360 /* A duplicate foreign key name has been found */
4361 dict_foreign_free(foreign);
4362 return(DB_CANNOT_ADD_CONSTRAINT);
4363 }
4364
4365 foreign->foreign_table = table;
4366 foreign->foreign_table_name = mem_heap_strdup(
4367 foreign->heap, table->name.m_name);
4368 dict_mem_foreign_table_name_lookup_set(foreign, TRUE);
4369
4370 foreign->foreign_index = index;
4371 foreign->n_fields = (unsigned int) i;
4372
4373 foreign->foreign_col_names = static_cast<const char**>(
4374 mem_heap_alloc(foreign->heap, i * sizeof(void*)));
4375
4376 for (i = 0; i < foreign->n_fields; i++) {
4377 foreign->foreign_col_names[i] = mem_heap_strdup(
4378 foreign->heap, column_names[i]);
4379 }
4380
4381 ptr = dict_scan_table_name(cs, ptr, &referenced_table, name,
4382 &success, heap, &referenced_table_name);
4383
4384 /* Note that referenced_table can be NULL if the user has suppressed
4385 checking of foreign key constraints! */
4386
4387 if (!success || (!referenced_table && trx->check_foreigns)) {
4388 char buf[MAX_TABLE_NAME_LEN + 1] = "";
4389 char* bufend;
4390
4391 bufend = innobase_convert_name(buf, MAX_TABLE_NAME_LEN,
4392 referenced_table_name, strlen(referenced_table_name),
4393 trx->mysql_thd);
4394 buf[bufend - buf] = '\0';
4395
4396 ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
4397 "%s table %s with foreign key constraint failed. Referenced table %s not found in the data dictionary "
4398 "near '%s'.",
4399 operation, create_name, buf, start_of_latest_foreign);
4400 mutex_enter(&dict_foreign_err_mutex);
4401 dict_foreign_error_report_low(ef, create_name);
4402 fprintf(ef,
4403 "%s table %s with foreign key constraint failed. Referenced table %s not found in the data dictionary "
4404 "near '%s'.\n",
4405 operation, create_name, buf, start_of_latest_foreign);
4406
4407 mutex_exit(&dict_foreign_err_mutex);
4408
4409 return(DB_CANNOT_ADD_CONSTRAINT);
4410 }
4411
4412 /* Don't allow foreign keys on partitioned tables yet. */
4413 if (referenced_table && dict_table_is_partition(referenced_table)) {
4414 /* How could one make a referenced table to be a partition? */
4415 ut_ad(0);
4416 my_error(ER_FOREIGN_KEY_ON_PARTITIONED,MYF(0));
4417 return(DB_CANNOT_ADD_CONSTRAINT);
4418 }
4419
4420 ptr = dict_accept(cs, ptr, "(", &success);
4421
4422 if (!success) {
4423 dict_foreign_report_syntax_err(
4424 "%s table %s with foreign key constraint"
4425 " failed. Parse error in '%s'"
4426 " near '%s'.\n",
4427 operation, create_name, start_of_latest_foreign, orig);
4428
4429 ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
4430 "%s table %s with foreign key constraint"
4431 " failed. Parse error in '%s'"
4432 " near '%s'.",
4433 operation, create_name, start_of_latest_foreign, orig);
4434
4435 return(DB_CANNOT_ADD_CONSTRAINT);
4436 }
4437
4438 /* Scan the columns in the second list */
4439 i = 0;
4440
4441 col_loop2:
4442 orig = ptr;
4443 ptr = dict_scan_col(cs, ptr, &success, referenced_table, columns + i,
4444 heap, ref_column_names + i);
4445 i++;
4446
4447 if (!success) {
4448
4449 mutex_enter(&dict_foreign_err_mutex);
4450 dict_foreign_error_report_low(ef, create_name);
4451 fprintf(ef,
4452 "%s table %s with foreign key constraint"
4453 " failed. Parse error in '%s'"
4454 " near '%s'.\n",
4455 operation, create_name, start_of_latest_foreign, orig);
4456 mutex_exit(&dict_foreign_err_mutex);
4457 ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
4458 "%s table %s with foreign key constraint"
4459 " failed. Parse error in '%s'"
4460 " near '%s'.",
4461 operation, create_name, start_of_latest_foreign, orig);
4462
4463 return(DB_CANNOT_ADD_CONSTRAINT);
4464 }
4465
4466 orig = ptr;
4467 ptr = dict_accept(cs, ptr, ",", &success);
4468
4469 if (success) {
4470 goto col_loop2;
4471 }
4472
4473 orig = ptr;
4474 ptr = dict_accept(cs, ptr, ")", &success);
4475
4476 if (!success || foreign->n_fields != i) {
4477
4478 dict_foreign_report_syntax_err(
4479 "%s table %s with foreign key constraint"
4480 " failed. Parse error in '%s' near '%s'. Referencing column count does not match referenced column count.\n",
4481 operation, create_name, start_of_latest_foreign, orig);
4482
4483 ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
4484 "%s table %s with foreign key constraint"
4485 " failed. Parse error in '%s' near '%s'. Referencing column count %d does not match referenced column count %d.\n",
4486 operation, create_name, start_of_latest_foreign, orig, i, foreign->n_fields);
4487
4488 return(DB_CANNOT_ADD_CONSTRAINT);
4489 }
4490
4491 n_on_deletes = 0;
4492 n_on_updates = 0;
4493
4494 scan_on_conditions:
4495 /* Loop here as long as we can find ON ... conditions */
4496
4497 start_of_latest_set = ptr;
4498 ptr = dict_accept(cs, ptr, "ON", &success);
4499
4500 if (!success) {
4501
4502 goto try_find_index;
4503 }
4504
4505 orig = ptr;
4506 ptr = dict_accept(cs, ptr, "DELETE", &success);
4507
4508 if (!success) {
4509 orig = ptr;
4510 ptr = dict_accept(cs, ptr, "UPDATE", &success);
4511
4512 if (!success) {
4513
4514 dict_foreign_report_syntax_err(
4515 "%s table %s with foreign key constraint"
4516 " failed. Parse error in '%s'"
4517 " near '%s'.\n",
4518 operation, create_name, start_of_latest_foreign, start_of_latest_set);
4519
4520 ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
4521 "%s table %s with foreign key constraint"
4522 " failed. Parse error in '%s'"
4523 " near '%s'.",
4524 operation, create_name, start_of_latest_foreign, start_of_latest_set);
4525
4526 return(DB_CANNOT_ADD_CONSTRAINT);
4527 }
4528
4529 is_on_delete = FALSE;
4530 n_on_updates++;
4531 } else {
4532 is_on_delete = TRUE;
4533 n_on_deletes++;
4534 }
4535
4536 orig = ptr;
4537 ptr = dict_accept(cs, ptr, "RESTRICT", &success);
4538
4539 if (success) {
4540 goto scan_on_conditions;
4541 }
4542
4543 orig = ptr;
4544 ptr = dict_accept(cs, ptr, "CASCADE", &success);
4545
4546 if (success) {
4547 if (is_on_delete) {
4548 foreign->type |= DICT_FOREIGN_ON_DELETE_CASCADE;
4549 } else {
4550 foreign->type |= DICT_FOREIGN_ON_UPDATE_CASCADE;
4551 }
4552
4553 goto scan_on_conditions;
4554 }
4555
4556 orig = ptr;
4557 ptr = dict_accept(cs, ptr, "NO", &success);
4558
4559 if (success) {
4560 orig = ptr;
4561 ptr = dict_accept(cs, ptr, "ACTION", &success);
4562
4563 if (!success) {
4564 dict_foreign_report_syntax_err(
4565 "%s table %s with foreign key constraint"
4566 " failed. Parse error in '%s'"
4567 " near '%s'.\n",
4568 operation, create_name, start_of_latest_foreign, start_of_latest_set);
4569
4570 ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
4571 "%s table %s with foreign key constraint"
4572 " failed. Parse error in '%s'"
4573 " near '%s'.",
4574 operation, create_name, start_of_latest_foreign, start_of_latest_set);
4575
4576 return(DB_CANNOT_ADD_CONSTRAINT);
4577 }
4578
4579 if (is_on_delete) {
4580 foreign->type |= DICT_FOREIGN_ON_DELETE_NO_ACTION;
4581 } else {
4582 foreign->type |= DICT_FOREIGN_ON_UPDATE_NO_ACTION;
4583 }
4584
4585 goto scan_on_conditions;
4586 }
4587
4588 orig = ptr;
4589 ptr = dict_accept(cs, ptr, "SET", &success);
4590
4591 if (!success) {
4592 dict_foreign_report_syntax_err(
4593 "%s table %s with foreign key constraint"
4594 " failed. Parse error in '%s'"
4595 " near '%s'.\n",
4596 operation, create_name, start_of_latest_foreign, start_of_latest_set);
4597
4598 ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
4599 "%s table %s with foreign key constraint"
4600 " failed. Parse error in '%s'"
4601 " near '%s'.",
4602 operation, create_name, start_of_latest_foreign, start_of_latest_set);
4603 return(DB_CANNOT_ADD_CONSTRAINT);
4604 }
4605
4606 orig = ptr;
4607 ptr = dict_accept(cs, ptr, "NULL", &success);
4608
4609 if (!success) {
4610 dict_foreign_report_syntax_err(
4611 "%s table %s with foreign key constraint"
4612 " failed. Parse error in '%s'"
4613 " near '%s'.\n",
4614 operation, create_name, start_of_latest_foreign, start_of_latest_set);
4615
4616 ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
4617 "%s table %s with foreign key constraint"
4618 " failed. Parse error in '%s'"
4619 " near '%s'.",
4620 operation, create_name, start_of_latest_foreign, start_of_latest_set);
4621
4622 return(DB_CANNOT_ADD_CONSTRAINT);
4623 }
4624
4625 for (j = 0; j < foreign->n_fields; j++) {
4626 if ((dict_index_get_nth_col(foreign->foreign_index, j)->prtype)
4627 & DATA_NOT_NULL) {
4628 const dict_col_t* col
4629 = dict_index_get_nth_col(foreign->foreign_index, j);
4630 const char* col_name = dict_table_get_col_name(foreign->foreign_index->table,
4631 dict_col_get_no(col));
4632
4633 /* It is not sensible to define SET NULL
4634 if the column is not allowed to be NULL! */
4635
4636 mutex_enter(&dict_foreign_err_mutex);
4637 dict_foreign_error_report_low(ef, create_name);
4638 fprintf(ef,
4639 "%s table %s with foreign key constraint"
4640 " failed. You have defined a SET NULL condition but column '%s' is defined as NOT NULL"
4641 " in '%s' near '%s'.\n",
4642 operation, create_name, col_name, start_of_latest_foreign, start_of_latest_set);
4643 mutex_exit(&dict_foreign_err_mutex);
4644
4645 ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
4646 "%s table %s with foreign key constraint"
4647 " failed. You have defined a SET NULL condition but column '%s' is defined as NOT NULL"
4648 " in '%s' near '%s'.",
4649 operation, create_name, col_name, start_of_latest_foreign, start_of_latest_set);
4650
4651 return(DB_CANNOT_ADD_CONSTRAINT);
4652 }
4653 }
4654
4655 if (is_on_delete) {
4656 foreign->type |= DICT_FOREIGN_ON_DELETE_SET_NULL;
4657 } else {
4658 foreign->type |= DICT_FOREIGN_ON_UPDATE_SET_NULL;
4659 }
4660
4661 goto scan_on_conditions;
4662
4663 try_find_index:
4664 if (n_on_deletes > 1 || n_on_updates > 1) {
4665 /* It is an error to define more than 1 action */
4666
4667 mutex_enter(&dict_foreign_err_mutex);
4668 dict_foreign_error_report_low(ef, create_name);
4669 fprintf(ef,
4670 "%s table %s with foreign key constraint"
4671 " failed. You have more than one on delete or on update clause"
4672 " in '%s' near '%s'.\n",
4673 operation, create_name, start_of_latest_foreign, start_of_latest_set);
4674 mutex_exit(&dict_foreign_err_mutex);
4675
4676 ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
4677 "%s table %s with foreign key constraint"
4678 " failed. You have more than one on delete or on update clause"
4679 " in '%s' near '%s'.",
4680 operation, create_name, start_of_latest_foreign, start_of_latest_set);
4681
4682 dict_foreign_free(foreign);
4683
4684 return(DB_CANNOT_ADD_CONSTRAINT);
4685 }
4686
4687 /* Try to find an index which contains the columns as the first fields
4688 and in the right order, and the types are the same as in
4689 foreign->foreign_index */
4690
4691 if (referenced_table) {
4692 index = dict_foreign_find_index(referenced_table, NULL,
4693 ref_column_names, i,
4694 foreign->foreign_index,
4695 TRUE, FALSE, &index_error, &err_col, &err_index);
4696
4697 if (!index) {
4698 mutex_enter(&dict_foreign_err_mutex);
4699 dict_foreign_error_report_low(ef, create_name);
4700 fprintf(ef, "%s:\n"
4701 "Cannot find an index in the"
4702 " referenced table where the\n"
4703 "referenced columns appear as the"
4704 " first columns, or column types\n"
4705 "in the table and the referenced table"
4706 " do not match for constraint.\n"
4707 "Note that the internal storage type of"
4708 " ENUM and SET changed in\n"
4709 "tables created with >= InnoDB-4.1.12,"
4710 " and such columns in old tables\n"
4711 "cannot be referenced by such columns"
4712 " in new tables.\n%s\n",
4713 start_of_latest_foreign,
4714 FOREIGN_KEY_CONSTRAINTS_MSG);
4715
4716 dict_foreign_push_index_error(trx, operation, create_name, start_of_latest_foreign,
4717 column_names, index_error, err_col, err_index, referenced_table, ef);
4718
4719 mutex_exit(&dict_foreign_err_mutex);
4720
4721 return(DB_CANNOT_ADD_CONSTRAINT);
4722 }
4723 } else {
4724 ut_a(trx->check_foreigns == FALSE);
4725 index = NULL;
4726 }
4727
4728 foreign->referenced_index = index;
4729 foreign->referenced_table = referenced_table;
4730
4731 foreign->referenced_table_name = mem_heap_strdup(
4732 foreign->heap, referenced_table_name);
4733 dict_mem_referenced_table_name_lookup_set(foreign, TRUE);
4734
4735 foreign->referenced_col_names = static_cast<const char**>(
4736 mem_heap_alloc(foreign->heap, i * sizeof(void*)));
4737
4738 for (i = 0; i < foreign->n_fields; i++) {
4739 foreign->referenced_col_names[i]
4740 = mem_heap_strdup(foreign->heap, ref_column_names[i]);
4741 }
4742
4743 goto loop;
4744 }
4745
4746 /** Scans a table create SQL string and adds to the data dictionary
4747 the foreign key constraints declared in the string. This function
4748 should be called after the indexes for a table have been created.
4749 Each foreign key constraint must be accompanied with indexes in
4750 bot participating tables. The indexes are allowed to contain more
4751 fields than mentioned in the constraint.
4752
4753 @param[in] trx transaction
4754 @param[in] sql_string table create statement where
4755 foreign keys are declared like:
4756 FOREIGN KEY (a, b) REFERENCES table2(c, d),
4757 table2 can be written also with the database
4758 name before it: test.table2; the default
4759 database id the database of parameter name
4760 @param[in] sql_length length of sql_string
4761 @param[in] name table full name in normalized form
4762 @param[in] reject_fks if TRUE, fail with error code
4763 DB_CANNOT_ADD_CONSTRAINT if any
4764 foreign keys are found.
4765 @return error code or DB_SUCCESS */
4766 dberr_t
dict_create_foreign_constraints(trx_t * trx,const char * sql_string,size_t sql_length,const char * name,ibool reject_fks)4767 dict_create_foreign_constraints(
4768 trx_t* trx,
4769 const char* sql_string,
4770 size_t sql_length,
4771 const char* name,
4772 ibool reject_fks)
4773 {
4774 char* str;
4775 dberr_t err;
4776 mem_heap_t* heap;
4777
4778 ut_a(trx);
4779 ut_a(trx->mysql_thd);
4780
4781 str = dict_strip_comments(sql_string, sql_length);
4782 heap = mem_heap_create(10000);
4783
4784 err = dict_create_foreign_constraints_low(
4785 trx, heap, thd_charset(trx->mysql_thd),
4786 str, name, reject_fks);
4787
4788 mem_heap_free(heap);
4789 ut_free(str);
4790
4791 return(err);
4792 }
4793
4794 /**********************************************************************//**
4795 Parses the CONSTRAINT id's to be dropped in an ALTER TABLE statement.
4796 @return DB_SUCCESS or DB_CANNOT_DROP_CONSTRAINT if syntax error or the
4797 constraint id does not match */
4798 dberr_t
dict_foreign_parse_drop_constraints(mem_heap_t * heap,trx_t * trx,dict_table_t * table,ulint * n,const char *** constraints_to_drop)4799 dict_foreign_parse_drop_constraints(
4800 /*================================*/
4801 mem_heap_t* heap, /*!< in: heap from which we can
4802 allocate memory */
4803 trx_t* trx, /*!< in: transaction */
4804 dict_table_t* table, /*!< in: table */
4805 ulint* n, /*!< out: number of constraints
4806 to drop */
4807 const char*** constraints_to_drop) /*!< out: id's of the
4808 constraints to drop */
4809 {
4810 ibool success;
4811 char* str;
4812 size_t len;
4813 const char* ptr;
4814 const char* ptr1;
4815 const char* id;
4816 CHARSET_INFO* cs;
4817
4818 ut_a(trx->mysql_thd);
4819
4820 cs = thd_charset(trx->mysql_thd);
4821
4822 *n = 0;
4823
4824 *constraints_to_drop = static_cast<const char**>(
4825 mem_heap_alloc(heap, 1000 * sizeof(char*)));
4826
4827 ptr = innobase_get_stmt_unsafe(trx->mysql_thd, &len);
4828
4829 str = dict_strip_comments(ptr, len);
4830
4831 ptr = str;
4832
4833 ut_ad(mutex_own(&dict_sys->mutex));
4834 loop:
4835 ptr = dict_scan_to(ptr, "DROP");
4836
4837 if (*ptr == '\0') {
4838 ut_free(str);
4839
4840 return(DB_SUCCESS);
4841 }
4842
4843 ptr = dict_accept(cs, ptr, "DROP", &success);
4844
4845 if (!my_isspace(cs, *ptr)) {
4846
4847 goto loop;
4848 }
4849
4850 ptr = dict_accept(cs, ptr, "FOREIGN", &success);
4851
4852 if (!success || !my_isspace(cs, *ptr)) {
4853
4854 goto loop;
4855 }
4856
4857 ptr = dict_accept(cs, ptr, "KEY", &success);
4858
4859 if (!success) {
4860
4861 goto syntax_error;
4862 }
4863
4864 ptr1 = dict_accept(cs, ptr, "IF", &success);
4865
4866 if (success && my_isspace(cs, *ptr1)) {
4867 ptr1 = dict_accept(cs, ptr1, "EXISTS", &success);
4868 if (success) {
4869 ptr = ptr1;
4870 }
4871 }
4872
4873 ptr = dict_scan_id(cs, ptr, heap, &id, FALSE, TRUE);
4874
4875 if (id == NULL) {
4876
4877 goto syntax_error;
4878 }
4879
4880 ut_a(*n < 1000);
4881 (*constraints_to_drop)[*n] = id;
4882 (*n)++;
4883
4884 if (std::find_if(table->foreign_set.begin(),
4885 table->foreign_set.end(),
4886 dict_foreign_matches_id(id))
4887 == table->foreign_set.end()) {
4888
4889 if (!srv_read_only_mode) {
4890 FILE* ef = dict_foreign_err_file;
4891
4892 mutex_enter(&dict_foreign_err_mutex);
4893 rewind(ef);
4894 ut_print_timestamp(ef);
4895 fputs(" Error in dropping of a foreign key"
4896 " constraint of table ", ef);
4897 ut_print_name(ef, NULL, table->name.m_name);
4898 fprintf(ef, ",\nin SQL command\n%s"
4899 "\nCannot find a constraint with the"
4900 " given id %s.\n", str, id);
4901 mutex_exit(&dict_foreign_err_mutex);
4902 }
4903
4904 ut_free(str);
4905
4906 return(DB_CANNOT_DROP_CONSTRAINT);
4907 }
4908
4909 goto loop;
4910
4911 syntax_error:
4912 if (!srv_read_only_mode) {
4913 FILE* ef = dict_foreign_err_file;
4914
4915 mutex_enter(&dict_foreign_err_mutex);
4916 rewind(ef);
4917 ut_print_timestamp(ef);
4918 fputs(" Syntax error in dropping of a"
4919 " foreign key constraint of table ", ef);
4920 ut_print_name(ef, NULL, table->name.m_name);
4921 fprintf(ef, ",\n"
4922 "close to:\n%s\n in SQL command\n%s\n", ptr, str);
4923 mutex_exit(&dict_foreign_err_mutex);
4924 }
4925
4926 ut_free(str);
4927
4928 return(DB_CANNOT_DROP_CONSTRAINT);
4929 }
4930
4931 /*==================== END OF FOREIGN KEY PROCESSING ====================*/
4932
4933 /**********************************************************************//**
4934 Returns an index object if it is found in the dictionary cache.
4935 Assumes that dict_sys->mutex is already being held.
4936 @return index, NULL if not found */
4937 dict_index_t*
dict_index_get_if_in_cache_low(index_id_t index_id)4938 dict_index_get_if_in_cache_low(
4939 /*===========================*/
4940 index_id_t index_id) /*!< in: index id */
4941 {
4942 ut_ad(mutex_own(&dict_sys->mutex));
4943
4944 return(dict_index_find_on_id_low(index_id));
4945 }
4946
4947 #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG
4948 /**********************************************************************//**
4949 Returns an index object if it is found in the dictionary cache.
4950 @return index, NULL if not found */
4951 dict_index_t*
dict_index_get_if_in_cache(index_id_t index_id)4952 dict_index_get_if_in_cache(
4953 /*=======================*/
4954 index_id_t index_id) /*!< in: index id */
4955 {
4956 dict_index_t* index;
4957
4958 if (dict_sys == NULL) {
4959 return(NULL);
4960 }
4961
4962 mutex_enter(&dict_sys->mutex);
4963
4964 index = dict_index_get_if_in_cache_low(index_id);
4965
4966 mutex_exit(&dict_sys->mutex);
4967
4968 return(index);
4969 }
4970 #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */
4971
4972 #ifdef UNIV_DEBUG
4973 /**********************************************************************//**
4974 Checks that a tuple has n_fields_cmp value in a sensible range, so that
4975 no comparison can occur with the page number field in a node pointer.
4976 @return TRUE if ok */
4977 ibool
dict_index_check_search_tuple(const dict_index_t * index,const dtuple_t * tuple)4978 dict_index_check_search_tuple(
4979 /*==========================*/
4980 const dict_index_t* index, /*!< in: index tree */
4981 const dtuple_t* tuple) /*!< in: tuple used in a search */
4982 {
4983 ut_ad(dtuple_get_n_fields_cmp(tuple)
4984 <= dict_index_get_n_unique_in_tree(index));
4985 return(TRUE);
4986 }
4987 #endif /* UNIV_DEBUG */
4988
4989 /**********************************************************************//**
4990 Builds a node pointer out of a physical record and a page number.
4991 @return own: node pointer */
4992 dtuple_t*
dict_index_build_node_ptr(const dict_index_t * index,const rec_t * rec,ulint page_no,mem_heap_t * heap,ulint level)4993 dict_index_build_node_ptr(
4994 /*======================*/
4995 const dict_index_t* index, /*!< in: index */
4996 const rec_t* rec, /*!< in: record for which to build node
4997 pointer */
4998 ulint page_no,/*!< in: page number to put in node
4999 pointer */
5000 mem_heap_t* heap, /*!< in: memory heap where pointer
5001 created */
5002 ulint level) /*!< in: level of rec in tree:
5003 0 means leaf level */
5004 {
5005 dtuple_t* tuple;
5006 dfield_t* field;
5007 byte* buf;
5008 ulint n_unique;
5009
5010 if (dict_index_is_ibuf(index)) {
5011 /* In a universal index tree, we take the whole record as
5012 the node pointer if the record is on the leaf level,
5013 on non-leaf levels we remove the last field, which
5014 contains the page number of the child page */
5015
5016 ut_a(!dict_table_is_comp(index->table));
5017 n_unique = rec_get_n_fields_old(rec);
5018
5019 if (level > 0) {
5020 ut_a(n_unique > 1);
5021 n_unique--;
5022 }
5023 } else {
5024 n_unique = dict_index_get_n_unique_in_tree_nonleaf(index);
5025 }
5026
5027 tuple = dtuple_create(heap, n_unique + 1);
5028
5029 /* When searching in the tree for the node pointer, we must not do
5030 comparison on the last field, the page number field, as on upper
5031 levels in the tree there may be identical node pointers with a
5032 different page number; therefore, we set the n_fields_cmp to one
5033 less: */
5034
5035 dtuple_set_n_fields_cmp(tuple, n_unique);
5036
5037 dict_index_copy_types(tuple, index, n_unique);
5038
5039 buf = static_cast<byte*>(mem_heap_alloc(heap, 4));
5040
5041 mach_write_to_4(buf, page_no);
5042
5043 field = dtuple_get_nth_field(tuple, n_unique);
5044 dfield_set_data(field, buf, 4);
5045
5046 dtype_set(dfield_get_type(field), DATA_SYS_CHILD, DATA_NOT_NULL, 4);
5047
5048 rec_copy_prefix_to_dtuple(tuple, rec, index,
5049 level ? 0 : index->n_core_fields,
5050 n_unique, heap);
5051 dtuple_set_info_bits(tuple, dtuple_get_info_bits(tuple)
5052 | REC_STATUS_NODE_PTR);
5053
5054 ut_ad(dtuple_check_typed(tuple));
5055
5056 return(tuple);
5057 }
5058
5059 /** Convert a physical record into a search tuple.
5060 @param[in] rec index record (not necessarily in an index page)
5061 @param[in] index index
5062 @param[in] leaf whether rec is in a leaf page
5063 @param[in] n_fields number of data fields
5064 @param[in,out] heap memory heap for allocation
5065 @return own: data tuple */
5066 dtuple_t*
dict_index_build_data_tuple(const rec_t * rec,const dict_index_t * index,bool leaf,ulint n_fields,mem_heap_t * heap)5067 dict_index_build_data_tuple(
5068 const rec_t* rec,
5069 const dict_index_t* index,
5070 bool leaf,
5071 ulint n_fields,
5072 mem_heap_t* heap)
5073 {
5074 ut_ad(!index->is_clust());
5075
5076 dtuple_t* tuple = dtuple_create(heap, n_fields);
5077
5078 dict_index_copy_types(tuple, index, n_fields);
5079
5080 rec_copy_prefix_to_dtuple(tuple, rec, index,
5081 leaf ? n_fields : 0, n_fields, heap);
5082
5083 ut_ad(dtuple_check_typed(tuple));
5084
5085 return(tuple);
5086 }
5087
5088 /*********************************************************************//**
5089 Calculates the minimum record length in an index. */
5090 ulint
dict_index_calc_min_rec_len(const dict_index_t * index)5091 dict_index_calc_min_rec_len(
5092 /*========================*/
5093 const dict_index_t* index) /*!< in: index */
5094 {
5095 ulint sum = 0;
5096 ulint i;
5097 ulint comp = dict_table_is_comp(index->table);
5098
5099 if (comp) {
5100 ulint nullable = 0;
5101 sum = REC_N_NEW_EXTRA_BYTES;
5102 for (i = 0; i < dict_index_get_n_fields(index); i++) {
5103 const dict_col_t* col
5104 = dict_index_get_nth_col(index, i);
5105 ulint size = dict_col_get_fixed_size(col, comp);
5106 sum += size;
5107 if (!size) {
5108 size = col->len;
5109 sum += size < 128 ? 1 : 2;
5110 }
5111 if (!(col->prtype & DATA_NOT_NULL)) {
5112 nullable++;
5113 }
5114 }
5115
5116 /* round the NULL flags up to full bytes */
5117 sum += UT_BITS_IN_BYTES(nullable);
5118
5119 return(sum);
5120 }
5121
5122 for (i = 0; i < dict_index_get_n_fields(index); i++) {
5123 sum += dict_col_get_fixed_size(
5124 dict_index_get_nth_col(index, i), comp);
5125 }
5126
5127 if (sum > 127) {
5128 sum += 2 * dict_index_get_n_fields(index);
5129 } else {
5130 sum += dict_index_get_n_fields(index);
5131 }
5132
5133 sum += REC_N_OLD_EXTRA_BYTES;
5134
5135 return(sum);
5136 }
5137
5138 /**********************************************************************//**
5139 Outputs info on a foreign key of a table in a format suitable for
5140 CREATE TABLE. */
5141 std::string
dict_print_info_on_foreign_key_in_create_format(trx_t * trx,dict_foreign_t * foreign,ibool add_newline)5142 dict_print_info_on_foreign_key_in_create_format(
5143 /*============================================*/
5144 trx_t* trx, /*!< in: transaction */
5145 dict_foreign_t* foreign, /*!< in: foreign key constraint */
5146 ibool add_newline) /*!< in: whether to add a newline */
5147 {
5148 const char* stripped_id;
5149 ulint i;
5150 std::string str;
5151
5152 if (strchr(foreign->id, '/')) {
5153 /* Strip the preceding database name from the constraint id */
5154 stripped_id = foreign->id + 1
5155 + dict_get_db_name_len(foreign->id);
5156 } else {
5157 stripped_id = foreign->id;
5158 }
5159
5160 str.append(",");
5161
5162 if (add_newline) {
5163 /* SHOW CREATE TABLE wants constraints each printed nicely
5164 on its own line, while error messages want no newlines
5165 inserted. */
5166 str.append("\n ");
5167 }
5168
5169 str.append(" CONSTRAINT ");
5170
5171 str.append(innobase_quote_identifier(trx, stripped_id));
5172 str.append(" FOREIGN KEY (");
5173
5174 for (i = 0;;) {
5175 str.append(innobase_quote_identifier(trx, foreign->foreign_col_names[i]));
5176
5177 if (++i < foreign->n_fields) {
5178 str.append(", ");
5179 } else {
5180 break;
5181 }
5182 }
5183
5184 str.append(") REFERENCES ");
5185
5186 if (dict_tables_have_same_db(foreign->foreign_table_name_lookup,
5187 foreign->referenced_table_name_lookup)) {
5188 /* Do not print the database name of the referenced table */
5189 str.append(ut_get_name(trx,
5190 dict_remove_db_name(
5191 foreign->referenced_table_name)));
5192 } else {
5193 str.append(ut_get_name(trx,
5194 foreign->referenced_table_name));
5195 }
5196
5197 str.append(" (");
5198
5199 for (i = 0;;) {
5200 str.append(innobase_quote_identifier(trx,
5201 foreign->referenced_col_names[i]));
5202
5203 if (++i < foreign->n_fields) {
5204 str.append(", ");
5205 } else {
5206 break;
5207 }
5208 }
5209
5210 str.append(")");
5211
5212 if (foreign->type & DICT_FOREIGN_ON_DELETE_CASCADE) {
5213 str.append(" ON DELETE CASCADE");
5214 }
5215
5216 if (foreign->type & DICT_FOREIGN_ON_DELETE_SET_NULL) {
5217 str.append(" ON DELETE SET NULL");
5218 }
5219
5220 if (foreign->type & DICT_FOREIGN_ON_DELETE_NO_ACTION) {
5221 str.append(" ON DELETE NO ACTION");
5222 }
5223
5224 if (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE) {
5225 str.append(" ON UPDATE CASCADE");
5226 }
5227
5228 if (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL) {
5229 str.append(" ON UPDATE SET NULL");
5230 }
5231
5232 if (foreign->type & DICT_FOREIGN_ON_UPDATE_NO_ACTION) {
5233 str.append(" ON UPDATE NO ACTION");
5234 }
5235
5236 return str;
5237 }
5238
5239 /**********************************************************************//**
5240 Outputs info on foreign keys of a table. */
5241 std::string
dict_print_info_on_foreign_keys(ibool create_table_format,trx_t * trx,dict_table_t * table)5242 dict_print_info_on_foreign_keys(
5243 /*============================*/
5244 ibool create_table_format, /*!< in: if TRUE then print in
5245 a format suitable to be inserted into
5246 a CREATE TABLE, otherwise in the format
5247 of SHOW TABLE STATUS */
5248 trx_t* trx, /*!< in: transaction */
5249 dict_table_t* table) /*!< in: table */
5250 {
5251 dict_foreign_t* foreign;
5252 std::string str;
5253
5254 mutex_enter(&dict_sys->mutex);
5255
5256 for (dict_foreign_set::iterator it = table->foreign_set.begin();
5257 it != table->foreign_set.end();
5258 ++it) {
5259
5260 foreign = *it;
5261
5262 if (create_table_format) {
5263 str.append(
5264 dict_print_info_on_foreign_key_in_create_format(
5265 trx, foreign, TRUE));
5266 } else {
5267 ulint i;
5268 str.append("; (");
5269
5270 for (i = 0; i < foreign->n_fields; i++) {
5271 if (i) {
5272 str.append(" ");
5273 }
5274
5275 str.append(innobase_quote_identifier(trx,
5276 foreign->foreign_col_names[i]));
5277 }
5278
5279 str.append(") REFER ");
5280 str.append(ut_get_name(trx,
5281 foreign->referenced_table_name));
5282 str.append(")");
5283
5284 for (i = 0; i < foreign->n_fields; i++) {
5285 if (i) {
5286 str.append(" ");
5287 }
5288 str.append(innobase_quote_identifier(
5289 trx,
5290 foreign->referenced_col_names[i]));
5291 }
5292
5293 str.append(")");
5294
5295 if (foreign->type == DICT_FOREIGN_ON_DELETE_CASCADE) {
5296 str.append(" ON DELETE CASCADE");
5297 }
5298
5299 if (foreign->type == DICT_FOREIGN_ON_DELETE_SET_NULL) {
5300 str.append(" ON DELETE SET NULL");
5301 }
5302
5303 if (foreign->type & DICT_FOREIGN_ON_DELETE_NO_ACTION) {
5304 str.append(" ON DELETE NO ACTION");
5305 }
5306
5307 if (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE) {
5308 str.append(" ON UPDATE CASCADE");
5309 }
5310
5311 if (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL) {
5312 str.append(" ON UPDATE SET NULL");
5313 }
5314
5315 if (foreign->type & DICT_FOREIGN_ON_UPDATE_NO_ACTION) {
5316 str.append(" ON UPDATE NO ACTION");
5317 }
5318 }
5319 }
5320
5321 mutex_exit(&dict_sys->mutex);
5322 return str;
5323 }
5324
5325 /** Given a space_id of a file-per-table tablespace, search the
5326 dict_sys->table_LRU list and return the dict_table_t* pointer for it.
5327 @param space tablespace
5328 @return table if found, NULL if not */
5329 static
5330 dict_table_t*
dict_find_single_table_by_space(const fil_space_t * space)5331 dict_find_single_table_by_space(const fil_space_t* space)
5332 {
5333 dict_table_t* table;
5334 ulint num_item;
5335 ulint count = 0;
5336
5337 ut_ad(space->id > 0);
5338
5339 if (dict_sys == NULL) {
5340 /* This could happen when it's in redo processing. */
5341 return(NULL);
5342 }
5343
5344 table = UT_LIST_GET_FIRST(dict_sys->table_LRU);
5345 num_item = UT_LIST_GET_LEN(dict_sys->table_LRU);
5346
5347 /* This function intentionally does not acquire mutex as it is used
5348 by error handling code in deep call stack as last means to avoid
5349 killing the server, so it worth to risk some consequences for
5350 the action. */
5351 while (table && count < num_item) {
5352 if (table->space == space) {
5353 if (dict_table_is_file_per_table(table)) {
5354 return(table);
5355 }
5356 return(NULL);
5357 }
5358
5359 table = UT_LIST_GET_NEXT(table_LRU, table);
5360 count++;
5361 }
5362
5363 return(NULL);
5364 }
5365
5366 /**********************************************************************//**
5367 Flags a table with specified space_id corrupted in the data dictionary
5368 cache
5369 @return true if successful */
dict_set_corrupted_by_space(const fil_space_t * space)5370 bool dict_set_corrupted_by_space(const fil_space_t* space)
5371 {
5372 dict_table_t* table;
5373
5374 table = dict_find_single_table_by_space(space);
5375
5376 if (!table) {
5377 return false;
5378 }
5379
5380 /* mark the table->corrupted bit only, since the caller
5381 could be too deep in the stack for SYS_INDEXES update */
5382 table->corrupted = true;
5383 table->file_unreadable = true;
5384 return true;
5385 }
5386
5387 /** Flag a table encrypted in the data dictionary cache. */
dict_set_encrypted_by_space(const fil_space_t * space)5388 void dict_set_encrypted_by_space(const fil_space_t* space)
5389 {
5390 if (dict_table_t* table = dict_find_single_table_by_space(space)) {
5391 table->file_unreadable = true;
5392 }
5393 }
5394
5395 /**********************************************************************//**
5396 Flags an index corrupted both in the data dictionary cache
5397 and in the SYS_INDEXES */
5398 void
dict_set_corrupted(dict_index_t * index,trx_t * trx,const char * ctx)5399 dict_set_corrupted(
5400 /*===============*/
5401 dict_index_t* index, /*!< in/out: index */
5402 trx_t* trx, /*!< in/out: transaction */
5403 const char* ctx) /*!< in: context */
5404 {
5405 mem_heap_t* heap;
5406 mtr_t mtr;
5407 dict_index_t* sys_index;
5408 dtuple_t* tuple;
5409 dfield_t* dfield;
5410 byte* buf;
5411 const char* status;
5412 btr_cur_t cursor;
5413 bool locked = RW_X_LATCH == trx->dict_operation_lock_mode;
5414
5415 if (!locked) {
5416 row_mysql_lock_data_dictionary(trx);
5417 }
5418
5419 ut_ad(mutex_own(&dict_sys->mutex));
5420 ut_ad(!dict_table_is_comp(dict_sys->sys_tables));
5421 ut_ad(!dict_table_is_comp(dict_sys->sys_indexes));
5422 ut_ad(!sync_check_iterate(dict_sync_check()));
5423
5424 /* Mark the table as corrupted only if the clustered index
5425 is corrupted */
5426 if (dict_index_is_clust(index)) {
5427 index->table->corrupted = TRUE;
5428 }
5429
5430 if (index->type & DICT_CORRUPT) {
5431 /* The index was already flagged corrupted. */
5432 ut_ad(!dict_index_is_clust(index) || index->table->corrupted);
5433 goto func_exit;
5434 }
5435
5436 /* If this is read only mode, do not update SYS_INDEXES, just
5437 mark it as corrupted in memory */
5438 if (high_level_read_only) {
5439 index->type |= DICT_CORRUPT;
5440 goto func_exit;
5441 }
5442
5443 heap = mem_heap_create(sizeof(dtuple_t) + 2 * (sizeof(dfield_t)
5444 + sizeof(que_fork_t) + sizeof(upd_node_t)
5445 + sizeof(upd_t) + 12));
5446 mtr_start(&mtr);
5447 index->type |= DICT_CORRUPT;
5448
5449 sys_index = UT_LIST_GET_FIRST(dict_sys->sys_indexes->indexes);
5450
5451 /* Find the index row in SYS_INDEXES */
5452 tuple = dtuple_create(heap, 2);
5453
5454 dfield = dtuple_get_nth_field(tuple, 0);
5455 buf = static_cast<byte*>(mem_heap_alloc(heap, 8));
5456 mach_write_to_8(buf, index->table->id);
5457 dfield_set_data(dfield, buf, 8);
5458
5459 dfield = dtuple_get_nth_field(tuple, 1);
5460 buf = static_cast<byte*>(mem_heap_alloc(heap, 8));
5461 mach_write_to_8(buf, index->id);
5462 dfield_set_data(dfield, buf, 8);
5463
5464 dict_index_copy_types(tuple, sys_index, 2);
5465
5466 btr_cur_search_to_nth_level(sys_index, 0, tuple, PAGE_CUR_LE,
5467 BTR_MODIFY_LEAF,
5468 &cursor, 0, __FILE__, __LINE__, &mtr);
5469
5470 if (cursor.low_match == dtuple_get_n_fields(tuple)) {
5471 /* UPDATE SYS_INDEXES SET TYPE=index->type
5472 WHERE TABLE_ID=index->table->id AND INDEX_ID=index->id */
5473 ulint len;
5474 byte* field = rec_get_nth_field_old(
5475 btr_cur_get_rec(&cursor),
5476 DICT_FLD__SYS_INDEXES__TYPE, &len);
5477 if (len != 4) {
5478 goto fail;
5479 }
5480 mlog_write_ulint(field, index->type, MLOG_4BYTES, &mtr);
5481 status = "Flagged";
5482 } else {
5483 fail:
5484 status = "Unable to flag";
5485 }
5486
5487 mtr_commit(&mtr);
5488 mem_heap_empty(heap);
5489 ib::error() << status << " corruption of " << index->name
5490 << " in table " << index->table->name << " in " << ctx;
5491 mem_heap_free(heap);
5492
5493 func_exit:
5494 if (!locked) {
5495 row_mysql_unlock_data_dictionary(trx);
5496 }
5497 }
5498
5499 /** Flags an index corrupted in the data dictionary cache only. This
5500 is used mostly to mark a corrupted index when index's own dictionary
5501 is corrupted, and we force to load such index for repair purpose
5502 @param[in,out] index index which is corrupted */
5503 void
dict_set_corrupted_index_cache_only(dict_index_t * index)5504 dict_set_corrupted_index_cache_only(
5505 dict_index_t* index)
5506 {
5507 ut_ad(index != NULL);
5508 ut_ad(index->table != NULL);
5509 ut_ad(mutex_own(&dict_sys->mutex));
5510 ut_ad(!dict_table_is_comp(dict_sys->sys_tables));
5511 ut_ad(!dict_table_is_comp(dict_sys->sys_indexes));
5512
5513 /* Mark the table as corrupted only if the clustered index
5514 is corrupted */
5515 if (dict_index_is_clust(index)) {
5516 index->table->corrupted = TRUE;
5517 index->table->file_unreadable = true;
5518 }
5519
5520 index->type |= DICT_CORRUPT;
5521 }
5522
5523 /** Sets merge_threshold in the SYS_INDEXES
5524 @param[in,out] index index
5525 @param[in] merge_threshold value to set */
5526 void
dict_index_set_merge_threshold(dict_index_t * index,ulint merge_threshold)5527 dict_index_set_merge_threshold(
5528 dict_index_t* index,
5529 ulint merge_threshold)
5530 {
5531 mem_heap_t* heap;
5532 mtr_t mtr;
5533 dict_index_t* sys_index;
5534 dtuple_t* tuple;
5535 dfield_t* dfield;
5536 byte* buf;
5537 btr_cur_t cursor;
5538
5539 ut_ad(index != NULL);
5540 ut_ad(!dict_table_is_comp(dict_sys->sys_tables));
5541 ut_ad(!dict_table_is_comp(dict_sys->sys_indexes));
5542
5543 rw_lock_x_lock(&dict_operation_lock);
5544 mutex_enter(&(dict_sys->mutex));
5545
5546 heap = mem_heap_create(sizeof(dtuple_t) + 2 * (sizeof(dfield_t)
5547 + sizeof(que_fork_t) + sizeof(upd_node_t)
5548 + sizeof(upd_t) + 12));
5549
5550 mtr_start(&mtr);
5551
5552 sys_index = UT_LIST_GET_FIRST(dict_sys->sys_indexes->indexes);
5553
5554 /* Find the index row in SYS_INDEXES */
5555 tuple = dtuple_create(heap, 2);
5556
5557 dfield = dtuple_get_nth_field(tuple, 0);
5558 buf = static_cast<byte*>(mem_heap_alloc(heap, 8));
5559 mach_write_to_8(buf, index->table->id);
5560 dfield_set_data(dfield, buf, 8);
5561
5562 dfield = dtuple_get_nth_field(tuple, 1);
5563 buf = static_cast<byte*>(mem_heap_alloc(heap, 8));
5564 mach_write_to_8(buf, index->id);
5565 dfield_set_data(dfield, buf, 8);
5566
5567 dict_index_copy_types(tuple, sys_index, 2);
5568
5569 btr_cur_search_to_nth_level(sys_index, 0, tuple, PAGE_CUR_GE,
5570 BTR_MODIFY_LEAF,
5571 &cursor, 0, __FILE__, __LINE__, &mtr);
5572
5573 if (cursor.up_match == dtuple_get_n_fields(tuple)
5574 && rec_get_n_fields_old(btr_cur_get_rec(&cursor))
5575 == DICT_NUM_FIELDS__SYS_INDEXES) {
5576 ulint len;
5577 byte* field = rec_get_nth_field_old(
5578 btr_cur_get_rec(&cursor),
5579 DICT_FLD__SYS_INDEXES__MERGE_THRESHOLD, &len);
5580
5581 ut_ad(len == 4);
5582
5583 if (len == 4) {
5584 mlog_write_ulint(field, merge_threshold,
5585 MLOG_4BYTES, &mtr);
5586 }
5587 }
5588
5589 mtr_commit(&mtr);
5590 mem_heap_free(heap);
5591
5592 mutex_exit(&(dict_sys->mutex));
5593 rw_lock_x_unlock(&dict_operation_lock);
5594 }
5595
5596 #ifdef UNIV_DEBUG
5597 /** Sets merge_threshold for all indexes in the list of tables
5598 @param[in] list pointer to the list of tables */
5599 inline
5600 void
dict_set_merge_threshold_list_debug(UT_LIST_BASE_NODE_T (dict_table_t)* list,uint merge_threshold_all)5601 dict_set_merge_threshold_list_debug(
5602 UT_LIST_BASE_NODE_T(dict_table_t)* list,
5603 uint merge_threshold_all)
5604 {
5605 for (dict_table_t* table = UT_LIST_GET_FIRST(*list);
5606 table != NULL;
5607 table = UT_LIST_GET_NEXT(table_LRU, table)) {
5608 for (dict_index_t* index = UT_LIST_GET_FIRST(table->indexes);
5609 index != NULL;
5610 index = UT_LIST_GET_NEXT(indexes, index)) {
5611 rw_lock_x_lock(dict_index_get_lock(index));
5612 index->merge_threshold = merge_threshold_all;
5613 rw_lock_x_unlock(dict_index_get_lock(index));
5614 }
5615 }
5616 }
5617
5618 /** Sets merge_threshold for all indexes in dictionary cache for debug.
5619 @param[in] merge_threshold_all value to set for all indexes */
5620 void
dict_set_merge_threshold_all_debug(uint merge_threshold_all)5621 dict_set_merge_threshold_all_debug(
5622 uint merge_threshold_all)
5623 {
5624 mutex_enter(&dict_sys->mutex);
5625
5626 dict_set_merge_threshold_list_debug(
5627 &dict_sys->table_LRU, merge_threshold_all);
5628 dict_set_merge_threshold_list_debug(
5629 &dict_sys->table_non_LRU, merge_threshold_all);
5630
5631 mutex_exit(&dict_sys->mutex);
5632 }
5633
5634 #endif /* UNIV_DEBUG */
5635
5636 /** Initialize dict_ind_redundant. */
5637 void
dict_ind_init()5638 dict_ind_init()
5639 {
5640 dict_table_t* table;
5641
5642 /* create dummy table and index for REDUNDANT infimum and supremum */
5643 table = dict_mem_table_create("SYS_DUMMY1", NULL, 1, 0, 0, 0);
5644 dict_mem_table_add_col(table, NULL, NULL, DATA_CHAR,
5645 DATA_ENGLISH | DATA_NOT_NULL, 8);
5646
5647 dict_ind_redundant = dict_mem_index_create(table, "SYS_DUMMY1", 0, 1);
5648 dict_index_add_col(dict_ind_redundant, table,
5649 dict_table_get_nth_col(table, 0), 0);
5650 /* avoid ut_ad(index->cached) in dict_index_get_n_unique_in_tree */
5651 dict_ind_redundant->cached = TRUE;
5652 }
5653
5654 /** Free dict_ind_redundant. */
5655 void
dict_ind_free()5656 dict_ind_free()
5657 {
5658 dict_table_t* table = dict_ind_redundant->table;
5659 dict_mem_index_free(dict_ind_redundant);
5660 dict_ind_redundant = NULL;
5661 dict_mem_table_free(table);
5662 }
5663
5664 /** Get an index by name.
5665 @param[in] table the table where to look for the index
5666 @param[in] name the index name to look for
5667 @return index, NULL if does not exist */
5668 dict_index_t*
dict_table_get_index_on_name(dict_table_t * table,const char * name)5669 dict_table_get_index_on_name(dict_table_t* table, const char* name)
5670 {
5671 dict_index_t* index;
5672
5673 index = dict_table_get_first_index(table);
5674
5675 while (index != NULL) {
5676 if (index->is_committed() && !strcmp(index->name, name)) {
5677 return(index);
5678 }
5679
5680 index = dict_table_get_next_index(index);
5681 }
5682
5683 return(NULL);
5684 }
5685
5686 /**********************************************************************//**
5687 Replace the index passed in with another equivalent index in the
5688 foreign key lists of the table.
5689 @return whether all replacements were found */
5690 bool
dict_foreign_replace_index(dict_table_t * table,const char ** col_names,const dict_index_t * index)5691 dict_foreign_replace_index(
5692 /*=======================*/
5693 dict_table_t* table, /*!< in/out: table */
5694 const char** col_names,
5695 /*!< in: column names, or NULL
5696 to use table->col_names */
5697 const dict_index_t* index) /*!< in: index to be replaced */
5698 {
5699 bool found = true;
5700 dict_foreign_t* foreign;
5701
5702 ut_ad(index->to_be_dropped);
5703 ut_ad(index->table == table);
5704
5705 for (dict_foreign_set::iterator it = table->foreign_set.begin();
5706 it != table->foreign_set.end();
5707 ++it) {
5708
5709 foreign = *it;
5710 if (foreign->foreign_index == index) {
5711 ut_ad(foreign->foreign_table == index->table);
5712
5713 dict_index_t* new_index = dict_foreign_find_index(
5714 foreign->foreign_table, col_names,
5715 foreign->foreign_col_names,
5716 foreign->n_fields, index,
5717 /*check_charsets=*/TRUE, /*check_null=*/FALSE,
5718 NULL, NULL, NULL);
5719 if (new_index) {
5720 ut_ad(new_index->table == index->table);
5721 ut_ad(!new_index->to_be_dropped);
5722 } else {
5723 found = false;
5724 }
5725
5726 foreign->foreign_index = new_index;
5727 }
5728 }
5729
5730 for (dict_foreign_set::iterator it = table->referenced_set.begin();
5731 it != table->referenced_set.end();
5732 ++it) {
5733
5734 foreign = *it;
5735 if (foreign->referenced_index == index) {
5736 ut_ad(foreign->referenced_table == index->table);
5737
5738 dict_index_t* new_index = dict_foreign_find_index(
5739 foreign->referenced_table, NULL,
5740 foreign->referenced_col_names,
5741 foreign->n_fields, index,
5742 /*check_charsets=*/TRUE, /*check_null=*/FALSE,
5743 NULL, NULL, NULL);
5744 /* There must exist an alternative index,
5745 since this must have been checked earlier. */
5746 if (new_index) {
5747 ut_ad(new_index->table == index->table);
5748 ut_ad(!new_index->to_be_dropped);
5749 } else {
5750 found = false;
5751 }
5752
5753 foreign->referenced_index = new_index;
5754 }
5755 }
5756
5757 return(found);
5758 }
5759
5760 #ifdef UNIV_DEBUG
5761 /**********************************************************************//**
5762 Check for duplicate index entries in a table [using the index name] */
5763 void
dict_table_check_for_dup_indexes(const dict_table_t * table,enum check_name check)5764 dict_table_check_for_dup_indexes(
5765 /*=============================*/
5766 const dict_table_t* table, /*!< in: Check for dup indexes
5767 in this table */
5768 enum check_name check) /*!< in: whether and when to allow
5769 temporary index names */
5770 {
5771 /* Check for duplicates, ignoring indexes that are marked
5772 as to be dropped */
5773
5774 const dict_index_t* index1;
5775 const dict_index_t* index2;
5776
5777 ut_ad(mutex_own(&dict_sys->mutex));
5778
5779 /* The primary index _must_ exist */
5780 ut_a(UT_LIST_GET_LEN(table->indexes) > 0);
5781
5782 index1 = UT_LIST_GET_FIRST(table->indexes);
5783
5784 do {
5785 if (!index1->is_committed()) {
5786 ut_a(!dict_index_is_clust(index1));
5787
5788 switch (check) {
5789 case CHECK_ALL_COMPLETE:
5790 ut_error;
5791 case CHECK_ABORTED_OK:
5792 switch (dict_index_get_online_status(index1)) {
5793 case ONLINE_INDEX_COMPLETE:
5794 case ONLINE_INDEX_CREATION:
5795 ut_error;
5796 break;
5797 case ONLINE_INDEX_ABORTED:
5798 case ONLINE_INDEX_ABORTED_DROPPED:
5799 break;
5800 }
5801 /* fall through */
5802 case CHECK_PARTIAL_OK:
5803 break;
5804 }
5805 }
5806
5807 for (index2 = UT_LIST_GET_NEXT(indexes, index1);
5808 index2 != NULL;
5809 index2 = UT_LIST_GET_NEXT(indexes, index2)) {
5810 ut_ad(index1->is_committed()
5811 != index2->is_committed()
5812 || strcmp(index1->name, index2->name) != 0);
5813 }
5814
5815 index1 = UT_LIST_GET_NEXT(indexes, index1);
5816 } while (index1);
5817 }
5818 #endif /* UNIV_DEBUG */
5819
5820 /** Auxiliary macro used inside dict_table_schema_check(). */
5821 #define CREATE_TYPES_NAMES() \
5822 dtype_sql_name((unsigned) req_schema->columns[i].mtype, \
5823 (unsigned) req_schema->columns[i].prtype_mask, \
5824 (unsigned) req_schema->columns[i].len, \
5825 req_type, sizeof(req_type)); \
5826 dtype_sql_name(table->cols[j].mtype, \
5827 table->cols[j].prtype, \
5828 table->cols[j].len, \
5829 actual_type, sizeof(actual_type))
5830
5831 /*********************************************************************//**
5832 Checks whether a table exists and whether it has the given structure.
5833 The table must have the same number of columns with the same names and
5834 types. The order of the columns does not matter.
5835 The caller must own the dictionary mutex.
5836 dict_table_schema_check() @{
5837 @return DB_SUCCESS if the table exists and contains the necessary columns */
5838 dberr_t
dict_table_schema_check(dict_table_schema_t * req_schema,char * errstr,size_t errstr_sz)5839 dict_table_schema_check(
5840 /*====================*/
5841 dict_table_schema_t* req_schema, /*!< in/out: required table
5842 schema */
5843 char* errstr, /*!< out: human readable error
5844 message if != DB_SUCCESS is
5845 returned */
5846 size_t errstr_sz) /*!< in: errstr size */
5847 {
5848 char buf[MAX_FULL_NAME_LEN];
5849 char req_type[64];
5850 char actual_type[64];
5851 dict_table_t* table;
5852 ulint i;
5853
5854 ut_ad(mutex_own(&dict_sys->mutex));
5855
5856 table = dict_table_get_low(req_schema->table_name);
5857
5858 if (table == NULL) {
5859 bool should_print=true;
5860 /* no such table */
5861
5862 if (innobase_strcasecmp(req_schema->table_name, "mysql/innodb_table_stats") == 0) {
5863 if (innodb_table_stats_not_found_reported == false) {
5864 innodb_table_stats_not_found = true;
5865 innodb_table_stats_not_found_reported = true;
5866 } else {
5867 should_print = false;
5868 }
5869 } else if (innobase_strcasecmp(req_schema->table_name, "mysql/innodb_index_stats") == 0 ) {
5870 if (innodb_index_stats_not_found_reported == false) {
5871 innodb_index_stats_not_found = true;
5872 innodb_index_stats_not_found_reported = true;
5873 } else {
5874 should_print = false;
5875 }
5876 }
5877
5878 if (should_print) {
5879 snprintf(errstr, errstr_sz,
5880 "Table %s not found.",
5881 ut_format_name(req_schema->table_name,
5882 buf, sizeof(buf)));
5883 return(DB_TABLE_NOT_FOUND);
5884 } else {
5885 return(DB_STATS_DO_NOT_EXIST);
5886 }
5887 }
5888
5889 if (!table->is_readable() && !table->space) {
5890 /* missing tablespace */
5891
5892 snprintf(errstr, errstr_sz,
5893 "Tablespace for table %s is missing.",
5894 ut_format_name(req_schema->table_name,
5895 buf, sizeof(buf)));
5896
5897 return(DB_TABLE_NOT_FOUND);
5898 }
5899
5900 if (ulint(table->n_def - DATA_N_SYS_COLS) != req_schema->n_cols) {
5901 /* the table has a different number of columns than required */
5902 snprintf(errstr, errstr_sz,
5903 "%s has %d columns but should have " ULINTPF ".",
5904 ut_format_name(req_schema->table_name, buf,
5905 sizeof buf),
5906 table->n_def - DATA_N_SYS_COLS,
5907 req_schema->n_cols);
5908
5909 return(DB_ERROR);
5910 }
5911
5912 /* For each column from req_schema->columns[] search
5913 whether it is present in table->cols[].
5914 The following algorithm is O(n_cols^2), but is optimized to
5915 be O(n_cols) if the columns are in the same order in both arrays. */
5916
5917 for (i = 0; i < req_schema->n_cols; i++) {
5918 ulint j = dict_table_has_column(
5919 table, req_schema->columns[i].name, i);
5920
5921 if (j == table->n_def) {
5922
5923 snprintf(errstr, errstr_sz,
5924 "required column %s"
5925 " not found in table %s.",
5926 req_schema->columns[i].name,
5927 ut_format_name(
5928 req_schema->table_name,
5929 buf, sizeof(buf)));
5930
5931 return(DB_ERROR);
5932 }
5933
5934 /* we found a column with the same name on j'th position,
5935 compare column types and flags */
5936
5937 /* check length for exact match */
5938 if (req_schema->columns[i].len == table->cols[j].len) {
5939 } else if (!strcmp(req_schema->table_name, TABLE_STATS_NAME)
5940 || !strcmp(req_schema->table_name,
5941 INDEX_STATS_NAME)) {
5942 ut_ad(table->cols[j].len < req_schema->columns[i].len);
5943 ib::warn() << "Table " << req_schema->table_name
5944 << " has length mismatch in the"
5945 << " column name "
5946 << req_schema->columns[i].name
5947 << ". Please run mysql_upgrade";
5948 } else {
5949 CREATE_TYPES_NAMES();
5950
5951 snprintf(errstr, errstr_sz,
5952 "Column %s in table %s is %s"
5953 " but should be %s (length mismatch).",
5954 req_schema->columns[i].name,
5955 ut_format_name(req_schema->table_name,
5956 buf, sizeof(buf)),
5957 actual_type, req_type);
5958
5959 return(DB_ERROR);
5960 }
5961
5962 /*
5963 check mtype for exact match.
5964 This check is relaxed to allow use to use TIMESTAMP
5965 (ie INT) for last_update instead of DATA_BINARY.
5966 We have to test for both values as the innodb_table_stats
5967 table may come from MySQL and have the old type.
5968 */
5969 if (req_schema->columns[i].mtype != table->cols[j].mtype &&
5970 !(req_schema->columns[i].mtype == DATA_INT &&
5971 table->cols[j].mtype == DATA_FIXBINARY))
5972 {
5973 CREATE_TYPES_NAMES();
5974
5975 snprintf(errstr, errstr_sz,
5976 "Column %s in table %s is %s"
5977 " but should be %s (type mismatch).",
5978 req_schema->columns[i].name,
5979 ut_format_name(req_schema->table_name,
5980 buf, sizeof(buf)),
5981 actual_type, req_type);
5982
5983 return(DB_ERROR);
5984 }
5985
5986 /* check whether required prtype mask is set */
5987 if (req_schema->columns[i].prtype_mask != 0
5988 && (table->cols[j].prtype
5989 & req_schema->columns[i].prtype_mask)
5990 != req_schema->columns[i].prtype_mask) {
5991
5992 CREATE_TYPES_NAMES();
5993
5994 snprintf(errstr, errstr_sz,
5995 "Column %s in table %s is %s"
5996 " but should be %s (flags mismatch).",
5997 req_schema->columns[i].name,
5998 ut_format_name(req_schema->table_name,
5999 buf, sizeof(buf)),
6000 actual_type, req_type);
6001
6002 return(DB_ERROR);
6003 }
6004 }
6005
6006 if (req_schema->n_foreign != table->foreign_set.size()) {
6007 snprintf(
6008 errstr, errstr_sz,
6009 "Table %s has " ULINTPF " foreign key(s) pointing"
6010 " to other tables, but it must have " ULINTPF ".",
6011 ut_format_name(req_schema->table_name,
6012 buf, sizeof(buf)),
6013 static_cast<ulint>(table->foreign_set.size()),
6014 req_schema->n_foreign);
6015 return(DB_ERROR);
6016 }
6017
6018 if (req_schema->n_referenced != table->referenced_set.size()) {
6019 snprintf(
6020 errstr, errstr_sz,
6021 "There are " ULINTPF " foreign key(s) pointing to %s, "
6022 "but there must be " ULINTPF ".",
6023 static_cast<ulint>(table->referenced_set.size()),
6024 ut_format_name(req_schema->table_name,
6025 buf, sizeof(buf)),
6026 req_schema->n_referenced);
6027 return(DB_ERROR);
6028 }
6029
6030 return(DB_SUCCESS);
6031 }
6032 /* @} */
6033
6034 /*********************************************************************//**
6035 Converts a database and table name from filesystem encoding
6036 (e.g. d@i1b/a@q1b@1Kc, same format as used in dict_table_t::name) in two
6037 strings in UTF8 encoding (e.g. dцb and aюbØc). The output buffers must be
6038 at least MAX_DB_UTF8_LEN and MAX_TABLE_UTF8_LEN bytes. */
6039 void
dict_fs2utf8(const char * db_and_table,char * db_utf8,size_t db_utf8_size,char * table_utf8,size_t table_utf8_size)6040 dict_fs2utf8(
6041 /*=========*/
6042 const char* db_and_table, /*!< in: database and table names,
6043 e.g. d@i1b/a@q1b@1Kc */
6044 char* db_utf8, /*!< out: database name, e.g. dцb */
6045 size_t db_utf8_size, /*!< in: dbname_utf8 size */
6046 char* table_utf8, /*!< out: table name, e.g. aюbØc */
6047 size_t table_utf8_size)/*!< in: table_utf8 size */
6048 {
6049 char db[MAX_DATABASE_NAME_LEN + 1];
6050 ulint db_len;
6051 uint errors;
6052
6053 db_len = dict_get_db_name_len(db_and_table);
6054
6055 ut_a(db_len <= sizeof(db));
6056
6057 memcpy(db, db_and_table, db_len);
6058 db[db_len] = '\0';
6059
6060 strconvert(
6061 &my_charset_filename, db, uint(db_len), system_charset_info,
6062 db_utf8, uint(db_utf8_size), &errors);
6063
6064 /* convert each # to @0023 in table name and store the result in buf */
6065 const char* table = dict_remove_db_name(db_and_table);
6066 const char* table_p;
6067 char buf[MAX_TABLE_NAME_LEN * 5 + 1];
6068 char* buf_p;
6069 for (table_p = table, buf_p = buf; table_p[0] != '\0'; table_p++) {
6070 if (table_p[0] != '#') {
6071 buf_p[0] = table_p[0];
6072 buf_p++;
6073 } else {
6074 buf_p[0] = '@';
6075 buf_p[1] = '0';
6076 buf_p[2] = '0';
6077 buf_p[3] = '2';
6078 buf_p[4] = '3';
6079 buf_p += 5;
6080 }
6081 ut_a((size_t) (buf_p - buf) < sizeof(buf));
6082 }
6083 buf_p[0] = '\0';
6084
6085 errors = 0;
6086 strconvert(
6087 &my_charset_filename, buf, (uint) (buf_p - buf),
6088 system_charset_info,
6089 table_utf8, uint(table_utf8_size),
6090 &errors);
6091
6092 if (errors != 0) {
6093 snprintf(table_utf8, table_utf8_size, "%s%s",
6094 srv_mysql50_table_name_prefix, table);
6095 }
6096 }
6097
6098 /** Resize the hash tables besed on the current buffer pool size. */
6099 void
dict_resize()6100 dict_resize()
6101 {
6102 dict_table_t* table;
6103
6104 mutex_enter(&dict_sys->mutex);
6105
6106 /* all table entries are in table_LRU and table_non_LRU lists */
6107 hash_table_free(dict_sys->table_hash);
6108 hash_table_free(dict_sys->table_id_hash);
6109
6110 dict_sys->table_hash = hash_create(
6111 buf_pool_get_curr_size()
6112 / (DICT_POOL_PER_TABLE_HASH * UNIV_WORD_SIZE));
6113
6114 dict_sys->table_id_hash = hash_create(
6115 buf_pool_get_curr_size()
6116 / (DICT_POOL_PER_TABLE_HASH * UNIV_WORD_SIZE));
6117
6118 for (table = UT_LIST_GET_FIRST(dict_sys->table_LRU); table;
6119 table = UT_LIST_GET_NEXT(table_LRU, table)) {
6120 ulint fold = ut_fold_string(table->name.m_name);
6121 ulint id_fold = ut_fold_ull(table->id);
6122
6123 HASH_INSERT(dict_table_t, name_hash, dict_sys->table_hash,
6124 fold, table);
6125
6126 HASH_INSERT(dict_table_t, id_hash, dict_sys->table_id_hash,
6127 id_fold, table);
6128 }
6129
6130 for (table = UT_LIST_GET_FIRST(dict_sys->table_non_LRU); table;
6131 table = UT_LIST_GET_NEXT(table_LRU, table)) {
6132 ulint fold = ut_fold_string(table->name.m_name);
6133 ulint id_fold = ut_fold_ull(table->id);
6134
6135 HASH_INSERT(dict_table_t, name_hash, dict_sys->table_hash,
6136 fold, table);
6137
6138 HASH_INSERT(dict_table_t, id_hash, dict_sys->table_id_hash,
6139 id_fold, table);
6140 }
6141
6142 mutex_exit(&dict_sys->mutex);
6143 }
6144
6145 /**********************************************************************//**
6146 Closes the data dictionary module. */
6147 void
dict_close(void)6148 dict_close(void)
6149 /*============*/
6150 {
6151 if (dict_sys == NULL) {
6152 /* This should only happen if a failure occurred
6153 during redo log processing. */
6154 return;
6155 }
6156
6157 /* Acquire only because it's a pre-condition. */
6158 mutex_enter(&dict_sys->mutex);
6159
6160 /* Free the hash elements. We don't remove them from the table
6161 because we are going to destroy the table anyway. */
6162 for (ulint i = 0; i < hash_get_n_cells(dict_sys->table_id_hash); i++) {
6163 dict_table_t* table;
6164
6165 table = static_cast<dict_table_t*>(
6166 HASH_GET_FIRST(dict_sys->table_hash, i));
6167
6168 while (table) {
6169 dict_table_t* prev_table = table;
6170
6171 table = static_cast<dict_table_t*>(
6172 HASH_GET_NEXT(name_hash, prev_table));
6173 ut_ad(prev_table->magic_n == DICT_TABLE_MAGIC_N);
6174 dict_table_remove_from_cache(prev_table);
6175 }
6176 }
6177
6178 hash_table_free(dict_sys->table_hash);
6179
6180 /* The elements are the same instance as in dict_sys->table_hash,
6181 therefore we don't delete the individual elements. */
6182 hash_table_free(dict_sys->table_id_hash);
6183
6184 mutex_exit(&dict_sys->mutex);
6185 mutex_free(&dict_sys->mutex);
6186
6187 rw_lock_free(&dict_operation_lock);
6188
6189 mutex_free(&dict_foreign_err_mutex);
6190
6191 if (dict_foreign_err_file) {
6192 fclose(dict_foreign_err_file);
6193 dict_foreign_err_file = NULL;
6194 }
6195
6196 ut_free(dict_sys);
6197
6198 dict_sys = NULL;
6199 }
6200
6201 #ifdef UNIV_DEBUG
6202 /**********************************************************************//**
6203 Validate the dictionary table LRU list.
6204 @return TRUE if valid */
6205 static
6206 ibool
dict_lru_validate(void)6207 dict_lru_validate(void)
6208 /*===================*/
6209 {
6210 dict_table_t* table;
6211
6212 ut_ad(mutex_own(&dict_sys->mutex));
6213
6214 for (table = UT_LIST_GET_FIRST(dict_sys->table_LRU);
6215 table != NULL;
6216 table = UT_LIST_GET_NEXT(table_LRU, table)) {
6217
6218 ut_a(table->can_be_evicted);
6219 }
6220
6221 for (table = UT_LIST_GET_FIRST(dict_sys->table_non_LRU);
6222 table != NULL;
6223 table = UT_LIST_GET_NEXT(table_LRU, table)) {
6224
6225 ut_a(!table->can_be_evicted);
6226 }
6227
6228 return(TRUE);
6229 }
6230
6231 /**********************************************************************//**
6232 Check if a table exists in the dict table LRU list.
6233 @return TRUE if table found in LRU list */
6234 static
6235 ibool
dict_lru_find_table(const dict_table_t * find_table)6236 dict_lru_find_table(
6237 /*================*/
6238 const dict_table_t* find_table) /*!< in: table to find */
6239 {
6240 dict_table_t* table;
6241
6242 ut_ad(find_table != NULL);
6243 ut_ad(mutex_own(&dict_sys->mutex));
6244
6245 for (table = UT_LIST_GET_FIRST(dict_sys->table_LRU);
6246 table != NULL;
6247 table = UT_LIST_GET_NEXT(table_LRU, table)) {
6248
6249 ut_a(table->can_be_evicted);
6250
6251 if (table == find_table) {
6252 return(TRUE);
6253 }
6254 }
6255
6256 return(FALSE);
6257 }
6258
6259 /**********************************************************************//**
6260 Check if a table exists in the dict table non-LRU list.
6261 @return TRUE if table found in non-LRU list */
6262 static
6263 ibool
dict_non_lru_find_table(const dict_table_t * find_table)6264 dict_non_lru_find_table(
6265 /*====================*/
6266 const dict_table_t* find_table) /*!< in: table to find */
6267 {
6268 dict_table_t* table;
6269
6270 ut_ad(find_table != NULL);
6271 ut_ad(mutex_own(&dict_sys->mutex));
6272
6273 for (table = UT_LIST_GET_FIRST(dict_sys->table_non_LRU);
6274 table != NULL;
6275 table = UT_LIST_GET_NEXT(table_LRU, table)) {
6276
6277 ut_a(!table->can_be_evicted);
6278
6279 if (table == find_table) {
6280 return(TRUE);
6281 }
6282 }
6283
6284 return(FALSE);
6285 }
6286 #endif /* UNIV_DEBUG */
6287 /*********************************************************************//**
6288 Check an index to see whether its first fields are the columns in the array,
6289 in the same order and is not marked for deletion and is not the same
6290 as types_idx.
6291 @return true if the index qualifies, otherwise false */
6292 bool
dict_foreign_qualify_index(const dict_table_t * table,const char ** col_names,const char ** columns,ulint n_cols,const dict_index_t * index,const dict_index_t * types_idx,bool check_charsets,ulint check_null,fkerr_t * error,ulint * err_col_no,dict_index_t ** err_index)6293 dict_foreign_qualify_index(
6294 /*=======================*/
6295 const dict_table_t* table, /*!< in: table */
6296 const char** col_names,
6297 /*!< in: column names, or NULL
6298 to use table->col_names */
6299 const char** columns,/*!< in: array of column names */
6300 ulint n_cols, /*!< in: number of columns */
6301 const dict_index_t* index, /*!< in: index to check */
6302 const dict_index_t* types_idx,
6303 /*!< in: NULL or an index
6304 whose types the column types
6305 must match */
6306 bool check_charsets,
6307 /*!< in: whether to check
6308 charsets. only has an effect
6309 if types_idx != NULL */
6310 ulint check_null,
6311 /*!< in: nonzero if none of
6312 the columns must be declared
6313 NOT NULL */
6314 fkerr_t* error, /*!< out: error code */
6315 ulint* err_col_no,
6316 /*!< out: column number where
6317 error happened */
6318 dict_index_t** err_index)
6319 /*!< out: index where error
6320 happened */
6321 {
6322 if (dict_index_get_n_fields(index) < n_cols) {
6323 return(false);
6324 }
6325
6326 if (index->type & (DICT_SPATIAL | DICT_FTS | DICT_CORRUPT)) {
6327 return false;
6328 }
6329
6330 if (index->online_status >= ONLINE_INDEX_ABORTED) {
6331 return false;
6332 }
6333
6334 for (ulint i = 0; i < n_cols; i++) {
6335 dict_field_t* field;
6336 const char* col_name;
6337 ulint col_no;
6338
6339 field = dict_index_get_nth_field(index, i);
6340 col_no = dict_col_get_no(field->col);
6341
6342 if (field->prefix_len != 0) {
6343 /* We do not accept column prefix
6344 indexes here */
6345 if (error && err_col_no && err_index) {
6346 *error = FK_IS_PREFIX_INDEX;
6347 *err_col_no = i;
6348 *err_index = (dict_index_t*)index;
6349 }
6350 return(false);
6351 }
6352
6353 if (check_null
6354 && (field->col->prtype & DATA_NOT_NULL)) {
6355 if (error && err_col_no && err_index) {
6356 *error = FK_COL_NOT_NULL;
6357 *err_col_no = i;
6358 *err_index = (dict_index_t*)index;
6359 }
6360 return(false);
6361 }
6362
6363 if (field->col->is_virtual()) {
6364 col_name = "";
6365 for (ulint j = 0; j < table->n_v_def; j++) {
6366 col_name = dict_table_get_v_col_name(table, j);
6367 if (innobase_strcasecmp(field->name,col_name) == 0) {
6368 break;
6369 }
6370 }
6371 } else {
6372 col_name = col_names
6373 ? col_names[col_no]
6374 : dict_table_get_col_name(table, col_no);
6375 }
6376
6377 if (0 != innobase_strcasecmp(columns[i], col_name)) {
6378 return(false);
6379 }
6380
6381 if (types_idx && !cmp_cols_are_equal(
6382 dict_index_get_nth_col(index, i),
6383 dict_index_get_nth_col(types_idx, i),
6384 check_charsets)) {
6385 if (error && err_col_no && err_index) {
6386 *error = FK_COLS_NOT_EQUAL;
6387 *err_col_no = i;
6388 *err_index = (dict_index_t*)index;
6389 }
6390
6391 return(false);
6392 }
6393 }
6394
6395 return(true);
6396 }
6397
6398 /*********************************************************************//**
6399 Update the state of compression failure padding heuristics. This is
6400 called whenever a compression operation succeeds or fails.
6401 The caller must be holding info->mutex */
6402 static
6403 void
dict_index_zip_pad_update(zip_pad_info_t * info,ulint zip_threshold)6404 dict_index_zip_pad_update(
6405 /*======================*/
6406 zip_pad_info_t* info, /*<! in/out: info to be updated */
6407 ulint zip_threshold) /*<! in: zip threshold value */
6408 {
6409 ulint total;
6410 ulint fail_pct;
6411
6412 ut_ad(info);
6413
6414 total = info->success + info->failure;
6415
6416 ut_ad(total > 0);
6417
6418 if (zip_threshold == 0) {
6419 /* User has just disabled the padding. */
6420 return;
6421 }
6422
6423 if (total < ZIP_PAD_ROUND_LEN) {
6424 /* We are in middle of a round. Do nothing. */
6425 return;
6426 }
6427
6428 /* We are at a 'round' boundary. Reset the values but first
6429 calculate fail rate for our heuristic. */
6430 fail_pct = (info->failure * 100) / total;
6431 info->failure = 0;
6432 info->success = 0;
6433
6434 if (fail_pct > zip_threshold) {
6435 /* Compression failures are more then user defined
6436 threshold. Increase the pad size to reduce chances of
6437 compression failures. */
6438 ut_ad(info->pad % ZIP_PAD_INCR == 0);
6439
6440 /* Only do increment if it won't increase padding
6441 beyond max pad size. */
6442 if (info->pad + ZIP_PAD_INCR
6443 < (srv_page_size * zip_pad_max) / 100) {
6444 /* Use atomics even though we have the mutex.
6445 This is to ensure that we are able to read
6446 info->pad atomically. */
6447 my_atomic_addlint(&info->pad, ZIP_PAD_INCR);
6448
6449 MONITOR_INC(MONITOR_PAD_INCREMENTS);
6450 }
6451
6452 info->n_rounds = 0;
6453
6454 } else {
6455 /* Failure rate was OK. Another successful round
6456 completed. */
6457 ++info->n_rounds;
6458
6459 /* If enough successful rounds are completed with
6460 compression failure rate in control, decrease the
6461 padding. */
6462 if (info->n_rounds >= ZIP_PAD_SUCCESSFUL_ROUND_LIMIT
6463 && info->pad > 0) {
6464
6465 ut_ad(info->pad % ZIP_PAD_INCR == 0);
6466 /* Use atomics even though we have the mutex.
6467 This is to ensure that we are able to read
6468 info->pad atomically. */
6469 my_atomic_addlint(&info->pad, ulint(-ZIP_PAD_INCR));
6470
6471 info->n_rounds = 0;
6472
6473 MONITOR_INC(MONITOR_PAD_DECREMENTS);
6474 }
6475 }
6476 }
6477
6478 /*********************************************************************//**
6479 This function should be called whenever a page is successfully
6480 compressed. Updates the compression padding information. */
6481 void
dict_index_zip_success(dict_index_t * index)6482 dict_index_zip_success(
6483 /*===================*/
6484 dict_index_t* index) /*!< in/out: index to be updated. */
6485 {
6486 ulint zip_threshold = zip_failure_threshold_pct;
6487 if (!zip_threshold) {
6488 /* Disabled by user. */
6489 return;
6490 }
6491
6492 dict_index_zip_pad_lock(index);
6493 ++index->zip_pad.success;
6494 dict_index_zip_pad_update(&index->zip_pad, zip_threshold);
6495 dict_index_zip_pad_unlock(index);
6496 }
6497
6498 /*********************************************************************//**
6499 This function should be called whenever a page compression attempt
6500 fails. Updates the compression padding information. */
6501 void
dict_index_zip_failure(dict_index_t * index)6502 dict_index_zip_failure(
6503 /*===================*/
6504 dict_index_t* index) /*!< in/out: index to be updated. */
6505 {
6506 ulint zip_threshold = zip_failure_threshold_pct;
6507 if (!zip_threshold) {
6508 /* Disabled by user. */
6509 return;
6510 }
6511
6512 dict_index_zip_pad_lock(index);
6513 ++index->zip_pad.failure;
6514 dict_index_zip_pad_update(&index->zip_pad, zip_threshold);
6515 dict_index_zip_pad_unlock(index);
6516 }
6517
6518 /*********************************************************************//**
6519 Return the optimal page size, for which page will likely compress.
6520 @return page size beyond which page might not compress */
6521 ulint
dict_index_zip_pad_optimal_page_size(dict_index_t * index)6522 dict_index_zip_pad_optimal_page_size(
6523 /*=================================*/
6524 dict_index_t* index) /*!< in: index for which page size
6525 is requested */
6526 {
6527 ulint pad;
6528 ulint min_sz;
6529 ulint sz;
6530
6531 if (!zip_failure_threshold_pct) {
6532 /* Disabled by user. */
6533 return(srv_page_size);
6534 }
6535
6536 pad = my_atomic_loadlint(&index->zip_pad.pad);
6537
6538 ut_ad(pad < srv_page_size);
6539 sz = srv_page_size - pad;
6540
6541 /* Min size allowed by user. */
6542 ut_ad(zip_pad_max < 100);
6543 min_sz = (srv_page_size * (100 - zip_pad_max)) / 100;
6544
6545 return(ut_max(sz, min_sz));
6546 }
6547
6548 /*************************************************************//**
6549 Convert table flag to row format string.
6550 @return row format name. */
6551 const char*
dict_tf_to_row_format_string(ulint table_flag)6552 dict_tf_to_row_format_string(
6553 /*=========================*/
6554 ulint table_flag) /*!< in: row format setting */
6555 {
6556 switch (dict_tf_get_rec_format(table_flag)) {
6557 case REC_FORMAT_REDUNDANT:
6558 return("ROW_TYPE_REDUNDANT");
6559 case REC_FORMAT_COMPACT:
6560 return("ROW_TYPE_COMPACT");
6561 case REC_FORMAT_COMPRESSED:
6562 return("ROW_TYPE_COMPRESSED");
6563 case REC_FORMAT_DYNAMIC:
6564 return("ROW_TYPE_DYNAMIC");
6565 }
6566
6567 ut_error;
6568 return(0);
6569 }
6570
6571 /** Calculate the used memory occupied by the data dictionary
6572 table and index objects.
6573 @return number of bytes occupied. */
6574 UNIV_INTERN
6575 ulint
dict_sys_get_size()6576 dict_sys_get_size()
6577 {
6578 /* No mutex; this is a very crude approximation anyway */
6579 ulint size = UT_LIST_GET_LEN(dict_sys->table_LRU)
6580 + UT_LIST_GET_LEN(dict_sys->table_non_LRU);
6581 size *= sizeof(dict_table_t)
6582 + sizeof(dict_index_t) * 2
6583 + (sizeof(dict_col_t) + sizeof(dict_field_t)) * 10
6584 + sizeof(dict_field_t) * 5 /* total number of key fields */
6585 + 200; /* arbitrary, covering names and overhead */
6586
6587 return size;
6588 }
6589
6590 size_t
get_overflow_field_local_len() const6591 dict_table_t::get_overflow_field_local_len() const
6592 {
6593 if (dict_table_has_atomic_blobs(this)) {
6594 /* new-format table: do not store any BLOB prefix locally */
6595 return BTR_EXTERN_FIELD_REF_SIZE;
6596 }
6597 /* up to MySQL 5.1: store a 768-byte prefix locally */
6598 return BTR_EXTERN_FIELD_REF_SIZE + DICT_ANTELOPE_MAX_INDEX_COL_LEN;
6599 }
6600
is_stats_table() const6601 bool dict_table_t::is_stats_table() const
6602 {
6603 return !strcmp(name.m_name, TABLE_STATS_NAME) ||
6604 !strcmp(name.m_name, INDEX_STATS_NAME);
6605 }
6606