| 1 | /***************************************************************************** |
| 2 | |
| 3 | Copyright (c) 2013, 2016, Oracle and/or its affiliates. All Rights Reserved. |
| 4 | |
| 5 | This program is free software; you can redistribute it and/or modify it under |
| 6 | the terms of the GNU General Public License as published by the Free Software |
| 7 | Foundation; version 2 of the License. |
| 8 | |
| 9 | This program is distributed in the hope that it will be useful, but WITHOUT |
| 10 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| 11 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
| 12 | |
| 13 | You should have received a copy of the GNU General Public License along with |
| 14 | this program; if not, write to the Free Software Foundation, Inc., |
| 15 | 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA |
| 16 | |
| 17 | *****************************************************************************/ |
| 18 | |
| 19 | /**************************************************//** |
| 20 | @file fsp/fsp0space.cc |
| 21 | Multi file, shared, system tablespace implementation. |
| 22 | |
| 23 | Created 2012-11-16 by Sunny Bains as srv/srv0space.cc |
| 24 | Refactored 2013-7-26 by Kevin Lewis |
| 25 | *******************************************************/ |
| 26 | |
| 27 | #include "ha_prototypes.h" |
| 28 | |
| 29 | #include "fsp0sysspace.h" |
| 30 | #include "srv0start.h" |
| 31 | #include "trx0sys.h" |
| 32 | #include "dict0load.h" |
| 33 | #include "mem0mem.h" |
| 34 | #include "os0file.h" |
| 35 | #include "row0mysql.h" |
| 36 | #include "ut0new.h" |
| 37 | |
| 38 | /** The server header file is included to access opt_initialize global variable. |
| 39 | If server passes the option for create/open DB to SE, we should remove such |
| 40 | direct reference to server header and global variable */ |
| 41 | #include "mysqld.h" |
| 42 | |
| 43 | /** The control info of the system tablespace. */ |
| 44 | SysTablespace srv_sys_space; |
| 45 | |
| 46 | /** The control info of a temporary table shared tablespace. */ |
| 47 | SysTablespace srv_tmp_space; |
| 48 | |
| 49 | /** If the last data file is auto-extended, we add this many pages to it |
| 50 | at a time. We have to make this public because it is a config variable. */ |
| 51 | ulong sys_tablespace_auto_extend_increment; |
| 52 | |
| 53 | /** Convert a numeric string that optionally ends in G or M or K, |
| 54 | to a number containing megabytes. |
| 55 | @param[in] str String with a quantity in bytes |
| 56 | @param[out] megs The number in megabytes |
| 57 | @return next character in string */ |
| 58 | char* |
| 59 | SysTablespace::parse_units( |
| 60 | char* ptr, |
| 61 | ulint* megs) |
| 62 | { |
| 63 | char* endp; |
| 64 | |
| 65 | *megs = strtoul(ptr, &endp, 10); |
| 66 | |
| 67 | ptr = endp; |
| 68 | |
| 69 | switch (*ptr) { |
| 70 | case 'G': case 'g': |
| 71 | *megs *= 1024; |
| 72 | /* fall through */ |
| 73 | case 'M': case 'm': |
| 74 | ++ptr; |
| 75 | break; |
| 76 | case 'K': case 'k': |
| 77 | *megs /= 1024; |
| 78 | ++ptr; |
| 79 | break; |
| 80 | default: |
| 81 | *megs /= 1024 * 1024; |
| 82 | break; |
| 83 | } |
| 84 | |
| 85 | return(ptr); |
| 86 | } |
| 87 | |
| 88 | /** Parse the input params and populate member variables. |
| 89 | @param[in] filepath path to data files |
| 90 | @param[in] supports_raw true if the tablespace supports raw devices |
| 91 | @return true on success parse */ |
| 92 | bool |
| 93 | SysTablespace::parse_params( |
| 94 | const char* filepath_spec, |
| 95 | bool supports_raw) |
| 96 | { |
| 97 | char* filepath; |
| 98 | ulint size; |
| 99 | char* input_str; |
| 100 | ulint n_files = 0; |
| 101 | |
| 102 | ut_ad(m_last_file_size_max == 0); |
| 103 | ut_ad(!m_auto_extend_last_file); |
| 104 | |
| 105 | char* new_str = mem_strdup(filepath_spec); |
| 106 | char* str = new_str; |
| 107 | |
| 108 | input_str = str; |
| 109 | |
| 110 | /*---------------------- PASS 1 ---------------------------*/ |
| 111 | /* First calculate the number of data files and check syntax: |
| 112 | filepath:size[K |M | G];filepath:size[K |M | G]... . |
| 113 | Note that a Windows path may contain a drive name and a ':'. */ |
| 114 | while (*str != '\0') { |
| 115 | filepath = str; |
| 116 | |
| 117 | while ((*str != ':' && *str != '\0') |
| 118 | || (*str == ':' |
| 119 | && (*(str + 1) == '\\' || *(str + 1) == '/' |
| 120 | || *(str + 1) == ':'))) { |
| 121 | str++; |
| 122 | } |
| 123 | |
| 124 | if (*str == '\0') { |
| 125 | ut_free(new_str); |
| 126 | |
| 127 | ib::error() |
| 128 | << "syntax error in file path or size" |
| 129 | " specified is less than 1 megabyte" ; |
| 130 | return(false); |
| 131 | } |
| 132 | |
| 133 | str++; |
| 134 | |
| 135 | str = parse_units(str, &size); |
| 136 | |
| 137 | if (0 == strncmp(str, ":autoextend" , |
| 138 | (sizeof ":autoextend" ) - 1)) { |
| 139 | |
| 140 | str += (sizeof ":autoextend" ) - 1; |
| 141 | |
| 142 | if (0 == strncmp(str, ":max:" , |
| 143 | (sizeof ":max:" ) - 1)) { |
| 144 | |
| 145 | str += (sizeof ":max:" ) - 1; |
| 146 | |
| 147 | str = parse_units(str, &size); |
| 148 | } |
| 149 | |
| 150 | if (*str != '\0') { |
| 151 | ut_free(new_str); |
| 152 | ib::error() |
| 153 | << "syntax error in file path or" |
| 154 | << " size specified is less than" |
| 155 | << " 1 megabyte" ; |
| 156 | return(false); |
| 157 | } |
| 158 | } |
| 159 | |
| 160 | if (::strlen(str) >= 6 |
| 161 | && *str == 'n' |
| 162 | && *(str + 1) == 'e' |
| 163 | && *(str + 2) == 'w') { |
| 164 | |
| 165 | if (!supports_raw) { |
| 166 | ib::error() |
| 167 | << "Tablespace doesn't support raw" |
| 168 | " devices" ; |
| 169 | ut_free(new_str); |
| 170 | return(false); |
| 171 | } |
| 172 | |
| 173 | str += 3; |
| 174 | } |
| 175 | |
| 176 | if (*str == 'r' && *(str + 1) == 'a' && *(str + 2) == 'w') { |
| 177 | str += 3; |
| 178 | |
| 179 | if (!supports_raw) { |
| 180 | ib::error() |
| 181 | << "Tablespace doesn't support raw" |
| 182 | " devices" ; |
| 183 | ut_free(new_str); |
| 184 | return(false); |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | if (size == 0) { |
| 189 | |
| 190 | ut_free(new_str); |
| 191 | |
| 192 | ib::error() |
| 193 | << "syntax error in file path or size" |
| 194 | " specified is less than 1 megabyte" ; |
| 195 | |
| 196 | return(false); |
| 197 | } |
| 198 | |
| 199 | ++n_files; |
| 200 | |
| 201 | if (*str == ';') { |
| 202 | str++; |
| 203 | } else if (*str != '\0') { |
| 204 | ut_free(new_str); |
| 205 | |
| 206 | ib::error() |
| 207 | << "syntax error in file path or size" |
| 208 | " specified is less than 1 megabyte" ; |
| 209 | return(false); |
| 210 | } |
| 211 | } |
| 212 | |
| 213 | if (n_files == 0) { |
| 214 | |
| 215 | /* filepath_spec must contain at least one data file |
| 216 | definition */ |
| 217 | |
| 218 | ut_free(new_str); |
| 219 | |
| 220 | ib::error() |
| 221 | << "syntax error in file path or size specified" |
| 222 | " is less than 1 megabyte" ; |
| 223 | |
| 224 | return(false); |
| 225 | } |
| 226 | |
| 227 | /*---------------------- PASS 2 ---------------------------*/ |
| 228 | /* Then store the actual values to our arrays */ |
| 229 | str = input_str; |
| 230 | ulint order = 0; |
| 231 | |
| 232 | while (*str != '\0') { |
| 233 | filepath = str; |
| 234 | |
| 235 | /* Note that we must step over the ':' in a Windows filepath; |
| 236 | a Windows path normally looks like C:\ibdata\ibdata1:1G, but |
| 237 | a Windows raw partition may have a specification like |
| 238 | \\.\C::1Gnewraw or \\.\PHYSICALDRIVE2:1Gnewraw */ |
| 239 | |
| 240 | while ((*str != ':' && *str != '\0') |
| 241 | || (*str == ':' |
| 242 | && (*(str + 1) == '\\' || *(str + 1) == '/' |
| 243 | || *(str + 1) == ':'))) { |
| 244 | str++; |
| 245 | } |
| 246 | |
| 247 | if (*str == ':') { |
| 248 | /* Make filepath a null-terminated string */ |
| 249 | *str = '\0'; |
| 250 | str++; |
| 251 | } |
| 252 | |
| 253 | str = parse_units(str, &size); |
| 254 | |
| 255 | if (0 == strncmp(str, ":autoextend" , |
| 256 | (sizeof ":autoextend" ) - 1)) { |
| 257 | |
| 258 | m_auto_extend_last_file = true; |
| 259 | |
| 260 | str += (sizeof ":autoextend" ) - 1; |
| 261 | |
| 262 | if (0 == strncmp(str, ":max:" , |
| 263 | (sizeof ":max:" ) - 1)) { |
| 264 | |
| 265 | str += (sizeof ":max:" ) - 1; |
| 266 | |
| 267 | str = parse_units(str, &m_last_file_size_max); |
| 268 | } |
| 269 | |
| 270 | if (*str != '\0') { |
| 271 | ut_free(new_str); |
| 272 | ib::error() << "syntax error in file path or" |
| 273 | " size specified is less than 1" |
| 274 | " megabyte" ; |
| 275 | return(false); |
| 276 | } |
| 277 | } |
| 278 | |
| 279 | m_files.push_back(Datafile(filepath, flags(), size, order)); |
| 280 | Datafile* datafile = &m_files.back(); |
| 281 | datafile->make_filepath(path(), filepath, NO_EXT); |
| 282 | |
| 283 | if (::strlen(str) >= 6 |
| 284 | && *str == 'n' |
| 285 | && *(str + 1) == 'e' |
| 286 | && *(str + 2) == 'w') { |
| 287 | |
| 288 | ut_a(supports_raw); |
| 289 | |
| 290 | str += 3; |
| 291 | |
| 292 | /* Initialize new raw device only during initialize */ |
| 293 | /* JAN: TODO: MySQL 5.7 used opt_initialize */ |
| 294 | m_files.back().m_type = |
| 295 | opt_bootstrap ? SRV_NEW_RAW : SRV_OLD_RAW; |
| 296 | } |
| 297 | |
| 298 | if (*str == 'r' && *(str + 1) == 'a' && *(str + 2) == 'w') { |
| 299 | |
| 300 | ut_a(supports_raw); |
| 301 | |
| 302 | str += 3; |
| 303 | |
| 304 | /* Initialize new raw device only during initialize */ |
| 305 | if (m_files.back().m_type == SRV_NOT_RAW) { |
| 306 | /* JAN: TODO: MySQL 5.7 used opt_initialize */ |
| 307 | m_files.back().m_type = |
| 308 | opt_bootstrap ? SRV_NEW_RAW : SRV_OLD_RAW; |
| 309 | } |
| 310 | } |
| 311 | |
| 312 | if (*str == ';') { |
| 313 | ++str; |
| 314 | } |
| 315 | order++; |
| 316 | } |
| 317 | |
| 318 | ut_ad(n_files == ulint(m_files.size())); |
| 319 | |
| 320 | ut_free(new_str); |
| 321 | |
| 322 | return(true); |
| 323 | } |
| 324 | |
| 325 | /** Frees the memory allocated by the parse method. */ |
| 326 | void |
| 327 | SysTablespace::shutdown() |
| 328 | { |
| 329 | Tablespace::shutdown(); |
| 330 | |
| 331 | m_auto_extend_last_file = 0; |
| 332 | m_last_file_size_max = 0; |
| 333 | m_created_new_raw = 0; |
| 334 | m_is_tablespace_full = false; |
| 335 | m_sanity_checks_done = false; |
| 336 | } |
| 337 | |
| 338 | /** Verify the size of the physical file. |
| 339 | @param[in] file data file object |
| 340 | @return DB_SUCCESS if OK else error code. */ |
| 341 | dberr_t |
| 342 | SysTablespace::check_size( |
| 343 | Datafile& file) |
| 344 | { |
| 345 | os_offset_t size = os_file_get_size(file.m_handle); |
| 346 | ut_a(size != (os_offset_t) -1); |
| 347 | |
| 348 | /* Under some error conditions like disk full scenarios |
| 349 | or file size reaching filesystem limit the data file |
| 350 | could contain an incomplete extent at the end. When we |
| 351 | extend a data file and if some failure happens, then |
| 352 | also the data file could contain an incomplete extent. |
| 353 | So we need to round the size downward to a megabyte.*/ |
| 354 | |
| 355 | const ulint rounded_size_pages = static_cast<ulint>( |
| 356 | size >> srv_page_size_shift); |
| 357 | |
| 358 | /* If last file */ |
| 359 | if (&file == &m_files.back() && m_auto_extend_last_file) { |
| 360 | |
| 361 | if (file.m_size > rounded_size_pages |
| 362 | || (m_last_file_size_max > 0 |
| 363 | && m_last_file_size_max < rounded_size_pages)) { |
| 364 | ib::error() << "The Auto-extending " << name() |
| 365 | << " data file '" << file.filepath() << "' is" |
| 366 | " of a different size " << rounded_size_pages |
| 367 | << " pages than specified" |
| 368 | " in the .cnf file: initial " << file.m_size |
| 369 | << " pages, max " << m_last_file_size_max |
| 370 | << " (relevant if non-zero) pages!" ; |
| 371 | return(DB_ERROR); |
| 372 | } |
| 373 | |
| 374 | file.m_size = rounded_size_pages; |
| 375 | } |
| 376 | |
| 377 | if (rounded_size_pages != file.m_size) { |
| 378 | ib::error() << "The " << name() << " data file '" |
| 379 | << file.filepath() << "' is of a different size " |
| 380 | << rounded_size_pages << " pages" |
| 381 | " than the " << file.m_size << " pages specified in" |
| 382 | " the .cnf file!" ; |
| 383 | return(DB_ERROR); |
| 384 | } |
| 385 | |
| 386 | return(DB_SUCCESS); |
| 387 | } |
| 388 | |
| 389 | /** Set the size of the file. |
| 390 | @param[in] file data file object |
| 391 | @return DB_SUCCESS or error code */ |
| 392 | dberr_t |
| 393 | SysTablespace::set_size( |
| 394 | Datafile& file) |
| 395 | { |
| 396 | ut_a(!srv_read_only_mode || m_ignore_read_only); |
| 397 | |
| 398 | /* We created the data file and now write it full of zeros */ |
| 399 | ib::info() << "Setting file '" << file.filepath() << "' size to " |
| 400 | << (file.m_size >> (20U - srv_page_size_shift)) << " MB." |
| 401 | " Physically writing the file full; Please wait ..." ; |
| 402 | |
| 403 | bool success = os_file_set_size( |
| 404 | file.m_filepath, file.m_handle, |
| 405 | static_cast<os_offset_t>(file.m_size) << srv_page_size_shift); |
| 406 | |
| 407 | if (success) { |
| 408 | ib::info() << "File '" << file.filepath() << "' size is now " |
| 409 | << (file.m_size >> (20U - srv_page_size_shift)) |
| 410 | << " MB." ; |
| 411 | } else { |
| 412 | ib::error() << "Could not set the file size of '" |
| 413 | << file.filepath() << "'. Probably out of disk space" ; |
| 414 | |
| 415 | return(DB_ERROR); |
| 416 | } |
| 417 | |
| 418 | return(DB_SUCCESS); |
| 419 | } |
| 420 | |
| 421 | /** Create a data file. |
| 422 | @param[in] file data file object |
| 423 | @return DB_SUCCESS or error code */ |
| 424 | dberr_t |
| 425 | SysTablespace::create_file( |
| 426 | Datafile& file) |
| 427 | { |
| 428 | dberr_t err = DB_SUCCESS; |
| 429 | |
| 430 | ut_a(!file.m_exists); |
| 431 | ut_a(!srv_read_only_mode || m_ignore_read_only); |
| 432 | |
| 433 | switch (file.m_type) { |
| 434 | case SRV_NEW_RAW: |
| 435 | |
| 436 | /* The partition is opened, not created; then it is |
| 437 | written over */ |
| 438 | m_created_new_raw = true; |
| 439 | |
| 440 | /* Fall through. */ |
| 441 | |
| 442 | case SRV_OLD_RAW: |
| 443 | |
| 444 | srv_start_raw_disk_in_use = TRUE; |
| 445 | |
| 446 | /* Fall through. */ |
| 447 | |
| 448 | case SRV_NOT_RAW: |
| 449 | err = file.open_or_create( |
| 450 | m_ignore_read_only ? false : srv_read_only_mode); |
| 451 | break; |
| 452 | } |
| 453 | |
| 454 | |
| 455 | if (err == DB_SUCCESS && file.m_type != SRV_OLD_RAW) { |
| 456 | err = set_size(file); |
| 457 | } |
| 458 | |
| 459 | return(err); |
| 460 | } |
| 461 | |
| 462 | /** Open a data file. |
| 463 | @param[in] file data file object |
| 464 | @return DB_SUCCESS or error code */ |
| 465 | dberr_t |
| 466 | SysTablespace::open_file( |
| 467 | Datafile& file) |
| 468 | { |
| 469 | dberr_t err = DB_SUCCESS; |
| 470 | |
| 471 | ut_a(file.m_exists); |
| 472 | |
| 473 | switch (file.m_type) { |
| 474 | case SRV_NEW_RAW: |
| 475 | /* The partition is opened, not created; then it is |
| 476 | written over */ |
| 477 | m_created_new_raw = true; |
| 478 | |
| 479 | /* Fall through */ |
| 480 | |
| 481 | case SRV_OLD_RAW: |
| 482 | srv_start_raw_disk_in_use = TRUE; |
| 483 | |
| 484 | if (srv_read_only_mode && !m_ignore_read_only) { |
| 485 | ib::error() << "Can't open a raw device '" |
| 486 | << file.m_filepath << "' when" |
| 487 | " --innodb-read-only is set" ; |
| 488 | |
| 489 | return(DB_ERROR); |
| 490 | } |
| 491 | |
| 492 | /* Fall through */ |
| 493 | |
| 494 | case SRV_NOT_RAW: |
| 495 | err = file.open_or_create( |
| 496 | m_ignore_read_only ? false : srv_read_only_mode); |
| 497 | |
| 498 | if (err != DB_SUCCESS) { |
| 499 | return(err); |
| 500 | } |
| 501 | break; |
| 502 | } |
| 503 | |
| 504 | switch (file.m_type) { |
| 505 | case SRV_NEW_RAW: |
| 506 | /* Set file size for new raw device. */ |
| 507 | err = set_size(file); |
| 508 | break; |
| 509 | |
| 510 | case SRV_NOT_RAW: |
| 511 | /* Check file size for existing file. */ |
| 512 | err = check_size(file); |
| 513 | break; |
| 514 | |
| 515 | case SRV_OLD_RAW: |
| 516 | err = DB_SUCCESS; |
| 517 | break; |
| 518 | |
| 519 | } |
| 520 | |
| 521 | if (err != DB_SUCCESS) { |
| 522 | file.close(); |
| 523 | } |
| 524 | |
| 525 | return(err); |
| 526 | } |
| 527 | |
| 528 | /** Check the tablespace header for this tablespace. |
| 529 | @param[out] flushed_lsn the value of FIL_PAGE_FILE_FLUSH_LSN |
| 530 | @return DB_SUCCESS or error code */ |
| 531 | dberr_t |
| 532 | SysTablespace::read_lsn_and_check_flags(lsn_t* flushed_lsn) |
| 533 | { |
| 534 | dberr_t err; |
| 535 | |
| 536 | /* Only relevant for the system tablespace. */ |
| 537 | ut_ad(space_id() == TRX_SYS_SPACE); |
| 538 | |
| 539 | files_t::iterator it = m_files.begin(); |
| 540 | |
| 541 | ut_a(it->m_exists); |
| 542 | |
| 543 | if (it->m_handle == OS_FILE_CLOSED) { |
| 544 | |
| 545 | err = it->open_or_create( |
| 546 | m_ignore_read_only ? false : srv_read_only_mode); |
| 547 | |
| 548 | if (err != DB_SUCCESS) { |
| 549 | return(err); |
| 550 | } |
| 551 | } |
| 552 | |
| 553 | err = it->read_first_page( |
| 554 | m_ignore_read_only ? false : srv_read_only_mode); |
| 555 | |
| 556 | if (err != DB_SUCCESS) { |
| 557 | return(err); |
| 558 | } |
| 559 | |
| 560 | ut_a(it->order() == 0); |
| 561 | |
| 562 | if (srv_operation == SRV_OPERATION_NORMAL) { |
| 563 | buf_dblwr_init_or_load_pages(it->handle(), it->filepath()); |
| 564 | } |
| 565 | |
| 566 | /* Check the contents of the first page of the |
| 567 | first datafile. */ |
| 568 | for (int retry = 0; retry < 2; ++retry) { |
| 569 | |
| 570 | err = it->validate_first_page(flushed_lsn); |
| 571 | |
| 572 | if (err != DB_SUCCESS |
| 573 | && (retry == 1 |
| 574 | || it->restore_from_doublewrite())) { |
| 575 | |
| 576 | it->close(); |
| 577 | |
| 578 | return(err); |
| 579 | } |
| 580 | } |
| 581 | |
| 582 | /* Make sure the tablespace space ID matches the |
| 583 | space ID on the first page of the first datafile. */ |
| 584 | if (space_id() != it->m_space_id) { |
| 585 | |
| 586 | ib::error() |
| 587 | << "The " << name() << " data file '" << it->name() |
| 588 | << "' has the wrong space ID. It should be " |
| 589 | << space_id() << ", but " << it->m_space_id |
| 590 | << " was found" ; |
| 591 | |
| 592 | it->close(); |
| 593 | |
| 594 | return(err); |
| 595 | } |
| 596 | |
| 597 | it->close(); |
| 598 | |
| 599 | return(DB_SUCCESS); |
| 600 | } |
| 601 | |
| 602 | /** Check if a file can be opened in the correct mode. |
| 603 | @param[in] file data file object |
| 604 | @param[out] reason exact reason if file_status check failed. |
| 605 | @return DB_SUCCESS or error code. */ |
| 606 | dberr_t |
| 607 | SysTablespace::check_file_status( |
| 608 | const Datafile& file, |
| 609 | file_status_t& reason) |
| 610 | { |
| 611 | os_file_stat_t stat; |
| 612 | |
| 613 | memset(&stat, 0x0, sizeof(stat)); |
| 614 | |
| 615 | dberr_t err = os_file_get_status( |
| 616 | file.m_filepath, &stat, true, |
| 617 | m_ignore_read_only ? false : srv_read_only_mode); |
| 618 | |
| 619 | reason = FILE_STATUS_VOID; |
| 620 | /* File exists but we can't read the rw-permission settings. */ |
| 621 | switch (err) { |
| 622 | case DB_FAIL: |
| 623 | ib::error() << "os_file_get_status() failed on '" |
| 624 | << file.filepath() |
| 625 | << "'. Can't determine file permissions" ; |
| 626 | err = DB_ERROR; |
| 627 | reason = FILE_STATUS_RW_PERMISSION_ERROR; |
| 628 | break; |
| 629 | |
| 630 | case DB_SUCCESS: |
| 631 | |
| 632 | /* Note: stat.rw_perm is only valid for "regular" files */ |
| 633 | |
| 634 | if (stat.type == OS_FILE_TYPE_FILE) { |
| 635 | |
| 636 | if (!stat.rw_perm) { |
| 637 | const char *p = (!srv_read_only_mode |
| 638 | || m_ignore_read_only) |
| 639 | ? "writable" |
| 640 | : "readable" ; |
| 641 | |
| 642 | ib::error() << "The " << name() << " data file" |
| 643 | << " '" << file.name() << "' must be " |
| 644 | << p; |
| 645 | |
| 646 | err = DB_ERROR; |
| 647 | reason = FILE_STATUS_READ_WRITE_ERROR; |
| 648 | } |
| 649 | |
| 650 | } else { |
| 651 | /* Not a regular file, bail out. */ |
| 652 | ib::error() << "The " << name() << " data file '" |
| 653 | << file.name() << "' is not a regular" |
| 654 | " InnoDB data file." ; |
| 655 | |
| 656 | err = DB_ERROR; |
| 657 | reason = FILE_STATUS_NOT_REGULAR_FILE_ERROR; |
| 658 | } |
| 659 | break; |
| 660 | |
| 661 | case DB_NOT_FOUND: |
| 662 | break; |
| 663 | |
| 664 | default: |
| 665 | ut_ad(0); |
| 666 | } |
| 667 | |
| 668 | return(err); |
| 669 | } |
| 670 | |
| 671 | /** Note that the data file was not found. |
| 672 | @param[in] file data file object |
| 673 | @param[out] create_new_db true if a new instance to be created |
| 674 | @return DB_SUCESS or error code */ |
| 675 | dberr_t |
| 676 | SysTablespace::file_not_found( |
| 677 | Datafile& file, |
| 678 | bool* create_new_db) |
| 679 | { |
| 680 | file.m_exists = false; |
| 681 | |
| 682 | if (srv_read_only_mode && !m_ignore_read_only) { |
| 683 | ib::error() << "Can't create file '" << file.filepath() |
| 684 | << "' when --innodb-read-only is set" ; |
| 685 | |
| 686 | return(DB_ERROR); |
| 687 | |
| 688 | } else if (&file == &m_files.front()) { |
| 689 | |
| 690 | /* First data file. */ |
| 691 | ut_a(!*create_new_db); |
| 692 | *create_new_db = TRUE; |
| 693 | |
| 694 | if (space_id() == TRX_SYS_SPACE) { |
| 695 | ib::info() << "The first " << name() << " data file '" |
| 696 | << file.name() << "' did not exist." |
| 697 | " A new tablespace will be created!" ; |
| 698 | } |
| 699 | |
| 700 | } else { |
| 701 | ib::info() << "Need to create a new " << name() |
| 702 | << " data file '" << file.name() << "'." ; |
| 703 | } |
| 704 | |
| 705 | /* Set the file create mode. */ |
| 706 | switch (file.m_type) { |
| 707 | case SRV_NOT_RAW: |
| 708 | file.set_open_flags(OS_FILE_CREATE); |
| 709 | break; |
| 710 | |
| 711 | case SRV_NEW_RAW: |
| 712 | case SRV_OLD_RAW: |
| 713 | file.set_open_flags(OS_FILE_OPEN_RAW); |
| 714 | break; |
| 715 | } |
| 716 | |
| 717 | return(DB_SUCCESS); |
| 718 | } |
| 719 | |
| 720 | /** Note that the data file was found. |
| 721 | @param[in,out] file data file object |
| 722 | @return true if a new instance to be created */ |
| 723 | bool |
| 724 | SysTablespace::file_found( |
| 725 | Datafile& file) |
| 726 | { |
| 727 | /* Note that the file exists and can be opened |
| 728 | in the appropriate mode. */ |
| 729 | file.m_exists = true; |
| 730 | |
| 731 | /* Set the file open mode */ |
| 732 | switch (file.m_type) { |
| 733 | case SRV_NOT_RAW: |
| 734 | file.set_open_flags( |
| 735 | &file == &m_files.front() |
| 736 | ? OS_FILE_OPEN_RETRY : OS_FILE_OPEN); |
| 737 | break; |
| 738 | |
| 739 | case SRV_NEW_RAW: |
| 740 | case SRV_OLD_RAW: |
| 741 | file.set_open_flags(OS_FILE_OPEN_RAW); |
| 742 | break; |
| 743 | } |
| 744 | |
| 745 | /* Need to create the system tablespace for new raw device. */ |
| 746 | return(file.m_type == SRV_NEW_RAW); |
| 747 | } |
| 748 | |
| 749 | /** Check the data file specification. |
| 750 | @param[out] create_new_db true if a new database is to be created |
| 751 | @param[in] min_expected_size Minimum expected tablespace size in bytes |
| 752 | @return DB_SUCCESS if all OK else error code */ |
| 753 | dberr_t |
| 754 | SysTablespace::check_file_spec( |
| 755 | bool* create_new_db, |
| 756 | ulint min_expected_size) |
| 757 | { |
| 758 | *create_new_db = FALSE; |
| 759 | |
| 760 | if (m_files.size() >= 1000) { |
| 761 | ib::error() << "There must be < 1000 data files in " |
| 762 | << name() << " but " << m_files.size() << " have been" |
| 763 | " defined." ; |
| 764 | |
| 765 | return(DB_ERROR); |
| 766 | } |
| 767 | |
| 768 | if (!m_auto_extend_last_file |
| 769 | && get_sum_of_sizes() |
| 770 | < (min_expected_size >> srv_page_size_shift)) { |
| 771 | ib::error() << "Tablespace size must be at least " |
| 772 | << (min_expected_size >> 20) << " MB" ; |
| 773 | return(DB_ERROR); |
| 774 | } |
| 775 | |
| 776 | dberr_t err = DB_SUCCESS; |
| 777 | |
| 778 | ut_a(!m_files.empty()); |
| 779 | |
| 780 | /* If there is more than one data file and the last data file |
| 781 | doesn't exist, that is OK. We allow adding of new data files. */ |
| 782 | |
| 783 | files_t::iterator begin = m_files.begin(); |
| 784 | files_t::iterator end = m_files.end(); |
| 785 | |
| 786 | for (files_t::iterator it = begin; it != end; ++it) { |
| 787 | |
| 788 | file_status_t reason_if_failed; |
| 789 | err = check_file_status(*it, reason_if_failed); |
| 790 | |
| 791 | if (err == DB_NOT_FOUND) { |
| 792 | |
| 793 | err = file_not_found(*it, create_new_db); |
| 794 | |
| 795 | if (err != DB_SUCCESS) { |
| 796 | break; |
| 797 | } |
| 798 | |
| 799 | } else if (err != DB_SUCCESS) { |
| 800 | if (reason_if_failed == FILE_STATUS_READ_WRITE_ERROR) { |
| 801 | const char* p = (!srv_read_only_mode |
| 802 | || m_ignore_read_only) |
| 803 | ? "writable" : "readable" ; |
| 804 | ib::error() << "The " << name() << " data file" |
| 805 | << " '" << it->name() << "' must be " |
| 806 | << p; |
| 807 | } |
| 808 | |
| 809 | ut_a(err != DB_FAIL); |
| 810 | break; |
| 811 | |
| 812 | } else if (*create_new_db) { |
| 813 | ib::error() << "The " << name() << " data file '" |
| 814 | << begin->m_name << "' was not found but" |
| 815 | " one of the other data files '" << it->m_name |
| 816 | << "' exists." ; |
| 817 | |
| 818 | err = DB_ERROR; |
| 819 | break; |
| 820 | |
| 821 | } else { |
| 822 | *create_new_db = file_found(*it); |
| 823 | } |
| 824 | } |
| 825 | |
| 826 | return(err); |
| 827 | } |
| 828 | |
| 829 | /** Open or create the data files |
| 830 | @param[in] is_temp whether this is a temporary tablespace |
| 831 | @param[in] create_new_db whether we are creating a new database |
| 832 | @param[out] sum_new_sizes sum of sizes of the new files added |
| 833 | @param[out] flush_lsn FIL_PAGE_FILE_FLUSH_LSN of first file |
| 834 | @return DB_SUCCESS or error code */ |
| 835 | dberr_t |
| 836 | SysTablespace::open_or_create( |
| 837 | bool is_temp, |
| 838 | bool create_new_db, |
| 839 | ulint* sum_new_sizes, |
| 840 | lsn_t* flush_lsn) |
| 841 | { |
| 842 | dberr_t err = DB_SUCCESS; |
| 843 | fil_space_t* space = NULL; |
| 844 | |
| 845 | ut_ad(!m_files.empty()); |
| 846 | |
| 847 | if (sum_new_sizes) { |
| 848 | *sum_new_sizes = 0; |
| 849 | } |
| 850 | |
| 851 | files_t::iterator begin = m_files.begin(); |
| 852 | files_t::iterator end = m_files.end(); |
| 853 | |
| 854 | ut_ad(begin->order() == 0); |
| 855 | |
| 856 | for (files_t::iterator it = begin; it != end; ++it) { |
| 857 | |
| 858 | if (it->m_exists) { |
| 859 | err = open_file(*it); |
| 860 | |
| 861 | /* For new raw device increment new size. */ |
| 862 | if (sum_new_sizes && it->m_type == SRV_NEW_RAW) { |
| 863 | |
| 864 | *sum_new_sizes += it->m_size; |
| 865 | } |
| 866 | |
| 867 | } else { |
| 868 | err = create_file(*it); |
| 869 | |
| 870 | if (sum_new_sizes) { |
| 871 | *sum_new_sizes += it->m_size; |
| 872 | } |
| 873 | |
| 874 | /* Set the correct open flags now that we have |
| 875 | successfully created the file. */ |
| 876 | if (err == DB_SUCCESS) { |
| 877 | /* We ignore new_db OUT parameter here |
| 878 | as the information is known at this stage */ |
| 879 | file_found(*it); |
| 880 | } |
| 881 | } |
| 882 | |
| 883 | if (err != DB_SUCCESS) { |
| 884 | return(err); |
| 885 | } |
| 886 | |
| 887 | } |
| 888 | |
| 889 | if (!create_new_db && flush_lsn) { |
| 890 | /* Validate the header page in the first datafile |
| 891 | and read LSNs fom the others. */ |
| 892 | err = read_lsn_and_check_flags(flush_lsn); |
| 893 | if (err != DB_SUCCESS) { |
| 894 | return(err); |
| 895 | } |
| 896 | } |
| 897 | |
| 898 | /* Close the curent handles, add space and file info to the |
| 899 | fil_system cache and the Data Dictionary, and re-open them |
| 900 | in file_system cache so that they stay open until shutdown. */ |
| 901 | ulint node_counter = 0; |
| 902 | for (files_t::iterator it = begin; it != end; ++it) { |
| 903 | it->close(); |
| 904 | it->m_exists = true; |
| 905 | |
| 906 | if (it != begin) { |
| 907 | } else if (is_temp) { |
| 908 | ut_ad(!fil_system.temp_space); |
| 909 | ut_ad(space_id() == SRV_TMP_SPACE_ID); |
| 910 | space = fil_system.temp_space = fil_space_create( |
| 911 | name(), SRV_TMP_SPACE_ID, flags(), |
| 912 | FIL_TYPE_TEMPORARY, NULL); |
| 913 | } else { |
| 914 | ut_ad(!fil_system.sys_space); |
| 915 | ut_ad(space_id() == TRX_SYS_SPACE); |
| 916 | space = fil_system.sys_space = fil_space_create( |
| 917 | name(), TRX_SYS_SPACE, flags(), |
| 918 | FIL_TYPE_TABLESPACE, NULL); |
| 919 | } |
| 920 | |
| 921 | ut_a(fil_validate()); |
| 922 | |
| 923 | ulint max_size = (++node_counter == m_files.size() |
| 924 | ? (m_last_file_size_max == 0 |
| 925 | ? ULINT_MAX |
| 926 | : m_last_file_size_max) |
| 927 | : it->m_size); |
| 928 | |
| 929 | /* Add the datafile to the fil_system cache. */ |
| 930 | if (!fil_node_create( |
| 931 | it->m_filepath, it->m_size, |
| 932 | space, it->m_type != SRV_NOT_RAW, |
| 933 | TRUE, max_size)) { |
| 934 | |
| 935 | err = DB_ERROR; |
| 936 | break; |
| 937 | } |
| 938 | } |
| 939 | |
| 940 | return(err); |
| 941 | } |
| 942 | |
| 943 | /** Normalize the file size, convert from megabytes to number of pages. */ |
| 944 | void |
| 945 | SysTablespace::normalize_size() |
| 946 | { |
| 947 | files_t::iterator end = m_files.end(); |
| 948 | |
| 949 | for (files_t::iterator it = m_files.begin(); it != end; ++it) { |
| 950 | |
| 951 | it->m_size <<= (20U - srv_page_size_shift); |
| 952 | } |
| 953 | |
| 954 | m_last_file_size_max <<= (20U - srv_page_size_shift); |
| 955 | } |
| 956 | |
| 957 | |
| 958 | /** |
| 959 | @return next increment size */ |
| 960 | ulint |
| 961 | SysTablespace::get_increment() const |
| 962 | { |
| 963 | ulint increment; |
| 964 | |
| 965 | if (m_last_file_size_max == 0) { |
| 966 | increment = get_autoextend_increment(); |
| 967 | } else { |
| 968 | |
| 969 | if (!is_valid_size()) { |
| 970 | ib::error() << "The last data file in " << name() |
| 971 | << " has a size of " << last_file_size() |
| 972 | << " but the max size allowed is " |
| 973 | << m_last_file_size_max; |
| 974 | } |
| 975 | |
| 976 | increment = m_last_file_size_max - last_file_size(); |
| 977 | } |
| 978 | |
| 979 | if (increment > get_autoextend_increment()) { |
| 980 | increment = get_autoextend_increment(); |
| 981 | } |
| 982 | |
| 983 | return(increment); |
| 984 | } |
| 985 | |
| 986 | |
| 987 | /** |
| 988 | @return true if configured to use raw devices */ |
| 989 | bool |
| 990 | SysTablespace::has_raw_device() |
| 991 | { |
| 992 | files_t::iterator end = m_files.end(); |
| 993 | |
| 994 | for (files_t::iterator it = m_files.begin(); it != end; ++it) { |
| 995 | |
| 996 | if (it->is_raw_device()) { |
| 997 | return(true); |
| 998 | } |
| 999 | } |
| 1000 | |
| 1001 | return(false); |
| 1002 | } |
| 1003 | |