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