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