1/*****************************************************************************
2
3Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
4Copyright (c) 2017, 2018, MariaDB Corporation.
5
6This program is free software; you can redistribute it and/or modify it under
7the terms of the GNU General Public License as published by the Free Software
8Foundation; version 2 of the License.
9
10This program is distributed in the hope that it will be useful, but WITHOUT
11ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License along with
15this program; if not, write to the Free Software Foundation, Inc.,
1651 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
17
18*****************************************************************************/
19
20/**************************************************//**
21@file trx/trx0rseg.cc
22Rollback segment
23
24Created 3/26/1996 Heikki Tuuri
25*******************************************************/
26
27#include "trx0rseg.h"
28#include "trx0undo.h"
29#include "fut0lst.h"
30#include "srv0srv.h"
31#include "trx0purge.h"
32#include "srv0mon.h"
33#include "fsp0sysspace.h"
34
35#include <algorithm>
36
37#ifdef WITH_WSREP
38#include <mysql/service_wsrep.h>
39
40#ifdef UNIV_DEBUG
41/** The latest known WSREP XID sequence number */
42static long long wsrep_seqno = -1;
43#endif /* UNIV_DEBUG */
44/** The latest known WSREP XID UUID */
45static unsigned char wsrep_uuid[16];
46
47/** Update the WSREP XID information in rollback segment header.
48@param[in,out] rseg_header rollback segment header
49@param[in] xid WSREP XID
50@param[in,out] mtr mini-transaction */
51void
52trx_rseg_update_wsrep_checkpoint(
53 trx_rsegf_t* rseg_header,
54 const XID* xid,
55 mtr_t* mtr)
56{
57 ut_ad(wsrep_is_wsrep_xid(xid));
58
59#ifdef UNIV_DEBUG
60 /* Check that seqno is monotonically increasing */
61 long long xid_seqno = wsrep_xid_seqno(xid);
62 const byte* xid_uuid = wsrep_xid_uuid(xid);
63
64 if (!memcmp(xid_uuid, wsrep_uuid, sizeof wsrep_uuid)) {
65 ut_ad(xid_seqno > wsrep_seqno);
66 } else {
67 memcpy(wsrep_uuid, xid_uuid, sizeof wsrep_uuid);
68 }
69 wsrep_seqno = xid_seqno;
70#endif /* UNIV_DEBUG */
71
72 mlog_write_ulint(TRX_RSEG_WSREP_XID_FORMAT + rseg_header,
73 uint32_t(xid->formatID),
74 MLOG_4BYTES, mtr);
75
76 mlog_write_ulint(TRX_RSEG_WSREP_XID_GTRID_LEN + rseg_header,
77 uint32_t(xid->gtrid_length),
78 MLOG_4BYTES, mtr);
79
80 mlog_write_ulint(TRX_RSEG_WSREP_XID_BQUAL_LEN + rseg_header,
81 uint32_t(xid->bqual_length),
82 MLOG_4BYTES, mtr);
83
84 mlog_write_string(TRX_RSEG_WSREP_XID_DATA + rseg_header,
85 reinterpret_cast<const byte*>(xid->data),
86 XIDDATASIZE, mtr);
87}
88
89/** Update WSREP checkpoint XID in first rollback segment header
90as part of wsrep_set_SE_checkpoint() when it is guaranteed that there
91are no wsrep transactions committing.
92If the UUID part of the WSREP XID does not match to the UUIDs of XIDs already
93stored into rollback segments, the WSREP XID in all the remaining rollback
94segments will be reset.
95@param[in] xid WSREP XID */
96void trx_rseg_update_wsrep_checkpoint(const XID* xid)
97{
98 mtr_t mtr;
99 mtr.start();
100
101 const trx_rseg_t* rseg = trx_sys.rseg_array[0];
102
103 trx_rsegf_t* rseg_header = trx_rsegf_get(rseg->space, rseg->page_no,
104 &mtr);
105 if (UNIV_UNLIKELY(mach_read_from_4(rseg_header + TRX_RSEG_FORMAT))) {
106 trx_rseg_format_upgrade(rseg_header, &mtr);
107 }
108
109 trx_rseg_update_wsrep_checkpoint(rseg_header, xid, &mtr);
110
111 const byte* xid_uuid = wsrep_xid_uuid(xid);
112 if (memcmp(wsrep_uuid, xid_uuid, sizeof wsrep_uuid)) {
113 memcpy(wsrep_uuid, xid_uuid, sizeof wsrep_uuid);
114
115 /* Because the UUID part of the WSREP XID differed
116 from current_xid_uuid, the WSREP group UUID was
117 changed, and we must reset the XID in all rollback
118 segment headers. */
119 for (ulint rseg_id = 1; rseg_id < TRX_SYS_N_RSEGS; ++rseg_id) {
120 if (const trx_rseg_t* rseg =
121 trx_sys.rseg_array[rseg_id]) {
122 trx_rseg_update_wsrep_checkpoint(
123 trx_rsegf_get(rseg->space,
124 rseg->page_no, &mtr),
125 xid, &mtr);
126 }
127 }
128 }
129
130 mtr.commit();
131}
132
133/** Read the WSREP XID information in rollback segment header.
134@param[in] rseg_header Rollback segment header
135@param[out] xid Transaction XID
136@return whether the WSREP XID was present */
137static
138bool trx_rseg_read_wsrep_checkpoint(const trx_rsegf_t* rseg_header, XID& xid)
139{
140 int formatID = static_cast<int>(
141 mach_read_from_4(
142 TRX_RSEG_WSREP_XID_FORMAT + rseg_header));
143 if (formatID == 0) {
144 return false;
145 }
146
147 xid.formatID = formatID;
148 xid.gtrid_length = static_cast<int>(
149 mach_read_from_4(
150 TRX_RSEG_WSREP_XID_GTRID_LEN + rseg_header));
151
152 xid.bqual_length = static_cast<int>(
153 mach_read_from_4(
154 TRX_RSEG_WSREP_XID_BQUAL_LEN + rseg_header));
155
156 memcpy(xid.data, TRX_RSEG_WSREP_XID_DATA + rseg_header, XIDDATASIZE);
157
158 return true;
159}
160
161/** Read the WSREP XID from the TRX_SYS page (in case of upgrade).
162@param[in] page TRX_SYS page
163@param[out] xid WSREP XID (if present)
164@return whether the WSREP XID is present */
165static bool trx_rseg_init_wsrep_xid(const page_t* page, XID& xid)
166{
167 if (mach_read_from_4(TRX_SYS + TRX_SYS_WSREP_XID_INFO
168 + TRX_SYS_WSREP_XID_MAGIC_N_FLD
169 + page)
170 != TRX_SYS_WSREP_XID_MAGIC_N) {
171 return false;
172 }
173
174 xid.formatID = static_cast<int>(
175 mach_read_from_4(
176 TRX_SYS + TRX_SYS_WSREP_XID_INFO
177 + TRX_SYS_WSREP_XID_FORMAT + page));
178 xid.gtrid_length = static_cast<int>(
179 mach_read_from_4(
180 TRX_SYS + TRX_SYS_WSREP_XID_INFO
181 + TRX_SYS_WSREP_XID_GTRID_LEN + page));
182 xid.bqual_length = static_cast<int>(
183 mach_read_from_4(
184 TRX_SYS + TRX_SYS_WSREP_XID_INFO
185 + TRX_SYS_WSREP_XID_BQUAL_LEN + page));
186 memcpy(xid.data,
187 TRX_SYS + TRX_SYS_WSREP_XID_INFO
188 + TRX_SYS_WSREP_XID_DATA + page, XIDDATASIZE);
189 return true;
190}
191
192/** Recover the latest WSREP checkpoint XID.
193@param[out] xid WSREP XID
194@return whether the WSREP XID was found */
195bool trx_rseg_read_wsrep_checkpoint(XID& xid)
196{
197 mtr_t mtr;
198 long long max_xid_seqno = -1;
199 bool found = false;
200
201 for (ulint rseg_id = 0; rseg_id < TRX_SYS_N_RSEGS;
202 rseg_id++, mtr.commit()) {
203 mtr.start();
204 const buf_block_t* sys = trx_sysf_get(&mtr, false);
205 if (rseg_id == 0) {
206 found = trx_rseg_init_wsrep_xid(sys->frame, xid);
207 ut_ad(!found || xid.formatID == 1);
208 if (found) {
209 max_xid_seqno = wsrep_xid_seqno(&xid);
210 memcpy(wsrep_uuid, wsrep_xid_uuid(&xid),
211 sizeof wsrep_uuid);
212 }
213 }
214
215 const uint32_t page_no = trx_sysf_rseg_get_page_no(
216 sys, rseg_id);
217
218 if (page_no == FIL_NULL) {
219 continue;
220 }
221
222 const trx_rsegf_t* rseg_header = trx_rsegf_get_new(
223 trx_sysf_rseg_get_space(sys, rseg_id), page_no, &mtr);
224
225 if (mach_read_from_4(rseg_header + TRX_RSEG_FORMAT)) {
226 continue;
227 }
228
229 XID tmp_xid;
230 long long tmp_seqno = 0;
231 if (trx_rseg_read_wsrep_checkpoint(rseg_header, tmp_xid)
232 && (tmp_seqno = wsrep_xid_seqno(&tmp_xid))
233 > max_xid_seqno) {
234 found = true;
235 max_xid_seqno = tmp_seqno;
236 xid = tmp_xid;
237 memcpy(wsrep_uuid, wsrep_xid_uuid(&tmp_xid),
238 sizeof wsrep_uuid);
239 }
240 }
241
242 return found;
243}
244#endif /* WITH_WSREP */
245
246/** Upgrade a rollback segment header page to MariaDB 10.3 format.
247@param[in,out] rseg_header rollback segment header page
248@param[in,out] mtr mini-transaction */
249void trx_rseg_format_upgrade(trx_rsegf_t* rseg_header, mtr_t* mtr)
250{
251 ut_ad(page_offset(rseg_header) == TRX_RSEG);
252 byte* rseg_format = TRX_RSEG_FORMAT + rseg_header;
253 mlog_write_ulint(rseg_format, 0, MLOG_4BYTES, mtr);
254 /* Clear also possible garbage at the end of the page. Old
255 InnoDB versions did not initialize unused parts of pages. */
256 byte* b = rseg_header + TRX_RSEG_MAX_TRX_ID + 8;
257 ulint len = srv_page_size
258 - (FIL_PAGE_DATA_END
259 + TRX_RSEG + TRX_RSEG_MAX_TRX_ID + 8);
260 memset(b, 0, len);
261 mlog_log_string(b, len, mtr);
262}
263
264/** Create a rollback segment header.
265@param[in,out] space system, undo, or temporary tablespace
266@param[in] rseg_id rollback segment identifier
267@param[in,out] sys_header the TRX_SYS page (NULL for temporary rseg)
268@param[in,out] mtr mini-transaction
269@return page number of the created segment, FIL_NULL if fail */
270ulint
271trx_rseg_header_create(
272 fil_space_t* space,
273 ulint rseg_id,
274 buf_block_t* sys_header,
275 mtr_t* mtr)
276{
277 ulint page_no;
278 trx_rsegf_t* rsegf;
279 buf_block_t* block;
280
281 ut_ad(mtr_memo_contains(mtr, &space->latch, MTR_MEMO_X_LOCK));
282 ut_ad(!sys_header == (space == fil_system.temp_space));
283
284 /* Allocate a new file segment for the rollback segment */
285 block = fseg_create(space, 0, TRX_RSEG + TRX_RSEG_FSEG_HEADER, mtr);
286
287 if (block == NULL) {
288 /* No space left */
289
290 return(FIL_NULL);
291 }
292
293 buf_block_dbg_add_level(block, SYNC_RSEG_HEADER_NEW);
294
295 page_no = block->page.id.page_no();
296
297 /* Get the rollback segment file page */
298 rsegf = trx_rsegf_get_new(space->id, page_no, mtr);
299
300 mlog_write_ulint(rsegf + TRX_RSEG_FORMAT, 0, MLOG_4BYTES, mtr);
301
302 /* Initialize the history list */
303
304 mlog_write_ulint(rsegf + TRX_RSEG_HISTORY_SIZE, 0, MLOG_4BYTES, mtr);
305 flst_init(rsegf + TRX_RSEG_HISTORY, mtr);
306
307 /* Reset the undo log slots */
308 for (ulint i = 0; i < TRX_RSEG_N_SLOTS; i++) {
309
310 trx_rsegf_set_nth_undo(rsegf, i, FIL_NULL, mtr);
311 }
312
313 if (sys_header) {
314 /* Add the rollback segment info to the free slot in
315 the trx system header */
316
317 mlog_write_ulint(TRX_SYS + TRX_SYS_RSEGS
318 + TRX_SYS_RSEG_SPACE
319 + rseg_id * TRX_SYS_RSEG_SLOT_SIZE
320 + sys_header->frame,
321 space->id, MLOG_4BYTES, mtr);
322 mlog_write_ulint(TRX_SYS + TRX_SYS_RSEGS
323 + TRX_SYS_RSEG_PAGE_NO
324 + rseg_id * TRX_SYS_RSEG_SLOT_SIZE
325 + sys_header->frame,
326 page_no, MLOG_4BYTES, mtr);
327 }
328
329 return(page_no);
330}
331
332/** Free a rollback segment in memory. */
333void
334trx_rseg_mem_free(trx_rseg_t* rseg)
335{
336 trx_undo_t* undo;
337 trx_undo_t* next_undo;
338
339 mutex_free(&rseg->mutex);
340
341 /* There can't be any active transactions. */
342 ut_a(UT_LIST_GET_LEN(rseg->undo_list) == 0);
343 ut_a(UT_LIST_GET_LEN(rseg->old_insert_list) == 0);
344
345 for (undo = UT_LIST_GET_FIRST(rseg->undo_cached);
346 undo != NULL;
347 undo = next_undo) {
348
349 next_undo = UT_LIST_GET_NEXT(undo_list, undo);
350
351 UT_LIST_REMOVE(rseg->undo_cached, undo);
352
353 MONITOR_DEC(MONITOR_NUM_UNDO_SLOT_CACHED);
354
355 ut_free(undo);
356 }
357
358 ut_free(rseg);
359}
360
361/** Create a rollback segment object.
362@param[in] id rollback segment id
363@param[in] space space where the segment is placed
364@param[in] page_no page number of the segment header */
365static
366trx_rseg_t*
367trx_rseg_mem_create(ulint id, fil_space_t* space, ulint page_no)
368{
369 trx_rseg_t* rseg = static_cast<trx_rseg_t*>(
370 ut_zalloc_nokey(sizeof *rseg));
371
372 rseg->id = id;
373 rseg->space = space;
374 rseg->page_no = page_no;
375 rseg->last_page_no = FIL_NULL;
376 rseg->curr_size = 1;
377
378 mutex_create(rseg->is_persistent()
379 ? LATCH_ID_REDO_RSEG : LATCH_ID_NOREDO_RSEG,
380 &rseg->mutex);
381
382 UT_LIST_INIT(rseg->undo_list, &trx_undo_t::undo_list);
383 UT_LIST_INIT(rseg->old_insert_list, &trx_undo_t::undo_list);
384 UT_LIST_INIT(rseg->undo_cached, &trx_undo_t::undo_list);
385
386 return(rseg);
387}
388
389/** Read the undo log lists.
390@param[in,out] rseg rollback segment
391@param[in,out] max_trx_id maximum observed transaction identifier
392@param[in] rseg_header rollback segment header
393@return the combined size of undo log segments in pages */
394static
395ulint
396trx_undo_lists_init(trx_rseg_t* rseg, trx_id_t& max_trx_id,
397 const trx_rsegf_t* rseg_header)
398{
399 ut_ad(srv_force_recovery < SRV_FORCE_NO_UNDO_LOG_SCAN);
400
401 ulint size = 0;
402
403 for (ulint i = 0; i < TRX_RSEG_N_SLOTS; i++) {
404 ulint page_no = trx_rsegf_get_nth_undo(rseg_header, i);
405 if (page_no != FIL_NULL) {
406 size += trx_undo_mem_create_at_db_start(
407 rseg, i, page_no, max_trx_id);
408 MONITOR_INC(MONITOR_NUM_UNDO_SLOT_USED);
409 }
410 }
411
412 return(size);
413}
414
415/** Restore the state of a persistent rollback segment.
416@param[in,out] rseg persistent rollback segment
417@param[in,out] max_trx_id maximum observed transaction identifier
418@param[in,out] mtr mini-transaction */
419static
420void
421trx_rseg_mem_restore(trx_rseg_t* rseg, trx_id_t& max_trx_id, mtr_t* mtr)
422{
423 trx_rsegf_t* rseg_header = trx_rsegf_get_new(
424 rseg->space->id, rseg->page_no, mtr);
425
426 if (mach_read_from_4(rseg_header + TRX_RSEG_FORMAT) == 0) {
427 trx_id_t id = mach_read_from_8(rseg_header
428 + TRX_RSEG_MAX_TRX_ID);
429
430 if (id > max_trx_id) {
431 max_trx_id = id;
432 }
433
434 if (rseg_header[TRX_RSEG_BINLOG_NAME]) {
435 const char* binlog_name = reinterpret_cast<const char*>
436 (rseg_header) + TRX_RSEG_BINLOG_NAME;
437 compile_time_assert(TRX_RSEG_BINLOG_NAME_LEN == sizeof
438 trx_sys.recovered_binlog_filename);
439
440 int cmp = *trx_sys.recovered_binlog_filename
441 ? strncmp(binlog_name,
442 trx_sys.recovered_binlog_filename,
443 TRX_RSEG_BINLOG_NAME_LEN)
444 : 1;
445
446 if (cmp >= 0) {
447 uint64_t binlog_offset = mach_read_from_8(
448 rseg_header + TRX_RSEG_BINLOG_OFFSET);
449 if (cmp) {
450 memcpy(trx_sys.
451 recovered_binlog_filename,
452 binlog_name,
453 TRX_RSEG_BINLOG_NAME_LEN);
454 trx_sys.recovered_binlog_offset
455 = binlog_offset;
456 } else if (binlog_offset >
457 trx_sys.recovered_binlog_offset) {
458 trx_sys.recovered_binlog_offset
459 = binlog_offset;
460 }
461 }
462
463#ifdef WITH_WSREP
464 trx_rseg_read_wsrep_checkpoint(
465 rseg_header, trx_sys.recovered_wsrep_xid);
466#endif
467 }
468 }
469
470 if (srv_operation == SRV_OPERATION_RESTORE) {
471 /* mariabackup --prepare only deals with
472 the redo log and the data files, not with
473 transactions or the data dictionary. */
474 return;
475 }
476
477 /* Initialize the undo log lists according to the rseg header */
478
479 rseg->curr_size = mach_read_from_4(rseg_header + TRX_RSEG_HISTORY_SIZE)
480 + 1 + trx_undo_lists_init(rseg, max_trx_id, rseg_header);
481
482 if (ulint len = flst_get_len(rseg_header + TRX_RSEG_HISTORY)) {
483 trx_sys.history_add(int32(len));
484
485 fil_addr_t node_addr = trx_purge_get_log_from_hist(
486 flst_get_last(rseg_header + TRX_RSEG_HISTORY, mtr));
487
488 rseg->last_page_no = node_addr.page;
489 rseg->last_offset = node_addr.boffset;
490
491 const trx_ulogf_t* undo_log_hdr = trx_undo_page_get(
492 page_id_t(rseg->space->id, node_addr.page), mtr)
493 + node_addr.boffset;
494
495 trx_id_t id = mach_read_from_8(undo_log_hdr + TRX_UNDO_TRX_ID);
496 if (id > max_trx_id) {
497 max_trx_id = id;
498 }
499 id = mach_read_from_8(undo_log_hdr + TRX_UNDO_TRX_NO);
500 if (id > max_trx_id) {
501 max_trx_id = id;
502 }
503 unsigned purge = mach_read_from_2(
504 undo_log_hdr + TRX_UNDO_NEEDS_PURGE);
505 ut_ad(purge <= 1);
506 rseg->set_last_trx_no(id, purge != 0);
507 rseg->needs_purge = purge != 0;
508
509 if (rseg->last_page_no != FIL_NULL) {
510
511 /* There is no need to cover this operation by the purge
512 mutex because we are still bootstrapping. */
513 purge_sys.purge_queue.push(*rseg);
514 }
515 }
516}
517
518/** Read binlog metadata from the TRX_SYS page, in case we are upgrading
519from MySQL or a MariaDB version older than 10.3.5. */
520static void trx_rseg_init_binlog_info(const page_t* page)
521{
522 if (mach_read_from_4(TRX_SYS + TRX_SYS_MYSQL_LOG_INFO
523 + TRX_SYS_MYSQL_LOG_MAGIC_N_FLD
524 + page)
525 == TRX_SYS_MYSQL_LOG_MAGIC_N) {
526 memcpy(trx_sys.recovered_binlog_filename,
527 TRX_SYS_MYSQL_LOG_INFO + TRX_SYS_MYSQL_LOG_NAME
528 + TRX_SYS + page, TRX_SYS_MYSQL_LOG_NAME_LEN);
529 trx_sys.recovered_binlog_offset = mach_read_from_8(
530 TRX_SYS_MYSQL_LOG_INFO + TRX_SYS_MYSQL_LOG_OFFSET
531 + TRX_SYS + page);
532 }
533
534#ifdef WITH_WSREP
535 trx_rseg_init_wsrep_xid(page, trx_sys.recovered_wsrep_xid);
536#endif
537}
538
539/** Initialize the rollback segments in memory at database startup. */
540void
541trx_rseg_array_init()
542{
543 trx_id_t max_trx_id = 0;
544
545 *trx_sys.recovered_binlog_filename = '\0';
546 trx_sys.recovered_binlog_offset = 0;
547#ifdef WITH_WSREP
548 memset(&trx_sys.recovered_wsrep_xid, 0,
549 sizeof trx_sys.recovered_wsrep_xid);
550 trx_sys.recovered_wsrep_xid.formatID = -1;
551#endif
552
553 for (ulint rseg_id = 0; rseg_id < TRX_SYS_N_RSEGS; rseg_id++) {
554 mtr_t mtr;
555 mtr.start();
556 if (const buf_block_t* sys = trx_sysf_get(&mtr, false)) {
557 if (rseg_id == 0) {
558 /* In case this is an upgrade from
559 before MariaDB 10.3.5, fetch the base
560 information from the TRX_SYS page. */
561 max_trx_id = mach_read_from_8(
562 TRX_SYS + TRX_SYS_TRX_ID_STORE
563 + sys->frame);
564 trx_rseg_init_binlog_info(sys->frame);
565 }
566
567 const uint32_t page_no = trx_sysf_rseg_get_page_no(
568 sys, rseg_id);
569 if (page_no != FIL_NULL) {
570 trx_rseg_t* rseg = trx_rseg_mem_create(
571 rseg_id,
572 fil_space_get(trx_sysf_rseg_get_space(
573 sys, rseg_id)),
574 page_no);
575 ut_ad(rseg->is_persistent());
576 ut_ad(rseg->id == rseg_id);
577 ut_ad(!trx_sys.rseg_array[rseg_id]);
578 trx_sys.rseg_array[rseg_id] = rseg;
579 trx_rseg_mem_restore(rseg, max_trx_id, &mtr);
580 }
581 }
582
583 mtr.commit();
584 }
585
586 trx_sys.init_max_trx_id(max_trx_id + 1);
587}
588
589/** Create a persistent rollback segment.
590@param[in] space_id system or undo tablespace id
591@return pointer to new rollback segment
592@retval NULL on failure */
593trx_rseg_t*
594trx_rseg_create(ulint space_id)
595{
596 trx_rseg_t* rseg = NULL;
597 mtr_t mtr;
598
599 mtr.start();
600
601 /* To obey the latching order, acquire the file space
602 x-latch before the trx_sys.mutex. */
603 fil_space_t* space = mtr_x_lock_space(space_id, &mtr);
604 ut_ad(space->purpose == FIL_TYPE_TABLESPACE);
605
606 if (buf_block_t* sys_header = trx_sysf_get(&mtr)) {
607 ulint rseg_id = trx_sys_rseg_find_free(sys_header);
608 ulint page_no = rseg_id == ULINT_UNDEFINED
609 ? FIL_NULL
610 : trx_rseg_header_create(space, rseg_id, sys_header,
611 &mtr);
612 if (page_no != FIL_NULL) {
613 ut_ad(trx_sysf_rseg_get_space(sys_header, rseg_id)
614 == space_id);
615 rseg = trx_rseg_mem_create(rseg_id, space, page_no);
616 ut_ad(rseg->id == rseg_id);
617 ut_ad(rseg->is_persistent());
618 ut_ad(!trx_sys.rseg_array[rseg->id]);
619 trx_sys.rseg_array[rseg->id] = rseg;
620 }
621 }
622
623 mtr.commit();
624
625 return(rseg);
626}
627
628/** Create the temporary rollback segments. */
629void
630trx_temp_rseg_create()
631{
632 mtr_t mtr;
633
634 for (ulong i = 0; i < TRX_SYS_N_RSEGS; i++) {
635 mtr.start();
636 mtr.set_log_mode(MTR_LOG_NO_REDO);
637 mtr_x_lock(&fil_system.temp_space->latch, &mtr);
638
639 ulint page_no = trx_rseg_header_create(
640 fil_system.temp_space, i, NULL, &mtr);
641 trx_rseg_t* rseg = trx_rseg_mem_create(
642 i, fil_system.temp_space, page_no);
643 ut_ad(!rseg->is_persistent());
644 ut_ad(!trx_sys.temp_rsegs[i]);
645 trx_sys.temp_rsegs[i] = rseg;
646 mtr.commit();
647 }
648}
649
650/********************************************************************
651Get the number of unique rollback tablespaces in use except space id 0.
652The last space id will be the sentinel value ULINT_UNDEFINED. The array
653will be sorted on space id. Note: space_ids should have have space for
654TRX_SYS_N_RSEGS + 1 elements.
655@return number of unique rollback tablespaces in use. */
656ulint
657trx_rseg_get_n_undo_tablespaces(
658/*============================*/
659 ulint* space_ids) /*!< out: array of space ids of
660 UNDO tablespaces */
661{
662 mtr_t mtr;
663 mtr.start();
664
665 buf_block_t* sys_header = trx_sysf_get(&mtr, false);
666 if (!sys_header) {
667 mtr.commit();
668 return 0;
669 }
670
671 ulint* end = space_ids;
672
673 for (ulint rseg_id = 0; rseg_id < TRX_SYS_N_RSEGS; rseg_id++) {
674 uint32_t page_no = trx_sysf_rseg_get_page_no(sys_header,
675 rseg_id);
676
677 if (page_no == FIL_NULL) {
678 continue;
679 }
680
681 if (ulint space = trx_sysf_rseg_get_space(sys_header,
682 rseg_id)) {
683 if (std::find(space_ids, end, space) == end) {
684 *end++ = space;
685 }
686 }
687 }
688
689 mtr.commit();
690
691 ut_a(end - space_ids <= TRX_SYS_N_RSEGS);
692 *end = ULINT_UNDEFINED;
693
694 std::sort(space_ids, end);
695
696 return ulint(end - space_ids);
697}
698
699/** Update the offset information about the end of the binlog entry
700which corresponds to the transaction just being committed.
701In a replication slave, this updates the master binlog position
702up to which replication has proceeded.
703@param[in,out] rseg_header rollback segment header
704@param[in] trx committing transaction
705@param[in,out] mtr mini-transaction */
706void
707trx_rseg_update_binlog_offset(byte* rseg_header, const trx_t* trx, mtr_t* mtr)
708{
709 DBUG_LOG("trx", "trx_mysql_binlog_offset: " << trx->mysql_log_offset);
710
711 const size_t len = strlen(trx->mysql_log_file_name) + 1;
712
713 ut_ad(len > 1);
714
715 if (UNIV_UNLIKELY(len > TRX_RSEG_BINLOG_NAME_LEN)) {
716 return;
717 }
718
719 mlog_write_ull(rseg_header + TRX_RSEG_BINLOG_OFFSET,
720 trx->mysql_log_offset, mtr);
721 byte* p = rseg_header + TRX_RSEG_BINLOG_NAME;
722 const byte* binlog_name = reinterpret_cast<const byte*>
723 (trx->mysql_log_file_name);
724
725 if (memcmp(binlog_name, p, len)) {
726 mlog_write_string(p, binlog_name, len, mtr);
727 }
728}
729