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