1 /*****************************************************************************
2
3 Copyright (c) 1995, 2021, Oracle and/or its affiliates.
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 mtr/mtr0log.cc
29 Mini-transaction log routines
30
31 Created 12/7/1995 Heikki Tuuri
32 *******************************************************/
33
34 #include "mtr0log.h"
35
36 #ifdef UNIV_NONINL
37 #include "mtr0log.ic"
38 #endif /* UNIV_NOINL */
39
40 #include "buf0buf.h"
41 #include "dict0dict.h"
42 #include "log0recv.h"
43 #include "page0page.h"
44 #include "buf0dblwr.h"
45
46 #ifndef UNIV_HOTBACKUP
47 # include "dict0boot.h"
48
49 /********************************************************//**
50 Catenates n bytes to the mtr log. */
51 void
mlog_catenate_string(mtr_t * mtr,const byte * str,ulint len)52 mlog_catenate_string(
53 /*=================*/
54 mtr_t* mtr, /*!< in: mtr */
55 const byte* str, /*!< in: string to write */
56 ulint len) /*!< in: string length */
57 {
58 if (mtr_get_log_mode(mtr) == MTR_LOG_NONE) {
59
60 return;
61 }
62
63 mtr->get_log()->push(str, ib_uint32_t(len));
64 }
65
66 /********************************************************//**
67 Writes the initial part of a log record consisting of one-byte item
68 type and four-byte space and page numbers. Also pushes info
69 to the mtr memo that a buffer page has been modified. */
70 void
mlog_write_initial_log_record(const byte * ptr,mlog_id_t type,mtr_t * mtr)71 mlog_write_initial_log_record(
72 /*==========================*/
73 const byte* ptr, /*!< in: pointer to (inside) a buffer
74 frame holding the file page where
75 modification is made */
76 mlog_id_t type, /*!< in: log item type: MLOG_1BYTE, ... */
77 mtr_t* mtr) /*!< in: mini-transaction handle */
78 {
79 byte* log_ptr;
80
81 ut_ad(type <= MLOG_BIGGEST_TYPE);
82 ut_ad(type > MLOG_8BYTES);
83
84 log_ptr = mlog_open(mtr, 11);
85
86 /* If no logging is requested, we may return now */
87 if (log_ptr == NULL) {
88
89 return;
90 }
91
92 log_ptr = mlog_write_initial_log_record_fast(ptr, type, log_ptr, mtr);
93
94 mlog_close(mtr, log_ptr);
95 }
96 #endif /* !UNIV_HOTBACKUP */
97
98 /********************************************************//**
99 Parses an initial log record written by mlog_write_initial_log_record.
100 @return parsed record end, NULL if not a complete record */
101 byte*
mlog_parse_initial_log_record(const byte * ptr,const byte * end_ptr,mlog_id_t * type,ulint * space,ulint * page_no)102 mlog_parse_initial_log_record(
103 /*==========================*/
104 const byte* ptr, /*!< in: buffer */
105 const byte* end_ptr,/*!< in: buffer end */
106 mlog_id_t* type, /*!< out: log record type: MLOG_1BYTE, ... */
107 ulint* space, /*!< out: space id */
108 ulint* page_no)/*!< out: page number */
109 {
110 if (end_ptr < ptr + 1) {
111
112 return(NULL);
113 }
114
115 *type = (mlog_id_t)((ulint)*ptr & ~MLOG_SINGLE_REC_FLAG);
116 ut_ad(*type <= MLOG_BIGGEST_TYPE);
117
118 ptr++;
119
120 if (end_ptr < ptr + 2) {
121
122 return(NULL);
123 }
124
125 *space = mach_parse_compressed(&ptr, end_ptr);
126
127 if (ptr != NULL) {
128 *page_no = mach_parse_compressed(&ptr, end_ptr);
129 }
130
131 return(const_cast<byte*>(ptr));
132 }
133
134 /********************************************************//**
135 Parses a log record written by mlog_write_ulint or mlog_write_ull.
136 @return parsed record end, NULL if not a complete record or a corrupt record */
137 byte*
mlog_parse_nbytes(mlog_id_t type,const byte * ptr,const byte * end_ptr,byte * page,void * page_zip)138 mlog_parse_nbytes(
139 /*==============*/
140 mlog_id_t type, /*!< in: log record type: MLOG_1BYTE, ... */
141 const byte* ptr, /*!< in: buffer */
142 const byte* end_ptr,/*!< in: buffer end */
143 byte* page, /*!< in: page where to apply the log
144 record, or NULL */
145 void* page_zip)/*!< in/out: compressed page, or NULL */
146 {
147 ulint offset;
148 ulint val;
149 ib_uint64_t dval;
150
151 ut_a(type <= MLOG_8BYTES);
152 ut_a(!page || !page_zip
153 || !fil_page_index_page_check(page));
154
155 if (end_ptr < ptr + 2) {
156
157 return(NULL);
158 }
159
160 offset = mach_read_from_2(ptr);
161 ptr += 2;
162
163 if (offset >= UNIV_PAGE_SIZE) {
164 recv_sys->found_corrupt_log = TRUE;
165
166 return(NULL);
167 }
168
169 if (type == MLOG_8BYTES) {
170 dval = mach_u64_parse_compressed(&ptr, end_ptr);
171
172 if (ptr == NULL) {
173
174 return(NULL);
175 }
176
177 if (page) {
178 if (page_zip) {
179 mach_write_to_8
180 (((page_zip_des_t*) page_zip)->data
181 + offset, dval);
182 }
183 mach_write_to_8(page + offset, dval);
184 }
185
186 return(const_cast<byte*>(ptr));
187 }
188
189 val = mach_parse_compressed(&ptr, end_ptr);
190
191 if (ptr == NULL) {
192
193 return(NULL);
194 }
195
196 switch (type) {
197 case MLOG_1BYTE:
198 if (val > 0xFFUL) {
199 goto corrupt;
200 }
201 if (page) {
202 if (page_zip) {
203 mach_write_to_1
204 (((page_zip_des_t*) page_zip)->data
205 + offset, val);
206 }
207 mach_write_to_1(page + offset, val);
208 }
209 break;
210 case MLOG_2BYTES:
211 if (val > 0xFFFFUL) {
212 goto corrupt;
213 }
214 if (page) {
215 if (page_zip) {
216 mach_write_to_2
217 (((page_zip_des_t*) page_zip)->data
218 + offset, val);
219 }
220 mach_write_to_2(page + offset, val);
221 }
222 break;
223 case MLOG_4BYTES:
224 if (page) {
225 if (page_zip) {
226 mach_write_to_4
227 (((page_zip_des_t*) page_zip)->data
228 + offset, val);
229 }
230 mach_write_to_4(page + offset, val);
231 }
232 break;
233 default:
234 corrupt:
235 recv_sys->found_corrupt_log = TRUE;
236 ptr = NULL;
237 }
238
239 return(const_cast<byte*>(ptr));
240 }
241
242 /********************************************************//**
243 Writes 1, 2 or 4 bytes to a file page. Writes the corresponding log
244 record to the mini-transaction log if mtr is not NULL. */
245 void
mlog_write_ulint(byte * ptr,ulint val,mlog_id_t type,mtr_t * mtr)246 mlog_write_ulint(
247 /*=============*/
248 byte* ptr, /*!< in: pointer where to write */
249 ulint val, /*!< in: value to write */
250 mlog_id_t type, /*!< in: MLOG_1BYTE, MLOG_2BYTES, MLOG_4BYTES */
251 mtr_t* mtr) /*!< in: mini-transaction handle */
252 {
253 switch (type) {
254 case MLOG_1BYTE:
255 mach_write_to_1(ptr, val);
256 break;
257 case MLOG_2BYTES:
258 mach_write_to_2(ptr, val);
259 break;
260 case MLOG_4BYTES:
261 mach_write_to_4(ptr, val);
262 break;
263 default:
264 ut_error;
265 }
266
267 if (mtr != 0) {
268 byte* log_ptr = mlog_open(mtr, 11 + 2 + 5);
269
270 /* If no logging is requested, we may return now */
271
272 if (log_ptr != 0) {
273
274 log_ptr = mlog_write_initial_log_record_fast(
275 ptr, type, log_ptr, mtr);
276
277 mach_write_to_2(log_ptr, page_offset(ptr));
278 log_ptr += 2;
279
280 log_ptr += mach_write_compressed(log_ptr, val);
281
282 mlog_close(mtr, log_ptr);
283 }
284 }
285 }
286
287 /********************************************************//**
288 Writes 8 bytes to a file page. Writes the corresponding log
289 record to the mini-transaction log, only if mtr is not NULL */
290 void
mlog_write_ull(byte * ptr,ib_uint64_t val,mtr_t * mtr)291 mlog_write_ull(
292 /*===========*/
293 byte* ptr, /*!< in: pointer where to write */
294 ib_uint64_t val, /*!< in: value to write */
295 mtr_t* mtr) /*!< in: mini-transaction handle */
296 {
297 mach_write_to_8(ptr, val);
298
299 if (mtr != 0) {
300 byte* log_ptr = mlog_open(mtr, 11 + 2 + 9);
301
302 /* If no logging is requested, we may return now */
303 if (log_ptr != 0) {
304
305 log_ptr = mlog_write_initial_log_record_fast(
306 ptr, MLOG_8BYTES, log_ptr, mtr);
307
308 mach_write_to_2(log_ptr, page_offset(ptr));
309 log_ptr += 2;
310
311 log_ptr += mach_u64_write_compressed(log_ptr, val);
312
313 mlog_close(mtr, log_ptr);
314 }
315 }
316 }
317
318 #ifndef UNIV_HOTBACKUP
319 /********************************************************//**
320 Writes a string to a file page buffered in the buffer pool. Writes the
321 corresponding log record to the mini-transaction log. */
322 void
mlog_write_string(byte * ptr,const byte * str,ulint len,mtr_t * mtr)323 mlog_write_string(
324 /*==============*/
325 byte* ptr, /*!< in: pointer where to write */
326 const byte* str, /*!< in: string to write */
327 ulint len, /*!< in: string length */
328 mtr_t* mtr) /*!< in: mini-transaction handle */
329 {
330 ut_ad(ptr && mtr);
331 ut_a(len < UNIV_PAGE_SIZE);
332
333 memcpy(ptr, str, len);
334
335 mlog_log_string(ptr, len, mtr);
336 }
337
338 /********************************************************//**
339 Logs a write of a string to a file page buffered in the buffer pool.
340 Writes the corresponding log record to the mini-transaction log. */
341 void
mlog_log_string(byte * ptr,ulint len,mtr_t * mtr)342 mlog_log_string(
343 /*============*/
344 byte* ptr, /*!< in: pointer written to */
345 ulint len, /*!< in: string length */
346 mtr_t* mtr) /*!< in: mini-transaction handle */
347 {
348 byte* log_ptr;
349
350 ut_ad(ptr && mtr);
351 ut_ad(len <= UNIV_PAGE_SIZE);
352
353 log_ptr = mlog_open(mtr, 30);
354
355 /* If no logging is requested, we may return now */
356 if (log_ptr == NULL) {
357
358 return;
359 }
360
361 log_ptr = mlog_write_initial_log_record_fast(ptr, MLOG_WRITE_STRING,
362 log_ptr, mtr);
363 mach_write_to_2(log_ptr, page_offset(ptr));
364 log_ptr += 2;
365
366 mach_write_to_2(log_ptr, len);
367 log_ptr += 2;
368
369 mlog_close(mtr, log_ptr);
370
371 mlog_catenate_string(mtr, ptr, len);
372 }
373 #endif /* !UNIV_HOTBACKUP */
374
375 /********************************************************//**
376 Parses a log record written by mlog_write_string.
377 @return parsed record end, NULL if not a complete record */
378 byte*
mlog_parse_string(byte * ptr,byte * end_ptr,byte * page,void * page_zip)379 mlog_parse_string(
380 /*==============*/
381 byte* ptr, /*!< in: buffer */
382 byte* end_ptr,/*!< in: buffer end */
383 byte* page, /*!< in: page where to apply the log record, or NULL */
384 void* page_zip)/*!< in/out: compressed page, or NULL */
385 {
386 ulint offset;
387 ulint len;
388
389 ut_a(!page || !page_zip
390 || (fil_page_get_type(page) != FIL_PAGE_INDEX
391 && fil_page_get_type(page) != FIL_PAGE_RTREE));
392
393 if (end_ptr < ptr + 4) {
394
395 return(NULL);
396 }
397
398 offset = mach_read_from_2(ptr);
399 ptr += 2;
400 len = mach_read_from_2(ptr);
401 ptr += 2;
402
403 if (offset >= UNIV_PAGE_SIZE || len + offset > UNIV_PAGE_SIZE) {
404 recv_sys->found_corrupt_log = TRUE;
405
406 return(NULL);
407 }
408
409 if (end_ptr < ptr + len) {
410
411 return(NULL);
412 }
413
414 if (page) {
415 if (page_zip) {
416 memcpy(((page_zip_des_t*) page_zip)->data
417 + offset, ptr, len);
418 }
419 memcpy(page + offset, ptr, len);
420 }
421
422 return(ptr + len);
423 }
424
425 #ifndef UNIV_HOTBACKUP
426 /********************************************************//**
427 Opens a buffer for mlog, writes the initial log record and,
428 if needed, the field lengths of an index.
429 @return buffer, NULL if log mode MTR_LOG_NONE */
430 byte*
mlog_open_and_write_index(mtr_t * mtr,const byte * rec,const dict_index_t * index,mlog_id_t type,ulint size)431 mlog_open_and_write_index(
432 /*======================*/
433 mtr_t* mtr, /*!< in: mtr */
434 const byte* rec, /*!< in: index record or page */
435 const dict_index_t* index, /*!< in: record descriptor */
436 mlog_id_t type, /*!< in: log item type */
437 ulint size) /*!< in: requested buffer size in bytes
438 (if 0, calls mlog_close() and
439 returns NULL) */
440 {
441 byte* log_ptr;
442 const byte* log_start;
443 const byte* log_end;
444
445 ut_ad(!!page_rec_is_comp(rec) == dict_table_is_comp(index->table));
446
447 if (!page_rec_is_comp(rec)) {
448 log_start = log_ptr = mlog_open(mtr, 11 + size);
449 if (!log_ptr) {
450 return(NULL); /* logging is disabled */
451 }
452 log_ptr = mlog_write_initial_log_record_fast(rec, type,
453 log_ptr, mtr);
454 log_end = log_ptr + 11 + size;
455 } else {
456 ulint i;
457 ulint n = dict_index_get_n_fields(index);
458 ulint total = 11 + size + (n + 2) * 2;
459 ulint alloc = total;
460
461 if (alloc > mtr_buf_t::MAX_DATA_SIZE) {
462 alloc = mtr_buf_t::MAX_DATA_SIZE;
463 }
464
465 /* For spatial index, on non-leaf page, we just keep
466 2 fields, MBR and page no. */
467 if (dict_index_is_spatial(index)
468 && !page_is_leaf(page_align(rec))) {
469 n = DICT_INDEX_SPATIAL_NODEPTR_SIZE;
470 }
471
472 log_start = log_ptr = mlog_open(mtr, alloc);
473
474 if (!log_ptr) {
475 return(NULL); /* logging is disabled */
476 }
477
478 log_end = log_ptr + alloc;
479
480 log_ptr = mlog_write_initial_log_record_fast(
481 rec, type, log_ptr, mtr);
482
483 mach_write_to_2(log_ptr, n);
484 log_ptr += 2;
485
486 if (page_is_leaf(page_align(rec))) {
487 mach_write_to_2(
488 log_ptr, dict_index_get_n_unique_in_tree(index));
489 } else {
490 mach_write_to_2(
491 log_ptr,
492 dict_index_get_n_unique_in_tree_nonleaf(index));
493 }
494
495 log_ptr += 2;
496
497 for (i = 0; i < n; i++) {
498 dict_field_t* field;
499 const dict_col_t* col;
500 ulint len;
501
502 field = dict_index_get_nth_field(index, i);
503 col = dict_field_get_col(field);
504 len = field->fixed_len;
505 ut_ad(len < 0x7fff);
506 if (len == 0
507 && (DATA_BIG_COL(col))) {
508 /* variable-length field
509 with maximum length > 255 */
510 len = 0x7fff;
511 }
512 if (col->prtype & DATA_NOT_NULL) {
513 len |= 0x8000;
514 }
515 if (log_ptr + 2 > log_end) {
516 mlog_close(mtr, log_ptr);
517 ut_a(total > (ulint) (log_ptr - log_start));
518 total -= log_ptr - log_start;
519 alloc = total;
520
521 if (alloc > mtr_buf_t::MAX_DATA_SIZE) {
522 alloc = mtr_buf_t::MAX_DATA_SIZE;
523 }
524
525 log_start = log_ptr = mlog_open(mtr, alloc);
526
527 if (!log_ptr) {
528 return(NULL); /* logging is disabled */
529 }
530 log_end = log_ptr + alloc;
531 }
532 mach_write_to_2(log_ptr, len);
533 log_ptr += 2;
534 }
535 }
536 if (size == 0) {
537 mlog_close(mtr, log_ptr);
538 log_ptr = NULL;
539 } else if (log_ptr + size > log_end) {
540 mlog_close(mtr, log_ptr);
541 log_ptr = mlog_open(mtr, size);
542 }
543 return(log_ptr);
544 }
545 #endif /* !UNIV_HOTBACKUP */
546
547 /********************************************************//**
548 Parses a log record written by mlog_open_and_write_index.
549 @return parsed record end, NULL if not a complete record */
550 byte*
mlog_parse_index(byte * ptr,const byte * end_ptr,ibool comp,dict_index_t ** index)551 mlog_parse_index(
552 /*=============*/
553 byte* ptr, /*!< in: buffer */
554 const byte* end_ptr,/*!< in: buffer end */
555 ibool comp, /*!< in: TRUE=compact row format */
556 dict_index_t** index) /*!< out, own: dummy index */
557 {
558 ulint i, n, n_uniq;
559 dict_table_t* table;
560 dict_index_t* ind;
561
562 ut_ad(comp == FALSE || comp == TRUE);
563
564 if (comp) {
565 if (end_ptr < ptr + 4) {
566 return(NULL);
567 }
568 n = mach_read_from_2(ptr);
569 ptr += 2;
570 n_uniq = mach_read_from_2(ptr);
571 ptr += 2;
572 ut_ad(n_uniq <= n);
573 if (end_ptr < ptr + n * 2) {
574 return(NULL);
575 }
576 } else {
577 n = n_uniq = 1;
578 }
579 table = dict_mem_table_create("LOG_DUMMY", DICT_HDR_SPACE, n, 0,
580 comp ? DICT_TF_COMPACT : 0, 0);
581 ind = dict_mem_index_create("LOG_DUMMY", "LOG_DUMMY",
582 DICT_HDR_SPACE, 0, n);
583 ind->table = table;
584 ind->n_uniq = (unsigned int) n_uniq;
585 if (n_uniq != n) {
586 ut_a(n_uniq + DATA_ROLL_PTR <= n);
587 ind->type = DICT_CLUSTERED;
588 }
589 if (comp) {
590 for (i = 0; i < n; i++) {
591 ulint len = mach_read_from_2(ptr);
592 ptr += 2;
593 /* The high-order bit of len is the NOT NULL flag;
594 the rest is 0 or 0x7fff for variable-length fields,
595 and 1..0x7ffe for fixed-length fields. */
596 dict_mem_table_add_col(
597 table, NULL, NULL,
598 ((len + 1) & 0x7fff) <= 1
599 ? DATA_BINARY : DATA_FIXBINARY,
600 len & 0x8000 ? DATA_NOT_NULL : 0,
601 len & 0x7fff);
602
603 dict_index_add_col(ind, table,
604 dict_table_get_nth_col(table, i),
605 0);
606 }
607 dict_table_add_system_columns(table, table->heap);
608 if (n_uniq != n) {
609 /* Identify DB_TRX_ID and DB_ROLL_PTR in the index. */
610 ut_a(DATA_TRX_ID_LEN
611 == dict_index_get_nth_col(ind, DATA_TRX_ID - 1
612 + n_uniq)->len);
613 ut_a(DATA_ROLL_PTR_LEN
614 == dict_index_get_nth_col(ind, DATA_ROLL_PTR - 1
615 + n_uniq)->len);
616 ind->fields[DATA_TRX_ID - 1 + n_uniq].col
617 = &table->cols[n + DATA_TRX_ID];
618 ind->fields[DATA_ROLL_PTR - 1 + n_uniq].col
619 = &table->cols[n + DATA_ROLL_PTR];
620 }
621 }
622 /* avoid ut_ad(index->cached) in dict_index_get_n_unique_in_tree */
623 ind->cached = TRUE;
624 *index = ind;
625 return(ptr);
626 }
627