summaryrefslogtreecommitdiff
path: root/innobase/mtr
diff options
context:
space:
mode:
Diffstat (limited to 'innobase/mtr')
-rw-r--r--innobase/mtr/Makefile.am24
-rw-r--r--innobase/mtr/makefilewin14
-rw-r--r--innobase/mtr/mtr0log.c327
-rw-r--r--innobase/mtr/mtr0mtr.c522
-rw-r--r--innobase/mtr/ts/makefile8
-rw-r--r--innobase/mtr/ts/tsbuf.c531
-rw-r--r--innobase/mtr/ts/tsmtr.c158
7 files changed, 1584 insertions, 0 deletions
diff --git a/innobase/mtr/Makefile.am b/innobase/mtr/Makefile.am
new file mode 100644
index 00000000000..972dcaca80e
--- /dev/null
+++ b/innobase/mtr/Makefile.am
@@ -0,0 +1,24 @@
+# Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+# & Innobase Oy
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+include ../include/Makefile.i
+
+libs_LIBRARIES = libmtr.a
+
+libmtr_a_SOURCES = mtr0mtr.c mtr0log.c
+
+EXTRA_PROGRAMS =
diff --git a/innobase/mtr/makefilewin b/innobase/mtr/makefilewin
new file mode 100644
index 00000000000..9da0863bd28
--- /dev/null
+++ b/innobase/mtr/makefilewin
@@ -0,0 +1,14 @@
+include ..\include\makefile.i
+
+mtr.lib: mtr0mtr.obj mtr0log.obj
+ lib -out:..\libs\mtr.lib mtr0mtr.obj mtr0log.obj
+
+mtr0mtr.obj: mtr0mtr.c
+ $(CCOM) $(CFL) -c mtr0mtr.c
+
+mtr0log.obj: mtr0log.c
+ $(CCOM) $(CFL) -c mtr0log.c
+
+
+
+
diff --git a/innobase/mtr/mtr0log.c b/innobase/mtr/mtr0log.c
new file mode 100644
index 00000000000..11c0c476fcb
--- /dev/null
+++ b/innobase/mtr/mtr0log.c
@@ -0,0 +1,327 @@
+/******************************************************
+Mini-transaction log routines
+
+(c) 1995 Innobase Oy
+
+Created 12/7/1995 Heikki Tuuri
+*******************************************************/
+
+#include "mtr0log.h"
+
+#ifdef UNIV_NONINL
+#include "mtr0log.ic"
+#endif
+
+#include "buf0buf.h"
+#include "dict0boot.h"
+
+/************************************************************
+Catenates n bytes to the mtr log. */
+
+void
+mlog_catenate_string(
+/*=================*/
+ mtr_t* mtr, /* in: mtr */
+ byte* str, /* in: string to write */
+ ulint len) /* in: string length */
+{
+ dyn_array_t* mlog;
+
+ if (mtr_get_log_mode(mtr) == MTR_LOG_NONE) {
+
+ return;
+ }
+
+ mlog = &(mtr->log);
+
+ dyn_push_string(mlog, str, len);
+}
+
+/************************************************************
+Writes the initial part of a log record consisting of one-byte item
+type and four-byte space and page numbers. Also pushes info
+to the mtr memo that a buffer page has been modified. */
+
+void
+mlog_write_initial_log_record(
+/*==========================*/
+ byte* ptr, /* in: pointer to (inside) a buffer frame holding the
+ file page where modification is made */
+ byte type, /* in: log item type: MLOG_1BYTE, ... */
+ mtr_t* mtr) /* in: mini-transaction handle */
+{
+ byte* log_ptr;
+
+ ut_ad(type <= MLOG_BIGGEST_TYPE);
+
+ log_ptr = mlog_open(mtr, 20);
+
+ /* If no logging is requested, we may return now */
+ if (log_ptr == NULL) {
+
+ return;
+ }
+
+ log_ptr = mlog_write_initial_log_record_fast(ptr, type, log_ptr, mtr);
+
+ mlog_close(mtr, log_ptr);
+}
+
+/************************************************************
+Parses an initial log record written by mlog_write_initial_log_record. */
+
+byte*
+mlog_parse_initial_log_record(
+/*==========================*/
+ /* out: parsed record end, NULL if not a complete
+ record */
+ byte* ptr, /* in: buffer */
+ byte* end_ptr,/* in: buffer end */
+ byte* type, /* out: log record type: MLOG_1BYTE, ... */
+ ulint* space, /* out: space id */
+ ulint* page_no)/* out: page number */
+{
+ if (end_ptr < ptr + 1) {
+
+ return(NULL);
+ }
+
+ *type = (byte)((ulint)*ptr & ~MLOG_SINGLE_REC_FLAG);
+
+ ptr++;
+
+ if (end_ptr < ptr + 2) {
+
+ return(NULL);
+ }
+
+ ptr = mach_parse_compressed(ptr, end_ptr, space);
+
+ if (ptr == NULL) {
+
+ return(NULL);
+ }
+
+ ptr = mach_parse_compressed(ptr, end_ptr, page_no);
+
+ return(ptr);
+}
+
+/************************************************************
+Parses a log record written by mlog_write_ulint or mlog_write_dulint. */
+
+byte*
+mlog_parse_nbytes(
+/*==============*/
+ /* out: parsed record end, NULL if not a complete
+ record */
+ ulint type, /* in: log record type: MLOG_1BYTE, ... */
+ byte* ptr, /* in: buffer */
+ byte* end_ptr,/* in: buffer end */
+ byte* page) /* in: page where to apply the log record, or NULL */
+{
+ ulint offset;
+ ulint val;
+ dulint dval;
+
+ ut_a(type <= MLOG_8BYTES);
+
+ if (end_ptr < ptr + 2) {
+
+ return(NULL);
+ }
+
+ offset = mach_read_from_2(ptr);
+ ptr += 2;
+
+ if (type == MLOG_8BYTES) {
+ ptr = mach_dulint_parse_compressed(ptr, end_ptr, &dval);
+
+ if (ptr == NULL) {
+
+ return(NULL);
+ }
+
+ if (page) {
+ mach_write_to_8(page + offset, dval);
+ }
+
+ return(ptr);
+ }
+
+ ptr = mach_parse_compressed(ptr, end_ptr, &val);
+
+ if (ptr == NULL) {
+
+ return(NULL);
+ }
+
+ if (page) {
+ if (type == MLOG_1BYTE) {
+ mach_write_to_1(page + offset, val);
+ } else if (type == MLOG_2BYTES) {
+ mach_write_to_2(page + offset, val);
+ } else {
+ ut_ad(type == MLOG_4BYTES);
+ mach_write_to_4(page + offset, val);
+ }
+ }
+
+ return(ptr);
+}
+
+/************************************************************
+Writes 1 - 4 bytes to a file page buffered in the buffer pool.
+Writes the corresponding log record to the mini-transaction log. */
+
+void
+mlog_write_ulint(
+/*=============*/
+ byte* ptr, /* in: pointer where to write */
+ ulint val, /* in: value to write */
+ byte type, /* in: MLOG_1BYTE, MLOG_2BYTES, MLOG_4BYTES */
+ mtr_t* mtr) /* in: mini-transaction handle */
+{
+ byte* log_ptr;
+
+ if (type == MLOG_1BYTE) {
+ mach_write_to_1(ptr, val);
+ } else if (type == MLOG_2BYTES) {
+ mach_write_to_2(ptr, val);
+ } else {
+ ut_ad(type == MLOG_4BYTES);
+ mach_write_to_4(ptr, val);
+ }
+
+ log_ptr = mlog_open(mtr, 30);
+
+ /* If no logging is requested, we may return now */
+ if (log_ptr == NULL) {
+
+ return;
+ }
+
+ log_ptr = mlog_write_initial_log_record_fast(ptr, type, log_ptr, mtr);
+
+ mach_write_to_2(log_ptr, ptr - buf_frame_align(ptr));
+ log_ptr += 2;
+
+ log_ptr += mach_write_compressed(log_ptr, val);
+
+ mlog_close(mtr, log_ptr);
+}
+
+/************************************************************
+Writes 8 bytes to a file page buffered in the buffer pool.
+Writes the corresponding log record to the mini-transaction log. */
+
+void
+mlog_write_dulint(
+/*==============*/
+ byte* ptr, /* in: pointer where to write */
+ dulint val, /* in: value to write */
+ byte type, /* in: MLOG_8BYTES */
+ mtr_t* mtr) /* in: mini-transaction handle */
+{
+ byte* log_ptr;
+
+ ut_ad(ptr && mtr);
+ ut_ad(type == MLOG_8BYTES);
+
+ mach_write_to_8(ptr, val);
+
+ log_ptr = mlog_open(mtr, 30);
+
+ /* If no logging is requested, we may return now */
+ if (log_ptr == NULL) {
+
+ return;
+ }
+
+ log_ptr = mlog_write_initial_log_record_fast(ptr, type, log_ptr, mtr);
+
+ mach_write_to_2(log_ptr, ptr - buf_frame_align(ptr));
+ log_ptr += 2;
+
+ log_ptr += mach_dulint_write_compressed(log_ptr, val);
+
+ mlog_close(mtr, log_ptr);
+}
+
+/************************************************************
+Writes a string to a file page buffered in the buffer pool. Writes the
+corresponding log record to the mini-transaction log. */
+
+void
+mlog_write_string(
+/*==============*/
+ byte* ptr, /* in: pointer where to write */
+ byte* str, /* in: string to write */
+ ulint len, /* in: string length */
+ mtr_t* mtr) /* in: mini-transaction handle */
+{
+ byte* log_ptr;
+
+ ut_ad(ptr && mtr);
+ ut_ad(len < UNIV_PAGE_SIZE);
+
+ ut_memcpy(ptr, str, len);
+
+ log_ptr = mlog_open(mtr, 30);
+
+ /* If no logging is requested, we may return now */
+ if (log_ptr == NULL) {
+
+ return;
+ }
+
+ log_ptr = mlog_write_initial_log_record_fast(ptr, MLOG_WRITE_STRING,
+ log_ptr, mtr);
+ mach_write_to_2(log_ptr, ptr - buf_frame_align(ptr));
+ log_ptr += 2;
+
+ mach_write_to_2(log_ptr, len);
+ log_ptr += 2;
+
+ mlog_close(mtr, log_ptr);
+
+ mlog_catenate_string(mtr, str, len);
+}
+
+/************************************************************
+Parses a log record written by mlog_write_string. */
+
+byte*
+mlog_parse_string(
+/*==============*/
+ /* out: parsed record end, NULL if not a complete
+ record */
+ byte* ptr, /* in: buffer */
+ byte* end_ptr,/* in: buffer end */
+ byte* page) /* in: page where to apply the log record, or NULL */
+{
+ ulint offset;
+ ulint len;
+
+ if (end_ptr < ptr + 4) {
+
+ return(NULL);
+ }
+
+ offset = mach_read_from_2(ptr);
+ ptr += 2;
+
+ len = mach_read_from_2(ptr);
+ ptr += 2;
+
+ if (end_ptr < ptr + len) {
+
+ return(NULL);
+ }
+
+ if (page) {
+ ut_memcpy(page + offset, ptr, len);
+ }
+
+ return(ptr + len);
+}
diff --git a/innobase/mtr/mtr0mtr.c b/innobase/mtr/mtr0mtr.c
new file mode 100644
index 00000000000..f38aa6793b9
--- /dev/null
+++ b/innobase/mtr/mtr0mtr.c
@@ -0,0 +1,522 @@
+/******************************************************
+Mini-transaction buffer
+
+(c) 1995 Innobase Oy
+
+Created 11/26/1995 Heikki Tuuri
+*******************************************************/
+
+#include "mtr0mtr.h"
+
+#ifdef UNIV_NONINL
+#include "mtr0mtr.ic"
+#endif
+
+#include "buf0buf.h"
+#include "page0types.h"
+#include "mtr0log.h"
+#include "log0log.h"
+
+/*******************************************************************
+Starts a mini-transaction and creates a mini-transaction handle
+and buffer in the memory buffer given by the caller. */
+
+mtr_t*
+mtr_start_noninline(
+/*================*/
+ /* out: mtr buffer which also acts as
+ the mtr handle */
+ mtr_t* mtr) /* in: memory buffer for the mtr buffer */
+{
+ return(mtr_start(mtr));
+}
+
+/*********************************************************************
+Releases the item in the slot given. */
+UNIV_INLINE
+void
+mtr_memo_slot_release(
+/*==================*/
+ mtr_t* mtr, /* in: mtr */
+ mtr_memo_slot_t* slot) /* in: memo slot */
+{
+ void* object;
+ ulint type;
+
+ ut_ad(mtr && slot);
+
+ object = slot->object;
+ type = slot->type;
+
+ if (object != NULL) {
+ if (type <= MTR_MEMO_BUF_FIX) {
+ buf_page_release((buf_block_t*)object, type, mtr);
+ } else if (type == MTR_MEMO_S_LOCK) {
+ rw_lock_s_unlock((rw_lock_t*)object);
+#ifndef UNIV_DEBUG
+ } else {
+ rw_lock_x_unlock((rw_lock_t*)object);
+ }
+#endif
+#ifdef UNIV_DEBUG
+ } else if (type == MTR_MEMO_X_LOCK) {
+ rw_lock_x_unlock((rw_lock_t*)object);
+ } else {
+ ut_ad(type == MTR_MEMO_MODIFY);
+ ut_ad(mtr_memo_contains(mtr, object,
+ MTR_MEMO_PAGE_X_FIX));
+ }
+#endif
+ }
+
+ slot->object = NULL;
+}
+
+/**************************************************************
+Releases the mlocks and other objects stored in an mtr memo. They are released
+in the order opposite to which they were pushed to the memo. NOTE! It is
+essential that the x-rw-lock on a modified buffer page is not released before
+buf_page_note_modification is called for that page! Otherwise, some thread
+might race to modify it, and the flush list sort order on lsn would be
+destroyed. */
+UNIV_INLINE
+void
+mtr_memo_pop_all(
+/*=============*/
+ mtr_t* mtr) /* in: mtr */
+{
+ mtr_memo_slot_t* slot;
+ dyn_array_t* memo;
+ ulint offset;
+
+ ut_ad(mtr);
+ ut_ad(mtr->magic_n == MTR_MAGIC_N);
+ ut_ad(mtr->state == MTR_COMMITTING); /* Currently only used in
+ commit */
+ memo = &(mtr->memo);
+
+ offset = dyn_array_get_data_size(memo);
+
+ while (offset > 0) {
+ offset -= sizeof(mtr_memo_slot_t);
+ slot = dyn_array_get_element(memo, offset);
+
+ mtr_memo_slot_release(mtr, slot);
+ }
+}
+
+/****************************************************************
+Writes to the log the contents of a full page. This is called when the
+database is in the online backup state. */
+static
+void
+mtr_log_write_full_page(
+/*====================*/
+ page_t* page, /* in: page to write */
+ ulint i, /* in: i'th page for mtr */
+ ulint n_pages,/* in: total number of pages for mtr */
+ mtr_t* mtr) /* in: mtr */
+{
+ byte* buf;
+ byte* ptr;
+ ulint len;
+
+ buf = mem_alloc(UNIV_PAGE_SIZE + 50);
+
+ ptr = mlog_write_initial_log_record_fast(page, MLOG_FULL_PAGE, buf,
+ mtr);
+ ut_memcpy(ptr, page, UNIV_PAGE_SIZE);
+
+ len = (ptr - buf) + UNIV_PAGE_SIZE;
+
+ if (i == n_pages - 1) {
+ if (n_pages > 1) {
+ *(buf + len) = MLOG_MULTI_REC_END;
+ len++;
+ } else {
+ *buf = (byte)((ulint)*buf | MLOG_SINGLE_REC_FLAG);
+ }
+ }
+
+ ut_ad(len < UNIV_PAGE_SIZE + 50);
+
+ log_write_low(buf, len);
+
+ mem_free(buf);
+}
+
+/****************************************************************
+Parses a log record which contains the full contents of a page. */
+
+byte*
+mtr_log_parse_full_page(
+/*====================*/
+ /* out: end of log record or NULL */
+ byte* ptr, /* in: buffer */
+ byte* end_ptr,/* in: buffer end */
+ page_t* page) /* in: page or NULL */
+{
+ if (end_ptr < ptr + UNIV_PAGE_SIZE) {
+
+ return(NULL);
+ }
+
+ if (page) {
+ ut_memcpy(page, ptr, UNIV_PAGE_SIZE);
+ }
+
+ return(ptr + UNIV_PAGE_SIZE);
+}
+
+/****************************************************************
+Writes to the database log the full contents of the pages that this mtr has
+modified. */
+
+void
+mtr_log_write_backup_full_pages(
+/*============================*/
+ mtr_t* mtr, /* in: mini-transaction */
+ ulint n_pages)/* in: number of pages modified by mtr */
+{
+ mtr_memo_slot_t* slot;
+ dyn_array_t* memo;
+ buf_block_t* block;
+ ulint offset;
+ ulint type;
+ ulint i;
+
+ ut_ad(mtr);
+ ut_ad(mtr->magic_n == MTR_MAGIC_N);
+ ut_ad(mtr->state == MTR_COMMITTING);
+
+ /* Open the database log for log_write_low */
+ mtr->start_lsn = log_reserve_and_open(n_pages * (UNIV_PAGE_SIZE + 50));
+
+ memo = &(mtr->memo);
+
+ offset = dyn_array_get_data_size(memo);
+
+ i = 0;
+
+ while (offset > 0) {
+ offset -= sizeof(mtr_memo_slot_t);
+ slot = dyn_array_get_element(memo, offset);
+
+ block = slot->object;
+ type = slot->type;
+
+ if ((block != NULL) && (type == MTR_MEMO_PAGE_X_FIX)) {
+
+ mtr_log_write_full_page(block->frame, i, n_pages, mtr);
+
+ i++;
+ }
+ }
+
+ ut_ad(i == n_pages);
+}
+
+/****************************************************************
+Checks if mtr is the first to modify any page after online_backup_lsn. */
+static
+ibool
+mtr_first_to_modify_page_after_backup(
+/*==================================*/
+ /* out: TRUE if first for a page */
+ mtr_t* mtr, /* in: mini-transaction */
+ ulint* n_pages) /* out: number of modified pages (all modified
+ pages, backup_lsn does not matter here) */
+{
+ mtr_memo_slot_t* slot;
+ dyn_array_t* memo;
+ ulint offset;
+ buf_block_t* block;
+ ulint type;
+ dulint backup_lsn;
+ ibool ret = FALSE;
+
+ ut_ad(mtr);
+ ut_ad(mtr->magic_n == MTR_MAGIC_N);
+ ut_ad(mtr->state == MTR_COMMITTING);
+
+ backup_lsn = log_get_online_backup_lsn_low();
+
+ memo = &(mtr->memo);
+
+ offset = dyn_array_get_data_size(memo);
+
+ *n_pages = 0;
+
+ while (offset > 0) {
+ offset -= sizeof(mtr_memo_slot_t);
+ slot = dyn_array_get_element(memo, offset);
+
+ block = slot->object;
+ type = slot->type;
+
+ if ((block != NULL) && (type == MTR_MEMO_PAGE_X_FIX)) {
+
+ *n_pages = *n_pages + 1;
+
+ if (ut_dulint_cmp(buf_frame_get_newest_modification(
+ block->frame),
+ backup_lsn) <= 0) {
+
+ printf("Page %lu newest %lu backup %lu\n",
+ block->offset,
+ ut_dulint_get_low(
+ buf_frame_get_newest_modification(
+ block->frame)),
+ ut_dulint_get_low(backup_lsn));
+
+ ret = TRUE;
+ }
+ }
+ }
+
+ return(ret);
+}
+
+/****************************************************************
+Writes the contents of a mini-transaction log, if any, to the database log. */
+static
+void
+mtr_log_reserve_and_write(
+/*======================*/
+ mtr_t* mtr) /* in: mtr */
+{
+ dyn_array_t* mlog;
+ dyn_block_t* block;
+ ulint data_size;
+ ibool success;
+ byte* first_data;
+ ulint n_modified_pages;
+
+ ut_ad(mtr);
+
+ mlog = &(mtr->log);
+
+ first_data = dyn_block_get_data(mlog);
+
+ if (mtr->n_log_recs > 1) {
+ mlog_catenate_ulint(mtr, MLOG_MULTI_REC_END, MLOG_1BYTE);
+ } else {
+ *first_data = (byte)((ulint)*first_data | MLOG_SINGLE_REC_FLAG);
+ }
+
+ if (mlog->heap == NULL) {
+ mtr->end_lsn = log_reserve_and_write_fast(first_data,
+ dyn_block_get_used(mlog),
+ &(mtr->start_lsn), &success);
+ if (success) {
+
+ return;
+ }
+ }
+
+ data_size = dyn_array_get_data_size(mlog);
+
+ /* Open the database log for log_write_low */
+ mtr->start_lsn = log_reserve_and_open(data_size);
+
+ if (mtr->log_mode == MTR_LOG_ALL) {
+
+ if (log_get_online_backup_state_low()
+ && mtr_first_to_modify_page_after_backup(mtr,
+ &n_modified_pages)) {
+
+ /* The database is in the online backup state: write
+ to the log the full contents of all the pages if this
+ mtr is the first to modify any page in the buffer pool
+ after online_backup_lsn */
+
+ log_close();
+ log_release();
+
+ mtr_log_write_backup_full_pages(mtr, n_modified_pages);
+ } else {
+ block = mlog;
+
+ while (block != NULL) {
+ log_write_low(dyn_block_get_data(block),
+ dyn_block_get_used(block));
+ block = dyn_array_get_next_block(mlog, block);
+ }
+ }
+ } else {
+ ut_ad(mtr->log_mode == MTR_LOG_NONE);
+ /* Do nothing */
+ }
+
+ mtr->end_lsn = log_close();
+}
+
+/*******************************************************************
+Commits a mini-transaction. */
+
+void
+mtr_commit(
+/*=======*/
+ mtr_t* mtr) /* in: mini-transaction */
+{
+ ut_ad(mtr);
+ ut_ad(mtr->magic_n == MTR_MAGIC_N);
+ ut_ad(mtr->state == MTR_ACTIVE);
+#ifdef UNIV_DEBUG
+ mtr->state = MTR_COMMITTING;
+#endif
+ if (mtr->modifications) {
+ mtr_log_reserve_and_write(mtr);
+ }
+
+ /* We first update the modification info to buffer pages, and only
+ after that release the log mutex: this guarantees that when the log
+ mutex is free, all buffer pages contain an up-to-date info of their
+ modifications. This fact is used in making a checkpoint when we look
+ at the oldest modification of any page in the buffer pool. It is also
+ required when we insert modified buffer pages in to the flush list
+ which must be sorted on oldest_modification. */
+
+ mtr_memo_pop_all(mtr);
+
+ if (mtr->modifications) {
+ log_release();
+ }
+
+#ifdef UNIV_DEBUG
+ mtr->state = MTR_COMMITTED;
+#endif
+ dyn_array_free(&(mtr->memo));
+ dyn_array_free(&(mtr->log));
+}
+
+/**************************************************************
+Releases the latches stored in an mtr memo down to a savepoint.
+NOTE! The mtr must not have made changes to buffer pages after the
+savepoint, as these can be handled only by mtr_commit. */
+
+void
+mtr_rollback_to_savepoint(
+/*======================*/
+ mtr_t* mtr, /* in: mtr */
+ ulint savepoint) /* in: savepoint */
+{
+ mtr_memo_slot_t* slot;
+ dyn_array_t* memo;
+ ulint offset;
+
+ ut_ad(mtr);
+ ut_ad(mtr->magic_n == MTR_MAGIC_N);
+ ut_ad(mtr->state == MTR_ACTIVE);
+
+ memo = &(mtr->memo);
+
+ offset = dyn_array_get_data_size(memo);
+ ut_ad(offset >= savepoint);
+
+ while (offset > savepoint) {
+ offset -= sizeof(mtr_memo_slot_t);
+
+ slot = dyn_array_get_element(memo, offset);
+
+ ut_ad(slot->type != MTR_MEMO_MODIFY);
+ mtr_memo_slot_release(mtr, slot);
+ }
+}
+
+/*******************************************************
+Releases an object in the memo stack. */
+
+void
+mtr_memo_release(
+/*=============*/
+ mtr_t* mtr, /* in: mtr */
+ void* object, /* in: object */
+ ulint type) /* in: object type: MTR_MEMO_S_LOCK, ... */
+{
+ mtr_memo_slot_t* slot;
+ dyn_array_t* memo;
+ ulint offset;
+
+ ut_ad(mtr);
+ ut_ad(mtr->magic_n == MTR_MAGIC_N);
+ ut_ad(mtr->state == MTR_ACTIVE);
+
+ memo = &(mtr->memo);
+
+ offset = dyn_array_get_data_size(memo);
+
+ while (offset > 0) {
+ offset -= sizeof(mtr_memo_slot_t);
+
+ slot = dyn_array_get_element(memo, offset);
+
+ if ((object == slot->object) && (type == slot->type)) {
+
+ mtr_memo_slot_release(mtr, slot);
+
+ break;
+ }
+ }
+}
+
+/************************************************************
+Reads 1 - 4 bytes from a file page buffered in the buffer pool. */
+
+ulint
+mtr_read_ulint(
+/*===========*/
+ /* out: value read */
+ byte* ptr, /* in: pointer from where to read */
+ ulint type, /* in: MLOG_1BYTE, MLOG_2BYTES, MLOG_4BYTES */
+ mtr_t* mtr) /* in: mini-transaction handle */
+{
+ ut_ad(mtr->state == MTR_ACTIVE);
+ ut_ad(mtr_memo_contains(mtr, buf_block_align(ptr),
+ MTR_MEMO_PAGE_S_FIX) ||
+ mtr_memo_contains(mtr, buf_block_align(ptr),
+ MTR_MEMO_PAGE_X_FIX));
+ if (type == MLOG_1BYTE) {
+ return(mach_read_from_1(ptr));
+ } else if (type == MLOG_2BYTES) {
+ return(mach_read_from_2(ptr));
+ } else {
+ ut_ad(type == MLOG_4BYTES);
+ return(mach_read_from_4(ptr));
+ }
+}
+
+/************************************************************
+Reads 8 bytes from a file page buffered in the buffer pool. */
+
+dulint
+mtr_read_dulint(
+/*===========*/
+ /* out: value read */
+ byte* ptr, /* in: pointer from where to read */
+ ulint type, /* in: MLOG_8BYTES */
+ mtr_t* mtr) /* in: mini-transaction handle */
+{
+ ut_ad(mtr->state == MTR_ACTIVE);
+ ut_ad(ptr && mtr);
+ ut_ad(type == MLOG_8BYTES);
+ ut_ad(mtr_memo_contains(mtr, buf_block_align(ptr),
+ MTR_MEMO_PAGE_S_FIX) ||
+ mtr_memo_contains(mtr, buf_block_align(ptr),
+ MTR_MEMO_PAGE_X_FIX));
+ return(mach_read_from_8(ptr));
+}
+
+/*************************************************************
+Prints info of an mtr handle. */
+
+void
+mtr_print(
+/*======*/
+ mtr_t* mtr) /* in: mtr */
+{
+ printf(
+ "Mini-transaction handle: memo size %lu bytes log size %lu bytes\n",
+ dyn_array_get_data_size(&(mtr->memo)),
+ dyn_array_get_data_size(&(mtr->log)));
+}
diff --git a/innobase/mtr/ts/makefile b/innobase/mtr/ts/makefile
new file mode 100644
index 00000000000..3a155fb1401
--- /dev/null
+++ b/innobase/mtr/ts/makefile
@@ -0,0 +1,8 @@
+
+
+
+include ..\..\makefile.i
+
+tsmtr: ..\mtr.lib tsmtr.c
+ $(CCOM) $(CFL) -I.. -I..\.. ..\mtr.lib ..\..\dyn.lib ..\..\log.lib ..\..\mach.lib ..\..\buf.lib ..\..\fil.lib ..\..\ha.lib ..\..\ut.lib ..\..\sync.lib ..\..\mem.lib ..\..\os.lib tsmtr.c $(LFL)
+
diff --git a/innobase/mtr/ts/tsbuf.c b/innobase/mtr/ts/tsbuf.c
new file mode 100644
index 00000000000..a2a18d7afaf
--- /dev/null
+++ b/innobase/mtr/ts/tsbuf.c
@@ -0,0 +1,531 @@
+/************************************************************************
+The test module for the file system and buffer manager
+
+(c) 1995 Innobase Oy
+
+Created 11/16/1995 Heikki Tuuri
+*************************************************************************/
+
+#include "os0thread.h"
+#include "os0file.h"
+#include "ut0ut.h"
+#include "sync0sync.h"
+#include "mem0mem.h"
+#include "fil0fil.h"
+#include "..\buf0buf.h"
+#include "..\buf0flu.h"
+#include "..\buf0lru.h"
+
+os_file_t files[1000];
+
+mutex_t ios_mutex;
+ulint ios;
+ulint n[5];
+
+
+/************************************************************************
+Io-handler thread function. */
+
+ulint
+handler_thread(
+/*===========*/
+ void* arg)
+{
+ ulint segment;
+ void* mess;
+ ulint i;
+ bool ret;
+
+ segment = *((ulint*)arg);
+
+ printf("Thread %lu starts\n", segment);
+
+ for (i = 0;; i++) {
+ ret = fil_aio_wait(segment, &mess);
+ ut_a(ret);
+
+ buf_page_io_complete((buf_block_t*)mess);
+
+ mutex_enter(&ios_mutex);
+ ios++;
+ mutex_exit(&ios_mutex);
+
+ }
+
+ return(0);
+}
+
+/************************************************************************
+Creates the test database files. */
+
+void
+create_db(void)
+/*===========*/
+{
+ ulint i;
+ buf_block_t* block;
+ byte* frame;
+ ulint j;
+ ulint tm, oldtm;
+
+ oldtm = ut_clock();
+
+ for (i = 0; i < 1; i++) {
+ for (j = 0; j < 4096; j++) {
+ block = buf_page_create(i, j);
+
+ frame = buf_block_get_frame(block);
+
+ rw_lock_x_lock(buf_page_get_lock(block));
+
+ if (j > 0) {
+ fil_page_set_prev(frame, j - 1);
+ } else {
+ fil_page_set_prev(frame, 0);
+ }
+
+ if (j < 4095) {
+ fil_page_set_next(frame, j + 1);
+ } else {
+ fil_page_set_next(frame, 0);
+ }
+
+ *((ulint*)(frame + 16)) = j;
+
+ buf_page_note_modification(block);
+
+ rw_lock_x_unlock(buf_page_get_lock(block));
+
+ buf_page_release(block);
+ }
+ }
+
+ tm = ut_clock();
+ printf("Wall clock time for test %lu milliseconds\n", tm - oldtm);
+
+/* buf_LRU_print(); */
+
+ /* Flush the pool of dirty pages by reading low-offset pages */
+ for (i = 0; i < 1000; i++) {
+
+ block = buf_page_get(0, i, NULL);
+
+ frame = buf_block_get_frame(block);
+
+ rw_lock_s_lock(buf_page_get_lock(block));
+
+ ut_a(*((ulint*)(frame + 16)) == i);
+
+ rw_lock_s_unlock(buf_page_get_lock(block));
+
+ buf_page_release(block);
+ }
+
+/* buf_LRU_print(); */
+
+ os_thread_sleep(1000000);
+
+ ut_a(buf_all_freed());
+}
+
+/*************************************************************************
+Creates the files for the file system test and inserts them to
+the file system. */
+
+void
+create_files(void)
+/*==============*/
+{
+ bool ret;
+ ulint i, k;
+ char name[10];
+ os_thread_t thr[5];
+ os_thread_id_t id[5];
+
+ name[0] = 't';
+ name[1] = 's';
+ name[2] = 'f';
+ name[3] = 'i';
+ name[4] = 'l';
+ name[5] = 'e';
+ name[8] = '\0';
+
+ for (k = 0; k < 1; k++) {
+ for (i = 0; i < 1; i++) {
+
+ name[6] = (char)(k + (ulint)'a');
+ name[7] = (char)(i + (ulint)'a');
+
+ files[i] = os_file_create("j:\\tsfile4", OS_FILE_CREATE,
+ OS_FILE_TABLESPACE, &ret);
+
+ if (ret == FALSE) {
+ ut_a(os_file_get_last_error() ==
+ OS_FILE_ALREADY_EXISTS);
+
+ files[i] = os_file_create(
+ "j:\\tsfile4", OS_FILE_OPEN,
+ OS_FILE_TABLESPACE, &ret);
+
+ ut_a(ret);
+ }
+
+ ret = os_file_set_size(files[i], 4096 * 8192, 0);
+ ut_a(ret);
+
+ ret = os_file_close(files[i]);
+ ut_a(ret);
+
+ if (i == 0) {
+ fil_space_create("noname", k, OS_FILE_TABLESPACE);
+ }
+
+ ut_a(fil_validate());
+
+ fil_node_create("j:\\tsfile4", 4096, k);
+ }
+ }
+
+ ios = 0;
+
+ mutex_create(&ios_mutex);
+
+ for (i = 0; i < 5; i++) {
+ n[i] = i;
+
+ thr[i] = os_thread_create(handler_thread, n + i, id + i);
+ }
+}
+
+/************************************************************************
+Reads the test database files. */
+
+void
+test1(void)
+/*=======*/
+{
+ ulint i, j, k;
+ buf_block_t* block;
+ byte* frame;
+ ulint tm, oldtm;
+
+ buf_flush_batch(BUF_FLUSH_LIST, 1000);
+
+ os_thread_sleep(1000000);
+
+ buf_all_freed();
+
+ oldtm = ut_clock();
+
+ for (k = 0; k < 1; k++) {
+ for (i = 0; i < 1; i++) {
+ for (j = 0; j < 409; j++) {
+ block = buf_page_get(i, j, NULL);
+
+ frame = buf_block_get_frame(block);
+
+ rw_lock_s_lock(buf_page_get_lock(block));
+
+ ut_a(*((ulint*)(frame + 16)) == j);
+
+ rw_lock_s_unlock(buf_page_get_lock(block));
+
+ buf_page_release(block);
+ }
+ }
+ }
+
+ tm = ut_clock();
+ printf("Wall clock time for test %lu milliseconds\n", tm - oldtm);
+
+}
+
+/************************************************************************
+Reads the test database files. */
+
+void
+test2(void)
+/*=======*/
+{
+ ulint i, j, k, rnd;
+ buf_block_t* block;
+ byte* frame;
+ ulint tm, oldtm;
+
+ oldtm = ut_clock();
+
+ rnd = 123;
+
+ for (k = 0; k < 100; k++) {
+ rnd += 23651;
+ rnd = rnd % 4096;
+
+ i = rnd / 4096;
+ j = rnd % 2048;
+
+ block = buf_page_get(i, j, NULL);
+
+ frame = buf_block_get_frame(block);
+
+ rw_lock_s_lock(buf_page_get_lock(block));
+
+ ut_a(*((ulint*)(frame + 16)) == j);
+
+ rw_lock_s_unlock(buf_page_get_lock(block));
+
+ buf_page_release(block);
+ }
+
+ tm = ut_clock();
+ printf("Wall clock time for random read %lu milliseconds\n",
+ tm - oldtm);
+}
+
+/************************************************************************
+Reads the test database files. */
+
+void
+test4(void)
+/*=======*/
+{
+ ulint i, j, k, rnd;
+ buf_block_t* block;
+ byte* frame;
+ ulint tm, oldtm;
+
+ /* Flush the pool of high-offset pages */
+ for (i = 0; i < 1000; i++) {
+
+ block = buf_page_get(0, i, NULL);
+
+ frame = buf_block_get_frame(block);
+
+ rw_lock_s_lock(buf_page_get_lock(block));
+
+ ut_a(*((ulint*)(frame + 16)) == i);
+
+ rw_lock_s_unlock(buf_page_get_lock(block));
+
+ buf_page_release(block);
+ }
+
+ printf("Test starts\n");
+
+ oldtm = ut_clock();
+
+ rnd = 123;
+
+ for (k = 0; k < 400; k++) {
+
+ rnd += 4357;
+
+ i = 0;
+ j = 1001 + rnd % 3000;
+
+ block = buf_page_get(i, j, NULL);
+
+ frame = buf_block_get_frame(block);
+
+ rw_lock_s_lock(buf_page_get_lock(block));
+
+ ut_a(*((ulint*)(frame + 16)) == j);
+
+ rw_lock_s_unlock(buf_page_get_lock(block));
+
+ buf_page_release(block);
+ }
+
+ tm = ut_clock();
+ printf(
+ "Wall clock time for %lu random no read-ahead %lu milliseconds\n",
+ k, tm - oldtm);
+
+ /* Flush the pool of high-offset pages */
+ for (i = 0; i < 1000; i++) {
+
+ block = buf_page_get(0, i, NULL);
+
+ frame = buf_block_get_frame(block);
+
+ rw_lock_s_lock(buf_page_get_lock(block));
+
+ ut_a(*((ulint*)(frame + 16)) == i);
+
+ rw_lock_s_unlock(buf_page_get_lock(block));
+
+ buf_page_release(block);
+ }
+
+ printf("Test starts\n");
+
+ oldtm = ut_clock();
+
+ rnd = 123;
+
+ for (k = 0; k < 400; k++) {
+
+ rnd += 4357;
+
+ i = 0;
+ j = 1001 + rnd % 400;
+
+ block = buf_page_get(i, j, NULL);
+
+ frame = buf_block_get_frame(block);
+
+ rw_lock_s_lock(buf_page_get_lock(block));
+
+ ut_a(*((ulint*)(frame + 16)) == j);
+
+ rw_lock_s_unlock(buf_page_get_lock(block));
+
+ buf_page_release(block);
+ }
+
+ tm = ut_clock();
+ printf(
+ "Wall clock time for %lu random read-ahead %lu milliseconds\n",
+ k, tm - oldtm);
+
+}
+
+/************************************************************************
+Tests speed of CPU algorithms. */
+
+void
+test3(void)
+/*=======*/
+{
+ ulint i, j;
+ buf_block_t* block;
+ ulint tm, oldtm;
+
+ for (i = 0; i < 400; i++) {
+
+ block = buf_page_get(0, i, NULL);
+
+ buf_page_release(block);
+ }
+
+ os_thread_sleep(2000000);
+
+ oldtm = ut_clock();
+
+ for (j = 0; j < 500; j++) {
+ for (i = 0; i < 200; i++) {
+
+ block = buf_page_get(0, i, NULL);
+
+/*
+ rw_lock_s_lock(buf_page_get_lock(block));
+
+ rw_lock_s_unlock(buf_page_get_lock(block));
+*/
+
+ buf_page_release(block);
+
+ }
+ }
+
+ tm = ut_clock();
+ printf("Wall clock time for %lu page get-release %lu milliseconds\n",
+ i * j, tm - oldtm);
+
+ oldtm = ut_clock();
+
+ for (j = 0; j < 500; j++) {
+ for (i = 0; i < 200; i++) {
+
+ buf_block_get(block);
+/*
+ rw_lock_s_lock(buf_page_get_lock(block));
+
+ rw_lock_s_unlock(buf_page_get_lock(block));
+*/
+ buf_page_release(block);
+ }
+ }
+
+ tm = ut_clock();
+ printf("Wall clock time for %lu block get-release %lu milliseconds\n",
+ i * j, tm - oldtm);
+
+
+ oldtm = ut_clock();
+
+ for (i = 0; i < 100000; i++) {
+ block = buf_block_alloc();
+ buf_block_free(block);
+ }
+
+ tm = ut_clock();
+ printf("Wall clock time for %lu block alloc-free %lu milliseconds\n",
+ i, tm - oldtm);
+
+ ha_print_info(buf_pool->page_hash);
+}
+
+/************************************************************************
+Frees the spaces in the file system. */
+
+void
+free_system(void)
+/*=============*/
+{
+ ulint i;
+
+ for (i = 0; i < 1; i++) {
+ fil_space_free(i);
+ }
+}
+
+/************************************************************************
+Main test function. */
+
+void
+main(void)
+/*======*/
+{
+ ulint tm, oldtm;
+ ulint n;
+
+ oldtm = ut_clock();
+
+ os_aio_init(160, 5);
+ sync_init();
+ mem_init();
+ fil_init(26); /* Allow 25 open files at a time */
+ buf_pool_init(1000, 1000);
+
+ buf_validate();
+
+ ut_a(fil_validate());
+
+ create_files();
+
+ create_db();
+
+ buf_validate();
+
+ test1();
+
+ test3();
+
+ test4();
+
+ test2();
+
+ buf_validate();
+
+ n = buf_flush_batch(BUF_FLUSH_LIST, 500);
+
+ os_thread_sleep(1000000);
+
+ buf_all_freed();
+
+ free_system();
+
+ tm = ut_clock();
+ printf("Wall clock time for test %lu milliseconds\n", tm - oldtm);
+ printf("TESTS COMPLETED SUCCESSFULLY!\n");
+}
diff --git a/innobase/mtr/ts/tsmtr.c b/innobase/mtr/ts/tsmtr.c
new file mode 100644
index 00000000000..42997cd2699
--- /dev/null
+++ b/innobase/mtr/ts/tsmtr.c
@@ -0,0 +1,158 @@
+/************************************************************************
+The test module for the mini-transaction utilities
+
+(c) 1995 Innobase Oy
+
+Created 11/26/1995 Heikki Tuuri
+*************************************************************************/
+
+#include "sync0sync.h"
+#include "sync0rw.h"
+#include "mem0mem.h"
+#include "log0log.h"
+#include "..\mtr0mtr.h"
+#include "..\mtr0log.h"
+
+rw_lock_t rwl[MTR_BUF_MEMO_SIZE];
+
+/*********************************************************************
+Test for mtr buffer */
+
+void
+test1(void)
+/*=======*/
+{
+ ulint i;
+ mtr_t mtr;
+
+ printf("-------------------------------------------\n");
+ printf("MTR-TEST 1. Test of mtr buffer\n");
+
+ mtr_start(&mtr);
+
+ for (i = 0; i < MTR_BUF_MEMO_SIZE; i++) {
+ rw_lock_create(rwl + i);
+ }
+
+ for (i = 0; i < MTR_BUF_MEMO_SIZE; i++) {
+ rw_lock_s_lock(rwl + i);
+ mtr_memo_push(&mtr, rwl + i, MTR_MEMO_S_LOCK);
+ }
+
+ mtr_commit(&mtr);
+
+ rw_lock_list_print_info();
+ ut_ad(rw_lock_n_locked() == 0);
+
+}
+
+/************************************************************************
+Speed test function. */
+void
+speed_mtr(void)
+/*===========*/
+{
+ mtr_t mtr;
+
+ mtr_start(&mtr);
+
+ mtr_s_lock(rwl, &mtr);
+ mtr_s_lock(rwl + 1, &mtr);
+ mtr_s_lock(rwl + 2, &mtr);
+
+ mtr_commit(&mtr);
+}
+
+/************************************************************************
+Speed test function without mtr. */
+void
+speed_no_mtr(void)
+/*===========*/
+{
+ rw_lock_s_lock(rwl);
+ rw_lock_s_lock(rwl + 1);
+ rw_lock_s_lock(rwl + 2);
+ rw_lock_s_unlock(rwl + 2);
+ rw_lock_s_unlock(rwl + 1);
+ rw_lock_s_unlock(rwl);
+}
+
+/************************************************************************
+Speed test function. */
+
+void
+test2(void)
+/*======*/
+{
+ ulint tm, oldtm;
+ ulint i, j;
+ mtr_t mtr;
+ byte buf[50];
+
+ oldtm = ut_clock();
+
+ for (i = 0; i < 1000 * UNIV_DBC * UNIV_DBC; i++) {
+ speed_mtr();
+ }
+
+ tm = ut_clock();
+ printf("Wall clock time for %lu mtrs %lu milliseconds\n",
+ i, tm - oldtm);
+ oldtm = ut_clock();
+
+ for (i = 0; i < 1000 * UNIV_DBC * UNIV_DBC; i++) {
+ speed_no_mtr();
+ }
+
+ tm = ut_clock();
+ printf("Wall clock time for %lu no-mtrs %lu milliseconds\n",
+ i, tm - oldtm);
+
+ oldtm = ut_clock();
+ for (i = 0; i < 4 * UNIV_DBC * UNIV_DBC; i++) {
+ mtr_start(&mtr);
+ for (j = 0; j < 250; j++) {
+ mlog_catenate_ulint(&mtr, 5, MLOG_1BYTE);
+ mlog_catenate_ulint(&mtr, i, MLOG_4BYTES);
+ mlog_catenate_ulint(&mtr, i + 1, MLOG_4BYTES);
+ mlog_catenate_string(&mtr, buf, 50);
+ }
+ mtr_commit(&mtr);
+ }
+ tm = ut_clock();
+ printf("Wall clock time for %lu log writes %lu milliseconds\n",
+ i * j, tm - oldtm);
+ mtr_start(&mtr);
+ for (j = 0; j < 250; j++) {
+ mlog_catenate_ulint(&mtr, 5, MLOG_1BYTE);
+ mlog_catenate_ulint(&mtr, i, MLOG_4BYTES);
+ mlog_catenate_ulint(&mtr, i + 1, MLOG_4BYTES);
+ mlog_catenate_string(&mtr, buf, 50);
+ }
+
+ mtr_print(&mtr);
+ mtr_commit(&mtr);
+}
+
+/************************************************************************
+Main test function. */
+
+void
+main(void)
+/*======*/
+{
+ ulint tm, oldtm;
+
+ oldtm = ut_clock();
+
+ sync_init();
+ mem_init();
+ log_init();
+
+ test1();
+ test2();
+
+ tm = ut_clock();
+ printf("Wall clock time for test %lu milliseconds\n", tm - oldtm);
+ printf("TESTS COMPLETED SUCCESSFULLY!\n");
+}