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 | |