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