diff --git a/mysql-test/include/have_innodb_change_buffering.inc b/mysql-test/include/have_innodb_change_buffering.inc new file mode 100644 index 00000000000..aadad84c6a1 --- /dev/null +++ b/mysql-test/include/have_innodb_change_buffering.inc @@ -0,0 +1,6 @@ +if (!`SHOW VARIABLES LIKE 'innodb_change_buffering_debug'`) +{ + # innodb_change_buffering_debug is enabled by UNIV_DEBUG or + # UNIV_IBUF_DEBUG + --skip Test requires binary with UNIV_DEBUG enabled +} diff --git a/mysql-test/include/search_pattern_in_file.inc b/mysql-test/include/search_pattern_in_file.inc new file mode 100644 index 00000000000..c047b5bc499 --- /dev/null +++ b/mysql-test/include/search_pattern_in_file.inc @@ -0,0 +1,66 @@ +# Purpose: +# Simple search with Perl for a pattern in some file. +# +# The advantages compared to thinkable auxiliary constructs using the +# mysqltest language and SQL are: +# 1. We do not need a running MySQL server. +# 2. SQL causes "noise" during debugging and increases the size of logs. +# Perl code does not disturb at all. +# +# The environment variables SEARCH_FILE and SEARCH_PATTERN must be set +# before sourcing this routine. +# +# In case of +# - SEARCH_FILE and/or SEARCH_PATTERN is not set +# - SEARCH_FILE cannot be opened +# - SEARCH_FILE does not contain SEARCH_PATTERN +# the test will abort immediate. +# MTR will report something like +# .... +# worker[1] Using MTR_BUILD_THREAD 300, with reserved ports 13000..13009 +# main.1st [ pass ] 3 +# innodb.innodb_page_size [ fail ] +# Test ended at 2011-11-11 18:15:58 +# +# CURRENT_TEST: innodb.innodb_page_size +# # ERROR: The file '' does not contain the expected pattern +# mysqltest: In included file "./include/search_pattern_in_file.inc": +# included from ./include/search_pattern_in_file.inc at line 36: +# At line 25: command "perl" failed with error 255. my_errno=175 +# +# The result from queries just before the failure was: +# ... +# - saving '' to '' +# main.1st [ pass ] 2 +# +# Typical use case (check invalid server startup options): +# let $error_log= $MYSQLTEST_VARDIR/log/my_restart.err; +# --error 0,1 +# --remove_file $error_log +# let SEARCH_FILE= $error_log; +# # Stop the server +# let $restart_file= $MYSQLTEST_VARDIR/tmp/mysqld.1.expect; +# --exec echo "wait" > $restart_file +# --shutdown_server 10 +# --source include/wait_until_disconnected.inc +# +# --error 1 +# --exec $MYSQLD_CMD > $error_log 2>&1 +# # The server restart aborts +# let SEARCH_PATTERN= \[ERROR\] Aborting; +# --source include/search_pattern_in_file.inc +# +# Created: 2011-11-11 mleich +# + +perl; + use strict; + my $search_file= $ENV{'SEARCH_FILE'} or die "SEARCH_FILE not set"; + my $search_pattern= $ENV{'SEARCH_PATTERN'} or die "SEARCH_PATTERN not set"; + open(FILE, "$search_file") or die("Unable to open '$search_file': $!\n"); + read(FILE, my $file_content, 50000, 0); + close(FILE); + if ( not $file_content =~ m{$search_pattern} ) { + die("# ERROR: The file '$search_file' does not contain the expected pattern $search_pattern\n->$file_content<-\n"); + } +EOF diff --git a/storage/innobase/btr/btr0cur.c b/storage/innobase/btr/btr0cur.c index 8dc0a92408b..4ad4bacdc0f 100644 --- a/storage/innobase/btr/btr0cur.c +++ b/storage/innobase/btr/btr0cur.c @@ -31,6 +31,7 @@ Created 10/16/1994 Heikki Tuuri #include "btr0sea.h" #include "row0upd.h" #include "trx0rec.h" +#include "trx0undo.h" #include "trx0roll.h" /* trx_roll_crash_recv_trx */ #include "que0que.h" #include "row0row.h" @@ -1363,18 +1364,31 @@ btr_cur_update_in_place_log( return; } - /* The code below assumes index is a clustered index: change index to - the clustered index if we are updating a secondary index record (or we - could as well skip writing the sys col values to the log in this case - because they are not needed for a secondary index record update) */ - - index = dict_table_get_first_index(index->table); + /* For secondary indexes, we could skip writing the dummy system fields + to the redo log but we have to change redo log parsing of + MLOG_REC_UPDATE_IN_PLACE/MLOG_COMP_REC_UPDATE_IN_PLACE or we have to add + new redo log record. For now, just write dummy sys fields to the redo + log if we are updating a secondary index record. + */ mach_write_to_1(log_ptr, flags); log_ptr++; - log_ptr = row_upd_write_sys_vals_to_log(index, trx, roll_ptr, log_ptr, - mtr); + if (index->type & DICT_CLUSTERED) { + log_ptr = row_upd_write_sys_vals_to_log( + index, trx, roll_ptr, log_ptr, mtr); + } else { + /* Dummy system fields for a secondary index */ + /* TRX_ID Position */ + log_ptr += mach_write_compressed(log_ptr, 0); + /* ROLL_PTR */ + trx_write_roll_ptr(log_ptr, ut_dulint_zero); + log_ptr += DATA_ROLL_PTR_LEN; + /* TRX_ID */ + log_ptr += mach_dulint_write_compressed(log_ptr, + ut_dulint_zero); + } + mach_write_to_2(log_ptr, page_offset(rec)); log_ptr += 2; diff --git a/storage/innobase/ibuf/ibuf0ibuf.c b/storage/innobase/ibuf/ibuf0ibuf.c index 2ba3d785194..ae6c5f6636f 100644 --- a/storage/innobase/ibuf/ibuf0ibuf.c +++ b/storage/innobase/ibuf/ibuf0ibuf.c @@ -3024,6 +3024,24 @@ updated_in_place: /* This is the easy case. Do something similar to btr_cur_update_in_place(). */ row_upd_rec_in_place(rec, offsets, update); + + /* Log the update in place operation. During recovery + MLOG_COMP_REC_UPDATE_IN_PLACE/MLOG_REC_UPDATE_IN_PLACE + expects trx_id, roll_ptr for secondary indexes. So we + just write dummy trx_id(0), roll_ptr(0) */ + btr_cur_update_in_place_log(BTR_KEEP_SYS_FLAG, rec, + index, update, + NULL, + ut_dulint_zero, mtr); + DBUG_EXECUTE_IF( + "crash_after_log_ibuf_upd_inplace", + log_buffer_flush_to_disk(); + fprintf(stderr, + "InnoDB: Wrote log record for ibuf " + "update in place operation\n"); + DBUG_SUICIDE(); + ); + goto updated_in_place; } diff --git a/storage/innobase/include/btr0cur.h b/storage/innobase/include/btr0cur.h index c2b81d0ae91..bab405fa91e 100644 --- a/storage/innobase/include/btr0cur.h +++ b/storage/innobase/include/btr0cur.h @@ -558,6 +558,19 @@ btr_push_update_extern_fields( const ulint* offsets,/* in: array returned by rec_get_offsets() */ upd_t* update);/* in: update vector or NULL */ +/*************************************************************** +Writes a redo log record of updating a record in-place. */ +UNIV_INLINE +void +btr_cur_update_in_place_log( +/*========================*/ + ulint flags, /* in: flags */ + rec_t* rec, /* in: record */ + dict_index_t* index, /* in: index where cursor positioned */ + upd_t* update, /* in: update vector */ + trx_t* trx, /* in: transaction */ + dulint roll_ptr, /* in: roll ptr */ + mtr_t* mtr); /* in: mtr */ /*######################################################################*/ diff --git a/storage/innodb_plugin/btr/btr0cur.c b/storage/innodb_plugin/btr/btr0cur.c index 2b98b416793..f653929193a 100644 --- a/storage/innodb_plugin/btr/btr0cur.c +++ b/storage/innodb_plugin/btr/btr0cur.c @@ -58,6 +58,7 @@ Created 10/16/1994 Heikki Tuuri #include "btr0btr.h" #include "btr0sea.h" #include "trx0rec.h" +#include "trx0undo.h" #include "trx0roll.h" /* trx_is_recv() */ #include "que0que.h" #include "row0row.h" @@ -1557,18 +1558,31 @@ btr_cur_update_in_place_log( return; } - /* The code below assumes index is a clustered index: change index to - the clustered index if we are updating a secondary index record (or we - could as well skip writing the sys col values to the log in this case - because they are not needed for a secondary index record update) */ - - index = dict_table_get_first_index(index->table); + /* For secondary indexes, we could skip writing the dummy system fields + to the redo log but we have to change redo log parsing of + MLOG_REC_UPDATE_IN_PLACE/MLOG_COMP_REC_UPDATE_IN_PLACE or we have to add + new redo log record. For now, just write dummy sys fields to the redo + log if we are updating a secondary index record. + */ mach_write_to_1(log_ptr, flags); log_ptr++; - log_ptr = row_upd_write_sys_vals_to_log(index, trx, roll_ptr, log_ptr, - mtr); + if (index->type & DICT_CLUSTERED) { + log_ptr = row_upd_write_sys_vals_to_log( + index, trx, roll_ptr, log_ptr, mtr); + } else { + /* Dummy system fields for a secondary index */ + /* TRX_ID Position */ + log_ptr += mach_write_compressed(log_ptr, 0); + /* ROLL_PTR */ + trx_write_roll_ptr(log_ptr, ut_dulint_zero); + log_ptr += DATA_ROLL_PTR_LEN; + /* TRX_ID */ + log_ptr += mach_dulint_write_compressed(log_ptr, + ut_dulint_zero); + } + mach_write_to_2(log_ptr, page_offset(rec)); log_ptr += 2; diff --git a/storage/innodb_plugin/ibuf/ibuf0ibuf.c b/storage/innodb_plugin/ibuf/ibuf0ibuf.c index e084a61d5d3..5d018bcdbc9 100644 --- a/storage/innodb_plugin/ibuf/ibuf0ibuf.c +++ b/storage/innodb_plugin/ibuf/ibuf0ibuf.c @@ -3072,6 +3072,24 @@ updated_in_place: to btr_cur_update_in_place(). */ row_upd_rec_in_place(rec, index, offsets, update, page_zip); + + /* Log the update in place operation. During recovery + MLOG_COMP_REC_UPDATE_IN_PLACE/MLOG_REC_UPDATE_IN_PLACE + expects trx_id, roll_ptr for secondary indexes. So we + just write dummy trx_id(0), roll_ptr(0) */ + btr_cur_update_in_place_log(BTR_KEEP_SYS_FLAG, rec, + index, update, + NULL, + ut_dulint_zero, mtr); + DBUG_EXECUTE_IF( + "crash_after_log_ibuf_upd_inplace", + log_buffer_flush_to_disk(); + fprintf(stderr, + "InnoDB: Wrote log record for ibuf " + "update in place operation\n"); + DBUG_SUICIDE(); + ); + goto updated_in_place; } diff --git a/storage/innodb_plugin/include/btr0cur.h b/storage/innodb_plugin/include/btr0cur.h index 7744d2d1ee2..2321d718f03 100644 --- a/storage/innodb_plugin/include/btr0cur.h +++ b/storage/innodb_plugin/include/btr0cur.h @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1994, 2012, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 1994, 2013, Oracle and/or its affiliates. All Rights Reserved. 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 @@ -636,6 +636,21 @@ btr_push_update_extern_fields( mem_heap_t* heap) /*!< in: memory heap */ __attribute__((nonnull)); +/***********************************************************//** +Writes a redo log record of updating a record in-place. */ +UNIV_INLINE +void +btr_cur_update_in_place_log( +/*========================*/ + ulint flags, /*!< in: flags */ + rec_t* rec, /*!< in: record */ + dict_index_t* index, /*!< in: index where cursor + positioned */ + const upd_t* update, /*!< in: update vector */ + trx_t* trx, /*!< in: transaction */ + roll_ptr_t roll_ptr, /*!< in: roll ptr */ + mtr_t* mtr); /*!< in: mtr */ + /*######################################################################*/ /** In the pessimistic delete, if the page data size drops below this