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