1/*****************************************************************************
2
3Copyright (c) 1995, 2015, Oracle and/or its affiliates. All Rights Reserved.
4Copyright (c) 2017, 2018, MariaDB Corporation.
5
6This program is free software; you can redistribute it and/or modify it under
7the terms of the GNU General Public License as published by the Free Software
8Foundation; version 2 of the License.
9
10This program is distributed in the hope that it will be useful, but WITHOUT
11ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License along with
15this program; if not, write to the Free Software Foundation, Inc.,
1651 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
17
18*****************************************************************************/
19
20/**************************************************//**
21@file include/log0log.ic
22Database log
23
24Created 12/9/1995 Heikki Tuuri
25*******************************************************/
26
27#include "mach0data.h"
28#include "srv0mon.h"
29#include "ut0crc32.h"
30
31#ifdef UNIV_LOG_LSN_DEBUG
32#include "mtr0types.h"
33#endif /* UNIV_LOG_LSN_DEBUG */
34extern ulong srv_log_buffer_size;
35
36/************************************************************//**
37Gets a log block flush bit.
38@return TRUE if this block was the first to be written in a log flush */
39UNIV_INLINE
40ibool
41log_block_get_flush_bit(
42/*====================*/
43 const byte* log_block) /*!< in: log block */
44{
45 if (LOG_BLOCK_FLUSH_BIT_MASK
46 & mach_read_from_4(log_block + LOG_BLOCK_HDR_NO)) {
47
48 return(TRUE);
49 }
50
51 return(FALSE);
52}
53
54/************************************************************//**
55Sets the log block flush bit. */
56UNIV_INLINE
57void
58log_block_set_flush_bit(
59/*====================*/
60 byte* log_block, /*!< in/out: log block */
61 ibool val) /*!< in: value to set */
62{
63 ulint field;
64
65 field = mach_read_from_4(log_block + LOG_BLOCK_HDR_NO);
66
67 if (val) {
68 field = field | LOG_BLOCK_FLUSH_BIT_MASK;
69 } else {
70 field = field & ~LOG_BLOCK_FLUSH_BIT_MASK;
71 }
72
73 mach_write_to_4(log_block + LOG_BLOCK_HDR_NO, field);
74}
75
76/************************************************************//**
77Gets a log block number stored in the header.
78@return log block number stored in the block header */
79UNIV_INLINE
80ulint
81log_block_get_hdr_no(
82/*=================*/
83 const byte* log_block) /*!< in: log block */
84{
85 return(~LOG_BLOCK_FLUSH_BIT_MASK
86 & mach_read_from_4(log_block + LOG_BLOCK_HDR_NO));
87}
88
89/************************************************************//**
90Sets the log block number stored in the header; NOTE that this must be set
91before the flush bit! */
92UNIV_INLINE
93void
94log_block_set_hdr_no(
95/*=================*/
96 byte* log_block, /*!< in/out: log block */
97 ulint n) /*!< in: log block number: must be > 0 and
98 < LOG_BLOCK_FLUSH_BIT_MASK */
99{
100 ut_ad(n > 0);
101 ut_ad(n < LOG_BLOCK_FLUSH_BIT_MASK);
102
103 mach_write_to_4(log_block + LOG_BLOCK_HDR_NO, n);
104}
105
106/************************************************************//**
107Gets a log block data length.
108@return log block data length measured as a byte offset from the block start */
109UNIV_INLINE
110ulint
111log_block_get_data_len(
112/*===================*/
113 const byte* log_block) /*!< in: log block */
114{
115 return(mach_read_from_2(log_block + LOG_BLOCK_HDR_DATA_LEN));
116}
117
118/************************************************************//**
119Sets the log block data length. */
120UNIV_INLINE
121void
122log_block_set_data_len(
123/*===================*/
124 byte* log_block, /*!< in/out: log block */
125 ulint len) /*!< in: data length */
126{
127 mach_write_to_2(log_block + LOG_BLOCK_HDR_DATA_LEN, len);
128}
129
130/************************************************************//**
131Gets a log block first mtr log record group offset.
132@return first mtr log record group byte offset from the block start, 0
133if none */
134UNIV_INLINE
135ulint
136log_block_get_first_rec_group(
137/*==========================*/
138 const byte* log_block) /*!< in: log block */
139{
140 return(mach_read_from_2(log_block + LOG_BLOCK_FIRST_REC_GROUP));
141}
142
143/************************************************************//**
144Sets the log block first mtr log record group offset. */
145UNIV_INLINE
146void
147log_block_set_first_rec_group(
148/*==========================*/
149 byte* log_block, /*!< in/out: log block */
150 ulint offset) /*!< in: offset, 0 if none */
151{
152 mach_write_to_2(log_block + LOG_BLOCK_FIRST_REC_GROUP, offset);
153}
154
155/************************************************************//**
156Gets a log block checkpoint number field (4 lowest bytes).
157@return checkpoint no (4 lowest bytes) */
158UNIV_INLINE
159ulint
160log_block_get_checkpoint_no(
161/*========================*/
162 const byte* log_block) /*!< in: log block */
163{
164 return(mach_read_from_4(log_block + LOG_BLOCK_CHECKPOINT_NO));
165}
166
167/************************************************************//**
168Sets a log block checkpoint number field (4 lowest bytes). */
169UNIV_INLINE
170void
171log_block_set_checkpoint_no(
172/*========================*/
173 byte* log_block, /*!< in/out: log block */
174 ib_uint64_t no) /*!< in: checkpoint no */
175{
176 mach_write_to_4(log_block + LOG_BLOCK_CHECKPOINT_NO, (ulint) no);
177}
178
179/************************************************************//**
180Converts a lsn to a log block number.
181@return log block number, it is > 0 and <= 1G */
182UNIV_INLINE
183ulint
184log_block_convert_lsn_to_no(
185/*========================*/
186 lsn_t lsn) /*!< in: lsn of a byte within the block */
187{
188 return(((ulint) (lsn / OS_FILE_LOG_BLOCK_SIZE) & 0x3FFFFFFFUL) + 1);
189}
190
191/************************************************************//**
192Calculates the checksum for a log block.
193@return checksum */
194UNIV_INLINE
195ulint
196log_block_calc_checksum(
197/*====================*/
198 const byte* block) /*!< in: log block */
199{
200 return(log_checksum_algorithm_ptr(block));
201}
202
203/** Calculate the checksum for a log block using the pre-5.7.9 algorithm.
204@param[in] block log block
205@return checksum */
206UNIV_INLINE
207ulint
208log_block_calc_checksum_format_0(
209 const byte* block)
210{
211 ulint sum;
212 ulint sh;
213 ulint i;
214
215 sum = 1;
216 sh = 0;
217
218 for (i = 0; i < OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_TRL_SIZE; i++) {
219 ulint b = (ulint) block[i];
220 sum &= 0x7FFFFFFFUL;
221 sum += b;
222 sum += b << sh;
223 sh++;
224 if (sh > 24) {
225 sh = 0;
226 }
227 }
228
229 return(sum);
230}
231
232/** Calculate the checksum for a log block using the MySQL 5.7 algorithm.
233@param[in] block log block
234@return checksum */
235UNIV_INLINE
236ulint
237log_block_calc_checksum_crc32(
238 const byte* block)
239{
240 return(ut_crc32(block, OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_TRL_SIZE));
241}
242
243/** Calculates the checksum for a log block using the "no-op" algorithm.
244@return checksum */
245UNIV_INLINE
246ulint
247log_block_calc_checksum_none(const byte*)
248{
249 return(LOG_NO_CHECKSUM_MAGIC);
250}
251
252/************************************************************//**
253Gets a log block checksum field value.
254@return checksum */
255UNIV_INLINE
256ulint
257log_block_get_checksum(
258/*===================*/
259 const byte* log_block) /*!< in: log block */
260{
261 return(mach_read_from_4(log_block + OS_FILE_LOG_BLOCK_SIZE
262 - LOG_BLOCK_CHECKSUM));
263}
264
265/************************************************************//**
266Sets a log block checksum field value. */
267UNIV_INLINE
268void
269log_block_set_checksum(
270/*===================*/
271 byte* log_block, /*!< in/out: log block */
272 ulint checksum) /*!< in: checksum */
273{
274 mach_write_to_4(log_block + OS_FILE_LOG_BLOCK_SIZE
275 - LOG_BLOCK_CHECKSUM,
276 checksum);
277}
278
279/************************************************************//**
280Initializes a log block in the log buffer. */
281UNIV_INLINE
282void
283log_block_init(
284/*===========*/
285 byte* log_block, /*!< in: pointer to the log buffer */
286 lsn_t lsn) /*!< in: lsn within the log block */
287{
288 ulint no;
289
290 no = log_block_convert_lsn_to_no(lsn);
291
292 log_block_set_hdr_no(log_block, no);
293
294 log_block_set_data_len(log_block, LOG_BLOCK_HDR_SIZE);
295 log_block_set_first_rec_group(log_block, 0);
296}
297
298/** Append a string to the log.
299@param[in] str string
300@param[in] len string length
301@param[out] start_lsn start LSN of the log record
302@return end lsn of the log record, zero if did not succeed */
303UNIV_INLINE
304lsn_t
305log_reserve_and_write_fast(
306 const void* str,
307 ulint len,
308 lsn_t* start_lsn)
309{
310 ut_ad(log_mutex_own());
311 ut_ad(len > 0);
312
313#ifdef UNIV_LOG_LSN_DEBUG
314 /* Append a MLOG_LSN record after mtr_commit(), except when
315 the last bytes could be a MLOG_CHECKPOINT marker. We have special
316 handling when the log consists of only a single MLOG_CHECKPOINT
317 record since the latest checkpoint, and appending the
318 MLOG_LSN would ruin that.
319
320 Note that a longer redo log record could happen to end in what
321 looks like MLOG_CHECKPOINT, and we could be omitting MLOG_LSN
322 without reason. This is OK, because writing the MLOG_LSN is
323 just a 'best effort', aimed at finding log corruption due to
324 bugs in the redo log writing logic. */
325 const ulint lsn_len
326 = len >= SIZE_OF_MLOG_CHECKPOINT
327 && MLOG_CHECKPOINT == static_cast<const char*>(str)[
328 len - SIZE_OF_MLOG_CHECKPOINT]
329 ? 0
330 : 1
331 + mach_get_compressed_size(log_sys.lsn >> 32)
332 + mach_get_compressed_size(log_sys.lsn & 0xFFFFFFFFUL);
333#endif /* UNIV_LOG_LSN_DEBUG */
334
335 const ulint data_len = len
336#ifdef UNIV_LOG_LSN_DEBUG
337 + lsn_len
338#endif /* UNIV_LOG_LSN_DEBUG */
339 + log_sys.buf_free % OS_FILE_LOG_BLOCK_SIZE;
340
341 if (data_len >= OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_TRL_SIZE) {
342
343 /* The string does not fit within the current log block
344 or the log block would become full */
345
346 return(0);
347 }
348
349 *start_lsn = log_sys.lsn;
350
351#ifdef UNIV_LOG_LSN_DEBUG
352 if (lsn_len) {
353 /* Write the LSN pseudo-record. */
354 byte* b = &log_sys.buf[log_sys.buf_free];
355
356 *b++ = MLOG_LSN | (MLOG_SINGLE_REC_FLAG & *(const byte*) str);
357
358 /* Write the LSN in two parts,
359 as a pseudo page number and space id. */
360 b += mach_write_compressed(b, log_sys.lsn >> 32);
361 b += mach_write_compressed(b, log_sys.lsn & 0xFFFFFFFFUL);
362 ut_a(b - lsn_len == &log_sys.buf[log_sys.buf_free]);
363
364 ::memcpy(b, str, len);
365
366 len += lsn_len;
367 } else
368#endif /* UNIV_LOG_LSN_DEBUG */
369 memcpy(log_sys.buf + log_sys.buf_free, str, len);
370
371 log_block_set_data_len(
372 reinterpret_cast<byte*>(ut_align_down(
373 log_sys.buf + log_sys.buf_free,
374 OS_FILE_LOG_BLOCK_SIZE)),
375 data_len);
376
377 log_sys.buf_free += ulong(len);
378
379 ut_ad(log_sys.buf_free <= srv_log_buffer_size);
380
381 log_sys.lsn += len;
382
383 MONITOR_SET(MONITOR_LSN_CHECKPOINT_AGE,
384 log_sys.lsn - log_sys.last_checkpoint_lsn);
385
386 return(log_sys.lsn);
387}
388
389/************************************************************//**
390Gets the current lsn.
391@return current lsn */
392UNIV_INLINE
393lsn_t
394log_get_lsn(void)
395/*=============*/
396{
397 lsn_t lsn;
398
399 log_mutex_enter();
400
401 lsn = log_sys.lsn;
402
403 log_mutex_exit();
404
405 return(lsn);
406}
407
408/************************************************************//**
409Gets the last lsn that is fully flushed to disk.
410@return last flushed lsn */
411UNIV_INLINE
412ib_uint64_t
413log_get_flush_lsn(void)
414{
415 ib_uint64_t lsn;
416
417 log_mutex_enter();
418
419 lsn = log_sys.flushed_to_disk_lsn;
420
421 log_mutex_exit();
422
423 return(lsn);
424}
425
426/************************************************************//**
427Gets the current lsn with a trylock
428@return current lsn or 0 if false*/
429UNIV_INLINE
430lsn_t
431log_get_lsn_nowait(void)
432/*====================*/
433{
434 lsn_t lsn=0;
435
436 if (!mutex_enter_nowait(&(log_sys.mutex))) {
437
438 lsn = log_sys.lsn;
439
440 mutex_exit(&(log_sys.mutex));
441 }
442
443 return(lsn);
444}
445
446/****************************************************************
447Gets the log group capacity. It is OK to read the value without
448holding log_sys.mutex because it is constant.
449@return log group capacity */
450UNIV_INLINE
451lsn_t
452log_get_capacity(void)
453/*==================*/
454{
455 return(log_sys.log_group_capacity);
456}
457
458/****************************************************************
459Get log_sys::max_modified_age_async. It is OK to read the value without
460holding log_sys::mutex because it is constant.
461@return max_modified_age_async */
462UNIV_INLINE
463lsn_t
464log_get_max_modified_age_async(void)
465/*================================*/
466{
467 return(log_sys.max_modified_age_async);
468}
469
470/***********************************************************************//**
471Checks if there is need for a log buffer flush or a new checkpoint, and does
472this if yes. Any database operation should call this when it has modified
473more than about 4 pages. NOTE that this function may only be called when the
474OS thread owns no synchronization objects except the dictionary mutex. */
475UNIV_INLINE
476void
477log_free_check(void)
478/*================*/
479{
480 /* During row_log_table_apply(), this function will be called while we
481 are holding some latches. This is OK, as long as we are not holding
482 any latches on buffer blocks. */
483
484#ifdef UNIV_DEBUG
485 static const latch_level_t latches[] = {
486 SYNC_DICT, /* dict_sys->mutex during
487 commit_try_rebuild() */
488 SYNC_DICT_OPERATION, /* dict_operation_lock X-latch during
489 commit_try_rebuild() */
490 SYNC_FTS_CACHE, /* fts_cache_t::lock */
491 SYNC_INDEX_TREE /* index->lock */
492 };
493#endif /* UNIV_DEBUG */
494
495 ut_ad(!sync_check_iterate(
496 sync_allowed_latches(latches,
497 latches + UT_ARR_SIZE(latches))));
498
499 if (log_sys.check_flush_or_checkpoint) {
500
501 log_check_margins();
502 }
503}
504