| 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, Suite 500, 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 "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 | /*********************************************************************//** |
| 41 | Write the meta data (index user fields) config file. |
| 42 | @return DB_SUCCESS or error code. */ |
| 43 | static MY_ATTRIBUTE((nonnull, warn_unused_result)) |
| 44 | dberr_t |
| 45 | row_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 | /*********************************************************************//** |
| 101 | Write the meta data config file index information. |
| 102 | @return DB_SUCCESS or error code. */ |
| 103 | static MY_ATTRIBUTE((nonnull, warn_unused_result)) |
| 104 | dberr_t |
| 105 | row_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 | /*********************************************************************//** |
| 212 | Write the meta data (table columns) config file. Serialise the contents of |
| 213 | dict_col_t structure, along with the column name. All fields are serialized |
| 214 | as ib_uint32_t. |
| 215 | @return DB_SUCCESS or error code. */ |
| 216 | static MY_ATTRIBUTE((nonnull, warn_unused_result)) |
| 217 | dberr_t |
| 218 | row_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 | /*********************************************************************//** |
| 301 | Write the meta data config file header. |
| 302 | @return DB_SUCCESS or error code. */ |
| 303 | static MY_ATTRIBUTE((nonnull, warn_unused_result)) |
| 304 | dberr_t |
| 305 | ( |
| 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 | /*********************************************************************//** |
| 422 | Write the table meta data after quiesce. |
| 423 | @return DB_SUCCESS or error code */ |
| 424 | static MY_ATTRIBUTE((nonnull, warn_unused_result)) |
| 425 | dberr_t |
| 426 | row_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 | /*********************************************************************//** |
| 483 | Check whether a table has an FTS index defined on it. |
| 484 | @return true if an FTS index exists on the table */ |
| 485 | static |
| 486 | bool |
| 487 | row_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 | /*********************************************************************//** |
| 511 | Quiesce the tablespace that the table resides in. */ |
| 512 | void |
| 513 | row_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 | /*********************************************************************//** |
| 570 | Cleanup after table quiesce. */ |
| 571 | void |
| 572 | row_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 | /*********************************************************************//** |
| 620 | Set a table's quiesce state. |
| 621 | @return DB_SUCCESS or error code. */ |
| 622 | dberr_t |
| 623 | row_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 | |