1 /*****************************************************************************
2
3 Copyright (c) 1996, 2017, Oracle and/or its affiliates. All Rights Reserved.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License, version 2.0,
7 as published by the Free Software Foundation.
8
9 This program is also distributed with certain software (including
10 but not limited to OpenSSL) that is licensed under separate terms,
11 as designated in a particular file or component or in included license
12 documentation. The authors of MySQL hereby grant you an additional
13 permission to link the program and your derivative works with the
14 separately licensed software that they have included with MySQL.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License, version 2.0, for more details.
20
21 You should have received a copy of the GNU General Public License along with
22 this program; if not, write to the Free Software Foundation, Inc.,
23 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
24
25 *****************************************************************************/
26
27 /**************************************************//**
28 @file trx/trx0sys.cc
29 Transaction system
30
31 Created 3/26/1996 Heikki Tuuri
32 *******************************************************/
33
34 #include "trx0sys.h"
35
36 #ifdef UNIV_NONINL
37 #include "trx0sys.ic"
38 #endif
39
40 #ifdef UNIV_HOTBACKUP
41 #include "fsp0types.h"
42
43 #else /* !UNIV_HOTBACKUP */
44 #include "fsp0fsp.h"
45 #include "mtr0log.h"
46 #include "mtr0log.h"
47 #include "trx0trx.h"
48 #include "trx0rseg.h"
49 #include "trx0undo.h"
50 #include "srv0srv.h"
51 #include "srv0start.h"
52 #include "trx0purge.h"
53 #include "log0log.h"
54 #include "log0recv.h"
55 #include "os0file.h"
56 #include "read0read.h"
57
58 /** The file format tag structure with id and name. */
59 struct file_format_t {
60 ulint id; /*!< id of the file format */
61 const char* name; /*!< text representation of the
62 file format */
63 ib_mutex_t mutex; /*!< covers changes to the above
64 fields */
65 };
66
67 /** The transaction system */
68 UNIV_INTERN trx_sys_t* trx_sys = NULL;
69
70 /** In a MySQL replication slave, in crash recovery we store the master log
71 file name and position here. */
72 /* @{ */
73 /** Master binlog file name */
74 UNIV_INTERN char trx_sys_mysql_master_log_name[TRX_SYS_MYSQL_LOG_NAME_LEN];
75 /** Master binlog file position. We have successfully got the updates
76 up to this position. -1 means that no crash recovery was needed, or
77 there was no master log position info inside InnoDB.*/
78 UNIV_INTERN ib_int64_t trx_sys_mysql_master_log_pos = -1;
79 /* @} */
80
81 /** If this MySQL server uses binary logging, after InnoDB has been inited
82 and if it has done a crash recovery, we store the binlog file name and position
83 here. */
84 /* @{ */
85 /** Binlog file name */
86 UNIV_INTERN char trx_sys_mysql_bin_log_name[TRX_SYS_MYSQL_LOG_NAME_LEN];
87 /** Binlog file position, or -1 if unknown */
88 UNIV_INTERN ib_int64_t trx_sys_mysql_bin_log_pos = -1;
89 /* @} */
90 #endif /* !UNIV_HOTBACKUP */
91
92 /** List of animal names representing file format. */
93 static const char* file_format_name_map[] = {
94 "Antelope",
95 "Barracuda",
96 "Cheetah",
97 "Dragon",
98 "Elk",
99 "Fox",
100 "Gazelle",
101 "Hornet",
102 "Impala",
103 "Jaguar",
104 "Kangaroo",
105 "Leopard",
106 "Moose",
107 "Nautilus",
108 "Ocelot",
109 "Porpoise",
110 "Quail",
111 "Rabbit",
112 "Shark",
113 "Tiger",
114 "Urchin",
115 "Viper",
116 "Whale",
117 "Xenops",
118 "Yak",
119 "Zebra"
120 };
121
122 /** The number of elements in the file format name array. */
123 static const ulint FILE_FORMAT_NAME_N
124 = sizeof(file_format_name_map) / sizeof(file_format_name_map[0]);
125
126 #ifdef UNIV_PFS_MUTEX
127 /* Key to register the mutex with performance schema */
128 UNIV_INTERN mysql_pfs_key_t file_format_max_mutex_key;
129 UNIV_INTERN mysql_pfs_key_t trx_sys_mutex_key;
130 #endif /* UNIV_PFS_RWLOCK */
131
132 #ifndef UNIV_HOTBACKUP
133 #ifdef UNIV_DEBUG
134 /* Flag to control TRX_RSEG_N_SLOTS behavior debugging. */
135 UNIV_INTERN uint trx_rseg_n_slots_debug = 0;
136 #endif
137
138 /** This is used to track the maximum file format id known to InnoDB. It's
139 updated via SET GLOBAL innodb_file_format_max = 'x' or when we open
140 or create a table. */
141 static file_format_t file_format_max;
142
143 #ifdef UNIV_DEBUG
144 /****************************************************************//**
145 Checks whether a trx is in one of rw_trx_list or ro_trx_list.
146 @return TRUE if is in */
147 UNIV_INTERN
148 ibool
trx_in_trx_list(const trx_t * in_trx)149 trx_in_trx_list(
150 /*============*/
151 const trx_t* in_trx) /*!< in: transaction */
152 {
153 const trx_t* trx;
154 trx_list_t* trx_list;
155
156 /* Non-locking autocommits should not hold any locks. */
157 assert_trx_in_list(in_trx);
158
159 trx_list = in_trx->read_only
160 ? &trx_sys->ro_trx_list : &trx_sys->rw_trx_list;
161
162 ut_ad(mutex_own(&trx_sys->mutex));
163
164 ut_ad(trx_assert_started(in_trx));
165
166 for (trx = UT_LIST_GET_FIRST(*trx_list);
167 trx != NULL && trx != in_trx;
168 trx = UT_LIST_GET_NEXT(trx_list, trx)) {
169
170 assert_trx_in_list(trx);
171 ut_ad(trx->read_only == (trx_list == &trx_sys->ro_trx_list));
172 }
173
174 return(trx != NULL);
175 }
176 #endif /* UNIV_DEBUG */
177
178 /*****************************************************************//**
179 Writes the value of max_trx_id to the file based trx system header. */
180 UNIV_INTERN
181 void
trx_sys_flush_max_trx_id(void)182 trx_sys_flush_max_trx_id(void)
183 /*==========================*/
184 {
185 mtr_t mtr;
186 trx_sysf_t* sys_header;
187
188 ut_ad(mutex_own(&trx_sys->mutex));
189
190 if (!srv_read_only_mode) {
191 mtr_start(&mtr);
192
193 sys_header = trx_sysf_get(&mtr);
194
195 mlog_write_ull(
196 sys_header + TRX_SYS_TRX_ID_STORE,
197 trx_sys->max_trx_id, &mtr);
198
199 mtr_commit(&mtr);
200 }
201 }
202
203 /*****************************************************************//**
204 Updates the offset information about the end of the MySQL binlog entry
205 which corresponds to the transaction just being committed. In a MySQL
206 replication slave updates the latest master binlog position up to which
207 replication has proceeded. */
208 UNIV_INTERN
209 void
trx_sys_update_mysql_binlog_offset(const char * file_name,ib_int64_t offset,ulint field,mtr_t * mtr)210 trx_sys_update_mysql_binlog_offset(
211 /*===============================*/
212 const char* file_name,/*!< in: MySQL log file name */
213 ib_int64_t offset, /*!< in: position in that log file */
214 ulint field, /*!< in: offset of the MySQL log info field in
215 the trx sys header */
216 mtr_t* mtr) /*!< in: mtr */
217 {
218 trx_sysf_t* sys_header;
219
220 if (ut_strlen(file_name) >= TRX_SYS_MYSQL_LOG_NAME_LEN) {
221
222 /* We cannot fit the name to the 512 bytes we have reserved */
223
224 return;
225 }
226
227 sys_header = trx_sysf_get(mtr);
228
229 if (mach_read_from_4(sys_header + field
230 + TRX_SYS_MYSQL_LOG_MAGIC_N_FLD)
231 != TRX_SYS_MYSQL_LOG_MAGIC_N) {
232
233 mlog_write_ulint(sys_header + field
234 + TRX_SYS_MYSQL_LOG_MAGIC_N_FLD,
235 TRX_SYS_MYSQL_LOG_MAGIC_N,
236 MLOG_4BYTES, mtr);
237 }
238
239 if (0 != strcmp((char*) (sys_header + field + TRX_SYS_MYSQL_LOG_NAME),
240 file_name)) {
241
242 mlog_write_string(sys_header + field
243 + TRX_SYS_MYSQL_LOG_NAME,
244 (byte*) file_name, 1 + ut_strlen(file_name),
245 mtr);
246 }
247
248 if (mach_read_from_4(sys_header + field
249 + TRX_SYS_MYSQL_LOG_OFFSET_HIGH) > 0
250 || (offset >> 32) > 0) {
251
252 mlog_write_ulint(sys_header + field
253 + TRX_SYS_MYSQL_LOG_OFFSET_HIGH,
254 (ulint)(offset >> 32),
255 MLOG_4BYTES, mtr);
256 }
257
258 mlog_write_ulint(sys_header + field
259 + TRX_SYS_MYSQL_LOG_OFFSET_LOW,
260 (ulint)(offset & 0xFFFFFFFFUL),
261 MLOG_4BYTES, mtr);
262 }
263
264 /*****************************************************************//**
265 Stores the MySQL binlog offset info in the trx system header if
266 the magic number shows it valid, and print the info to stderr */
267 UNIV_INTERN
268 void
trx_sys_print_mysql_binlog_offset(void)269 trx_sys_print_mysql_binlog_offset(void)
270 /*===================================*/
271 {
272 trx_sysf_t* sys_header;
273 mtr_t mtr;
274 ulint trx_sys_mysql_bin_log_pos_high;
275 ulint trx_sys_mysql_bin_log_pos_low;
276
277 mtr_start(&mtr);
278
279 sys_header = trx_sysf_get(&mtr);
280
281 if (mach_read_from_4(sys_header + TRX_SYS_MYSQL_LOG_INFO
282 + TRX_SYS_MYSQL_LOG_MAGIC_N_FLD)
283 != TRX_SYS_MYSQL_LOG_MAGIC_N) {
284
285 mtr_commit(&mtr);
286
287 return;
288 }
289
290 trx_sys_mysql_bin_log_pos_high = mach_read_from_4(
291 sys_header + TRX_SYS_MYSQL_LOG_INFO
292 + TRX_SYS_MYSQL_LOG_OFFSET_HIGH);
293 trx_sys_mysql_bin_log_pos_low = mach_read_from_4(
294 sys_header + TRX_SYS_MYSQL_LOG_INFO
295 + TRX_SYS_MYSQL_LOG_OFFSET_LOW);
296
297 trx_sys_mysql_bin_log_pos
298 = (((ib_int64_t) trx_sys_mysql_bin_log_pos_high) << 32)
299 + (ib_int64_t) trx_sys_mysql_bin_log_pos_low;
300
301 ut_memcpy(trx_sys_mysql_bin_log_name,
302 sys_header + TRX_SYS_MYSQL_LOG_INFO
303 + TRX_SYS_MYSQL_LOG_NAME, TRX_SYS_MYSQL_LOG_NAME_LEN);
304
305 fprintf(stderr,
306 "InnoDB: Last MySQL binlog file position %lu %lu,"
307 " file name %s\n",
308 trx_sys_mysql_bin_log_pos_high, trx_sys_mysql_bin_log_pos_low,
309 trx_sys_mysql_bin_log_name);
310
311 mtr_commit(&mtr);
312 }
313
314 /*****************************************************************//**
315 Prints to stderr the MySQL master log offset info in the trx system header if
316 the magic number shows it valid. */
317 UNIV_INTERN
318 void
trx_sys_print_mysql_master_log_pos(void)319 trx_sys_print_mysql_master_log_pos(void)
320 /*====================================*/
321 {
322 trx_sysf_t* sys_header;
323 mtr_t mtr;
324
325 mtr_start(&mtr);
326
327 sys_header = trx_sysf_get(&mtr);
328
329 if (mach_read_from_4(sys_header + TRX_SYS_MYSQL_MASTER_LOG_INFO
330 + TRX_SYS_MYSQL_LOG_MAGIC_N_FLD)
331 != TRX_SYS_MYSQL_LOG_MAGIC_N) {
332
333 mtr_commit(&mtr);
334
335 return;
336 }
337
338 fprintf(stderr,
339 "InnoDB: In a MySQL replication slave the last"
340 " master binlog file\n"
341 "InnoDB: position %lu %lu, file name %s\n",
342 (ulong) mach_read_from_4(sys_header
343 + TRX_SYS_MYSQL_MASTER_LOG_INFO
344 + TRX_SYS_MYSQL_LOG_OFFSET_HIGH),
345 (ulong) mach_read_from_4(sys_header
346 + TRX_SYS_MYSQL_MASTER_LOG_INFO
347 + TRX_SYS_MYSQL_LOG_OFFSET_LOW),
348 sys_header + TRX_SYS_MYSQL_MASTER_LOG_INFO
349 + TRX_SYS_MYSQL_LOG_NAME);
350 /* Copy the master log position info to global variables we can
351 use in ha_innobase.cc to initialize glob_mi to right values */
352
353 ut_memcpy(trx_sys_mysql_master_log_name,
354 sys_header + TRX_SYS_MYSQL_MASTER_LOG_INFO
355 + TRX_SYS_MYSQL_LOG_NAME,
356 TRX_SYS_MYSQL_LOG_NAME_LEN);
357
358 trx_sys_mysql_master_log_pos
359 = (((ib_int64_t) mach_read_from_4(
360 sys_header + TRX_SYS_MYSQL_MASTER_LOG_INFO
361 + TRX_SYS_MYSQL_LOG_OFFSET_HIGH)) << 32)
362 + ((ib_int64_t) mach_read_from_4(
363 sys_header + TRX_SYS_MYSQL_MASTER_LOG_INFO
364 + TRX_SYS_MYSQL_LOG_OFFSET_LOW));
365 mtr_commit(&mtr);
366 }
367
368 /****************************************************************//**
369 Looks for a free slot for a rollback segment in the trx system file copy.
370 @return slot index or ULINT_UNDEFINED if not found */
371 UNIV_INTERN
372 ulint
trx_sysf_rseg_find_free(mtr_t * mtr)373 trx_sysf_rseg_find_free(
374 /*====================*/
375 mtr_t* mtr) /*!< in: mtr */
376 {
377 ulint i;
378 trx_sysf_t* sys_header;
379
380 sys_header = trx_sysf_get(mtr);
381
382 for (i = 0; i < TRX_SYS_N_RSEGS; i++) {
383 ulint page_no;
384
385 page_no = trx_sysf_rseg_get_page_no(sys_header, i, mtr);
386
387 if (page_no == FIL_NULL) {
388
389 return(i);
390 }
391 }
392
393 return(ULINT_UNDEFINED);
394 }
395
396 /*****************************************************************//**
397 Creates the file page for the transaction system. This function is called only
398 at the database creation, before trx_sys_init. */
399 static
400 void
trx_sysf_create(mtr_t * mtr)401 trx_sysf_create(
402 /*============*/
403 mtr_t* mtr) /*!< in: mtr */
404 {
405 trx_sysf_t* sys_header;
406 ulint slot_no;
407 buf_block_t* block;
408 page_t* page;
409 ulint page_no;
410 byte* ptr;
411 ulint len;
412
413 ut_ad(mtr);
414
415 /* Note that below we first reserve the file space x-latch, and
416 then enter the kernel: we must do it in this order to conform
417 to the latching order rules. */
418
419 mtr_x_lock(fil_space_get_latch(TRX_SYS_SPACE, NULL), mtr);
420
421 /* Create the trx sys file block in a new allocated file segment */
422 block = fseg_create(TRX_SYS_SPACE, 0, TRX_SYS + TRX_SYS_FSEG_HEADER,
423 mtr);
424 buf_block_dbg_add_level(block, SYNC_TRX_SYS_HEADER);
425
426 ut_a(buf_block_get_page_no(block) == TRX_SYS_PAGE_NO);
427
428 page = buf_block_get_frame(block);
429
430 mlog_write_ulint(page + FIL_PAGE_TYPE, FIL_PAGE_TYPE_TRX_SYS,
431 MLOG_2BYTES, mtr);
432
433 /* Reset the doublewrite buffer magic number to zero so that we
434 know that the doublewrite buffer has not yet been created (this
435 suppresses a Valgrind warning) */
436
437 mlog_write_ulint(page + TRX_SYS_DOUBLEWRITE
438 + TRX_SYS_DOUBLEWRITE_MAGIC, 0, MLOG_4BYTES, mtr);
439
440 sys_header = trx_sysf_get(mtr);
441
442 /* Start counting transaction ids from number 1 up */
443 mach_write_to_8(sys_header + TRX_SYS_TRX_ID_STORE, 1);
444
445 /* Reset the rollback segment slots. Old versions of InnoDB
446 define TRX_SYS_N_RSEGS as 256 (TRX_SYS_OLD_N_RSEGS) and expect
447 that the whole array is initialized. */
448 ptr = TRX_SYS_RSEGS + sys_header;
449 len = ut_max(TRX_SYS_OLD_N_RSEGS, TRX_SYS_N_RSEGS)
450 * TRX_SYS_RSEG_SLOT_SIZE;
451 memset(ptr, 0xff, len);
452 ptr += len;
453 ut_a(ptr <= page + (UNIV_PAGE_SIZE - FIL_PAGE_DATA_END));
454
455 /* Initialize all of the page. This part used to be uninitialized. */
456 memset(ptr, 0, UNIV_PAGE_SIZE - FIL_PAGE_DATA_END + page - ptr);
457
458 mlog_log_string(sys_header, UNIV_PAGE_SIZE - FIL_PAGE_DATA_END
459 + page - sys_header, mtr);
460
461 /* Create the first rollback segment in the SYSTEM tablespace */
462 slot_no = trx_sysf_rseg_find_free(mtr);
463 page_no = trx_rseg_header_create(TRX_SYS_SPACE, 0, ULINT_MAX, slot_no,
464 mtr);
465
466 ut_a(slot_no == TRX_SYS_SYSTEM_RSEG_ID);
467 ut_a(page_no == FSP_FIRST_RSEG_PAGE_NO);
468 }
469
470 /*****************************************************************//**
471 Compare two trx_rseg_t instances on last_trx_no. */
472 static
473 int
trx_rseg_compare_last_trx_no(const void * p1,const void * p2)474 trx_rseg_compare_last_trx_no(
475 /*=========================*/
476 const void* p1, /*!< in: elem to compare */
477 const void* p2) /*!< in: elem to compare */
478 {
479 ib_int64_t cmp;
480
481 const rseg_queue_t* rseg_q1 = (const rseg_queue_t*) p1;
482 const rseg_queue_t* rseg_q2 = (const rseg_queue_t*) p2;
483
484 cmp = rseg_q1->trx_no - rseg_q2->trx_no;
485
486 if (cmp < 0) {
487 return(-1);
488 } else if (cmp > 0) {
489 return(1);
490 }
491
492 return(0);
493 }
494
495 /*****************************************************************//**
496 Creates and initializes the central memory structures for the transaction
497 system. This is called when the database is started.
498 @return min binary heap of rsegs to purge */
499 UNIV_INTERN
500 ib_bh_t*
trx_sys_init_at_db_start(void)501 trx_sys_init_at_db_start(void)
502 /*==========================*/
503 {
504 mtr_t mtr;
505 ib_bh_t* ib_bh;
506 trx_sysf_t* sys_header;
507 ib_uint64_t rows_to_undo = 0;
508 const char* unit = "";
509
510 /* We create the min binary heap here and pass ownership to
511 purge when we init the purge sub-system. Purge is responsible
512 for freeing the binary heap. */
513
514 ib_bh = ib_bh_create(
515 trx_rseg_compare_last_trx_no,
516 sizeof(rseg_queue_t), TRX_SYS_N_RSEGS);
517
518 mtr_start(&mtr);
519
520 /* Allocate the trx descriptors array */
521 trx_sys->descriptors = static_cast<trx_id_t*>(
522 ut_malloc(sizeof(trx_id_t) *
523 TRX_DESCR_ARRAY_INITIAL_SIZE));
524 trx_sys->descr_n_max = TRX_DESCR_ARRAY_INITIAL_SIZE;
525 trx_sys->descr_n_used = 0;
526 srv_descriptors_memory = TRX_DESCR_ARRAY_INITIAL_SIZE *
527 sizeof(trx_id_t);
528
529 sys_header = trx_sysf_get(&mtr);
530
531 if (srv_force_recovery < SRV_FORCE_NO_UNDO_LOG_SCAN) {
532 trx_rseg_array_init(sys_header, ib_bh, &mtr);
533 }
534
535 /* VERY important: after the database is started, max_trx_id value is
536 divisible by TRX_SYS_TRX_ID_WRITE_MARGIN, and the 'if' in
537 trx_sys_get_new_trx_id will evaluate to TRUE when the function
538 is first time called, and the value for trx id will be written
539 to the disk-based header! Thus trx id values will not overlap when
540 the database is repeatedly started! */
541
542 trx_sys->max_trx_id = 2 * TRX_SYS_TRX_ID_WRITE_MARGIN
543 + ut_uint64_align_up(mach_read_from_8(sys_header
544 + TRX_SYS_TRX_ID_STORE),
545 TRX_SYS_TRX_ID_WRITE_MARGIN);
546
547 ut_d(trx_sys->rw_max_trx_id = trx_sys->max_trx_id);
548
549 UT_LIST_INIT(trx_sys->mysql_trx_list);
550
551 trx_dummy_sess = sess_open();
552
553 trx_lists_init_at_db_start();
554
555 /* This S lock is not strictly required, it is here only to satisfy
556 the debug code (assertions). We are still running in single threaded
557 bootstrap mode. */
558
559 mutex_enter(&trx_sys->mutex);
560
561 ut_a(UT_LIST_GET_LEN(trx_sys->ro_trx_list) == 0);
562
563 if (UT_LIST_GET_LEN(trx_sys->rw_trx_list) > 0) {
564 const trx_t* trx;
565
566 for (trx = UT_LIST_GET_FIRST(trx_sys->rw_trx_list);
567 trx != NULL;
568 trx = UT_LIST_GET_NEXT(trx_list, trx)) {
569
570 ut_ad(trx->is_recovered);
571 assert_trx_in_rw_list(trx);
572
573 if (trx_state_eq(trx, TRX_STATE_ACTIVE)) {
574 rows_to_undo += trx->undo_no;
575 }
576 }
577
578 if (rows_to_undo > 1000000000) {
579 unit = "M";
580 rows_to_undo = rows_to_undo / 1000000;
581 }
582
583 fprintf(stderr,
584 "InnoDB: %lu transaction(s) which must be"
585 " rolled back or cleaned up\n"
586 "InnoDB: in total %lu%s row operations to undo\n",
587 (ulong) UT_LIST_GET_LEN(trx_sys->rw_trx_list),
588 (ulong) rows_to_undo, unit);
589
590 fprintf(stderr, "InnoDB: Trx id counter is " TRX_ID_FMT "\n",
591 trx_sys->max_trx_id);
592 }
593
594 mutex_exit(&trx_sys->mutex);
595
596 UT_LIST_INIT(trx_sys->view_list);
597
598 mtr_commit(&mtr);
599
600 return(ib_bh);
601 }
602
603 /*****************************************************************//**
604 Creates the trx_sys instance and initializes ib_bh and mutex. */
605 UNIV_INTERN
606 void
trx_sys_create(void)607 trx_sys_create(void)
608 /*================*/
609 {
610 ut_ad(trx_sys == NULL);
611
612 trx_sys = static_cast<trx_sys_t*>(mem_zalloc(sizeof(*trx_sys)));
613
614 mutex_create(trx_sys_mutex_key, &trx_sys->mutex, SYNC_TRX_SYS);
615 }
616
617 /*****************************************************************//**
618 Creates and initializes the transaction system at the database creation. */
619 UNIV_INTERN
620 void
trx_sys_create_sys_pages(void)621 trx_sys_create_sys_pages(void)
622 /*==========================*/
623 {
624 mtr_t mtr;
625
626 mtr_start(&mtr);
627
628 trx_sysf_create(&mtr);
629
630 mtr_commit(&mtr);
631 }
632
633 /*****************************************************************//**
634 Update the file format tag.
635 @return always TRUE */
636 static
637 ibool
trx_sys_file_format_max_write(ulint format_id,const char ** name)638 trx_sys_file_format_max_write(
639 /*==========================*/
640 ulint format_id, /*!< in: file format id */
641 const char** name) /*!< out: max file format name, can
642 be NULL */
643 {
644 mtr_t mtr;
645 byte* ptr;
646 buf_block_t* block;
647 ib_uint64_t tag_value;
648
649 mtr_start(&mtr);
650
651 block = buf_page_get(
652 TRX_SYS_SPACE, 0, TRX_SYS_PAGE_NO, RW_X_LATCH, &mtr);
653
654 file_format_max.id = format_id;
655 file_format_max.name = trx_sys_file_format_id_to_name(format_id);
656
657 ptr = buf_block_get_frame(block) + TRX_SYS_FILE_FORMAT_TAG;
658 tag_value = format_id + TRX_SYS_FILE_FORMAT_TAG_MAGIC_N;
659
660 if (name) {
661 *name = file_format_max.name;
662 }
663
664 mlog_write_ull(ptr, tag_value, &mtr);
665
666 mtr_commit(&mtr);
667
668 return(TRUE);
669 }
670
671 /*****************************************************************//**
672 Read the file format tag.
673 @return the file format or ULINT_UNDEFINED if not set. */
674 static
675 ulint
trx_sys_file_format_max_read(void)676 trx_sys_file_format_max_read(void)
677 /*==============================*/
678 {
679 mtr_t mtr;
680 const byte* ptr;
681 const buf_block_t* block;
682 ib_id_t file_format_id;
683
684 /* Since this is called during the startup phase it's safe to
685 read the value without a covering mutex. */
686 mtr_start(&mtr);
687
688 block = buf_page_get(
689 TRX_SYS_SPACE, 0, TRX_SYS_PAGE_NO, RW_X_LATCH, &mtr);
690
691 ptr = buf_block_get_frame(block) + TRX_SYS_FILE_FORMAT_TAG;
692 file_format_id = mach_read_from_8(ptr);
693
694 mtr_commit(&mtr);
695
696 file_format_id -= TRX_SYS_FILE_FORMAT_TAG_MAGIC_N;
697
698 if (file_format_id >= FILE_FORMAT_NAME_N) {
699
700 /* Either it has never been tagged, or garbage in it. */
701 return(ULINT_UNDEFINED);
702 }
703
704 return((ulint) file_format_id);
705 }
706
707 /*****************************************************************//**
708 Get the name representation of the file format from its id.
709 @return pointer to the name */
710 UNIV_INTERN
711 const char*
trx_sys_file_format_id_to_name(const ulint id)712 trx_sys_file_format_id_to_name(
713 /*===========================*/
714 const ulint id) /*!< in: id of the file format */
715 {
716 ut_a(id < FILE_FORMAT_NAME_N);
717
718 return(file_format_name_map[id]);
719 }
720
721 /*****************************************************************//**
722 Check for the max file format tag stored on disk. Note: If max_format_id
723 is == UNIV_FORMAT_MAX + 1 then we only print a warning.
724 @return DB_SUCCESS or error code */
725 UNIV_INTERN
726 dberr_t
trx_sys_file_format_max_check(ulint max_format_id)727 trx_sys_file_format_max_check(
728 /*==========================*/
729 ulint max_format_id) /*!< in: max format id to check */
730 {
731 ulint format_id;
732
733 /* Check the file format in the tablespace. Do not try to
734 recover if the file format is not supported by the engine
735 unless forced by the user. */
736 format_id = trx_sys_file_format_max_read();
737 if (format_id == ULINT_UNDEFINED) {
738 /* Format ID was not set. Set it to minimum possible
739 value. */
740 format_id = UNIV_FORMAT_MIN;
741 }
742
743 ib_logf(IB_LOG_LEVEL_INFO,
744 "Highest supported file format is %s.",
745 trx_sys_file_format_id_to_name(UNIV_FORMAT_MAX));
746
747 if (format_id > UNIV_FORMAT_MAX) {
748
749 ut_a(format_id < FILE_FORMAT_NAME_N);
750
751 ib_logf(max_format_id <= UNIV_FORMAT_MAX
752 ? IB_LOG_LEVEL_ERROR : IB_LOG_LEVEL_WARN,
753 "The system tablespace is in a file "
754 "format that this version doesn't support - %s.",
755 trx_sys_file_format_id_to_name(format_id));
756
757 if (max_format_id <= UNIV_FORMAT_MAX) {
758 return(DB_ERROR);
759 }
760 }
761
762 format_id = (format_id > max_format_id) ? format_id : max_format_id;
763
764 /* We don't need a mutex here, as this function should only
765 be called once at start up. */
766 file_format_max.id = format_id;
767 file_format_max.name = trx_sys_file_format_id_to_name(format_id);
768
769 return(DB_SUCCESS);
770 }
771
772 /*****************************************************************//**
773 Set the file format id unconditionally except if it's already the
774 same value.
775 @return TRUE if value updated */
776 UNIV_INTERN
777 ibool
trx_sys_file_format_max_set(ulint format_id,const char ** name)778 trx_sys_file_format_max_set(
779 /*========================*/
780 ulint format_id, /*!< in: file format id */
781 const char** name) /*!< out: max file format name or
782 NULL if not needed. */
783 {
784 ibool ret = FALSE;
785
786 ut_a(format_id <= UNIV_FORMAT_MAX);
787
788 mutex_enter(&file_format_max.mutex);
789
790 /* Only update if not already same value. */
791 if (format_id != file_format_max.id) {
792
793 ret = trx_sys_file_format_max_write(format_id, name);
794 }
795
796 mutex_exit(&file_format_max.mutex);
797
798 return(ret);
799 }
800
801 /********************************************************************//**
802 Tags the system table space with minimum format id if it has not been
803 tagged yet.
804 WARNING: This function is only called during the startup and AFTER the
805 redo log application during recovery has finished. */
806 UNIV_INTERN
807 void
trx_sys_file_format_tag_init(void)808 trx_sys_file_format_tag_init(void)
809 /*==============================*/
810 {
811 ulint format_id;
812
813 format_id = trx_sys_file_format_max_read();
814
815 /* If format_id is not set then set it to the minimum. */
816 if (format_id == ULINT_UNDEFINED) {
817 trx_sys_file_format_max_set(UNIV_FORMAT_MIN, NULL);
818 }
819 }
820
821 /********************************************************************//**
822 Update the file format tag in the system tablespace only if the given
823 format id is greater than the known max id.
824 @return TRUE if format_id was bigger than the known max id */
825 UNIV_INTERN
826 ibool
trx_sys_file_format_max_upgrade(const char ** name,ulint format_id)827 trx_sys_file_format_max_upgrade(
828 /*============================*/
829 const char** name, /*!< out: max file format name */
830 ulint format_id) /*!< in: file format identifier */
831 {
832 ibool ret = FALSE;
833
834 ut_a(name);
835 ut_a(file_format_max.name != NULL);
836 ut_a(format_id <= UNIV_FORMAT_MAX);
837
838 mutex_enter(&file_format_max.mutex);
839
840 if (format_id > file_format_max.id) {
841
842 ret = trx_sys_file_format_max_write(format_id, name);
843 }
844
845 mutex_exit(&file_format_max.mutex);
846
847 return(ret);
848 }
849
850 /*****************************************************************//**
851 Get the name representation of the file format from its id.
852 @return pointer to the max format name */
853 UNIV_INTERN
854 const char*
trx_sys_file_format_max_get(void)855 trx_sys_file_format_max_get(void)
856 /*=============================*/
857 {
858 return(file_format_max.name);
859 }
860
861 /*****************************************************************//**
862 Initializes the tablespace tag system. */
863 UNIV_INTERN
864 void
trx_sys_file_format_init(void)865 trx_sys_file_format_init(void)
866 /*==========================*/
867 {
868 mutex_create(file_format_max_mutex_key,
869 &file_format_max.mutex, SYNC_FILE_FORMAT_TAG);
870
871 /* We don't need a mutex here, as this function should only
872 be called once at start up. */
873 file_format_max.id = UNIV_FORMAT_MIN;
874
875 file_format_max.name = trx_sys_file_format_id_to_name(
876 file_format_max.id);
877 }
878
879 /*****************************************************************//**
880 Closes the tablespace tag system. */
881 UNIV_INTERN
882 void
trx_sys_file_format_close(void)883 trx_sys_file_format_close(void)
884 /*===========================*/
885 {
886 /* Does nothing at the moment */
887 }
888
889 /*********************************************************************
890 Creates the rollback segments.
891 @return number of rollback segments that are active. */
892 UNIV_INTERN
893 ulint
trx_sys_create_rsegs(ulint n_spaces,ulint n_rsegs)894 trx_sys_create_rsegs(
895 /*=================*/
896 ulint n_spaces, /*!< number of tablespaces for UNDO logs */
897 ulint n_rsegs) /*!< number of rollback segments to create */
898 {
899 mtr_t mtr;
900 ulint n_used;
901
902 ut_a(n_spaces < TRX_SYS_N_RSEGS);
903 ut_a(n_rsegs <= TRX_SYS_N_RSEGS);
904
905 if (srv_read_only_mode) {
906 return(ULINT_UNDEFINED);
907 }
908
909 /* This is executed in single-threaded mode therefore it is not
910 necessary to use the same mtr in trx_rseg_create(). n_used cannot
911 change while the function is executing. */
912
913 mtr_start(&mtr);
914 n_used = trx_sysf_rseg_find_free(&mtr);
915 mtr_commit(&mtr);
916
917 if (n_used == ULINT_UNDEFINED) {
918 n_used = TRX_SYS_N_RSEGS;
919 }
920
921 /* Do not create additional rollback segments if innodb_force_recovery
922 has been set and the database was not shutdown cleanly. */
923
924 if (!srv_force_recovery && !recv_needed_recovery && n_used < n_rsegs) {
925 ulint i;
926 ulint new_rsegs = n_rsegs - n_used;
927
928 for (i = 0; i < new_rsegs; ++i) {
929 ulint space_id;
930 space_id = (n_spaces == 0) ? 0
931 : (srv_undo_space_id_start + i % n_spaces);
932
933 /* Tablespace 0 is the system tablespace. */
934 if (trx_rseg_create(space_id) != NULL) {
935 ++n_used;
936 } else {
937 break;
938 }
939 }
940 }
941
942 ib_logf(IB_LOG_LEVEL_INFO,
943 "%lu rollback segment(s) are active.", n_used);
944
945 return(n_used);
946 }
947
948 #else /* !UNIV_HOTBACKUP */
949 /*****************************************************************//**
950 Prints to stderr the MySQL binlog info in the system header if the
951 magic number shows it valid. */
952 UNIV_INTERN
953 void
trx_sys_print_mysql_binlog_offset_from_page(const byte * page)954 trx_sys_print_mysql_binlog_offset_from_page(
955 /*========================================*/
956 const byte* page) /*!< in: buffer containing the trx
957 system header page, i.e., page number
958 TRX_SYS_PAGE_NO in the tablespace */
959 {
960 const trx_sysf_t* sys_header;
961
962 sys_header = page + TRX_SYS;
963
964 if (mach_read_from_4(sys_header + TRX_SYS_MYSQL_LOG_INFO
965 + TRX_SYS_MYSQL_LOG_MAGIC_N_FLD)
966 == TRX_SYS_MYSQL_LOG_MAGIC_N) {
967
968 fprintf(stderr,
969 "mysqlbackup: Last MySQL binlog file position %lu %lu,"
970 " file name %s\n",
971 (ulong) mach_read_from_4(
972 sys_header + TRX_SYS_MYSQL_LOG_INFO
973 + TRX_SYS_MYSQL_LOG_OFFSET_HIGH),
974 (ulong) mach_read_from_4(
975 sys_header + TRX_SYS_MYSQL_LOG_INFO
976 + TRX_SYS_MYSQL_LOG_OFFSET_LOW),
977 sys_header + TRX_SYS_MYSQL_LOG_INFO
978 + TRX_SYS_MYSQL_LOG_NAME);
979 }
980 }
981
982 /*****************************************************************//**
983 Reads the file format id from the first system table space file.
984 Even if the call succeeds and returns TRUE, the returned format id
985 may be ULINT_UNDEFINED signalling that the format id was not present
986 in the data file.
987 @return TRUE if call succeeds */
988 UNIV_INTERN
989 ibool
trx_sys_read_file_format_id(const char * pathname,ulint * format_id)990 trx_sys_read_file_format_id(
991 /*========================*/
992 const char *pathname, /*!< in: pathname of the first system
993 table space file */
994 ulint *format_id) /*!< out: file format of the system table
995 space */
996 {
997 os_file_t file;
998 ibool success;
999 byte buf[UNIV_PAGE_SIZE * 2];
1000 page_t* page = ut_align(buf, UNIV_PAGE_SIZE);
1001 const byte* ptr;
1002 ib_id_t file_format_id;
1003
1004 *format_id = ULINT_UNDEFINED;
1005
1006 file = os_file_create_simple_no_error_handling(
1007 innodb_file_data_key,
1008 pathname,
1009 OS_FILE_OPEN,
1010 OS_FILE_READ_ONLY,
1011 &success
1012 );
1013 if (!success) {
1014 /* The following call prints an error message */
1015 os_file_get_last_error(true);
1016
1017 ut_print_timestamp(stderr);
1018
1019 fprintf(stderr,
1020 " mysqlbackup: Error: trying to read system "
1021 "tablespace file format,\n"
1022 " mysqlbackup: but could not open the tablespace "
1023 "file %s!\n", pathname);
1024 return(FALSE);
1025 }
1026
1027 /* Read the page on which file format is stored */
1028
1029 success = os_file_read_no_error_handling(
1030 file, page, TRX_SYS_PAGE_NO * UNIV_PAGE_SIZE, UNIV_PAGE_SIZE);
1031
1032 if (!success) {
1033 /* The following call prints an error message */
1034 os_file_get_last_error(true);
1035
1036 ut_print_timestamp(stderr);
1037
1038 fprintf(stderr,
1039 " mysqlbackup: Error: trying to read system "
1040 "tablespace file format,\n"
1041 " mysqlbackup: but failed to read the tablespace "
1042 "file %s!\n", pathname);
1043
1044 os_file_close(file);
1045 return(FALSE);
1046 }
1047 os_file_close(file);
1048
1049 /* get the file format from the page */
1050 ptr = page + TRX_SYS_FILE_FORMAT_TAG;
1051 file_format_id = mach_read_from_8(ptr);
1052 file_format_id -= TRX_SYS_FILE_FORMAT_TAG_MAGIC_N;
1053
1054 if (file_format_id >= FILE_FORMAT_NAME_N) {
1055
1056 /* Either it has never been tagged, or garbage in it. */
1057 return(TRUE);
1058 }
1059
1060 *format_id = (ulint) file_format_id;
1061
1062 return(TRUE);
1063 }
1064
1065 /*****************************************************************//**
1066 Reads the file format id from the given per-table data file.
1067 @return TRUE if call succeeds */
1068 UNIV_INTERN
1069 ibool
trx_sys_read_pertable_file_format_id(const char * pathname,ulint * format_id)1070 trx_sys_read_pertable_file_format_id(
1071 /*=================================*/
1072 const char *pathname, /*!< in: pathname of a per-table
1073 datafile */
1074 ulint *format_id) /*!< out: file format of the per-table
1075 data file */
1076 {
1077 os_file_t file;
1078 ibool success;
1079 byte buf[UNIV_PAGE_SIZE * 2];
1080 page_t* page = ut_align(buf, UNIV_PAGE_SIZE);
1081 const byte* ptr;
1082 ib_uint32_t flags;
1083
1084 *format_id = ULINT_UNDEFINED;
1085
1086 file = os_file_create_simple_no_error_handling(
1087 innodb_file_data_key,
1088 pathname,
1089 OS_FILE_OPEN,
1090 OS_FILE_READ_ONLY,
1091 &success
1092 );
1093 if (!success) {
1094 /* The following call prints an error message */
1095 os_file_get_last_error(true);
1096
1097 ut_print_timestamp(stderr);
1098
1099 fprintf(stderr,
1100 " mysqlbackup: Error: trying to read per-table "
1101 "tablespace format,\n"
1102 " mysqlbackup: but could not open the tablespace "
1103 "file %s!\n", pathname);
1104
1105 return(FALSE);
1106 }
1107
1108 /* Read the first page of the per-table datafile */
1109
1110 success = os_file_read_no_error_handling(file, page, 0, UNIV_PAGE_SIZE);
1111
1112 if (!success) {
1113 /* The following call prints an error message */
1114 os_file_get_last_error(true);
1115
1116 ut_print_timestamp(stderr);
1117
1118 fprintf(stderr,
1119 " mysqlbackup: Error: trying to per-table data file "
1120 "format,\n"
1121 " mysqlbackup: but failed to read the tablespace "
1122 "file %s!\n", pathname);
1123
1124 os_file_close(file);
1125 return(FALSE);
1126 }
1127 os_file_close(file);
1128
1129 /* get the file format from the page */
1130 ptr = page + 54;
1131 flags = mach_read_from_4(ptr);
1132
1133 if (!fsp_flags_is_valid(flags) {
1134 /* bad tablespace flags */
1135 return(FALSE);
1136 }
1137
1138 *format_id = FSP_FLAGS_GET_POST_ANTELOPE(flags);
1139
1140 return(TRUE);
1141 }
1142
1143
1144 /*****************************************************************//**
1145 Get the name representation of the file format from its id.
1146 @return pointer to the name */
1147 UNIV_INTERN
1148 const char*
1149 trx_sys_file_format_id_to_name(
1150 /*===========================*/
1151 const ulint id) /*!< in: id of the file format */
1152 {
1153 if (!(id < FILE_FORMAT_NAME_N)) {
1154 /* unknown id */
1155 return("Unknown");
1156 }
1157
1158 return(file_format_name_map[id]);
1159 }
1160
1161 #endif /* !UNIV_HOTBACKUP */
1162
1163 #ifndef UNIV_HOTBACKUP
1164 /*********************************************************************
1165 Shutdown/Close the transaction system. */
1166 UNIV_INTERN
1167 void
1168 trx_sys_close(void)
1169 /*===============*/
1170 {
1171 ulint i;
1172 trx_t* trx;
1173 read_view_t* view;
1174
1175 ut_ad(trx_sys != NULL);
1176 ut_ad(srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS);
1177
1178 /* Check that all read views are closed except read view owned
1179 by a purge. */
1180
1181 mutex_enter(&trx_sys->mutex);
1182
1183 if (UT_LIST_GET_LEN(trx_sys->view_list) > 1) {
1184 fprintf(stderr,
1185 "InnoDB: Error: all read views were not closed"
1186 " before shutdown:\n"
1187 "InnoDB: %lu read views open \n",
1188 UT_LIST_GET_LEN(trx_sys->view_list) - 1);
1189 }
1190
1191 mutex_exit(&trx_sys->mutex);
1192
1193 sess_close(trx_dummy_sess);
1194 trx_dummy_sess = NULL;
1195
1196 trx_purge_sys_close();
1197
1198 /* Free the double write data structures. */
1199 buf_dblwr_free();
1200
1201 ut_a(UT_LIST_GET_LEN(trx_sys->ro_trx_list) == 0);
1202
1203 /* Only prepared transactions may be left in the system. Free them. */
1204 ut_a(UT_LIST_GET_LEN(trx_sys->rw_trx_list) == trx_sys->n_prepared_trx);
1205
1206 while ((trx = UT_LIST_GET_FIRST(trx_sys->rw_trx_list)) != NULL) {
1207 trx_free_prepared(trx);
1208 }
1209
1210 /* There can't be any active transactions. */
1211 for (i = 0; i < TRX_SYS_N_RSEGS; ++i) {
1212 trx_rseg_t* rseg;
1213
1214 rseg = trx_sys->rseg_array[i];
1215
1216 if (rseg != NULL) {
1217 trx_rseg_mem_free(rseg);
1218 } else {
1219 break;
1220 }
1221 }
1222
1223 view = UT_LIST_GET_FIRST(trx_sys->view_list);
1224
1225 while (view != NULL) {
1226 read_view_t* prev_view = view;
1227
1228 view = UT_LIST_GET_NEXT(view_list, prev_view);
1229
1230 /* Views are allocated from the trx_sys->global_read_view_heap.
1231 So, we simply remove the element here. */
1232 UT_LIST_REMOVE(view_list, trx_sys->view_list, prev_view);
1233 }
1234
1235 ut_a(UT_LIST_GET_LEN(trx_sys->view_list) == 0);
1236 ut_a(UT_LIST_GET_LEN(trx_sys->ro_trx_list) == 0);
1237 ut_a(UT_LIST_GET_LEN(trx_sys->rw_trx_list) == 0);
1238 ut_a(UT_LIST_GET_LEN(trx_sys->mysql_trx_list) == 0);
1239
1240 mutex_free(&trx_sys->mutex);
1241
1242 ut_ad(trx_sys->descr_n_used == 0);
1243 ut_free(trx_sys->descriptors);
1244
1245 mem_free(trx_sys);
1246
1247 trx_sys = NULL;
1248 }
1249
1250 /** @brief Convert an undo log to TRX_UNDO_PREPARED state on shutdown.
1251
1252 If any prepared ACTIVE transactions exist, and their rollback was
1253 prevented by innodb_force_recovery, we convert these transactions to
1254 XA PREPARE state in the main-memory data structures, so that shutdown
1255 will proceed normally. These transactions will again recover as ACTIVE
1256 on the next restart, and they will be rolled back unless
1257 innodb_force_recovery prevents it again.
1258
1259 @param[in] trx transaction
1260 @param[in,out] undo undo log to convert to TRX_UNDO_PREPARED */
1261 static
1262 void
1263 trx_undo_fake_prepared(
1264 const trx_t* trx,
1265 trx_undo_t* undo)
1266 {
1267 ut_ad(srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO);
1268 ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE));
1269 ut_ad(trx->is_recovered);
1270
1271 if (undo != NULL) {
1272 ut_ad(undo->state == TRX_UNDO_ACTIVE);
1273 undo->state = TRX_UNDO_PREPARED;
1274 }
1275 }
1276
1277 /*********************************************************************
1278 Check if there are any active (non-prepared) transactions.
1279 @return total number of active transactions or 0 if none */
1280 UNIV_INTERN
1281 ulint
1282 trx_sys_any_active_transactions(void)
1283 /*=================================*/
1284 {
1285 mutex_enter(&trx_sys->mutex);
1286
1287 ulint total_trx = UT_LIST_GET_LEN(trx_sys->mysql_trx_list);
1288
1289 if (total_trx == 0) {
1290 total_trx = UT_LIST_GET_LEN(trx_sys->rw_trx_list);
1291 ut_a(total_trx >= trx_sys->n_prepared_trx);
1292
1293 if (total_trx > trx_sys->n_prepared_trx
1294 && srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO) {
1295 for (trx_t* trx = UT_LIST_GET_FIRST(
1296 trx_sys->rw_trx_list);
1297 trx != NULL;
1298 trx = UT_LIST_GET_NEXT(trx_list, trx)) {
1299 if (!trx_state_eq(trx, TRX_STATE_ACTIVE)
1300 || !trx->is_recovered) {
1301 continue;
1302 }
1303 /* This was a recovered transaction
1304 whose rollback was disabled by
1305 the innodb_force_recovery setting.
1306 Pretend that it is in XA PREPARE
1307 state so that shutdown will work. */
1308 trx_undo_fake_prepared(
1309 trx, trx->insert_undo);
1310 trx_undo_fake_prepared(
1311 trx, trx->update_undo);
1312 trx->state = TRX_STATE_PREPARED;
1313 trx_sys->n_prepared_trx++;
1314 trx_sys->n_prepared_recovered_trx++;
1315 }
1316 }
1317
1318 ut_a(total_trx >= trx_sys->n_prepared_trx);
1319 total_trx -= trx_sys->n_prepared_trx;
1320 }
1321
1322 mutex_exit(&trx_sys->mutex);
1323
1324 return(total_trx);
1325 }
1326
1327 #ifdef UNIV_DEBUG
1328 /*************************************************************//**
1329 Validate the trx_list_t.
1330 @return TRUE if valid. */
1331 static
1332 ibool
1333 trx_sys_validate_trx_list_low(
1334 /*===========================*/
1335 trx_list_t* trx_list) /*!< in: &trx_sys->ro_trx_list
1336 or &trx_sys->rw_trx_list */
1337 {
1338 const trx_t* trx;
1339 const trx_t* prev_trx = NULL;
1340
1341 ut_ad(mutex_own(&trx_sys->mutex));
1342
1343 ut_ad(trx_list == &trx_sys->ro_trx_list
1344 || trx_list == &trx_sys->rw_trx_list);
1345
1346 for (trx = UT_LIST_GET_FIRST(*trx_list);
1347 trx != NULL;
1348 prev_trx = trx, trx = UT_LIST_GET_NEXT(trx_list, prev_trx)) {
1349
1350 assert_trx_in_list(trx);
1351 ut_ad(trx->read_only == (trx_list == &trx_sys->ro_trx_list));
1352
1353 ut_a(prev_trx == NULL || prev_trx->id > trx->id);
1354 }
1355
1356 return(TRUE);
1357 }
1358
1359 /*************************************************************//**
1360 Validate the trx_sys_t::ro_trx_list and trx_sys_t::rw_trx_list.
1361 @return TRUE if lists are valid. */
1362 UNIV_INTERN
1363 ibool
1364 trx_sys_validate_trx_list(void)
1365 /*===========================*/
1366 {
1367 ut_ad(mutex_own(&trx_sys->mutex));
1368
1369 ut_a(trx_sys_validate_trx_list_low(&trx_sys->ro_trx_list));
1370 ut_a(trx_sys_validate_trx_list_low(&trx_sys->rw_trx_list));
1371
1372 return(TRUE);
1373 }
1374 #endif /* UNIV_DEBUG */
1375 #endif /* !UNIV_HOTBACKUP */
1376