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