1 | /***************************************************************************** |
2 | |
3 | Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. |
4 | Copyright (c) 2014, 2018, MariaDB Corporation. |
5 | |
6 | This program is free software; you can redistribute it and/or modify it under |
7 | the terms of the GNU General Public License as published by the Free Software |
8 | Foundation; version 2 of the License. |
9 | |
10 | This program is distributed in the hope that it will be useful, but WITHOUT |
11 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
12 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU General Public License along with |
15 | this program; if not, write to the Free Software Foundation, Inc., |
16 | 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA |
17 | |
18 | *****************************************************************************/ |
19 | |
20 | /**************************************************//** |
21 | @file trx/trx0undo.cc |
22 | Transaction undo log |
23 | |
24 | Created 3/26/1996 Heikki Tuuri |
25 | *******************************************************/ |
26 | |
27 | #include "ha_prototypes.h" |
28 | |
29 | #include "trx0undo.h" |
30 | #include "fsp0fsp.h" |
31 | #include "mach0data.h" |
32 | #include "mtr0log.h" |
33 | #include "srv0mon.h" |
34 | #include "srv0srv.h" |
35 | #include "srv0start.h" |
36 | #include "trx0purge.h" |
37 | #include "trx0rec.h" |
38 | #include "trx0rseg.h" |
39 | #include "trx0trx.h" |
40 | |
41 | /* How should the old versions in the history list be managed? |
42 | ---------------------------------------------------------- |
43 | If each transaction is given a whole page for its update undo log, file |
44 | space consumption can be 10 times higher than necessary. Therefore, |
45 | partly filled update undo log pages should be reusable. But then there |
46 | is no way individual pages can be ordered so that the ordering agrees |
47 | with the serialization numbers of the transactions on the pages. Thus, |
48 | the history list must be formed of undo logs, not their header pages as |
49 | it was in the old implementation. |
50 | However, on a single header page the transactions are placed in |
51 | the order of their serialization numbers. As old versions are purged, we |
52 | may free the page when the last transaction on the page has been purged. |
53 | A problem is that the purge has to go through the transactions |
54 | in the serialization order. This means that we have to look through all |
55 | rollback segments for the one that has the smallest transaction number |
56 | in its history list. |
57 | When should we do a purge? A purge is necessary when space is |
58 | running out in any of the rollback segments. Then we may have to purge |
59 | also old version which might be needed by some consistent read. How do |
60 | we trigger the start of a purge? When a transaction writes to an undo log, |
61 | it may notice that the space is running out. When a read view is closed, |
62 | it may make some history superfluous. The server can have an utility which |
63 | periodically checks if it can purge some history. |
64 | In a parallellized purge we have the problem that a query thread |
65 | can remove a delete marked clustered index record before another query |
66 | thread has processed an earlier version of the record, which cannot then |
67 | be done because the row cannot be constructed from the clustered index |
68 | record. To avoid this problem, we will store in the update and delete mark |
69 | undo record also the columns necessary to construct the secondary index |
70 | entries which are modified. |
71 | We can latch the stack of versions of a single clustered index record |
72 | by taking a latch on the clustered index page. As long as the latch is held, |
73 | no new versions can be added and no versions removed by undo. But, a purge |
74 | can still remove old versions from the bottom of the stack. */ |
75 | |
76 | /* How to protect rollback segments, undo logs, and history lists with |
77 | ------------------------------------------------------------------- |
78 | latches? |
79 | ------- |
80 | The contention of the trx_sys.mutex should be minimized. When a transaction |
81 | does its first insert or modify in an index, an undo log is assigned for it. |
82 | Then we must have an x-latch to the rollback segment header. |
83 | When the transaction performs modifications or rolls back, its |
84 | undo log is protected by undo page latches. |
85 | Only the thread that is associated with the transaction may hold multiple |
86 | undo page latches at a time. Undo pages are always private to a single |
87 | transaction. Other threads that are performing MVCC reads |
88 | or checking for implicit locks will lock at most one undo page at a time |
89 | in trx_undo_get_undo_rec_low(). |
90 | When the transaction commits, its persistent undo log is added |
91 | to the history list. If it is not suitable for reuse, its slot is reset. |
92 | In both cases, an x-latch must be acquired on the rollback segment header page. |
93 | The purge operation steps through the history list without modifying |
94 | it until a truncate operation occurs, which can remove undo logs from the end |
95 | of the list and release undo log segments. In stepping through the list, |
96 | s-latches on the undo log pages are enough, but in a truncate, x-latches must |
97 | be obtained on the rollback segment and individual pages. */ |
98 | |
99 | /********************************************************************//** |
100 | Creates and initializes an undo log memory object. |
101 | @return own: the undo log memory object */ |
102 | static |
103 | trx_undo_t* |
104 | trx_undo_mem_create( |
105 | /*================*/ |
106 | trx_rseg_t* rseg, /*!< in: rollback segment memory object */ |
107 | ulint id, /*!< in: slot index within rseg */ |
108 | trx_id_t trx_id, /*!< in: id of the trx for which the undo log |
109 | is created */ |
110 | const XID* xid, /*!< in: X/Open XA transaction identification*/ |
111 | ulint page_no,/*!< in: undo log header page number */ |
112 | ulint offset);/*!< in: undo log header byte offset on page */ |
113 | |
114 | /** Determine the start offset of undo log records of an undo log page. |
115 | @param[in] undo_page undo log page |
116 | @param[in] page_no undo log header page number |
117 | @param[in] offset undo log header offset |
118 | @return start offset */ |
119 | static |
120 | uint16_t |
121 | trx_undo_page_get_start(const page_t* undo_page, ulint page_no, ulint offset) |
122 | { |
123 | return page_no == page_get_page_no(undo_page) |
124 | ? mach_read_from_2(offset + TRX_UNDO_LOG_START + undo_page) |
125 | : TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE; |
126 | } |
127 | |
128 | /** Get the first undo log record on a page. |
129 | @param[in] page undo log page |
130 | @param[in] page_no undo log header page number |
131 | @param[in] offset undo log header page offset |
132 | @return pointer to first record |
133 | @retval NULL if none exists */ |
134 | static |
135 | trx_undo_rec_t* |
136 | trx_undo_page_get_first_rec(page_t* page, ulint page_no, ulint offset) |
137 | { |
138 | ulint start = trx_undo_page_get_start(page, page_no, offset); |
139 | return start == trx_undo_page_get_end(page, page_no, offset) |
140 | ? NULL |
141 | : page + start; |
142 | } |
143 | |
144 | /** Get the last undo log record on a page. |
145 | @param[in] page undo log page |
146 | @param[in] page_no undo log header page number |
147 | @param[in] offset undo log header page offset |
148 | @return pointer to last record |
149 | @retval NULL if none exists */ |
150 | static |
151 | trx_undo_rec_t* |
152 | trx_undo_page_get_last_rec(page_t* page, ulint page_no, ulint offset) |
153 | { |
154 | ulint end = trx_undo_page_get_end(page, page_no, offset); |
155 | |
156 | return trx_undo_page_get_start(page, page_no, offset) == end |
157 | ? NULL |
158 | : page + mach_read_from_2(page + end - 2); |
159 | } |
160 | |
161 | /***********************************************************************//** |
162 | Gets the previous record in an undo log from the previous page. |
163 | @return undo log record, the page s-latched, NULL if none */ |
164 | static |
165 | trx_undo_rec_t* |
166 | trx_undo_get_prev_rec_from_prev_page( |
167 | /*=================================*/ |
168 | trx_undo_rec_t* rec, /*!< in: undo record */ |
169 | ulint page_no,/*!< in: undo log header page number */ |
170 | ulint offset, /*!< in: undo log header offset on page */ |
171 | bool shared, /*!< in: true=S-latch, false=X-latch */ |
172 | mtr_t* mtr) /*!< in: mtr */ |
173 | { |
174 | ulint space; |
175 | ulint prev_page_no; |
176 | page_t* prev_page; |
177 | page_t* undo_page; |
178 | |
179 | undo_page = page_align(rec); |
180 | |
181 | prev_page_no = flst_get_prev_addr(undo_page + TRX_UNDO_PAGE_HDR |
182 | + TRX_UNDO_PAGE_NODE, mtr) |
183 | .page; |
184 | |
185 | if (prev_page_no == FIL_NULL) { |
186 | |
187 | return(NULL); |
188 | } |
189 | |
190 | space = page_get_space_id(undo_page); |
191 | |
192 | buf_block_t* block = buf_page_get( |
193 | page_id_t(space, prev_page_no), univ_page_size, |
194 | shared ? RW_S_LATCH : RW_X_LATCH, mtr); |
195 | |
196 | buf_block_dbg_add_level(block, SYNC_TRX_UNDO_PAGE); |
197 | |
198 | prev_page = buf_block_get_frame(block); |
199 | |
200 | return(trx_undo_page_get_last_rec(prev_page, page_no, offset)); |
201 | } |
202 | |
203 | /** Get the previous undo log record. |
204 | @param[in] rec undo log record |
205 | @param[in] page_no undo log header page number |
206 | @param[in] offset undo log header page offset |
207 | @return pointer to record |
208 | @retval NULL if none */ |
209 | static |
210 | trx_undo_rec_t* |
211 | trx_undo_page_get_prev_rec(trx_undo_rec_t* rec, ulint page_no, ulint offset) |
212 | { |
213 | page_t* undo_page; |
214 | ulint start; |
215 | |
216 | undo_page = (page_t*) ut_align_down(rec, srv_page_size); |
217 | |
218 | start = trx_undo_page_get_start(undo_page, page_no, offset); |
219 | |
220 | if (start + undo_page == rec) { |
221 | |
222 | return(NULL); |
223 | } |
224 | |
225 | return(undo_page + mach_read_from_2(rec - 2)); |
226 | } |
227 | |
228 | /***********************************************************************//** |
229 | Gets the previous record in an undo log. |
230 | @return undo log record, the page s-latched, NULL if none */ |
231 | trx_undo_rec_t* |
232 | trx_undo_get_prev_rec( |
233 | /*==================*/ |
234 | trx_undo_rec_t* rec, /*!< in: undo record */ |
235 | ulint page_no,/*!< in: undo log header page number */ |
236 | ulint offset, /*!< in: undo log header offset on page */ |
237 | bool shared, /*!< in: true=S-latch, false=X-latch */ |
238 | mtr_t* mtr) /*!< in: mtr */ |
239 | { |
240 | trx_undo_rec_t* prev_rec; |
241 | |
242 | prev_rec = trx_undo_page_get_prev_rec(rec, page_no, offset); |
243 | |
244 | if (prev_rec) { |
245 | |
246 | return(prev_rec); |
247 | } |
248 | |
249 | /* We have to go to the previous undo log page to look for the |
250 | previous record */ |
251 | |
252 | return(trx_undo_get_prev_rec_from_prev_page(rec, page_no, offset, |
253 | shared, mtr)); |
254 | } |
255 | |
256 | /** Gets the next record in an undo log from the next page. |
257 | @param[in] space undo log header space |
258 | @param[in] undo_page undo log page |
259 | @param[in] page_no undo log header page number |
260 | @param[in] offset undo log header offset on page |
261 | @param[in] mode latch mode: RW_S_LATCH or RW_X_LATCH |
262 | @param[in,out] mtr mini-transaction |
263 | @return undo log record, the page latched, NULL if none */ |
264 | static |
265 | trx_undo_rec_t* |
266 | trx_undo_get_next_rec_from_next_page( |
267 | ulint space, |
268 | const page_t* undo_page, |
269 | ulint page_no, |
270 | ulint offset, |
271 | ulint mode, |
272 | mtr_t* mtr) |
273 | { |
274 | const trx_ulogf_t* log_hdr; |
275 | ulint next_page_no; |
276 | page_t* next_page; |
277 | ulint next; |
278 | |
279 | if (page_no == page_get_page_no(undo_page)) { |
280 | |
281 | log_hdr = undo_page + offset; |
282 | next = mach_read_from_2(log_hdr + TRX_UNDO_NEXT_LOG); |
283 | |
284 | if (next != 0) { |
285 | |
286 | return(NULL); |
287 | } |
288 | } |
289 | |
290 | next_page_no = flst_get_next_addr(undo_page + TRX_UNDO_PAGE_HDR |
291 | + TRX_UNDO_PAGE_NODE, mtr) |
292 | .page; |
293 | if (next_page_no == FIL_NULL) { |
294 | |
295 | return(NULL); |
296 | } |
297 | |
298 | const page_id_t next_page_id(space, next_page_no); |
299 | |
300 | if (mode == RW_S_LATCH) { |
301 | next_page = trx_undo_page_get_s_latched( |
302 | next_page_id, mtr); |
303 | } else { |
304 | ut_ad(mode == RW_X_LATCH); |
305 | next_page = trx_undo_page_get(next_page_id, mtr); |
306 | } |
307 | |
308 | return(trx_undo_page_get_first_rec(next_page, page_no, offset)); |
309 | } |
310 | |
311 | /***********************************************************************//** |
312 | Gets the next record in an undo log. |
313 | @return undo log record, the page s-latched, NULL if none */ |
314 | trx_undo_rec_t* |
315 | trx_undo_get_next_rec( |
316 | /*==================*/ |
317 | trx_undo_rec_t* rec, /*!< in: undo record */ |
318 | ulint page_no,/*!< in: undo log header page number */ |
319 | ulint offset, /*!< in: undo log header offset on page */ |
320 | mtr_t* mtr) /*!< in: mtr */ |
321 | { |
322 | ulint space; |
323 | trx_undo_rec_t* next_rec; |
324 | |
325 | next_rec = trx_undo_page_get_next_rec(rec, page_no, offset); |
326 | |
327 | if (next_rec) { |
328 | return(next_rec); |
329 | } |
330 | |
331 | space = page_get_space_id(page_align(rec)); |
332 | |
333 | return(trx_undo_get_next_rec_from_next_page(space, |
334 | page_align(rec), |
335 | page_no, offset, |
336 | RW_S_LATCH, mtr)); |
337 | } |
338 | |
339 | /** Gets the first record in an undo log. |
340 | @param[in] space undo log header space |
341 | @param[in] page_no undo log header page number |
342 | @param[in] offset undo log header offset on page |
343 | @param[in] mode latching mode: RW_S_LATCH or RW_X_LATCH |
344 | @param[in,out] mtr mini-transaction |
345 | @return undo log record, the page latched, NULL if none */ |
346 | trx_undo_rec_t* |
347 | trx_undo_get_first_rec( |
348 | fil_space_t* space, |
349 | ulint page_no, |
350 | ulint offset, |
351 | ulint mode, |
352 | mtr_t* mtr) |
353 | { |
354 | page_t* undo_page; |
355 | trx_undo_rec_t* rec; |
356 | |
357 | const page_id_t page_id(space->id, page_no); |
358 | |
359 | if (mode == RW_S_LATCH) { |
360 | undo_page = trx_undo_page_get_s_latched(page_id, mtr); |
361 | } else { |
362 | undo_page = trx_undo_page_get(page_id, mtr); |
363 | } |
364 | |
365 | rec = trx_undo_page_get_first_rec(undo_page, page_no, offset); |
366 | |
367 | if (rec) { |
368 | return(rec); |
369 | } |
370 | |
371 | return(trx_undo_get_next_rec_from_next_page(space->id, |
372 | undo_page, page_no, offset, |
373 | mode, mtr)); |
374 | } |
375 | |
376 | /*============== UNDO LOG FILE COPY CREATION AND FREEING ==================*/ |
377 | |
378 | /** Parse MLOG_UNDO_INIT. |
379 | @param[in] ptr log record |
380 | @param[in] end_ptr end of log record buffer |
381 | @param[in,out] page page or NULL |
382 | @return end of log record |
383 | @retval NULL if the log record is incomplete */ |
384 | byte* |
385 | trx_undo_parse_page_init(const byte* ptr, const byte* end_ptr, page_t* page) |
386 | { |
387 | if (end_ptr <= ptr) { |
388 | return NULL; |
389 | } |
390 | |
391 | const ulint type = *ptr++; |
392 | |
393 | if (type > TRX_UNDO_UPDATE) { |
394 | recv_sys->found_corrupt_log = true; |
395 | } else if (page) { |
396 | /* Starting with MDEV-12288 in MariaDB 10.3.1, we use |
397 | type=0 for the combined insert/update undo log |
398 | pages. MariaDB 10.2 would use TRX_UNDO_INSERT or |
399 | TRX_UNDO_UPDATE. */ |
400 | mach_write_to_2(FIL_PAGE_TYPE + page, FIL_PAGE_UNDO_LOG); |
401 | mach_write_to_2(TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_TYPE + page, |
402 | type); |
403 | mach_write_to_2(TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_START + page, |
404 | TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE); |
405 | mach_write_to_2(TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE + page, |
406 | TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE); |
407 | } |
408 | |
409 | return(const_cast<byte*>(ptr)); |
410 | } |
411 | |
412 | /** Parse MLOG_UNDO_HDR_REUSE for crash-upgrade from MariaDB 10.2. |
413 | @param[in] ptr redo log record |
414 | @param[in] end_ptr end of log buffer |
415 | @param[in,out] page undo log page or NULL |
416 | @return end of log record or NULL */ |
417 | byte* |
418 | ( |
419 | const byte* ptr, |
420 | const byte* end_ptr, |
421 | page_t* undo_page) |
422 | { |
423 | trx_id_t trx_id = mach_u64_parse_compressed(&ptr, end_ptr); |
424 | |
425 | if (!ptr || !undo_page) { |
426 | return(const_cast<byte*>(ptr)); |
427 | } |
428 | |
429 | compile_time_assert(TRX_UNDO_SEG_HDR + TRX_UNDO_SEG_HDR_SIZE |
430 | + TRX_UNDO_LOG_XA_HDR_SIZE |
431 | < UNIV_PAGE_SIZE_MIN - 100); |
432 | |
433 | const ulint new_free = TRX_UNDO_SEG_HDR + TRX_UNDO_SEG_HDR_SIZE |
434 | + TRX_UNDO_LOG_OLD_HDR_SIZE; |
435 | |
436 | /* Insert undo data is not needed after commit: we may free all |
437 | the space on the page */ |
438 | |
439 | ut_ad(mach_read_from_2(TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_TYPE |
440 | + undo_page) |
441 | == TRX_UNDO_INSERT); |
442 | |
443 | byte* page_hdr = undo_page + TRX_UNDO_PAGE_HDR; |
444 | mach_write_to_2(page_hdr + TRX_UNDO_PAGE_START, new_free); |
445 | mach_write_to_2(page_hdr + TRX_UNDO_PAGE_FREE, new_free); |
446 | mach_write_to_2(TRX_UNDO_SEG_HDR + TRX_UNDO_STATE + undo_page, |
447 | TRX_UNDO_ACTIVE); |
448 | |
449 | byte* log_hdr = undo_page + TRX_UNDO_SEG_HDR + TRX_UNDO_SEG_HDR_SIZE; |
450 | |
451 | mach_write_to_8(log_hdr + TRX_UNDO_TRX_ID, trx_id); |
452 | mach_write_to_2(log_hdr + TRX_UNDO_LOG_START, new_free); |
453 | |
454 | mach_write_to_1(log_hdr + TRX_UNDO_XID_EXISTS, FALSE); |
455 | mach_write_to_1(log_hdr + TRX_UNDO_DICT_TRANS, FALSE); |
456 | |
457 | return(const_cast<byte*>(ptr)); |
458 | } |
459 | |
460 | /** Initialize the fields in an undo log segment page. |
461 | @param[in,out] undo_block undo page |
462 | @param[in,out] mtr mini-transaction */ |
463 | static void trx_undo_page_init(buf_block_t* undo_block, mtr_t* mtr) |
464 | { |
465 | page_t* page = undo_block->frame; |
466 | mach_write_to_2(FIL_PAGE_TYPE + page, FIL_PAGE_UNDO_LOG); |
467 | mach_write_to_2(TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_TYPE + page, 0); |
468 | mach_write_to_2(TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_START + page, |
469 | TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE); |
470 | mach_write_to_2(TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE + page, |
471 | TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE); |
472 | |
473 | mtr->set_modified(); |
474 | switch (mtr->get_log_mode()) { |
475 | case MTR_LOG_NONE: |
476 | case MTR_LOG_NO_REDO: |
477 | return; |
478 | case MTR_LOG_SHORT_INSERTS: |
479 | ut_ad(0); |
480 | /* fall through */ |
481 | case MTR_LOG_ALL: |
482 | break; |
483 | } |
484 | |
485 | byte* log_ptr = mtr->get_log()->open(11 + 1); |
486 | log_ptr = mlog_write_initial_log_record_low( |
487 | MLOG_UNDO_INIT, |
488 | undo_block->page.id.space(), |
489 | undo_block->page.id.page_no(), |
490 | log_ptr, mtr); |
491 | *log_ptr++ = 0; |
492 | mlog_close(mtr, log_ptr); |
493 | } |
494 | |
495 | /** Create an undo log segment. |
496 | @param[in,out] space tablespace |
497 | @param[in,out] rseg_hdr rollback segment header (x-latched) |
498 | @param[out] id undo slot number |
499 | @param[out] err error code |
500 | @param[in,out] mtr mini-transaction |
501 | @return undo log block |
502 | @retval NULL on failure */ |
503 | static MY_ATTRIBUTE((nonnull, warn_unused_result)) |
504 | buf_block_t* |
505 | trx_undo_seg_create(fil_space_t* space, trx_rsegf_t* rseg_hdr, ulint* id, |
506 | dberr_t* err, mtr_t* mtr) |
507 | { |
508 | ulint slot_no; |
509 | buf_block_t* block; |
510 | ulint n_reserved; |
511 | bool success; |
512 | |
513 | slot_no = trx_rsegf_undo_find_free(rseg_hdr); |
514 | |
515 | if (slot_no == ULINT_UNDEFINED) { |
516 | ib::warn() << "Cannot find a free slot for an undo log. Do" |
517 | " you have too many active transactions running" |
518 | " concurrently?" ; |
519 | |
520 | *err = DB_TOO_MANY_CONCURRENT_TRXS; |
521 | return NULL; |
522 | } |
523 | |
524 | success = fsp_reserve_free_extents(&n_reserved, space, 2, FSP_UNDO, |
525 | mtr); |
526 | if (!success) { |
527 | *err = DB_OUT_OF_FILE_SPACE; |
528 | return NULL; |
529 | } |
530 | |
531 | /* Allocate a new file segment for the undo log */ |
532 | block = fseg_create(space, 0, TRX_UNDO_SEG_HDR + TRX_UNDO_FSEG_HEADER, |
533 | mtr, true); |
534 | |
535 | space->release_free_extents(n_reserved); |
536 | |
537 | if (block == NULL) { |
538 | *err = DB_OUT_OF_FILE_SPACE; |
539 | return NULL; |
540 | } |
541 | |
542 | buf_block_dbg_add_level(block, SYNC_TRX_UNDO_PAGE); |
543 | |
544 | trx_undo_page_init(block, mtr); |
545 | |
546 | mlog_write_ulint(TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE + block->frame, |
547 | TRX_UNDO_SEG_HDR + TRX_UNDO_SEG_HDR_SIZE, |
548 | MLOG_2BYTES, mtr); |
549 | |
550 | mlog_write_ulint(TRX_UNDO_SEG_HDR + TRX_UNDO_LAST_LOG + block->frame, |
551 | 0, MLOG_2BYTES, mtr); |
552 | |
553 | flst_init(TRX_UNDO_SEG_HDR + TRX_UNDO_PAGE_LIST + block->frame, mtr); |
554 | |
555 | flst_add_last(TRX_UNDO_SEG_HDR + TRX_UNDO_PAGE_LIST + block->frame, |
556 | TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_NODE + block->frame, |
557 | mtr); |
558 | |
559 | *id = slot_no; |
560 | trx_rsegf_set_nth_undo(rseg_hdr, slot_no, block->page.id.page_no(), |
561 | mtr); |
562 | |
563 | MONITOR_INC(MONITOR_NUM_UNDO_SLOT_USED); |
564 | |
565 | *err = DB_SUCCESS; |
566 | return block; |
567 | } |
568 | |
569 | /**********************************************************************//** |
570 | Writes the mtr log entry of an undo log header initialization. */ |
571 | UNIV_INLINE |
572 | void |
573 | ( |
574 | /*=======================*/ |
575 | const page_t* undo_page, /*!< in: undo log header page */ |
576 | trx_id_t trx_id, /*!< in: transaction id */ |
577 | mtr_t* mtr) /*!< in: mtr */ |
578 | { |
579 | mlog_write_initial_log_record(undo_page, MLOG_UNDO_HDR_CREATE, mtr); |
580 | |
581 | mlog_catenate_ull_compressed(mtr, trx_id); |
582 | } |
583 | |
584 | /***************************************************************//** |
585 | Creates a new undo log header in file. NOTE that this function has its own |
586 | log record type MLOG_UNDO_HDR_CREATE. You must NOT change the operation of |
587 | this function! |
588 | @return header byte offset on page */ |
589 | static |
590 | ulint |
591 | ( |
592 | /*===================*/ |
593 | page_t* undo_page, /*!< in/out: undo log segment |
594 | header page, x-latched; it is |
595 | assumed that there is |
596 | TRX_UNDO_LOG_XA_HDR_SIZE bytes |
597 | free space on it */ |
598 | trx_id_t trx_id, /*!< in: transaction id */ |
599 | mtr_t* mtr) /*!< in: mtr */ |
600 | { |
601 | trx_upagef_t* page_hdr; |
602 | trx_usegf_t* seg_hdr; |
603 | trx_ulogf_t* log_hdr; |
604 | ulint prev_log; |
605 | ulint free; |
606 | ulint new_free; |
607 | |
608 | ut_ad(mtr && undo_page); |
609 | |
610 | page_hdr = undo_page + TRX_UNDO_PAGE_HDR; |
611 | seg_hdr = undo_page + TRX_UNDO_SEG_HDR; |
612 | |
613 | free = mach_read_from_2(page_hdr + TRX_UNDO_PAGE_FREE); |
614 | |
615 | log_hdr = undo_page + free; |
616 | |
617 | new_free = free + TRX_UNDO_LOG_OLD_HDR_SIZE; |
618 | |
619 | ut_a(free + TRX_UNDO_LOG_XA_HDR_SIZE < srv_page_size - 100); |
620 | |
621 | mach_write_to_2(page_hdr + TRX_UNDO_PAGE_START, new_free); |
622 | |
623 | mach_write_to_2(page_hdr + TRX_UNDO_PAGE_FREE, new_free); |
624 | |
625 | mach_write_to_2(seg_hdr + TRX_UNDO_STATE, TRX_UNDO_ACTIVE); |
626 | |
627 | prev_log = mach_read_from_2(seg_hdr + TRX_UNDO_LAST_LOG); |
628 | |
629 | if (prev_log != 0) { |
630 | trx_ulogf_t* prev_log_hdr; |
631 | |
632 | prev_log_hdr = undo_page + prev_log; |
633 | |
634 | mach_write_to_2(prev_log_hdr + TRX_UNDO_NEXT_LOG, free); |
635 | } |
636 | |
637 | mach_write_to_2(seg_hdr + TRX_UNDO_LAST_LOG, free); |
638 | |
639 | log_hdr = undo_page + free; |
640 | |
641 | mach_write_to_2(log_hdr + TRX_UNDO_NEEDS_PURGE, 1); |
642 | |
643 | mach_write_to_8(log_hdr + TRX_UNDO_TRX_ID, trx_id); |
644 | mach_write_to_2(log_hdr + TRX_UNDO_LOG_START, new_free); |
645 | |
646 | mach_write_to_1(log_hdr + TRX_UNDO_XID_EXISTS, FALSE); |
647 | mach_write_to_1(log_hdr + TRX_UNDO_DICT_TRANS, FALSE); |
648 | |
649 | mach_write_to_2(log_hdr + TRX_UNDO_NEXT_LOG, 0); |
650 | mach_write_to_2(log_hdr + TRX_UNDO_PREV_LOG, prev_log); |
651 | |
652 | /* Write the log record about the header creation */ |
653 | trx_undo_header_create_log(undo_page, trx_id, mtr); |
654 | |
655 | return(free); |
656 | } |
657 | |
658 | /********************************************************************//** |
659 | Write X/Open XA Transaction Identification (XID) to undo log header */ |
660 | static |
661 | void |
662 | trx_undo_write_xid( |
663 | /*===============*/ |
664 | trx_ulogf_t* log_hdr,/*!< in: undo log header */ |
665 | const XID* xid, /*!< in: X/Open XA Transaction Identification */ |
666 | mtr_t* mtr) /*!< in: mtr */ |
667 | { |
668 | mlog_write_ulint(log_hdr + TRX_UNDO_XA_FORMAT, |
669 | static_cast<ulint>(xid->formatID), |
670 | MLOG_4BYTES, mtr); |
671 | |
672 | mlog_write_ulint(log_hdr + TRX_UNDO_XA_TRID_LEN, |
673 | static_cast<ulint>(xid->gtrid_length), |
674 | MLOG_4BYTES, mtr); |
675 | |
676 | mlog_write_ulint(log_hdr + TRX_UNDO_XA_BQUAL_LEN, |
677 | static_cast<ulint>(xid->bqual_length), |
678 | MLOG_4BYTES, mtr); |
679 | |
680 | mlog_write_string(log_hdr + TRX_UNDO_XA_XID, |
681 | reinterpret_cast<const byte*>(xid->data), |
682 | XIDDATASIZE, mtr); |
683 | } |
684 | |
685 | /********************************************************************//** |
686 | Read X/Open XA Transaction Identification (XID) from undo log header */ |
687 | static |
688 | void |
689 | trx_undo_read_xid(const trx_ulogf_t* log_hdr, XID* xid) |
690 | { |
691 | xid->formatID=static_cast<long>(mach_read_from_4( |
692 | log_hdr + TRX_UNDO_XA_FORMAT)); |
693 | |
694 | xid->gtrid_length=static_cast<long>(mach_read_from_4( |
695 | log_hdr + TRX_UNDO_XA_TRID_LEN)); |
696 | |
697 | xid->bqual_length=static_cast<long>(mach_read_from_4( |
698 | log_hdr + TRX_UNDO_XA_BQUAL_LEN)); |
699 | |
700 | memcpy(xid->data, log_hdr + TRX_UNDO_XA_XID, XIDDATASIZE); |
701 | } |
702 | |
703 | /***************************************************************//** |
704 | Adds space for the XA XID after an undo log old-style header. */ |
705 | static |
706 | void |
707 | ( |
708 | /*==============================*/ |
709 | page_t* undo_page,/*!< in: undo log segment header page */ |
710 | trx_ulogf_t* log_hdr,/*!< in: undo log header */ |
711 | mtr_t* mtr) /*!< in: mtr */ |
712 | { |
713 | trx_upagef_t* page_hdr; |
714 | ulint free; |
715 | ulint new_free; |
716 | |
717 | page_hdr = undo_page + TRX_UNDO_PAGE_HDR; |
718 | |
719 | free = mach_read_from_2(page_hdr + TRX_UNDO_PAGE_FREE); |
720 | |
721 | /* free is now the end offset of the old style undo log header */ |
722 | |
723 | ut_a(free == (ulint)(log_hdr - undo_page) + TRX_UNDO_LOG_OLD_HDR_SIZE); |
724 | |
725 | new_free = free + (TRX_UNDO_LOG_XA_HDR_SIZE |
726 | - TRX_UNDO_LOG_OLD_HDR_SIZE); |
727 | |
728 | /* Add space for a XID after the header, update the free offset |
729 | fields on the undo log page and in the undo log header */ |
730 | |
731 | mlog_write_ulint(page_hdr + TRX_UNDO_PAGE_START, new_free, |
732 | MLOG_2BYTES, mtr); |
733 | |
734 | mlog_write_ulint(page_hdr + TRX_UNDO_PAGE_FREE, new_free, |
735 | MLOG_2BYTES, mtr); |
736 | |
737 | mlog_write_ulint(log_hdr + TRX_UNDO_LOG_START, new_free, |
738 | MLOG_2BYTES, mtr); |
739 | } |
740 | |
741 | /** Parse the redo log entry of an undo log page header create. |
742 | @param[in] ptr redo log record |
743 | @param[in] end_ptr end of log buffer |
744 | @param[in,out] page page frame or NULL |
745 | @param[in,out] mtr mini-transaction or NULL |
746 | @return end of log record or NULL */ |
747 | byte* |
748 | ( |
749 | const byte* ptr, |
750 | const byte* end_ptr, |
751 | page_t* page, |
752 | mtr_t* mtr) |
753 | { |
754 | trx_id_t trx_id = mach_u64_parse_compressed(&ptr, end_ptr); |
755 | |
756 | if (ptr != NULL && page != NULL) { |
757 | trx_undo_header_create(page, trx_id, mtr); |
758 | return(const_cast<byte*>(ptr)); |
759 | } |
760 | |
761 | return(const_cast<byte*>(ptr)); |
762 | } |
763 | |
764 | /** Allocate an undo log page. |
765 | @param[in,out] undo undo log |
766 | @param[in,out] mtr mini-transaction that does not hold any page latch |
767 | @return X-latched block if success |
768 | @retval NULL on failure */ |
769 | buf_block_t* trx_undo_add_page(trx_undo_t* undo, mtr_t* mtr) |
770 | { |
771 | trx_rseg_t* rseg = undo->rseg; |
772 | buf_block_t* new_block = NULL; |
773 | ulint n_reserved; |
774 | page_t* ; |
775 | |
776 | /* When we add a page to an undo log, this is analogous to |
777 | a pessimistic insert in a B-tree, and we must reserve the |
778 | counterpart of the tree latch, which is the rseg mutex. */ |
779 | |
780 | mutex_enter(&rseg->mutex); |
781 | |
782 | header_page = trx_undo_page_get( |
783 | page_id_t(undo->rseg->space->id, undo->hdr_page_no), mtr); |
784 | |
785 | if (!fsp_reserve_free_extents(&n_reserved, undo->rseg->space, 1, |
786 | FSP_UNDO, mtr)) { |
787 | goto func_exit; |
788 | } |
789 | |
790 | new_block = fseg_alloc_free_page_general( |
791 | TRX_UNDO_SEG_HDR + TRX_UNDO_FSEG_HEADER |
792 | + header_page, |
793 | undo->top_page_no + 1, FSP_UP, TRUE, mtr, mtr); |
794 | |
795 | rseg->space->release_free_extents(n_reserved); |
796 | |
797 | if (!new_block) { |
798 | goto func_exit; |
799 | } |
800 | |
801 | ut_ad(rw_lock_get_x_lock_count(&new_block->lock) == 1); |
802 | buf_block_dbg_add_level(new_block, SYNC_TRX_UNDO_PAGE); |
803 | undo->last_page_no = new_block->page.id.page_no(); |
804 | |
805 | trx_undo_page_init(new_block, mtr); |
806 | |
807 | flst_add_last(TRX_UNDO_SEG_HDR + TRX_UNDO_PAGE_LIST |
808 | + header_page, |
809 | TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_NODE |
810 | + new_block->frame, |
811 | mtr); |
812 | undo->size++; |
813 | rseg->curr_size++; |
814 | |
815 | func_exit: |
816 | mutex_exit(&rseg->mutex); |
817 | return(new_block); |
818 | } |
819 | |
820 | /********************************************************************//** |
821 | Frees an undo log page that is not the header page. |
822 | @return last page number in remaining log */ |
823 | static |
824 | ulint |
825 | trx_undo_free_page( |
826 | /*===============*/ |
827 | trx_rseg_t* rseg, /*!< in: rollback segment */ |
828 | bool in_history, /*!< in: TRUE if the undo log is in the history |
829 | list */ |
830 | ulint hdr_page_no, /*!< in: header page number */ |
831 | ulint page_no, /*!< in: page number to free: must not be the |
832 | header page */ |
833 | mtr_t* mtr) /*!< in: mtr which does not have a latch to any |
834 | undo log page; the caller must have reserved |
835 | the rollback segment mutex */ |
836 | { |
837 | page_t* ; |
838 | page_t* undo_page; |
839 | fil_addr_t last_addr; |
840 | trx_rsegf_t* ; |
841 | ulint hist_size; |
842 | const ulint space = rseg->space->id; |
843 | |
844 | ut_a(hdr_page_no != page_no); |
845 | ut_ad(mutex_own(&(rseg->mutex))); |
846 | |
847 | undo_page = trx_undo_page_get(page_id_t(space, page_no), mtr); |
848 | |
849 | header_page = trx_undo_page_get(page_id_t(space, hdr_page_no), mtr); |
850 | |
851 | flst_remove(header_page + TRX_UNDO_SEG_HDR + TRX_UNDO_PAGE_LIST, |
852 | undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_NODE, mtr); |
853 | |
854 | fseg_free_page(header_page + TRX_UNDO_SEG_HDR + TRX_UNDO_FSEG_HEADER, |
855 | space, page_no, false, mtr); |
856 | |
857 | last_addr = flst_get_last(header_page + TRX_UNDO_SEG_HDR |
858 | + TRX_UNDO_PAGE_LIST, mtr); |
859 | rseg->curr_size--; |
860 | |
861 | if (in_history) { |
862 | rseg_header = trx_rsegf_get(rseg->space, rseg->page_no, mtr); |
863 | |
864 | hist_size = mtr_read_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE, |
865 | MLOG_4BYTES, mtr); |
866 | ut_ad(hist_size > 0); |
867 | mlog_write_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE, |
868 | hist_size - 1, MLOG_4BYTES, mtr); |
869 | } |
870 | |
871 | return(last_addr.page); |
872 | } |
873 | |
874 | /** Free the last undo log page. The caller must hold the rseg mutex. |
875 | @param[in,out] undo undo log |
876 | @param[in,out] mtr mini-transaction that does not hold any undo log page |
877 | or that has allocated the undo log page */ |
878 | void |
879 | trx_undo_free_last_page(trx_undo_t* undo, mtr_t* mtr) |
880 | { |
881 | ut_ad(undo->hdr_page_no != undo->last_page_no); |
882 | ut_ad(undo->size > 0); |
883 | |
884 | undo->last_page_no = trx_undo_free_page( |
885 | undo->rseg, false, undo->hdr_page_no, undo->last_page_no, mtr); |
886 | |
887 | undo->size--; |
888 | } |
889 | |
890 | /** Truncate the tail of an undo log during rollback. |
891 | @param[in,out] undo undo log |
892 | @param[in] limit all undo logs after this limit will be discarded |
893 | @param[in] is_temp whether this is temporary undo log */ |
894 | void |
895 | trx_undo_truncate_end(trx_undo_t* undo, undo_no_t limit, bool is_temp) |
896 | { |
897 | ut_ad(mutex_own(&undo->rseg->mutex)); |
898 | ut_ad(is_temp == !undo->rseg->is_persistent()); |
899 | |
900 | for (;;) { |
901 | mtr_t mtr; |
902 | mtr.start(); |
903 | if (is_temp) { |
904 | mtr.set_log_mode(MTR_LOG_NO_REDO); |
905 | } |
906 | |
907 | trx_undo_rec_t* trunc_here = NULL; |
908 | page_t* undo_page = trx_undo_page_get( |
909 | page_id_t(undo->rseg->space->id, undo->last_page_no), |
910 | &mtr); |
911 | trx_undo_rec_t* rec = trx_undo_page_get_last_rec( |
912 | undo_page, undo->hdr_page_no, undo->hdr_offset); |
913 | while (rec) { |
914 | if (trx_undo_rec_get_undo_no(rec) >= limit) { |
915 | /* Truncate at least this record off, maybe |
916 | more */ |
917 | trunc_here = rec; |
918 | } else { |
919 | goto function_exit; |
920 | } |
921 | |
922 | rec = trx_undo_page_get_prev_rec(rec, |
923 | undo->hdr_page_no, |
924 | undo->hdr_offset); |
925 | } |
926 | |
927 | if (undo->last_page_no == undo->hdr_page_no) { |
928 | function_exit: |
929 | if (trunc_here) { |
930 | mlog_write_ulint(undo_page + TRX_UNDO_PAGE_HDR |
931 | + TRX_UNDO_PAGE_FREE, |
932 | ulint(trunc_here - undo_page), |
933 | MLOG_2BYTES, &mtr); |
934 | } |
935 | |
936 | mtr.commit(); |
937 | return; |
938 | } |
939 | |
940 | trx_undo_free_last_page(undo, &mtr); |
941 | mtr.commit(); |
942 | } |
943 | } |
944 | |
945 | /** Truncate the head of an undo log. |
946 | NOTE that only whole pages are freed; the header page is not |
947 | freed, but emptied, if all the records there are below the limit. |
948 | @param[in,out] rseg rollback segment |
949 | @param[in] hdr_page_no header page number |
950 | @param[in] hdr_offset header offset on the page |
951 | @param[in] limit first undo number to preserve |
952 | (everything below the limit will be truncated) */ |
953 | void |
954 | trx_undo_truncate_start( |
955 | trx_rseg_t* rseg, |
956 | ulint hdr_page_no, |
957 | ulint hdr_offset, |
958 | undo_no_t limit) |
959 | { |
960 | page_t* undo_page; |
961 | trx_undo_rec_t* rec; |
962 | trx_undo_rec_t* last_rec; |
963 | ulint page_no; |
964 | mtr_t mtr; |
965 | |
966 | ut_ad(mutex_own(&(rseg->mutex))); |
967 | |
968 | if (!limit) { |
969 | return; |
970 | } |
971 | loop: |
972 | mtr_start(&mtr); |
973 | |
974 | if (!rseg->is_persistent()) { |
975 | mtr.set_log_mode(MTR_LOG_NO_REDO); |
976 | } |
977 | |
978 | rec = trx_undo_get_first_rec(rseg->space, hdr_page_no, hdr_offset, |
979 | RW_X_LATCH, &mtr); |
980 | if (rec == NULL) { |
981 | /* Already empty */ |
982 | |
983 | mtr_commit(&mtr); |
984 | |
985 | return; |
986 | } |
987 | |
988 | undo_page = page_align(rec); |
989 | |
990 | last_rec = trx_undo_page_get_last_rec(undo_page, hdr_page_no, |
991 | hdr_offset); |
992 | if (trx_undo_rec_get_undo_no(last_rec) >= limit) { |
993 | |
994 | mtr_commit(&mtr); |
995 | |
996 | return; |
997 | } |
998 | |
999 | page_no = page_get_page_no(undo_page); |
1000 | |
1001 | if (page_no == hdr_page_no) { |
1002 | uint16_t end = mach_read_from_2(hdr_offset + TRX_UNDO_NEXT_LOG |
1003 | + undo_page); |
1004 | if (end == 0) { |
1005 | end = mach_read_from_2(TRX_UNDO_PAGE_HDR |
1006 | + TRX_UNDO_PAGE_FREE |
1007 | + undo_page); |
1008 | } |
1009 | |
1010 | mlog_write_ulint(undo_page + hdr_offset + TRX_UNDO_LOG_START, |
1011 | end, MLOG_2BYTES, &mtr); |
1012 | } else { |
1013 | trx_undo_free_page(rseg, true, hdr_page_no, page_no, &mtr); |
1014 | } |
1015 | |
1016 | mtr_commit(&mtr); |
1017 | |
1018 | goto loop; |
1019 | } |
1020 | |
1021 | /** Frees an undo log segment which is not in the history list. |
1022 | @param[in] undo undo log |
1023 | @param[in] noredo whether the undo tablespace is redo logged */ |
1024 | static |
1025 | void |
1026 | trx_undo_seg_free( |
1027 | const trx_undo_t* undo, |
1028 | bool noredo) |
1029 | { |
1030 | trx_rseg_t* rseg; |
1031 | fseg_header_t* file_seg; |
1032 | trx_rsegf_t* ; |
1033 | trx_usegf_t* ; |
1034 | ibool finished; |
1035 | mtr_t mtr; |
1036 | |
1037 | rseg = undo->rseg; |
1038 | |
1039 | do { |
1040 | |
1041 | mtr_start(&mtr); |
1042 | |
1043 | if (noredo) { |
1044 | mtr.set_log_mode(MTR_LOG_NO_REDO); |
1045 | } |
1046 | |
1047 | mutex_enter(&(rseg->mutex)); |
1048 | |
1049 | seg_header = trx_undo_page_get(page_id_t(undo->rseg->space->id, |
1050 | undo->hdr_page_no), |
1051 | &mtr) |
1052 | + TRX_UNDO_SEG_HDR; |
1053 | |
1054 | file_seg = seg_header + TRX_UNDO_FSEG_HEADER; |
1055 | |
1056 | finished = fseg_free_step(file_seg, false, &mtr); |
1057 | |
1058 | if (finished) { |
1059 | /* Update the rseg header */ |
1060 | rseg_header = trx_rsegf_get( |
1061 | rseg->space, rseg->page_no, &mtr); |
1062 | trx_rsegf_set_nth_undo(rseg_header, undo->id, FIL_NULL, |
1063 | &mtr); |
1064 | |
1065 | MONITOR_DEC(MONITOR_NUM_UNDO_SLOT_USED); |
1066 | } |
1067 | |
1068 | mutex_exit(&(rseg->mutex)); |
1069 | mtr_commit(&mtr); |
1070 | } while (!finished); |
1071 | } |
1072 | |
1073 | /*========== UNDO LOG MEMORY COPY INITIALIZATION =====================*/ |
1074 | |
1075 | /** Read an undo log when starting up the database. |
1076 | @param[in,out] rseg rollback segment |
1077 | @param[in] id rollback segment slot |
1078 | @param[in] page_no undo log segment page number |
1079 | @param[in,out] max_trx_id the largest observed transaction ID |
1080 | @return size of the undo log in pages */ |
1081 | ulint |
1082 | trx_undo_mem_create_at_db_start(trx_rseg_t* rseg, ulint id, ulint page_no, |
1083 | trx_id_t& max_trx_id) |
1084 | { |
1085 | mtr_t mtr; |
1086 | XID xid; |
1087 | |
1088 | ut_ad(id < TRX_RSEG_N_SLOTS); |
1089 | |
1090 | mtr.start(); |
1091 | const page_t* undo_page = trx_undo_page_get( |
1092 | page_id_t(rseg->space->id, page_no), &mtr); |
1093 | const ulint type = mach_read_from_2( |
1094 | TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_TYPE + undo_page); |
1095 | ut_ad(type == 0 || type == TRX_UNDO_INSERT || type == TRX_UNDO_UPDATE); |
1096 | |
1097 | uint state = mach_read_from_2(TRX_UNDO_SEG_HDR + TRX_UNDO_STATE |
1098 | + undo_page); |
1099 | uint offset = mach_read_from_2(TRX_UNDO_SEG_HDR + TRX_UNDO_LAST_LOG |
1100 | + undo_page); |
1101 | |
1102 | const trx_ulogf_t* = undo_page + offset; |
1103 | |
1104 | /* Read X/Open XA transaction identification if it exists, or |
1105 | set it to NULL. */ |
1106 | |
1107 | if (undo_header[TRX_UNDO_XID_EXISTS]) { |
1108 | trx_undo_read_xid(undo_header, &xid); |
1109 | } else { |
1110 | xid.null(); |
1111 | } |
1112 | |
1113 | trx_id_t trx_id = mach_read_from_8(undo_header + TRX_UNDO_TRX_ID); |
1114 | if (trx_id > max_trx_id) { |
1115 | max_trx_id = trx_id; |
1116 | } |
1117 | |
1118 | mutex_enter(&rseg->mutex); |
1119 | trx_undo_t* undo = trx_undo_mem_create( |
1120 | rseg, id, trx_id, &xid, page_no, offset); |
1121 | mutex_exit(&rseg->mutex); |
1122 | |
1123 | undo->dict_operation = undo_header[TRX_UNDO_DICT_TRANS]; |
1124 | undo->table_id = mach_read_from_8(undo_header + TRX_UNDO_TABLE_ID); |
1125 | undo->size = flst_get_len(TRX_UNDO_SEG_HDR + TRX_UNDO_PAGE_LIST |
1126 | + undo_page); |
1127 | |
1128 | if (UNIV_UNLIKELY(state == TRX_UNDO_TO_FREE)) { |
1129 | /* This is an old-format insert_undo log segment that |
1130 | is being freed. The page list is inconsistent. */ |
1131 | ut_ad(type == TRX_UNDO_INSERT); |
1132 | state = TRX_UNDO_TO_PURGE; |
1133 | } else { |
1134 | if (state == TRX_UNDO_TO_PURGE |
1135 | || state == TRX_UNDO_CACHED) { |
1136 | trx_id_t id = mach_read_from_8(TRX_UNDO_TRX_NO |
1137 | + undo_header); |
1138 | if (id > max_trx_id) { |
1139 | max_trx_id = id; |
1140 | } |
1141 | } |
1142 | |
1143 | fil_addr_t last_addr = flst_get_last( |
1144 | TRX_UNDO_SEG_HDR + TRX_UNDO_PAGE_LIST + undo_page, |
1145 | &mtr); |
1146 | |
1147 | undo->last_page_no = last_addr.page; |
1148 | undo->top_page_no = last_addr.page; |
1149 | |
1150 | page_t* last_page = trx_undo_page_get( |
1151 | page_id_t(rseg->space->id, undo->last_page_no), &mtr); |
1152 | |
1153 | if (const trx_undo_rec_t* rec = trx_undo_page_get_last_rec( |
1154 | last_page, page_no, offset)) { |
1155 | undo->top_offset = ulint(rec - last_page); |
1156 | undo->top_undo_no = trx_undo_rec_get_undo_no(rec); |
1157 | ut_ad(!undo->empty()); |
1158 | } else { |
1159 | undo->top_undo_no = IB_ID_MAX; |
1160 | ut_ad(undo->empty()); |
1161 | } |
1162 | } |
1163 | |
1164 | undo->state = state; |
1165 | |
1166 | if (state != TRX_UNDO_CACHED) { |
1167 | UT_LIST_ADD_LAST(type == TRX_UNDO_INSERT |
1168 | ? rseg->old_insert_list |
1169 | : rseg->undo_list, undo); |
1170 | } else { |
1171 | UT_LIST_ADD_LAST(rseg->undo_cached, undo); |
1172 | MONITOR_INC(MONITOR_NUM_UNDO_SLOT_CACHED); |
1173 | } |
1174 | |
1175 | mtr.commit(); |
1176 | return undo->size; |
1177 | } |
1178 | |
1179 | /********************************************************************//** |
1180 | Creates and initializes an undo log memory object. |
1181 | @return own: the undo log memory object */ |
1182 | static |
1183 | trx_undo_t* |
1184 | trx_undo_mem_create( |
1185 | /*================*/ |
1186 | trx_rseg_t* rseg, /*!< in: rollback segment memory object */ |
1187 | ulint id, /*!< in: slot index within rseg */ |
1188 | trx_id_t trx_id, /*!< in: id of the trx for which the undo log |
1189 | is created */ |
1190 | const XID* xid, /*!< in: X/Open transaction identification */ |
1191 | ulint page_no,/*!< in: undo log header page number */ |
1192 | ulint offset) /*!< in: undo log header byte offset on page */ |
1193 | { |
1194 | trx_undo_t* undo; |
1195 | |
1196 | ut_ad(mutex_own(&(rseg->mutex))); |
1197 | |
1198 | ut_a(id < TRX_RSEG_N_SLOTS); |
1199 | |
1200 | undo = static_cast<trx_undo_t*>(ut_malloc_nokey(sizeof(*undo))); |
1201 | |
1202 | if (undo == NULL) { |
1203 | |
1204 | return(NULL); |
1205 | } |
1206 | |
1207 | undo->id = id; |
1208 | undo->state = TRX_UNDO_ACTIVE; |
1209 | undo->trx_id = trx_id; |
1210 | undo->xid = *xid; |
1211 | |
1212 | undo->dict_operation = FALSE; |
1213 | |
1214 | undo->rseg = rseg; |
1215 | |
1216 | undo->hdr_page_no = page_no; |
1217 | undo->hdr_offset = offset; |
1218 | undo->last_page_no = page_no; |
1219 | undo->size = 1; |
1220 | |
1221 | undo->top_undo_no = IB_ID_MAX; |
1222 | undo->top_page_no = page_no; |
1223 | undo->guess_block = NULL; |
1224 | undo->withdraw_clock = 0; |
1225 | ut_ad(undo->empty()); |
1226 | |
1227 | return(undo); |
1228 | } |
1229 | |
1230 | /********************************************************************//** |
1231 | Initializes a cached undo log object for new use. */ |
1232 | static |
1233 | void |
1234 | trx_undo_mem_init_for_reuse( |
1235 | /*========================*/ |
1236 | trx_undo_t* undo, /*!< in: undo log to init */ |
1237 | trx_id_t trx_id, /*!< in: id of the trx for which the undo log |
1238 | is created */ |
1239 | const XID* xid, /*!< in: X/Open XA transaction identification*/ |
1240 | ulint offset) /*!< in: undo log header byte offset on page */ |
1241 | { |
1242 | ut_ad(mutex_own(&((undo->rseg)->mutex))); |
1243 | |
1244 | ut_a(undo->id < TRX_RSEG_N_SLOTS); |
1245 | |
1246 | undo->state = TRX_UNDO_ACTIVE; |
1247 | undo->trx_id = trx_id; |
1248 | undo->xid = *xid; |
1249 | |
1250 | undo->dict_operation = FALSE; |
1251 | |
1252 | undo->hdr_offset = offset; |
1253 | undo->top_undo_no = IB_ID_MAX; |
1254 | ut_ad(undo->empty()); |
1255 | } |
1256 | |
1257 | /** Create an undo log. |
1258 | @param[in,out] trx transaction |
1259 | @param[in,out] rseg rollback segment |
1260 | @param[out] undo undo log object |
1261 | @param[out] err error code |
1262 | @param[in,out] mtr mini-transaction |
1263 | @return undo log block |
1264 | @retval NULL on failure */ |
1265 | static MY_ATTRIBUTE((nonnull, warn_unused_result)) |
1266 | buf_block_t* |
1267 | trx_undo_create(trx_t* trx, trx_rseg_t* rseg, trx_undo_t** undo, |
1268 | dberr_t* err, mtr_t* mtr) |
1269 | { |
1270 | ulint id; |
1271 | |
1272 | ut_ad(mutex_own(&(rseg->mutex))); |
1273 | |
1274 | buf_block_t* block = trx_undo_seg_create( |
1275 | rseg->space, |
1276 | trx_rsegf_get(rseg->space, rseg->page_no, mtr), &id, err, mtr); |
1277 | |
1278 | if (!block) { |
1279 | return NULL; |
1280 | } |
1281 | |
1282 | rseg->curr_size++; |
1283 | |
1284 | ulint offset = trx_undo_header_create(block->frame, trx->id, mtr); |
1285 | |
1286 | trx_undo_header_add_space_for_xid(block->frame, block->frame + offset, |
1287 | mtr); |
1288 | |
1289 | *undo = trx_undo_mem_create(rseg, id, trx->id, trx->xid, |
1290 | block->page.id.page_no(), offset); |
1291 | if (*undo == NULL) { |
1292 | *err = DB_OUT_OF_MEMORY; |
1293 | /* FIXME: this will not free the undo block to the file */ |
1294 | return NULL; |
1295 | } else if (rseg != trx->rsegs.m_redo.rseg) { |
1296 | return block; |
1297 | } |
1298 | |
1299 | switch (trx_get_dict_operation(trx)) { |
1300 | case TRX_DICT_OP_NONE: |
1301 | break; |
1302 | case TRX_DICT_OP_INDEX: |
1303 | /* Do not discard the table on recovery. */ |
1304 | trx->table_id = 0; |
1305 | /* fall through */ |
1306 | case TRX_DICT_OP_TABLE: |
1307 | (*undo)->table_id = trx->table_id; |
1308 | (*undo)->dict_operation = TRUE; |
1309 | mlog_write_ulint(block->frame + offset + TRX_UNDO_DICT_TRANS, |
1310 | TRUE, MLOG_1BYTE, mtr); |
1311 | mlog_write_ull(block->frame + offset + TRX_UNDO_TABLE_ID, |
1312 | trx->table_id, mtr); |
1313 | } |
1314 | |
1315 | *err = DB_SUCCESS; |
1316 | return block; |
1317 | } |
1318 | |
1319 | /*================ UNDO LOG ASSIGNMENT AND CLEANUP =====================*/ |
1320 | |
1321 | /** Reuse a cached undo log block. |
1322 | @param[in,out] trx transaction |
1323 | @param[in,out] rseg rollback segment |
1324 | @param[out] pundo the undo log memory object |
1325 | @param[in,out] mtr mini-transaction |
1326 | @return the undo log block |
1327 | @retval NULL if none cached */ |
1328 | static |
1329 | buf_block_t* |
1330 | trx_undo_reuse_cached(trx_t* trx, trx_rseg_t* rseg, trx_undo_t** pundo, |
1331 | mtr_t* mtr) |
1332 | { |
1333 | ut_ad(mutex_own(&rseg->mutex)); |
1334 | |
1335 | trx_undo_t* undo = UT_LIST_GET_FIRST(rseg->undo_cached); |
1336 | if (!undo) { |
1337 | return NULL; |
1338 | } |
1339 | |
1340 | ut_ad(undo->size == 1); |
1341 | ut_ad(undo->id < TRX_RSEG_N_SLOTS); |
1342 | |
1343 | buf_block_t* block = buf_page_get(page_id_t(undo->rseg->space->id, |
1344 | undo->hdr_page_no), |
1345 | univ_page_size, RW_X_LATCH, mtr); |
1346 | if (!block) { |
1347 | return NULL; |
1348 | } |
1349 | |
1350 | buf_block_dbg_add_level(block, SYNC_TRX_UNDO_PAGE); |
1351 | |
1352 | UT_LIST_REMOVE(rseg->undo_cached, undo); |
1353 | MONITOR_DEC(MONITOR_NUM_UNDO_SLOT_CACHED); |
1354 | |
1355 | *pundo = undo; |
1356 | |
1357 | ulint offset = trx_undo_header_create(block->frame, trx->id, mtr); |
1358 | |
1359 | trx_undo_header_add_space_for_xid(block->frame, block->frame + offset, |
1360 | mtr); |
1361 | |
1362 | trx_undo_mem_init_for_reuse(undo, trx->id, trx->xid, offset); |
1363 | |
1364 | if (rseg != trx->rsegs.m_redo.rseg) { |
1365 | return block; |
1366 | } |
1367 | |
1368 | switch (trx_get_dict_operation(trx)) { |
1369 | case TRX_DICT_OP_NONE: |
1370 | return block; |
1371 | case TRX_DICT_OP_INDEX: |
1372 | /* Do not discard the table on recovery. */ |
1373 | trx->table_id = 0; |
1374 | /* fall through */ |
1375 | case TRX_DICT_OP_TABLE: |
1376 | undo->table_id = trx->table_id; |
1377 | undo->dict_operation = TRUE; |
1378 | mlog_write_ulint(block->frame + offset + TRX_UNDO_DICT_TRANS, |
1379 | TRUE, MLOG_1BYTE, mtr); |
1380 | mlog_write_ull(block->frame + offset + TRX_UNDO_TABLE_ID, |
1381 | trx->table_id, mtr); |
1382 | } |
1383 | |
1384 | return block; |
1385 | } |
1386 | |
1387 | /** Assign an undo log for a persistent transaction. |
1388 | A new undo log is created or a cached undo log reused. |
1389 | @param[in,out] trx transaction |
1390 | @param[out] err error code |
1391 | @param[in,out] mtr mini-transaction |
1392 | @return the undo log block |
1393 | @retval NULL on error */ |
1394 | buf_block_t* |
1395 | trx_undo_assign(trx_t* trx, dberr_t* err, mtr_t* mtr) |
1396 | { |
1397 | ut_ad(mtr->get_log_mode() == MTR_LOG_ALL); |
1398 | |
1399 | trx_undo_t* undo = trx->rsegs.m_redo.undo; |
1400 | |
1401 | if (undo) { |
1402 | return buf_page_get_gen( |
1403 | page_id_t(undo->rseg->space->id, undo->last_page_no), |
1404 | univ_page_size, RW_X_LATCH, |
1405 | buf_pool_is_obsolete(undo->withdraw_clock) |
1406 | ? NULL : undo->guess_block, |
1407 | BUF_GET, __FILE__, __LINE__, mtr, err); |
1408 | } |
1409 | |
1410 | trx_rseg_t* rseg = trx->rsegs.m_redo.rseg; |
1411 | |
1412 | mutex_enter(&rseg->mutex); |
1413 | buf_block_t* block = trx_undo_reuse_cached( |
1414 | trx, rseg, &trx->rsegs.m_redo.undo, mtr); |
1415 | |
1416 | if (!block) { |
1417 | block = trx_undo_create(trx, rseg, &trx->rsegs.m_redo.undo, |
1418 | err, mtr); |
1419 | ut_ad(!block == (*err != DB_SUCCESS)); |
1420 | if (!block) { |
1421 | goto func_exit; |
1422 | } |
1423 | } else { |
1424 | *err = DB_SUCCESS; |
1425 | } |
1426 | |
1427 | UT_LIST_ADD_FIRST(rseg->undo_list, trx->rsegs.m_redo.undo); |
1428 | |
1429 | func_exit: |
1430 | mutex_exit(&rseg->mutex); |
1431 | return block; |
1432 | } |
1433 | |
1434 | /** Assign an undo log for a transaction. |
1435 | A new undo log is created or a cached undo log reused. |
1436 | @param[in,out] trx transaction |
1437 | @param[in] rseg rollback segment |
1438 | @param[out] undo the undo log |
1439 | @param[out] err error code |
1440 | @param[in,out] mtr mini-transaction |
1441 | @return the undo log block |
1442 | @retval NULL on error */ |
1443 | buf_block_t* |
1444 | trx_undo_assign_low(trx_t* trx, trx_rseg_t* rseg, trx_undo_t** undo, |
1445 | dberr_t* err, mtr_t* mtr) |
1446 | { |
1447 | const bool is_temp = rseg == trx->rsegs.m_noredo.rseg; |
1448 | |
1449 | ut_ad(rseg == trx->rsegs.m_redo.rseg |
1450 | || rseg == trx->rsegs.m_noredo.rseg); |
1451 | ut_ad(undo == (is_temp |
1452 | ? &trx->rsegs.m_noredo.undo |
1453 | : &trx->rsegs.m_redo.undo)); |
1454 | ut_ad(mtr->get_log_mode() |
1455 | == (is_temp ? MTR_LOG_NO_REDO : MTR_LOG_ALL)); |
1456 | |
1457 | if (*undo) { |
1458 | return buf_page_get_gen( |
1459 | page_id_t(rseg->space->id, (*undo)->last_page_no), |
1460 | univ_page_size, RW_X_LATCH, |
1461 | buf_pool_is_obsolete((*undo)->withdraw_clock) |
1462 | ? NULL : (*undo)->guess_block, |
1463 | BUF_GET, __FILE__, __LINE__, mtr, err); |
1464 | } |
1465 | |
1466 | DBUG_EXECUTE_IF( |
1467 | "ib_create_table_fail_too_many_trx" , |
1468 | *err = DB_TOO_MANY_CONCURRENT_TRXS; return NULL; |
1469 | ); |
1470 | |
1471 | mutex_enter(&rseg->mutex); |
1472 | |
1473 | buf_block_t* block = trx_undo_reuse_cached(trx, rseg, undo, mtr); |
1474 | |
1475 | if (!block) { |
1476 | block = trx_undo_create(trx, rseg, undo, err, mtr); |
1477 | ut_ad(!block == (*err != DB_SUCCESS)); |
1478 | if (!block) { |
1479 | goto func_exit; |
1480 | } |
1481 | } else { |
1482 | *err = DB_SUCCESS; |
1483 | } |
1484 | |
1485 | UT_LIST_ADD_FIRST(rseg->undo_list, *undo); |
1486 | |
1487 | func_exit: |
1488 | mutex_exit(&rseg->mutex); |
1489 | return block; |
1490 | } |
1491 | |
1492 | /******************************************************************//** |
1493 | Sets the state of the undo log segment at a transaction finish. |
1494 | @return undo log segment header page, x-latched */ |
1495 | page_t* |
1496 | trx_undo_set_state_at_finish( |
1497 | /*=========================*/ |
1498 | trx_undo_t* undo, /*!< in: undo log memory copy */ |
1499 | mtr_t* mtr) /*!< in: mtr */ |
1500 | { |
1501 | trx_usegf_t* seg_hdr; |
1502 | trx_upagef_t* page_hdr; |
1503 | page_t* undo_page; |
1504 | ulint state; |
1505 | |
1506 | ut_a(undo->id < TRX_RSEG_N_SLOTS); |
1507 | |
1508 | undo_page = trx_undo_page_get( |
1509 | page_id_t(undo->rseg->space->id, undo->hdr_page_no), mtr); |
1510 | |
1511 | seg_hdr = undo_page + TRX_UNDO_SEG_HDR; |
1512 | page_hdr = undo_page + TRX_UNDO_PAGE_HDR; |
1513 | |
1514 | if (undo->size == 1 |
1515 | && mach_read_from_2(page_hdr + TRX_UNDO_PAGE_FREE) |
1516 | < TRX_UNDO_PAGE_REUSE_LIMIT) { |
1517 | |
1518 | state = TRX_UNDO_CACHED; |
1519 | } else { |
1520 | state = TRX_UNDO_TO_PURGE; |
1521 | } |
1522 | |
1523 | undo->state = state; |
1524 | |
1525 | mlog_write_ulint(seg_hdr + TRX_UNDO_STATE, state, MLOG_2BYTES, mtr); |
1526 | |
1527 | return(undo_page); |
1528 | } |
1529 | |
1530 | /** Set the state of the undo log segment at a XA PREPARE or XA ROLLBACK. |
1531 | @param[in,out] trx transaction |
1532 | @param[in,out] undo undo log |
1533 | @param[in] rollback false=XA PREPARE, true=XA ROLLBACK |
1534 | @param[in,out] mtr mini-transaction |
1535 | @return undo log segment header page, x-latched */ |
1536 | page_t* |
1537 | trx_undo_set_state_at_prepare( |
1538 | trx_t* trx, |
1539 | trx_undo_t* undo, |
1540 | bool rollback, |
1541 | mtr_t* mtr) |
1542 | { |
1543 | trx_usegf_t* seg_hdr; |
1544 | trx_ulogf_t* ; |
1545 | page_t* undo_page; |
1546 | ulint offset; |
1547 | |
1548 | ut_ad(trx && undo && mtr); |
1549 | |
1550 | ut_a(undo->id < TRX_RSEG_N_SLOTS); |
1551 | |
1552 | undo_page = trx_undo_page_get( |
1553 | page_id_t(undo->rseg->space->id, undo->hdr_page_no), mtr); |
1554 | |
1555 | seg_hdr = undo_page + TRX_UNDO_SEG_HDR; |
1556 | |
1557 | if (rollback) { |
1558 | ut_ad(undo->state == TRX_UNDO_PREPARED); |
1559 | mlog_write_ulint(seg_hdr + TRX_UNDO_STATE, TRX_UNDO_ACTIVE, |
1560 | MLOG_2BYTES, mtr); |
1561 | return(undo_page); |
1562 | } |
1563 | |
1564 | /*------------------------------*/ |
1565 | ut_ad(undo->state == TRX_UNDO_ACTIVE); |
1566 | undo->state = TRX_UNDO_PREPARED; |
1567 | undo->xid = *trx->xid; |
1568 | /*------------------------------*/ |
1569 | |
1570 | mlog_write_ulint(seg_hdr + TRX_UNDO_STATE, undo->state, |
1571 | MLOG_2BYTES, mtr); |
1572 | |
1573 | offset = mach_read_from_2(seg_hdr + TRX_UNDO_LAST_LOG); |
1574 | undo_header = undo_page + offset; |
1575 | |
1576 | mlog_write_ulint(undo_header + TRX_UNDO_XID_EXISTS, |
1577 | TRUE, MLOG_1BYTE, mtr); |
1578 | |
1579 | trx_undo_write_xid(undo_header, &undo->xid, mtr); |
1580 | |
1581 | return(undo_page); |
1582 | } |
1583 | |
1584 | /** Free an old insert or temporary undo log after commit or rollback. |
1585 | The information is not needed after a commit or rollback, therefore |
1586 | the data can be discarded. |
1587 | @param[in,out] undo undo log |
1588 | @param[in] is_temp whether this is temporary undo log */ |
1589 | void |
1590 | trx_undo_commit_cleanup(trx_undo_t* undo, bool is_temp) |
1591 | { |
1592 | trx_rseg_t* rseg = undo->rseg; |
1593 | ut_ad(is_temp == !rseg->is_persistent()); |
1594 | ut_ad(!is_temp || 0 == UT_LIST_GET_LEN(rseg->old_insert_list)); |
1595 | |
1596 | mutex_enter(&rseg->mutex); |
1597 | |
1598 | UT_LIST_REMOVE(is_temp ? rseg->undo_list : rseg->old_insert_list, |
1599 | undo); |
1600 | |
1601 | if (undo->state == TRX_UNDO_CACHED) { |
1602 | UT_LIST_ADD_FIRST(rseg->undo_cached, undo); |
1603 | MONITOR_INC(MONITOR_NUM_UNDO_SLOT_CACHED); |
1604 | } else { |
1605 | ut_ad(undo->state == TRX_UNDO_TO_PURGE); |
1606 | |
1607 | /* Delete first the undo log segment in the file */ |
1608 | mutex_exit(&rseg->mutex); |
1609 | trx_undo_seg_free(undo, true); |
1610 | mutex_enter(&rseg->mutex); |
1611 | |
1612 | ut_ad(rseg->curr_size > undo->size); |
1613 | rseg->curr_size -= undo->size; |
1614 | |
1615 | ut_free(undo); |
1616 | } |
1617 | |
1618 | mutex_exit(&rseg->mutex); |
1619 | } |
1620 | |
1621 | /** At shutdown, frees the undo logs of a transaction. */ |
1622 | void |
1623 | trx_undo_free_at_shutdown(trx_t *trx) |
1624 | { |
1625 | if (trx_undo_t*& undo = trx->rsegs.m_redo.undo) { |
1626 | switch (undo->state) { |
1627 | case TRX_UNDO_PREPARED: |
1628 | break; |
1629 | case TRX_UNDO_CACHED: |
1630 | case TRX_UNDO_TO_FREE: |
1631 | case TRX_UNDO_TO_PURGE: |
1632 | ut_ad(trx_state_eq(trx, |
1633 | TRX_STATE_COMMITTED_IN_MEMORY)); |
1634 | /* fall through */ |
1635 | case TRX_UNDO_ACTIVE: |
1636 | /* lock_trx_release_locks() assigns |
1637 | trx->state = TRX_STATE_COMMITTED_IN_MEMORY. */ |
1638 | ut_a(!srv_was_started |
1639 | || srv_read_only_mode |
1640 | || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO |
1641 | || srv_fast_shutdown); |
1642 | break; |
1643 | default: |
1644 | ut_error; |
1645 | } |
1646 | |
1647 | UT_LIST_REMOVE(trx->rsegs.m_redo.rseg->undo_list, undo); |
1648 | ut_free(undo); |
1649 | undo = NULL; |
1650 | } |
1651 | |
1652 | if (trx_undo_t*& undo = trx->rsegs.m_redo.old_insert) { |
1653 | switch (undo->state) { |
1654 | case TRX_UNDO_PREPARED: |
1655 | break; |
1656 | case TRX_UNDO_CACHED: |
1657 | case TRX_UNDO_TO_FREE: |
1658 | case TRX_UNDO_TO_PURGE: |
1659 | ut_ad(trx_state_eq(trx, |
1660 | TRX_STATE_COMMITTED_IN_MEMORY)); |
1661 | /* fall through */ |
1662 | case TRX_UNDO_ACTIVE: |
1663 | /* lock_trx_release_locks() assigns |
1664 | trx->state = TRX_STATE_COMMITTED_IN_MEMORY. */ |
1665 | ut_a(!srv_was_started |
1666 | || srv_read_only_mode |
1667 | || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO |
1668 | || srv_fast_shutdown); |
1669 | break; |
1670 | default: |
1671 | ut_error; |
1672 | } |
1673 | |
1674 | UT_LIST_REMOVE(trx->rsegs.m_redo.rseg->old_insert_list, undo); |
1675 | ut_free(undo); |
1676 | undo = NULL; |
1677 | } |
1678 | |
1679 | if (trx_undo_t*& undo = trx->rsegs.m_noredo.undo) { |
1680 | ut_a(undo->state == TRX_UNDO_PREPARED); |
1681 | |
1682 | UT_LIST_REMOVE(trx->rsegs.m_noredo.rseg->undo_list, undo); |
1683 | ut_free(undo); |
1684 | undo = NULL; |
1685 | } |
1686 | } |
1687 | |
1688 | /** Truncate UNDO tablespace, reinitialize header and rseg. |
1689 | @param[in] undo_trunc UNDO tablespace handler |
1690 | @return true if success else false. */ |
1691 | bool |
1692 | trx_undo_truncate_tablespace( |
1693 | undo::Truncate* undo_trunc) |
1694 | |
1695 | { |
1696 | fil_space_t* space = fil_space_acquire( |
1697 | undo_trunc->get_marked_space_id()); |
1698 | if (!space) return false; |
1699 | |
1700 | /* Step-1: Truncate tablespace. */ |
1701 | if (!fil_truncate_tablespace( |
1702 | space, SRV_UNDO_TABLESPACE_SIZE_IN_PAGES)) { |
1703 | space->release(); |
1704 | return false; |
1705 | } |
1706 | |
1707 | /* Step-2: Re-initialize tablespace header. |
1708 | Avoid REDO logging as we don't want to apply the action if server |
1709 | crashes. For fix-up we have UNDO-truncate-ddl-log. */ |
1710 | mtr_t mtr; |
1711 | mtr_start(&mtr); |
1712 | mtr_set_log_mode(&mtr, MTR_LOG_NO_REDO); |
1713 | fsp_header_init(space, SRV_UNDO_TABLESPACE_SIZE_IN_PAGES, &mtr); |
1714 | mtr_commit(&mtr); |
1715 | |
1716 | /* Step-3: Re-initialize rollback segment header that resides |
1717 | in truncated tablespaced. */ |
1718 | mtr_start(&mtr); |
1719 | mtr_set_log_mode(&mtr, MTR_LOG_NO_REDO); |
1720 | mtr_x_lock(&space->latch, &mtr); |
1721 | buf_block_t* = trx_sysf_get(&mtr); |
1722 | |
1723 | for (ulint i = 0; i < undo_trunc->rsegs_size(); ++i) { |
1724 | trx_rsegf_t* ; |
1725 | |
1726 | trx_rseg_t* rseg = undo_trunc->get_ith_rseg(i); |
1727 | |
1728 | rseg->page_no = trx_rseg_header_create( |
1729 | space, rseg->id, sys_header, &mtr); |
1730 | |
1731 | rseg_header = trx_rsegf_get_new(space->id, rseg->page_no, |
1732 | &mtr); |
1733 | |
1734 | /* Before re-initialization ensure that we free the existing |
1735 | structure. There can't be any active transactions. */ |
1736 | ut_a(UT_LIST_GET_LEN(rseg->undo_list) == 0); |
1737 | |
1738 | trx_undo_t* next_undo; |
1739 | |
1740 | for (trx_undo_t* undo = UT_LIST_GET_FIRST(rseg->undo_cached); |
1741 | undo != NULL; |
1742 | undo = next_undo) { |
1743 | |
1744 | next_undo = UT_LIST_GET_NEXT(undo_list, undo); |
1745 | UT_LIST_REMOVE(rseg->undo_cached, undo); |
1746 | MONITOR_DEC(MONITOR_NUM_UNDO_SLOT_CACHED); |
1747 | ut_free(undo); |
1748 | } |
1749 | |
1750 | UT_LIST_INIT(rseg->undo_list, &trx_undo_t::undo_list); |
1751 | UT_LIST_INIT(rseg->undo_cached, &trx_undo_t::undo_list); |
1752 | |
1753 | /* Initialize the undo log lists according to the rseg header */ |
1754 | rseg->curr_size = mtr_read_ulint( |
1755 | rseg_header + TRX_RSEG_HISTORY_SIZE, MLOG_4BYTES, &mtr) |
1756 | + 1; |
1757 | |
1758 | ut_ad(rseg->curr_size == 1); |
1759 | |
1760 | rseg->trx_ref_count = 0; |
1761 | rseg->last_page_no = FIL_NULL; |
1762 | rseg->last_offset = 0; |
1763 | rseg->last_commit = 0; |
1764 | rseg->needs_purge = false; |
1765 | } |
1766 | mtr_commit(&mtr); |
1767 | space->release(); |
1768 | |
1769 | return true; |
1770 | } |
1771 | |