1 /*****************************************************************************
2 
3 Copyright (c) 2012, 2016, Oracle and/or its affiliates. All Rights Reserved.
4 Copyright (c) 2017, 2021, MariaDB Corporation.
5 
6 This program is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free Software
8 Foundation; version 2 of the License.
9 
10 This program is distributed in the hope that it will be useful, but WITHOUT
11 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13 
14 You should have received a copy of the GNU General Public License along with
15 this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
17 
18 *****************************************************************************/
19 
20 /**************************************************//**
21 @file row/row0quiesce.cc
22 Quiesce a tablespace.
23 
24 Created 2012-02-08 by Sunny Bains.
25 *******************************************************/
26 
27 #include "row0quiesce.h"
28 #include "row0mysql.h"
29 #include "ibuf0ibuf.h"
30 #include "srv0start.h"
31 #include "trx0purge.h"
32 
33 #ifdef HAVE_MY_AES_H
34 #include <my_aes.h>
35 #endif
36 
37 /*********************************************************************//**
38 Write the meta data (index user fields) config file.
39 @return DB_SUCCESS or error code. */
40 static	MY_ATTRIBUTE((nonnull, warn_unused_result))
41 dberr_t
row_quiesce_write_index_fields(const dict_index_t * index,FILE * file,THD * thd)42 row_quiesce_write_index_fields(
43 /*===========================*/
44 	const dict_index_t*	index,	/*!< in: write the meta data for
45 					this index */
46 	FILE*			file,	/*!< in: file to write to */
47 	THD*			thd)	/*!< in/out: session */
48 {
49 	byte			row[sizeof(ib_uint32_t) * 2];
50 
51 	for (ulint i = 0; i < index->n_fields; ++i) {
52 		byte*			ptr = row;
53 		const dict_field_t*	field = &index->fields[i];
54 
55 		mach_write_to_4(ptr, field->prefix_len);
56 		ptr += sizeof(ib_uint32_t);
57 
58 		mach_write_to_4(ptr, field->fixed_len);
59 
60 		DBUG_EXECUTE_IF("ib_export_io_write_failure_9",
61 				close(fileno(file)););
62 
63 		if (fwrite(row, 1, sizeof(row), file) != sizeof(row)) {
64 
65 			ib_senderrf(
66 				thd, IB_LOG_LEVEL_WARN, ER_IO_WRITE_ERROR,
67 				(ulong) errno, strerror(errno),
68 				"while writing index fields.");
69 
70 			return(DB_IO_ERROR);
71 		}
72 
73 		const char* field_name = field->name ? field->name : "";
74 		/* Include the NUL byte in the length. */
75 		ib_uint32_t	len = static_cast<ib_uint32_t>(strlen(field_name) + 1);
76 		mach_write_to_4(row, len);
77 
78 		DBUG_EXECUTE_IF("ib_export_io_write_failure_10",
79 				close(fileno(file)););
80 
81 		if (fwrite(row, 1,  sizeof(len), file) != sizeof(len)
82 		    || fwrite(field_name, 1, len, file) != len) {
83 
84 			ib_senderrf(
85 				thd, IB_LOG_LEVEL_WARN, ER_IO_WRITE_ERROR,
86 				(ulong) errno, strerror(errno),
87 				"while writing index column.");
88 
89 			return(DB_IO_ERROR);
90 		}
91 	}
92 
93 	return(DB_SUCCESS);
94 }
95 
96 /*********************************************************************//**
97 Write the meta data config file index information.
98 @return DB_SUCCESS or error code. */
99 static	MY_ATTRIBUTE((nonnull, warn_unused_result))
100 dberr_t
row_quiesce_write_indexes(const dict_table_t * table,FILE * file,THD * thd)101 row_quiesce_write_indexes(
102 /*======================*/
103 	const dict_table_t*	table,	/*!< in: write the meta data for
104 					this table */
105 	FILE*			file,	/*!< in: file to write to */
106 	THD*			thd)	/*!< in/out: session */
107 {
108 	{
109 		byte		row[sizeof(ib_uint32_t)];
110 
111 		/* Write the number of indexes in the table. */
112 		mach_write_to_4(row, UT_LIST_GET_LEN(table->indexes));
113 
114 		DBUG_EXECUTE_IF("ib_export_io_write_failure_11",
115 				close(fileno(file)););
116 
117 		if (fwrite(row, 1,  sizeof(row), file) != sizeof(row)) {
118 			ib_senderrf(
119 				thd, IB_LOG_LEVEL_WARN, ER_IO_WRITE_ERROR,
120 				(ulong) errno, strerror(errno),
121 				"while writing index count.");
122 
123 			return(DB_IO_ERROR);
124 		}
125 	}
126 
127 	dberr_t			err = DB_SUCCESS;
128 
129 	/* Write the index meta data. */
130 	for (const dict_index_t* index = UT_LIST_GET_FIRST(table->indexes);
131 	     index != 0 && err == DB_SUCCESS;
132 	     index = UT_LIST_GET_NEXT(indexes, index)) {
133 
134 		byte*		ptr;
135 		byte		row[sizeof(index_id_t)
136 				    + sizeof(ib_uint32_t) * 8];
137 
138 		ptr = row;
139 
140 		ut_ad(sizeof(index_id_t) == 8);
141 		mach_write_to_8(ptr, index->id);
142 		ptr += sizeof(index_id_t);
143 
144 		mach_write_to_4(ptr, table->space_id);
145 		ptr += sizeof(ib_uint32_t);
146 
147 		mach_write_to_4(ptr, index->page);
148 		ptr += sizeof(ib_uint32_t);
149 
150 		mach_write_to_4(ptr, index->type);
151 		ptr += sizeof(ib_uint32_t);
152 
153 		mach_write_to_4(ptr, index->trx_id_offset);
154 		ptr += sizeof(ib_uint32_t);
155 
156 		mach_write_to_4(ptr, index->n_user_defined_cols);
157 		ptr += sizeof(ib_uint32_t);
158 
159 		mach_write_to_4(ptr, index->n_uniq);
160 		ptr += sizeof(ib_uint32_t);
161 
162 		mach_write_to_4(ptr, index->n_nullable);
163 		ptr += sizeof(ib_uint32_t);
164 
165 		mach_write_to_4(ptr, index->n_fields);
166 
167 		DBUG_EXECUTE_IF("ib_export_io_write_failure_12",
168 				close(fileno(file)););
169 
170 		if (fwrite(row, 1, sizeof(row), file) != sizeof(row)) {
171 
172 			ib_senderrf(
173 				thd, IB_LOG_LEVEL_WARN, ER_IO_WRITE_ERROR,
174 				(ulong) errno, strerror(errno),
175 				"while writing index meta-data.");
176 
177 			return(DB_IO_ERROR);
178 		}
179 
180 		/* Write the length of the index name.
181 		NUL byte is included in the length. */
182 		ib_uint32_t	len = static_cast<ib_uint32_t>(strlen(index->name) + 1);
183 		ut_a(len > 1);
184 
185 		mach_write_to_4(row, len);
186 
187 		DBUG_EXECUTE_IF("ib_export_io_write_failure_1",
188 				close(fileno(file)););
189 
190 		if (fwrite(row, 1, sizeof(len), file) != sizeof(len)
191 		    || fwrite(index->name, 1, len, file) != len) {
192 
193 			ib_senderrf(
194 				thd, IB_LOG_LEVEL_WARN, ER_IO_WRITE_ERROR,
195 				(ulong) errno, strerror(errno),
196 				"while writing index name.");
197 
198 			return(DB_IO_ERROR);
199 		}
200 
201 		err = row_quiesce_write_index_fields(index, file, thd);
202 	}
203 
204 	return(err);
205 }
206 
207 /*********************************************************************//**
208 Write the meta data (table columns) config file. Serialise the contents of
209 dict_col_t structure, along with the column name. All fields are serialized
210 as ib_uint32_t.
211 @return DB_SUCCESS or error code. */
212 static	MY_ATTRIBUTE((nonnull, warn_unused_result))
213 dberr_t
row_quiesce_write_table(const dict_table_t * table,FILE * file,THD * thd)214 row_quiesce_write_table(
215 /*====================*/
216 	const dict_table_t*	table,	/*!< in: write the meta data for
217 					this table */
218 	FILE*			file,	/*!< in: file to write to */
219 	THD*			thd)	/*!< in/out: session */
220 {
221 	dict_col_t*		col;
222 	byte			row[sizeof(ib_uint32_t) * 7];
223 
224 	col = table->cols;
225 
226 	for (ulint i = 0; i < table->n_cols; ++i, ++col) {
227 		byte*		ptr = row;
228 
229 		mach_write_to_4(ptr, col->prtype);
230 		ptr += sizeof(ib_uint32_t);
231 
232 		mach_write_to_4(ptr, col->mtype);
233 		ptr += sizeof(ib_uint32_t);
234 
235 		mach_write_to_4(ptr, col->len);
236 		ptr += sizeof(ib_uint32_t);
237 
238 		/* FIXME: This will not work if mbminlen>4.
239 		This field is also redundant, because the lengths
240 		are a property of the character set encoding, which
241 		in turn is encodedin prtype above. */
242 		mach_write_to_4(ptr, ulint(col->mbmaxlen * 5 + col->mbminlen));
243 		ptr += sizeof(ib_uint32_t);
244 
245 		mach_write_to_4(ptr, col->ind);
246 		ptr += sizeof(ib_uint32_t);
247 
248 		mach_write_to_4(ptr, col->ord_part);
249 		ptr += sizeof(ib_uint32_t);
250 
251 		mach_write_to_4(ptr, col->max_prefix);
252 
253 		DBUG_EXECUTE_IF("ib_export_io_write_failure_2",
254 				close(fileno(file)););
255 
256 		if (fwrite(row, 1,  sizeof(row), file) != sizeof(row)) {
257 			ib_senderrf(
258 				thd, IB_LOG_LEVEL_WARN, ER_IO_WRITE_ERROR,
259 				(ulong) errno, strerror(errno),
260 				"while writing table column data.");
261 
262 			return(DB_IO_ERROR);
263 		}
264 
265 		/* Write out the column name as [len, byte array]. The len
266 		includes the NUL byte. */
267 		ib_uint32_t	len;
268 		const char*	col_name;
269 
270 		col_name = dict_table_get_col_name(table, dict_col_get_no(col));
271 
272 		/* Include the NUL byte in the length. */
273 		len = static_cast<ib_uint32_t>(strlen(col_name) + 1);
274 		ut_a(len > 1);
275 
276 		mach_write_to_4(row, len);
277 
278 		DBUG_EXECUTE_IF("ib_export_io_write_failure_3",
279 				close(fileno(file)););
280 
281 		if (fwrite(row, 1,  sizeof(len), file) != sizeof(len)
282 		    || fwrite(col_name, 1, len, file) != len) {
283 
284 			ib_senderrf(
285 				thd, IB_LOG_LEVEL_WARN, ER_IO_WRITE_ERROR,
286 				(ulong) errno, strerror(errno),
287 				"while writing column name.");
288 
289 			return(DB_IO_ERROR);
290 		}
291 	}
292 
293 	return(DB_SUCCESS);
294 }
295 
296 /*********************************************************************//**
297 Write the meta data config file header.
298 @return DB_SUCCESS or error code. */
299 static	MY_ATTRIBUTE((nonnull, warn_unused_result))
300 dberr_t
row_quiesce_write_header(const dict_table_t * table,FILE * file,THD * thd)301 row_quiesce_write_header(
302 /*=====================*/
303 	const dict_table_t*	table,	/*!< in: write the meta data for
304 					this table */
305 	FILE*			file,	/*!< in: file to write to */
306 	THD*			thd)	/*!< in/out: session */
307 {
308 	byte			value[sizeof(ib_uint32_t)];
309 
310 	/* Write the meta-data version number. */
311 	mach_write_to_4(value, IB_EXPORT_CFG_VERSION_V1);
312 
313 	DBUG_EXECUTE_IF("ib_export_io_write_failure_4", close(fileno(file)););
314 
315 	if (fwrite(&value, 1,  sizeof(value), file) != sizeof(value)) {
316 		ib_senderrf(
317 			thd, IB_LOG_LEVEL_WARN, ER_IO_WRITE_ERROR,
318 			(ulong) errno, strerror(errno),
319 			"while writing meta-data version number.");
320 
321 		return(DB_IO_ERROR);
322 	}
323 
324 	/* Write the server hostname. */
325 	ib_uint32_t		len;
326 	const char*		hostname = server_get_hostname();
327 
328 	/* Play it safe and check for NULL. */
329 	if (hostname == 0) {
330 		static const char	NullHostname[] = "Hostname unknown";
331 
332 		ib::warn() << "Unable to determine server hostname.";
333 
334 		hostname = NullHostname;
335 	}
336 
337 	/* The server hostname includes the NUL byte. */
338 	len = static_cast<ib_uint32_t>(strlen(hostname) + 1);
339 	mach_write_to_4(value, len);
340 
341 	DBUG_EXECUTE_IF("ib_export_io_write_failure_5", close(fileno(file)););
342 
343 	if (fwrite(&value, 1,  sizeof(value), file) != sizeof(value)
344 	    || fwrite(hostname, 1,  len, file) != len) {
345 
346 		ib_senderrf(
347 			thd, IB_LOG_LEVEL_WARN, ER_IO_WRITE_ERROR,
348 			(ulong) errno, strerror(errno),
349 			"while writing hostname.");
350 
351 		return(DB_IO_ERROR);
352 	}
353 
354 	/* The table name includes the NUL byte. */
355 	ut_a(table->name.m_name != NULL);
356 	len = static_cast<ib_uint32_t>(strlen(table->name.m_name) + 1);
357 
358 	/* Write the table name. */
359 	mach_write_to_4(value, len);
360 
361 	DBUG_EXECUTE_IF("ib_export_io_write_failure_6", close(fileno(file)););
362 
363 	if (fwrite(&value, 1,  sizeof(value), file) != sizeof(value)
364 	    || fwrite(table->name.m_name, 1,  len, file) != len) {
365 
366 		ib_senderrf(
367 			thd, IB_LOG_LEVEL_WARN, ER_IO_WRITE_ERROR,
368 			(ulong) errno, strerror(errno),
369 			"while writing table name.");
370 
371 		return(DB_IO_ERROR);
372 	}
373 
374 	byte		row[sizeof(ib_uint32_t) * 3];
375 
376 	/* Write the next autoinc value. */
377 	mach_write_to_8(row, table->autoinc);
378 
379 	DBUG_EXECUTE_IF("ib_export_io_write_failure_7", close(fileno(file)););
380 
381 	if (fwrite(row, 1,  sizeof(ib_uint64_t), file) != sizeof(ib_uint64_t)) {
382 		ib_senderrf(
383 			thd, IB_LOG_LEVEL_WARN, ER_IO_WRITE_ERROR,
384 			(ulong) errno, strerror(errno),
385 			"while writing table autoinc value.");
386 
387 		return(DB_IO_ERROR);
388 	}
389 
390 	byte*		ptr = row;
391 
392 	/* Write the system page size. */
393 	mach_write_to_4(ptr, srv_page_size);
394 	ptr += sizeof(ib_uint32_t);
395 
396 	/* Write the table->flags. */
397 	mach_write_to_4(ptr, table->flags);
398 	ptr += sizeof(ib_uint32_t);
399 
400 	/* Write the number of columns in the table. */
401 	mach_write_to_4(ptr, table->n_cols);
402 
403 	DBUG_EXECUTE_IF("ib_export_io_write_failure_8", close(fileno(file)););
404 
405 	if (fwrite(row, 1,  sizeof(row), file) != sizeof(row)) {
406 		ib_senderrf(
407 			thd, IB_LOG_LEVEL_WARN, ER_IO_WRITE_ERROR,
408 			(ulong) errno, strerror(errno),
409 			"while writing table meta-data.");
410 
411 		return(DB_IO_ERROR);
412 	}
413 
414 	return(DB_SUCCESS);
415 }
416 
417 /*********************************************************************//**
418 Write the table meta data after quiesce.
419 @return DB_SUCCESS or error code */
420 static	MY_ATTRIBUTE((nonnull, warn_unused_result))
421 dberr_t
row_quiesce_write_cfg(dict_table_t * table,THD * thd)422 row_quiesce_write_cfg(
423 /*==================*/
424 	dict_table_t*	table,	/*!< in: write the meta data for
425 					this table */
426 	THD*			thd)	/*!< in/out: session */
427 {
428 	dberr_t			err;
429 	char			name[OS_FILE_MAX_PATH];
430 
431 	srv_get_meta_data_filename(table, name, sizeof(name));
432 
433 	ib::info() << "Writing table metadata to '" << name << "'";
434 
435 	FILE*	file = fopen(name, "w+b");
436 
437 	if (file == NULL) {
438 		ib_errf(thd, IB_LOG_LEVEL_WARN, ER_CANT_CREATE_FILE,
439 			 name, errno, strerror(errno));
440 
441 		err = DB_IO_ERROR;
442 	} else {
443 		err = row_quiesce_write_header(table, file, thd);
444 
445 		if (err == DB_SUCCESS) {
446 			err = row_quiesce_write_table(table, file, thd);
447 		}
448 
449 		if (err == DB_SUCCESS) {
450 			err = row_quiesce_write_indexes(table, file, thd);
451 		}
452 
453 		if (fflush(file) != 0) {
454 
455 			char	msg[BUFSIZ];
456 
457 			snprintf(msg, sizeof(msg), "%s flush() failed", name);
458 
459 			ib_senderrf(
460 				thd, IB_LOG_LEVEL_WARN, ER_IO_WRITE_ERROR,
461 				(ulong) errno, strerror(errno), msg);
462 		}
463 
464 		if (fclose(file) != 0) {
465 			char	msg[BUFSIZ];
466 
467 			snprintf(msg, sizeof(msg), "%s flose() failed", name);
468 
469 			ib_senderrf(
470 				thd, IB_LOG_LEVEL_WARN, ER_IO_WRITE_ERROR,
471 				(ulong) errno, strerror(errno), msg);
472 		}
473 	}
474 
475 	return(err);
476 }
477 
478 /*********************************************************************//**
479 Check whether a table has an FTS index defined on it.
480 @return true if an FTS index exists on the table */
481 static
482 bool
row_quiesce_table_has_fts_index(const dict_table_t * table)483 row_quiesce_table_has_fts_index(
484 /*============================*/
485 	const dict_table_t*	table)	/*!< in: quiesce this table */
486 {
487 	bool			exists = false;
488 
489 	dict_mutex_enter_for_mysql();
490 
491 	for (const dict_index_t* index = UT_LIST_GET_FIRST(table->indexes);
492 	     index != 0;
493 	     index = UT_LIST_GET_NEXT(indexes, index)) {
494 
495 		if (index->type & DICT_FTS) {
496 			exists = true;
497 			break;
498 		}
499 	}
500 
501 	dict_mutex_exit_for_mysql();
502 
503 	return(exists);
504 }
505 
506 /*********************************************************************//**
507 Quiesce the tablespace that the table resides in. */
508 void
row_quiesce_table_start(dict_table_t * table,trx_t * trx)509 row_quiesce_table_start(
510 /*====================*/
511 	dict_table_t*	table,		/*!< in: quiesce this table */
512 	trx_t*		trx)		/*!< in/out: transaction/session */
513 {
514 	ut_a(trx->mysql_thd != 0);
515 	ut_a(srv_n_purge_threads > 0);
516 	ut_ad(!srv_read_only_mode);
517 
518 	ut_a(trx->mysql_thd != 0);
519 
520 	ut_ad(table->space != NULL);
521 	ib::info() << "Sync to disk of " << table->name << " started.";
522 
523 	if (srv_undo_sources) {
524 		purge_sys.stop();
525 	}
526 
527 	for (ulint count = 0;
528 	     ibuf_merge_space(table->space_id);
529 	     ++count) {
530 		if (trx_is_interrupted(trx)) {
531 			goto aborted;
532 		}
533 		if (!(count % 20)) {
534 			ib::info() << "Merging change buffer entries for "
535 				<< table->name;
536 		}
537 	}
538 
539 	while (buf_flush_list_space(table->space)) {
540 		if (trx_is_interrupted(trx)) {
541 			goto aborted;
542 		}
543 	}
544 
545 	if (!trx_is_interrupted(trx)) {
546 		/* Ensure that all asynchronous IO is completed. */
547 		os_aio_wait_until_no_pending_writes();
548 		table->space->flush<false>();
549 
550 		if (row_quiesce_write_cfg(table, trx->mysql_thd)
551 		    != DB_SUCCESS) {
552 			ib::warn() << "There was an error writing to the"
553 				" meta data file";
554 		} else {
555 			ib::info() << "Table " << table->name
556 				<< " flushed to disk";
557 		}
558 	} else {
559 aborted:
560 		ib::warn() << "Quiesce aborted!";
561 	}
562 
563 	dberr_t	err = row_quiesce_set_state(table, QUIESCE_COMPLETE, trx);
564 	ut_a(err == DB_SUCCESS);
565 }
566 
567 /*********************************************************************//**
568 Cleanup after table quiesce. */
569 void
row_quiesce_table_complete(dict_table_t * table,trx_t * trx)570 row_quiesce_table_complete(
571 /*=======================*/
572 	dict_table_t*	table,		/*!< in: quiesce this table */
573 	trx_t*		trx)		/*!< in/out: transaction/session */
574 {
575 	ulint		count = 0;
576 
577 	ut_a(trx->mysql_thd != 0);
578 
579 	/* We need to wait for the operation to complete if the
580 	transaction has been killed. */
581 
582 	while (table->quiesce != QUIESCE_COMPLETE) {
583 
584 		/* Print a warning after every minute. */
585 		if (!(count % 60)) {
586 			ib::warn() << "Waiting for quiesce of " << table->name
587 				<< " to complete";
588 		}
589 
590 		/* Sleep for a second. */
591 		os_thread_sleep(1000000);
592 
593 		++count;
594 	}
595 
596 	if (!opt_bootstrap) {
597 		/* Remove the .cfg file now that the user has resumed
598 		normal operations. Otherwise it will cause problems when
599 		the user tries to drop the database (remove directory). */
600 		char		cfg_name[OS_FILE_MAX_PATH];
601 
602 		srv_get_meta_data_filename(table, cfg_name, sizeof(cfg_name));
603 
604 		os_file_delete_if_exists(innodb_data_file_key, cfg_name, NULL);
605 
606 		ib::info() << "Deleting the meta-data file '" << cfg_name << "'";
607 	}
608 
609 	if (srv_undo_sources) {
610 		purge_sys.resume();
611 	}
612 
613 	dberr_t	err = row_quiesce_set_state(table, QUIESCE_NONE, trx);
614 	ut_a(err == DB_SUCCESS);
615 }
616 
617 /*********************************************************************//**
618 Set a table's quiesce state.
619 @return DB_SUCCESS or error code. */
620 dberr_t
row_quiesce_set_state(dict_table_t * table,ib_quiesce_t state,trx_t * trx)621 row_quiesce_set_state(
622 /*==================*/
623 	dict_table_t*	table,		/*!< in: quiesce this table */
624 	ib_quiesce_t	state,		/*!< in: quiesce state to set */
625 	trx_t*		trx)		/*!< in/out: transaction */
626 {
627 	ut_a(srv_n_purge_threads > 0);
628 
629 	if (srv_read_only_mode) {
630 
631 		ib_senderrf(trx->mysql_thd,
632 			    IB_LOG_LEVEL_WARN, ER_READ_ONLY_MODE);
633 
634 		return(DB_UNSUPPORTED);
635 
636 	} else if (table->is_temporary()) {
637 
638 		ib_senderrf(trx->mysql_thd, IB_LOG_LEVEL_WARN,
639 			    ER_CANNOT_DISCARD_TEMPORARY_TABLE);
640 
641 		return(DB_UNSUPPORTED);
642 	} else if (table->space_id == TRX_SYS_SPACE) {
643 
644 		char	table_name[MAX_FULL_NAME_LEN + 1];
645 
646 		innobase_format_name(
647 			table_name, sizeof(table_name),
648 			table->name.m_name);
649 
650 		ib_senderrf(trx->mysql_thd, IB_LOG_LEVEL_WARN,
651 			    ER_TABLE_IN_SYSTEM_TABLESPACE, table_name);
652 
653 		return(DB_UNSUPPORTED);
654 	} else if (row_quiesce_table_has_fts_index(table)) {
655 
656 		ib_senderrf(trx->mysql_thd, IB_LOG_LEVEL_WARN,
657 			    ER_NOT_SUPPORTED_YET,
658 			    "FLUSH TABLES on tables that have an FTS index."
659 			    " FTS auxiliary tables will not be flushed.");
660 
661 	} else if (DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS_HAS_DOC_ID)) {
662 		/* If this flag is set then the table may not have any active
663 		FTS indexes but it will still have the auxiliary tables. */
664 
665 		ib_senderrf(trx->mysql_thd, IB_LOG_LEVEL_WARN,
666 			    ER_NOT_SUPPORTED_YET,
667 			    "FLUSH TABLES on a table that had an FTS index,"
668 			    " created on a hidden column, the"
669 			    " auxiliary tables haven't been dropped as yet."
670 			    " FTS auxiliary tables will not be flushed.");
671 	}
672 
673 	dict_index_t* clust_index = dict_table_get_first_index(table);
674 
675 	row_mysql_lock_data_dictionary(trx);
676 
677 	for (dict_index_t* index = dict_table_get_next_index(clust_index);
678 	     index != NULL;
679 	     index = dict_table_get_next_index(index)) {
680 		rw_lock_x_lock(&index->lock);
681 	}
682 
683 	rw_lock_x_lock(&clust_index->lock);
684 
685 	switch (state) {
686 	case QUIESCE_START:
687 		break;
688 
689 	case QUIESCE_COMPLETE:
690 		ut_a(table->quiesce == QUIESCE_START);
691 		break;
692 
693 	case QUIESCE_NONE:
694 		ut_a(table->quiesce == QUIESCE_COMPLETE);
695 		break;
696 	}
697 
698 	table->quiesce = state;
699 
700 	for (dict_index_t* index = dict_table_get_first_index(table);
701 	     index != NULL;
702 	     index = dict_table_get_next_index(index)) {
703 		rw_lock_x_unlock(&index->lock);
704 	}
705 
706 	row_mysql_unlock_data_dictionary(trx);
707 
708 	return(DB_SUCCESS);
709 }
710 
711