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