diff --git a/CMakeLists.txt b/CMakeLists.txt index 873a73be0ec..d2b6162ba26 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,9 @@ SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") ADD_DEFINITIONS(-DMYSQL_SERVER -D_WIN32 -DWIN32 -D_LIB) +IF(EMBEDDED_ONLY) + ADD_DEFINITIONS(-DUSE_TLS) +ENDIF(EMBEDDED_ONLY) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/zlib include diff --git a/Makefile.am b/Makefile.am index a9a446e5857..d1ef9fd8cb3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -56,7 +56,9 @@ noinst_HEADERS = include/btr0btr.h include/btr0btr.ic \ include/ha0ha.ic include/hash0hash.h \ include/hash0hash.ic include/ibuf0ibuf.h \ include/ibuf0ibuf.ic include/ibuf0types.h \ + include/lock0iter.h \ include/lock0lock.h include/lock0lock.ic \ + include/lock0priv.h include/lock0priv.ic \ include/lock0types.h include/log0log.h \ include/log0log.ic include/log0recv.h \ include/log0recv.ic include/mach0data.h \ @@ -134,7 +136,8 @@ libinnobase_a_SOURCES = btr/btr0btr.c btr/btr0cur.c btr/btr0pcur.c \ eval/eval0eval.c eval/eval0proc.c \ fil/fil0fil.c fsp/fsp0fsp.c fut/fut0fut.c \ fut/fut0lst.c ha/ha0ha.c ha/hash0hash.c \ - ibuf/ibuf0ibuf.c lock/lock0lock.c \ + ibuf/ibuf0ibuf.c lock/lock0iter.c \ + lock/lock0lock.c \ log/log0log.c log/log0recv.c mach/mach0data.c \ mem/mem0mem.c mem/mem0pool.c mtr/mtr0log.c \ mtr/mtr0mtr.c os/os0file.c os/os0proc.c \ @@ -158,8 +161,8 @@ libinnobase_a_SOURCES = btr/btr0btr.c btr/btr0cur.c btr/btr0pcur.c \ ut/ut0ut.c ut/ut0vec.c ut/ut0wqueue.c \ handler/ha_innodb.cc -libinnobase_a_CXXFLAGS= $(AM_CFLAGS) -DMYSQL_SERVER -libinnobase_a_CFLAGS = $(AM_CFLAGS) -DMYSQL_SERVER +libinnobase_a_CXXFLAGS= $(AM_CFLAGS) +libinnobase_a_CFLAGS = $(AM_CFLAGS) EXTRA_LTLIBRARIES = ha_innodb.la pkglib_LTLIBRARIES = @plugin_innobase_shared_target@ diff --git a/btr/btr0sea.c b/btr/btr0sea.c index efdcff51046..dce543ae58b 100644 --- a/btr/btr0sea.c +++ b/btr/btr0sea.c @@ -895,7 +895,7 @@ btr_search_guess_on_hash( btr_search_n_succ++; #endif if (UNIV_LIKELY(!has_search_latch) - && buf_block_peek_if_too_old(block)) { + && buf_page_peek_if_too_old(&block->page)) { buf_page_make_young(&block->page); } diff --git a/buf/buf0buf.c b/buf/buf0buf.c index 44232f76aef..361e1adada1 100644 --- a/buf/buf0buf.c +++ b/buf/buf0buf.c @@ -1329,9 +1329,7 @@ buf_block_make_young( /* Note that we read freed_page_clock's without holding any mutex: this is allowed since the result is used only in heuristics */ - if (buf_pool->freed_page_clock - >= buf_page_get_freed_page_clock(bpage) - + 1 + (buf_pool->curr_size / 4)) { + if (buf_page_peek_if_too_old(bpage)) { mutex_enter(&buf_pool->mutex); /* There has been freeing activity in the LRU list: diff --git a/dict/dict0dict.c b/dict/dict0dict.c index 8d9e8845f1a..b12a0cafbcf 100644 --- a/dict/dict0dict.c +++ b/dict/dict0dict.c @@ -350,6 +350,18 @@ dict_table_get_col_name( return(s); } + +/************************************************************************ +Acquire the autoinc lock.*/ + +void +dict_table_autoinc_lock( +/*====================*/ + dict_table_t* table) +{ + mutex_enter(&table->autoinc_mutex); +} + /************************************************************************ Initializes the autoinc counter. It is not an error to initialize an already initialized counter. */ @@ -360,54 +372,8 @@ dict_table_autoinc_initialize( dict_table_t* table, /* in: table */ ib_longlong value) /* in: next value to assign to a row */ { - mutex_enter(&(table->autoinc_mutex)); - table->autoinc_inited = TRUE; table->autoinc = value; - - mutex_exit(&(table->autoinc_mutex)); -} - -/************************************************************************ -Gets the next autoinc value (== autoinc counter value), 0 if not yet -initialized. If initialized, increments the counter by 1. */ - -ib_longlong -dict_table_autoinc_get( -/*===================*/ - /* out: value for a new row, or 0 */ - dict_table_t* table) /* in: table */ -{ - ib_longlong value; - - mutex_enter(&(table->autoinc_mutex)); - - if (!table->autoinc_inited) { - - value = 0; - } else { - value = table->autoinc; - table->autoinc = table->autoinc + 1; - } - - mutex_exit(&(table->autoinc_mutex)); - - return(value); -} - -/************************************************************************ -Decrements the autoinc counter value by 1. */ - -void -dict_table_autoinc_decrement( -/*=========================*/ - dict_table_t* table) /* in: table */ -{ - mutex_enter(&(table->autoinc_mutex)); - - table->autoinc = table->autoinc - 1; - - mutex_exit(&(table->autoinc_mutex)); } /************************************************************************ @@ -422,32 +388,6 @@ dict_table_autoinc_read( { ib_longlong value; - mutex_enter(&(table->autoinc_mutex)); - - if (!table->autoinc_inited) { - - value = 0; - } else { - value = table->autoinc; - } - - mutex_exit(&(table->autoinc_mutex)); - - return(value); -} - -/************************************************************************ -Peeks the autoinc counter value, 0 if not yet initialized. Does not -increment the counter. The read not protected by any mutex! */ - -ib_longlong -dict_table_autoinc_peek( -/*====================*/ - /* out: value of the counter */ - dict_table_t* table) /* in: table */ -{ - ib_longlong value; - if (!table->autoinc_inited) { value = 0; @@ -459,7 +399,7 @@ dict_table_autoinc_peek( } /************************************************************************ -Updates the autoinc counter if the value supplied is equal or bigger than the +Updates the autoinc counter if the value supplied is greater than the current value. If not inited, does nothing. */ void @@ -469,15 +409,21 @@ dict_table_autoinc_update( dict_table_t* table, /* in: table */ ib_longlong value) /* in: value which was assigned to a row */ { - mutex_enter(&(table->autoinc_mutex)); + if (table->autoinc_inited && value > table->autoinc) { - if (table->autoinc_inited) { - if (value >= table->autoinc) { - table->autoinc = value + 1; - } + table->autoinc = value; } +} - mutex_exit(&(table->autoinc_mutex)); +/************************************************************************ +Release the autoinc lock.*/ + +void +dict_table_autoinc_unlock( +/*======================*/ + dict_table_t* table) /* in: release autoinc lock for this table */ +{ + mutex_exit(&table->autoinc_mutex); } /************************************************************************** diff --git a/dict/dict0mem.c b/dict/dict0mem.c index f09734a1578..068eaa7342a 100644 --- a/dict/dict0mem.c +++ b/dict/dict0mem.c @@ -92,6 +92,11 @@ dict_mem_table_create( mutex_create(&table->autoinc_mutex, SYNC_DICT_AUTOINC_MUTEX); table->autoinc_inited = FALSE; + + /* The actual increment value will be set by MySQL, we simply + default to 1 here.*/ + table->autoinc_increment = 1; + #ifdef UNIV_DEBUG table->magic_n = DICT_TABLE_MAGIC_N; #endif /* UNIV_DEBUG */ diff --git a/export.sh b/export.sh index ae66610a894..b8244301ffc 100755 --- a/export.sh +++ b/export.sh @@ -14,6 +14,9 @@ if [ $# -ne 2 ] ; then die "Usage: export.sh revision-number-of-last-snapshot current-revision-number" fi +START_REV=$(($1 + 1)) +END_REV=$2 + set +u if test -z $EDITOR; then die "\$EDITOR is not set" @@ -22,10 +25,11 @@ set -u rm -rf to-mysql mkdir to-mysql{,/storage,/patches,/mysql-test{,/t,/r,/include}} -svn log -v -r "$(($1 + 1)):BASE" > to-mysql/log +svn log -v -r "$START_REV:BASE" > to-mysql/log svn export -q . to-mysql/storage/innobase -seq $(($1+1)) $2|while read REV +REV=$START_REV +while [ $REV -le $END_REV ] do PATCH=to-mysql/patches/r$REV.patch svn log -v -r$REV > $PATCH @@ -35,6 +39,7 @@ do else rm $PATCH fi + REV=$(($REV + 1)) done cd to-mysql/storage/innobase diff --git a/fsp/fsp0fsp.c b/fsp/fsp0fsp.c index 055cc588a63..9474c08bf3a 100644 --- a/fsp/fsp0fsp.c +++ b/fsp/fsp0fsp.c @@ -2983,7 +2983,7 @@ will be able to insert new data to the database without running out the tablespace. Only free extents are taken into account and we also subtract the safety margin required by the above function fsp_reserve_free_extents. */ -ulint +ullint fsp_get_available_space_in_free_extents( /*====================================*/ /* out: available space in kB */ @@ -3056,10 +3056,12 @@ fsp_get_available_space_in_free_extents( } if (!zip_size) { - return(((n_free - reserve) * FSP_EXTENT_SIZE) + return((ullint) (n_free - reserve) + * FSP_EXTENT_SIZE * (UNIV_PAGE_SIZE / 1024)); } else { - return(((n_free - reserve) * FSP_EXTENT_SIZE) + return((ullint) (n_free - reserve) + * FSP_EXTENT_SIZE * (zip_size / 1024)); } } diff --git a/handler/ha_innodb.cc b/handler/ha_innodb.cc index c9f57d91d3b..cdea4f663b6 100644 --- a/handler/ha_innodb.cc +++ b/handler/ha_innodb.cc @@ -36,22 +36,7 @@ #include "ha_innodb.h" #include -#ifdef MYSQL_SERVER -/* Define some macros until these functions are declared in . -Once these functions are defined by MySQL, we may consider -removing -DMYSQL_SERVER from ../Makefile.am as well. */ -#define thd_charset(thd) (thd)->charset() -#define thd_get_xid(thd,xid_) ((*xid_) = (thd)->transaction.xid_state.xid) -#define thd_memdup(thd,str,len) (thd)->memdup(str, len) -#define thd_killed(thd) (thd)->killed -#define thd_slave_thread(thd) (thd)->slave_thread -#define thd_query(thd) (&(thd)->query) -#define thd_non_transactional_update(thd) ((thd)->no_trans_update.all) -#define mysql_bin_log_file_name() mysql_bin_log.get_log_fname() -#define mysql_bin_log_file_pos() mysql_bin_log.get_log_file()->pos_in_file -#define mysql_tmpfile() fileno(tmpfile())/* BUGGY: leaks memory, Bug #3998 */ -#define mysql_query_cache_invalidate4(a,b,c,d) query_cache.invalidate(a,b,c,d) -#else /* MYSQL_SERVER */ +#ifndef MYSQL_SERVER /* This is needed because of Bug #3596. Let us hope that pthread_mutex_t is defined the same in both builds: the MySQL server and the InnoDB plugin. */ extern pthread_mutex_t LOCK_thread_count; @@ -81,33 +66,33 @@ typedef uchar mysql_byte; /* Include necessary InnoDB headers */ extern "C" { -#include "univ.i" -#include "buf0buddy.h" -#include "os0file.h" -#include "os0thread.h" -#include "srv0start.h" -#include "srv0srv.h" -#include "trx0roll.h" -#include "trx0trx.h" -#include "trx0sys.h" -#include "mtr0mtr.h" -#include "row0ins.h" -#include "row0mysql.h" -#include "row0sel.h" -#include "row0upd.h" -#include "log0log.h" -#include "lock0lock.h" -#include "dict0crea.h" -#include "btr0cur.h" -#include "btr0btr.h" -#include "fsp0fsp.h" -#include "sync0sync.h" -#include "fil0fil.h" -#include "trx0xa.h" -#include "row0merge.h" -#include "thr0loc.h" -#include "dict0boot.h" -#include "ha_prototypes.h" +#include "../storage/innobase/include/univ.i" +#include "../storage/innobase/include/buf0buddy.h" +#include "../storage/innobase/include/os0file.h" +#include "../storage/innobase/include/os0thread.h" +#include "../storage/innobase/include/srv0start.h" +#include "../storage/innobase/include/srv0srv.h" +#include "../storage/innobase/include/trx0roll.h" +#include "../storage/innobase/include/trx0trx.h" +#include "../storage/innobase/include/trx0sys.h" +#include "../storage/innobase/include/mtr0mtr.h" +#include "../storage/innobase/include/row0ins.h" +#include "../storage/innobase/include/row0mysql.h" +#include "../storage/innobase/include/row0sel.h" +#include "../storage/innobase/include/row0upd.h" +#include "../storage/innobase/include/log0log.h" +#include "../storage/innobase/include/lock0lock.h" +#include "../storage/innobase/include/dict0crea.h" +#include "../storage/innobase/include/btr0cur.h" +#include "../storage/innobase/include/btr0btr.h" +#include "../storage/innobase/include/fsp0fsp.h" +#include "../storage/innobase/include/sync0sync.h" +#include "../storage/innobase/include/fil0fil.h" +#include "../storage/innobase/include/trx0xa.h" +#include "../storage/innobase/include/row0merge.h" +#include "../storage/innobase/include/thr0loc.h" +#include "../storage/innobase/include/dict0boot.h" +#include "../storage/innobase/include/ha_prototypes.h" } static long innobase_mirrored_log_groups, innobase_log_files_in_group, @@ -670,7 +655,7 @@ convert_error_code_to_mysql( } else if (error == (int) DB_TABLE_NOT_FOUND) { - return(HA_ERR_KEY_NOT_FOUND); + return(HA_ERR_NO_SUCH_TABLE); } else if (error == (int) DB_TOO_BIG_RECORD) { @@ -897,7 +882,28 @@ innobase_mysql_tmpfile(void) /*========================*/ /* out: temporary file descriptor, or < 0 on error */ { - return(mysql_tmpfile()); + int fd2 = -1; + File fd = mysql_tmpfile("ib"); + if (fd >= 0) { + /* Copy the file descriptor, so that the additional resources + allocated by create_temp_file() can be freed by invoking + my_close(). + + Because the file descriptor returned by this function + will be passed to fdopen(), it will be closed by invoking + fclose(), which in turn will invoke close() instead of + my_close(). */ + fd2 = dup(fd); + if (fd2 < 0) { + DBUG_PRINT("error",("Got error %d on dup",fd2)); + my_errno=errno; + my_error(EE_OUT_OF_FILERESOURCES, + MYF(ME_BELL+ME_WAITTANG), + "ib*", my_errno); + } + my_close(fd, MYF(MY_WME)); + } + return(fd2); } /************************************************************************* @@ -980,6 +986,7 @@ ha_innobase::ha_innobase(handlerton *hton, TABLE_SHARE *table_arg) HA_CAN_SQL_HANDLER | HA_PRIMARY_KEY_REQUIRED_FOR_POSITION | HA_PRIMARY_KEY_IN_READ_INDEX | + HA_BINLOG_ROW_CAPABLE | HA_CAN_GEOMETRY | HA_PARTIAL_COLUMN_READ | HA_TABLE_SCAN_ON_INDEX), start_of_scan(0), @@ -1671,7 +1678,6 @@ innobase_init( DBUG_RETURN(FALSE); error: - innobase_hton->state = SHOW_OPTION_DISABLED; DBUG_RETURN(TRUE); } @@ -1921,6 +1927,8 @@ retry: trx_mark_sql_stat_end(trx); } + trx->n_autoinc_rows = 0; /* Reset the number AUTO-INC rows required */ + if (trx->declared_to_be_inside_innodb) { /* Release our possible ticket in the FIFO */ @@ -2301,6 +2309,21 @@ ha_innobase::get_row_type() const return(ROW_TYPE_NOT_USED); } + + +/******************************************************************** +Get the table flags to use for the statement. */ +handler::Table_flags +ha_innobase::table_flags() const +{ + /* Need to use tx_isolation here since table flags is (also) + called before prebuilt is inited. */ + ulong const tx_isolation = thd_tx_isolation(current_thd); + if (tx_isolation <= ISO_READ_COMMITTED) + return int_table_flags; + return int_table_flags | HA_BINLOG_STMT_CAPABLE; +} + /******************************************************************** Gives the file extension of an InnoDB single-table tablespace. */ static const char* ha_innobase_exts[] = { @@ -3328,6 +3351,93 @@ skip_field: } } +/************************************************************************ +This special handling is really to overcome the limitations of MySQL's +binlogging. We need to eliminate the non-determinism that will arise in +INSERT ... SELECT type of statements, since MySQL binlog only stores the +min value of the autoinc interval. Once that is fixed we can get rid of +the special lock handling.*/ + +ulong +ha_innobase::innobase_autoinc_lock(void) +/*====================================*/ + /* out: DB_SUCCESS if all OK else + error code */ +{ + ulint error = DB_SUCCESS; + + if (thd_sql_command(user_thd) == SQLCOM_INSERT) { + dict_table_autoinc_lock(prebuilt->table); + + /* We peek at the dict_table_t::auto_inc_lock to check if + another statement has locked it */ + if (prebuilt->trx->auto_inc_lock != NULL) { + /* Release the mutex to avoid deadlocks */ + dict_table_autoinc_unlock(prebuilt->table); + + goto acquire_auto_inc_lock; + } + } else { +acquire_auto_inc_lock: + error = row_lock_table_autoinc_for_mysql(prebuilt); + + if (error == DB_SUCCESS) { + dict_table_autoinc_lock(prebuilt->table); + } + } + + return(ulong(error)); +} + +/************************************************************************ +Reset the autoinc value in the table.*/ + +ulong +ha_innobase::innobase_reset_autoinc( +/*================================*/ + /* out: DB_SUCCESS if all went well + else error code */ + ulonglong autoinc) /* in: value to store */ +{ + ulint error; + + error = innobase_autoinc_lock(); + + if (error == DB_SUCCESS) { + + dict_table_autoinc_initialize(prebuilt->table, autoinc); + + dict_table_autoinc_unlock(prebuilt->table); + } + + return(ulong(error)); +} + +/************************************************************************ +Store the autoinc value in the table. The autoinc value is only set if +it's greater than the existing autoinc value in the table.*/ + +ulong +ha_innobase::innobase_set_max_autoinc( +/*==================================*/ + /* out: DB_SUCCES if all went well + else error code */ + ulonglong auto_inc) /* in: value to store */ +{ + ulint error; + + error = innobase_autoinc_lock(); + + if (error == DB_SUCCESS) { + + dict_table_autoinc_update(prebuilt->table, auto_inc); + + dict_table_autoinc_unlock(prebuilt->table); + } + + return(ulong(error)); +} + /************************************************************************ Stores a row in an InnoDB database, to the table specified in this handle. */ @@ -3338,9 +3448,7 @@ ha_innobase::write_row( /* out: error code */ mysql_byte* record) /* in: a row in MySQL format */ { - int error; - longlong auto_inc; - longlong dummy; + int error = 0; ibool auto_inc_used= FALSE; ulint sql_command; trx_t* trx = thd_to_trx(user_thd); @@ -3439,62 +3547,20 @@ no_commit: num_write_row++; + /* This is the case where the table has an auto-increment column */ if (table->next_number_field && record == table->record[0]) { - /* This is the case where the table has an - auto-increment column */ - /* Initialize the auto-inc counter if it has not been - initialized yet */ - - if (0 == dict_table_autoinc_peek(prebuilt->table)) { - - /* This call initializes the counter */ - error = innobase_read_and_init_auto_inc(&dummy); - - if (error) { - /* Deadlock or lock wait timeout */ - - goto func_exit; - } - - /* We have to set sql_stat_start to TRUE because - the above call probably has called a select, and - has reset that flag; row_insert_for_mysql has to - know to set the IX intention lock on the table, - something it only does at the start of each - statement */ - - prebuilt->sql_stat_start = TRUE; - } - - /* We have to use the transactional lock mechanism on the - auto-inc counter of the table to ensure that replication and - roll-forward of the binlog exactly imitates also the given - auto-inc values. The lock is released at each SQL statement's - end. This lock also prevents a race where two threads would - call ::get_auto_increment() simultaneously. */ - - error = row_lock_table_autoinc_for_mysql(prebuilt); - - if (error != DB_SUCCESS) { - /* Deadlock or lock wait timeout */ - - error = convert_error_code_to_mysql(error, user_thd); + if ((error = update_auto_increment())) { goto func_exit; } - /* We must use the handler code to update the auto-increment - value to be sure that we increment it correctly. */ - - if ((error= update_auto_increment())) - goto func_exit; - auto_inc_used = 1; - + auto_inc_used = TRUE; } if (prebuilt->mysql_template == NULL - || prebuilt->template_type != ROW_MYSQL_WHOLE_ROW) { + || prebuilt->template_type != ROW_MYSQL_WHOLE_ROW) { + /* Build the template used in converting quickly between the two database formats */ @@ -3505,40 +3571,63 @@ no_commit: error = row_insert_for_mysql((byte*) record, prebuilt); - if (error == DB_SUCCESS && auto_inc_used) { + /* Handle duplicate key errors */ + if (auto_inc_used) { + ulonglong auto_inc; - /* Fetch the value that was set in the autoincrement field */ - - auto_inc = table->next_number_field->val_int(); - - if (auto_inc != 0) { - /* This call will update the counter according to the - value that was inserted in the table */ - - dict_table_autoinc_update(prebuilt->table, auto_inc); + /* Note the number of rows processed for this statement, used + by get_auto_increment() to determine the number of AUTO-INC + values to reserve. This is only useful for a mult-value INSERT + and is a statement level counter.*/ + if (trx->n_autoinc_rows > 0) { + --trx->n_autoinc_rows; } - } - - /* A REPLACE command and LOAD DATA INFILE REPLACE handle a duplicate - key error themselves, and we must update the autoinc counter if we are - performing those statements. */ - - if (error == DB_DUPLICATE_KEY && auto_inc_used - && (sql_command == SQLCOM_REPLACE - || sql_command == SQLCOM_REPLACE_SELECT - || (sql_command == SQLCOM_INSERT - && ((trx->duplicates - & (TRX_DUP_IGNORE | TRX_DUP_REPLACE)) - == TRX_DUP_IGNORE)) - || (sql_command == SQLCOM_LOAD - && ((trx->duplicates - & (TRX_DUP_IGNORE | TRX_DUP_REPLACE)) - == (TRX_DUP_IGNORE | TRX_DUP_REPLACE))))) { + /* Get the value that MySQL attempted to store in the table.*/ auto_inc = table->next_number_field->val_int(); - if (auto_inc != 0) { - dict_table_autoinc_update(prebuilt->table, auto_inc); + switch (error) { + case DB_DUPLICATE_KEY: + + /* A REPLACE command and LOAD DATA INFILE REPLACE + handle a duplicate key error themselves, but we + must update the autoinc counter if we are performing + those statements. */ + + switch (sql_command) { + case SQLCOM_LOAD: + if ((trx->duplicates + & (TRX_DUP_IGNORE | TRX_DUP_REPLACE))) { + + goto set_max_autoinc; + } + break; + + case SQLCOM_REPLACE: + case SQLCOM_INSERT_SELECT: + case SQLCOM_REPLACE_SELECT: + goto set_max_autoinc; + break; + + default: + break; + } + + break; + + case DB_SUCCESS: + /* If the actual value inserted is greater than + the upper limit of the interval, then we try and + update the table upper limit. Note: last_value + will be 0 if get_auto_increment() was not called.*/ + + if (auto_inc > prebuilt->last_value) { +set_max_autoinc: + auto_inc += prebuilt->table->autoinc_increment; + + innobase_set_max_autoinc(auto_inc); + } + break; } } @@ -3546,8 +3635,6 @@ no_commit: error = convert_error_code_to_mysql(error, user_thd); - /* Tell InnoDB server that there might be work for - utility threads: */ func_exit: innobase_active_small(); @@ -3749,6 +3836,32 @@ ha_innobase::update_row( error = row_update_for_mysql((byte*) old_row, prebuilt); + /* We need to do some special AUTOINC handling for the following case: + + INSERT INTO t (c1,c2) VALUES(x,y) ON DUPLICATE KEY UPDATE ... + + We need to use the AUTOINC counter that was actually used by + MySQL in the UPDATE statement, which can be different from the + value used in the INSERT statement.*/ + + if (error == DB_SUCCESS + && table->next_number_field + && new_row == table->record[0] + && thd_sql_command(user_thd) == SQLCOM_INSERT + && (trx->duplicates & (TRX_DUP_IGNORE | TRX_DUP_REPLACE)) + == TRX_DUP_IGNORE) { + + longlong auto_inc; + + auto_inc = table->next_number_field->val_int(); + + if (auto_inc != 0) { + auto_inc += prebuilt->table->autoinc_increment; + + innobase_set_max_autoinc(auto_inc); + } + } + innodb_srv_conc_exit_innodb(trx); error = convert_error_code_to_mysql(error, user_thd); @@ -3777,6 +3890,19 @@ ha_innobase::delete_row( ut_a(prebuilt->trx == trx); + /* Only if the table has an AUTOINC column */ + if (table->found_next_number_field && record == table->record[0]) { + ulonglong dummy = 0; + + error = innobase_get_auto_increment(&dummy); + + if (error == DB_SUCCESS) { + dict_table_autoinc_unlock(prebuilt->table); + } else { + goto error_exit; + } + } + if (!prebuilt->upd_node) { row_get_prebuilt_update_vector(prebuilt); } @@ -3791,6 +3917,7 @@ ha_innobase::delete_row( innodb_srv_conc_exit_innodb(trx); +error_exit: error = convert_error_code_to_mysql(error, user_thd); /* Tell the InnoDB server that there might be work for @@ -4134,6 +4261,46 @@ ha_innobase::index_read_last( return(index_read(buf, key_ptr, key_len, HA_READ_PREFIX_LAST)); } +/************************************************************************ +Get the index for a handle. Does not change active index.*/ + +dict_index_t* +ha_innobase::innobase_get_index( +/*============================*/ + /* out: NULL or index instance. */ + uint keynr) /* in: use this index; MAX_KEY means always + clustered index, even if it was internally + generated by InnoDB */ +{ + KEY* key = 0; + dict_index_t* index = 0; + + DBUG_ENTER("innobase_get_index"); + ha_statistic_increment(&SSV::ha_read_key_count); + + ut_ad(user_thd == ha_thd()); + ut_a(prebuilt->trx == thd_to_trx(user_thd)); + + if (keynr != MAX_KEY && table->s->keys > 0) { + key = table->key_info + keynr; + + index = dict_table_get_index_on_name(prebuilt->table, + key->name); + } else { + index = dict_table_get_first_index(prebuilt->table); + } + + if (!index) { + sql_print_error( + "Innodb could not find key n:o %u with name %s " + "from dict cache for table %s", + keynr, key ? key->name : "NULL", + prebuilt->table->name); + } + + DBUG_RETURN(index); +} + /************************************************************************ Changes the active index of a handle. */ @@ -4145,31 +4312,16 @@ ha_innobase::change_active_index( index, even if it was internally generated by InnoDB */ { - KEY* key = 0; DBUG_ENTER("change_active_index"); - ha_statistic_increment(&SSV::ha_read_key_count); ut_ad(user_thd == ha_thd()); ut_a(prebuilt->trx == thd_to_trx(user_thd)); active_index = keynr; - if (keynr != MAX_KEY && table->s->keys > 0) { - key = table->key_info + active_index; - - prebuilt->index = dict_table_get_index_on_name( - prebuilt->table, key->name); - } else { - prebuilt->index = dict_table_get_first_index(prebuilt->table); - } + prebuilt->index = innobase_get_index(keynr); if (!prebuilt->index) { - sql_print_error( - "Innodb could not find key n:o %u with name %s " - "from dict cache for table %s", - keynr, key ? key->name : "NULL", - prebuilt->table->name); - DBUG_RETURN(1); } @@ -5022,7 +5174,10 @@ ha_innobase::create( maximum value in the column. */ auto_inc_value = create_info->auto_increment_value; + + dict_table_autoinc_lock(innobase_table); dict_table_autoinc_initialize(innobase_table, auto_inc_value); + dict_table_autoinc_unlock(innobase_table); } /* Tell the InnoDB server that there might be work for @@ -5943,9 +6098,9 @@ ha_innobase::update_table_comment( mutex_enter(&srv_dict_tmpfile_mutex); rewind(srv_dict_tmpfile); - fprintf(srv_dict_tmpfile, "InnoDB free: %lu kB", - (ulong) fsp_get_available_space_in_free_extents( - prebuilt->table->space)); + fprintf(srv_dict_tmpfile, "InnoDB free: %llu kB", + fsp_get_available_space_in_free_extents( + prebuilt->table->space)); dict_print_info_on_foreign_keys(FALSE, srv_dict_tmpfile, prebuilt->trx, prebuilt->table); @@ -6068,8 +6223,8 @@ ha_innobase::get_foreign_key_list(THD *thd, List *f_key_list) while (tmp_buff[i] != '/') i++; tmp_buff+= i + 1; - f_key_info.forein_id= make_lex_string(thd, 0, tmp_buff, - (uint) strlen(tmp_buff), 1); + f_key_info.forein_id = thd_make_lex_string(thd, 0, + tmp_buff, (uint) strlen(tmp_buff), 1); tmp_buff= foreign->referenced_table_name; /* Database name */ @@ -6081,22 +6236,23 @@ ha_innobase::get_foreign_key_list(THD *thd, List *f_key_list) } db_name[i]= 0; ulen= filename_to_tablename(db_name, uname, sizeof(uname)); - f_key_info.referenced_db= make_lex_string(thd, 0, uname, ulen, 1); + f_key_info.referenced_db = thd_make_lex_string(thd, 0, + uname, ulen, 1); /* Table name */ tmp_buff+= i + 1; ulen= filename_to_tablename(tmp_buff, uname, sizeof(uname)); - f_key_info.referenced_table= make_lex_string(thd, 0, uname, - ulen, 1); + f_key_info.referenced_table = thd_make_lex_string(thd, 0, + uname, ulen, 1); for (i= 0;;) { tmp_buff= foreign->foreign_col_names[i]; - name= make_lex_string(thd, name, tmp_buff, - (uint) strlen(tmp_buff), 1); + name = thd_make_lex_string(thd, name, + tmp_buff, (uint) strlen(tmp_buff), 1); f_key_info.foreign_fields.push_back(name); tmp_buff= foreign->referenced_col_names[i]; - name= make_lex_string(thd, name, tmp_buff, - (uint) strlen(tmp_buff), 1); + name = thd_make_lex_string(thd, name, + tmp_buff, (uint) strlen(tmp_buff), 1); f_key_info.referenced_fields.push_back(name); if (++i >= foreign->n_fields) break; @@ -6123,8 +6279,8 @@ ha_innobase::get_foreign_key_list(THD *thd, List *f_key_list) length=8; tmp_buff= "RESTRICT"; } - f_key_info.delete_method= make_lex_string(thd, f_key_info.delete_method, - tmp_buff, length, 1); + f_key_info.delete_method = thd_make_lex_string( + thd, f_key_info.delete_method, tmp_buff, length, 1); if (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE) @@ -6147,19 +6303,19 @@ ha_innobase::get_foreign_key_list(THD *thd, List *f_key_list) length=8; tmp_buff= "RESTRICT"; } - f_key_info.update_method= make_lex_string(thd, f_key_info.update_method, - tmp_buff, length, 1); + f_key_info.update_method = thd_make_lex_string( + thd, f_key_info.update_method, tmp_buff, length, 1); if (foreign->referenced_index && foreign->referenced_index->name) { - f_key_info.referenced_key_name= - make_lex_string(thd, f_key_info.referenced_key_name, - foreign->referenced_index->name, - strlen(foreign->referenced_index->name), 1); + f_key_info.referenced_key_name = thd_make_lex_string( + thd, f_key_info.referenced_key_name, + foreign->referenced_index->name, + strlen(foreign->referenced_index->name), 1); } FOREIGN_KEY_INFO *pf_key_info = (FOREIGN_KEY_INFO *) - thd_memdup(thd, &f_key_info, sizeof f_key_info); + thd_memdup(thd, &f_key_info, sizeof(FOREIGN_KEY_INFO)); f_key_list->push_back(pf_key_info); foreign = UT_LIST_GET_NEXT(foreign_list, foreign); } @@ -6422,6 +6578,29 @@ ha_innobase::external_lock( update_thd(thd); + /* Statement based binlogging does not work in isolation level + READ UNCOMMITTED and READ COMMITTED since the necessary + locks cannot be taken. In this case, we print an + informative error message and return with an error. */ + if (lock_type == F_WRLCK) + { + ulong const binlog_format= thd_binlog_format(thd); + ulong const tx_isolation = thd_tx_isolation(current_thd); + if (tx_isolation <= ISO_READ_COMMITTED && + binlog_format == BINLOG_FORMAT_STMT) + { + char buf[256]; + my_snprintf(buf, sizeof(buf), + "Transaction level '%s' in" + " InnoDB is not safe for binlog mode '%s'", + tx_isolation_names[tx_isolation], + binlog_format_names[binlog_format]); + my_error(ER_BINLOG_LOGGING_IMPOSSIBLE, MYF(0), buf); + DBUG_RETURN(HA_ERR_LOGGING_IMPOSSIBLE); + } + } + + trx = prebuilt->trx; prebuilt->sql_stat_start = TRUE; @@ -6666,10 +6845,6 @@ innodb_show_status( DBUG_ENTER("innodb_show_status"); - if (hton->state != SHOW_OPTION_YES) { - DBUG_RETURN(FALSE); - } - trx = check_trx_exists(thd); innobase_release_stat_resources(trx); @@ -7091,18 +7266,6 @@ ha_innobase::store_lock( && !thd_tablespace_op(thd) && sql_command != SQLCOM_TRUNCATE && sql_command != SQLCOM_OPTIMIZE - -#ifdef __WIN__ - /* For alter table on win32 for successful - operation completion it is used TL_WRITE(=10) lock - instead of TL_WRITE_ALLOW_READ(=6), however here - in innodb handler TL_WRITE is lifted to - TL_WRITE_ALLOW_WRITE, which causes race condition - when several clients do alter table simultaneously - (bug #17264). This fix avoids the problem. */ - && sql_command != SQLCOM_ALTER_TABLE -#endif - && sql_command != SQLCOM_CREATE_TABLE) { lock_type = TL_WRITE_ALLOW_WRITE; @@ -7141,15 +7304,15 @@ the value of the auto-inc counter. */ int ha_innobase::innobase_read_and_init_auto_inc( /*=========================================*/ - /* out: 0 or error code: deadlock or lock wait - timeout */ - longlong* ret) /* out: auto-inc value */ + /* out: 0 or error code: + deadlock or lock wait timeout */ + longlong* value) /* out: the autoinc value */ { longlong auto_inc; - ulint old_select_lock_type; - ibool trx_was_not_started = FALSE; ibool stmt_start; - int error; + int mysql_error = 0; + dict_table_t* innodb_table = prebuilt->table; + ibool trx_was_not_started = FALSE; ut_a(prebuilt); ut_a(prebuilt->table); @@ -7170,103 +7333,47 @@ ha_innobase::innobase_read_and_init_auto_inc( trx_search_latch_release_if_reserved(prebuilt->trx); + dict_table_autoinc_lock(prebuilt->table); + auto_inc = dict_table_autoinc_read(prebuilt->table); - if (auto_inc != 0) { - /* Already initialized */ - *ret = auto_inc; - - error = 0; - - goto func_exit_early; + /* Was the AUTOINC counter reset during normal processing, if + so then we simply start count from 1. No need to go to the index.*/ + if (auto_inc == 0 && innodb_table->autoinc_inited) { + ++auto_inc; + dict_table_autoinc_initialize(innodb_table, auto_inc); } - error = row_lock_table_autoinc_for_mysql(prebuilt); + if (auto_inc == 0) { + dict_index_t* index; + ulint error = DB_SUCCESS; + const char* autoinc_col_name; - if (error != DB_SUCCESS) { - error = convert_error_code_to_mysql(error, user_thd); + ut_a(!innodb_table->autoinc_inited); - goto func_exit_early; - } + index = innobase_get_index(table->s->next_number_index); - /* Check again if someone has initialized the counter meanwhile */ - auto_inc = dict_table_autoinc_read(prebuilt->table); + autoinc_col_name = table->found_next_number_field->field_name; - if (auto_inc != 0) { - *ret = auto_inc; + error = row_search_max_autoinc( + index, autoinc_col_name, &auto_inc); - error = 0; - - goto func_exit_early; - } - - (void) extra(HA_EXTRA_KEYREAD); - index_init(table->s->next_number_index, 1); - - /* Starting from 5.0.9, we use a consistent read to read the auto-inc - column maximum value. This eliminates the spurious deadlocks caused - by the row X-lock that we previously used. Note the following flaw - in our algorithm: if some other user meanwhile UPDATEs the auto-inc - column, our consistent read will not return the largest value. We - accept this flaw, since the deadlocks were a bigger trouble. */ - - /* Fetch all the columns in the key */ - - prebuilt->hint_need_to_fetch_extra_cols = ROW_RETRIEVE_ALL_COLS; - - old_select_lock_type = prebuilt->select_lock_type; - prebuilt->select_lock_type = LOCK_NONE; - - /* Eliminate an InnoDB error print that happens when we try to SELECT - from a table when no table has been locked in ::external_lock(). */ - prebuilt->trx->n_mysql_tables_in_use++; - - error = index_last(table->record[1]); - - prebuilt->trx->n_mysql_tables_in_use--; - prebuilt->select_lock_type = old_select_lock_type; - - if (error) { - if (error == HA_ERR_END_OF_FILE) { - /* The table was empty, initialize to 1 */ - auto_inc = 1; - - error = 0; + if (error == DB_SUCCESS) { + ++auto_inc; + dict_table_autoinc_initialize(innodb_table, auto_inc); } else { - /* This should not happen in a consistent read */ - sql_print_error("Consistent read of auto-inc column " - "returned %lu", (ulong) error); - auto_inc = -1; + fprintf(stderr, " InnoDB error: Couldn't read the " + "max AUTOINC value from index (%s).\n", + index->name); - goto func_exit; + mysql_error = 1; } - } else { - /* Initialize to max(col) + 1; we use - 'found_next_number_field' below because MySQL in SHOW TABLE - STATUS does not seem to set 'next_number_field'. The comment - in table.h says that 'next_number_field' is set when it is - 'active'. - Since 5.1 MySQL enforces that we announce fields which we will - read; as we only do a val_*() call, dbug_tmp_use_all_columns() - with read_set is sufficient. */ - - my_bitmap_map *old_map; - old_map= dbug_tmp_use_all_columns(table, table->read_set); - auto_inc = (longlong) table->found_next_number_field-> - val_int_offset(table->s->rec_buff_length) + 1; - dbug_tmp_restore_column_map(table->read_set, old_map); } - dict_table_autoinc_initialize(prebuilt->table, auto_inc); + *value = auto_inc; -func_exit: - (void) extra(HA_EXTRA_NO_KEYREAD); + dict_table_autoinc_unlock(prebuilt->table); - index_end(); - - *ret = auto_inc; - -func_exit_early: /* Since MySQL does not seem to call autocommit after SHOW TABLE STATUS (even if we would register the trx here), we commit our transaction here if it was started here. This is to eliminate a @@ -7281,6 +7388,63 @@ func_exit_early: prebuilt->sql_stat_start = stmt_start; + return(mysql_error); +} + +/******************************************************************************* +Read the next autoinc value, initialize the table if it's not initialized. +On return if there is no error then the tables AUTOINC lock is locked.*/ + +ulong +ha_innobase::innobase_get_auto_increment( + ulonglong* value) /* out: autoinc value */ +{ + ulint error; + + do { + error = innobase_autoinc_lock(); + + if (error == DB_SUCCESS) { + ib_longlong autoinc; + + /* Determine the first value of the interval */ + autoinc = dict_table_autoinc_read(prebuilt->table); + + /* We need to initialize the AUTO-INC value, for + that we release all locks.*/ + if (autoinc <= 0) { + trx_t* trx; + + trx = prebuilt->trx; + dict_table_autoinc_unlock(prebuilt->table); + + if (trx->auto_inc_lock) { + /* If we had reserved the AUTO-INC + lock in this SQL statement we release + it before retrying.*/ + row_unlock_table_autoinc_for_mysql(trx); + } + + /* Just to make sure */ + ut_a(!trx->auto_inc_lock); + + int mysql_error; + + mysql_error = innobase_read_and_init_auto_inc( + &autoinc); + + if (!mysql_error) { + /* Should have read the proper value */ + ut_a(autoinc > 0); + } else { + error = DB_ERROR; + } + } else { + *value = (ulonglong) autoinc; + } + } + } while (*value == 0 && error == DB_SUCCESS); + return(error); } @@ -7292,37 +7456,87 @@ auto-inc counter in *first_value, and ULONGLONG_MAX in *nb_reserved_values (as we have a table-level lock). offset, increment, nb_desired_values are ignored. *first_value is set to -1 if error (deadlock or lock wait timeout) */ -void ha_innobase::get_auto_increment( +void +ha_innobase::get_auto_increment( /*=================================*/ - ulonglong offset, /* in */ - ulonglong increment, /* in */ - ulonglong nb_desired_values, /* in */ - ulonglong *first_value, /* out */ - ulonglong *nb_reserved_values) /* out */ + ulonglong offset, /* in: */ + ulonglong increment, /* in: table autoinc increment */ + ulonglong nb_desired_values, /* in: number of values reqd */ + ulonglong *first_value, /* out: the autoinc value */ + ulonglong *nb_reserved_values) /* out: count of reserved values */ { - longlong nr; - int error; + ulint error; + ulonglong autoinc = 0; /* Prepare prebuilt->trx in the table handle */ update_thd(ha_thd()); - error = innobase_read_and_init_auto_inc(&nr); + error = innobase_get_auto_increment(&autoinc); - if (error) { - /* This should never happen in the current (5.0.6) code, since - we call this function only after the counter has been - initialized. */ + if (error != DB_SUCCESS) { + /* This should never happen in the code > ver 5.0.6, + since we call this function only after the counter + has been initialized. */ ut_print_timestamp(stderr); - sql_print_error("Error %lu in ::get_auto_increment()", - (ulong) error); - *first_value= (~(ulonglong) 0); + sql_print_error("Error %lu in ::get_auto_increment()", error); + + *first_value = (~(ulonglong) 0); return; } - *first_value= (ulonglong) nr; - /* table-level autoinc lock reserves up to +inf */ - *nb_reserved_values= ULONGLONG_MAX; + /* This is a hack, since nb_desired_values seems to be accurate only + for the first call to get_auto_increment() for multi-row INSERT and + meaningless for other statements e.g, LOAD etc. Subsequent calls to + this method for the same statement results in different values which + don't make sense. Therefore we store the value the first time we are + called and count down from that as rows are written (see write_row()). + + We make one exception, if the *first_value is precomputed by MySQL + we use that value. And set the number of reserved values to 1 if + this is the first time we were called for the SQL statement, this + will force MySQL to call us for the next value. If we are in the + middle of a multi-row insert we preserve the existing counter.*/ + if (*first_value == 0) { + + /* Called for the first time ? */ + if (prebuilt->trx->n_autoinc_rows == 0) { + + prebuilt->trx->n_autoinc_rows = nb_desired_values; + + /* It's possible for nb_desired_values to be 0: + e.g., INSERT INTO T1(C) SELECT C FROM T2; */ + if (nb_desired_values == 0) { + + ++prebuilt->trx->n_autoinc_rows; + } + } + + *first_value = autoinc; + + } else if (prebuilt->trx->n_autoinc_rows == 0) { + + prebuilt->trx->n_autoinc_rows = 1; + } + + ut_a(prebuilt->trx->n_autoinc_rows > 0); + + *nb_reserved_values = prebuilt->trx->n_autoinc_rows; + + /* Compute the last value in the interval */ + prebuilt->last_value = *first_value + (*nb_reserved_values * increment); + + ut_a(prebuilt->last_value >= *first_value); + + /* Update the table autoinc variable */ + dict_table_autoinc_update(prebuilt->table, prebuilt->last_value); + + /* The increment to be used to increase the AUTOINC value, we use + this in write_row() and update_row() to increase the autoinc counter + for columns that are filled by the user.*/ + prebuilt->table->autoinc_increment = increment; + + dict_table_autoinc_unlock(prebuilt->table); } /* See comment in handler.h */ @@ -7343,7 +7557,7 @@ ha_innobase::reset_auto_increment(ulonglong value) DBUG_RETURN(error); } - dict_table_autoinc_initialize(prebuilt->table, value); + innobase_reset_autoinc(value); DBUG_RETURN(0); } @@ -7596,7 +7810,7 @@ innobase_xa_prepare( return(0); } - thd_get_xid(thd, &trx->xid); + thd_get_xid(thd, (MYSQL_XID*) &trx->xid); /* Release a possible FIFO ticket and search latch. Since we will reserve the kernel mutex, we have to release the search system latch @@ -7629,6 +7843,7 @@ innobase_xa_prepare( row_unlock_table_autoinc_for_mysql(trx); } + /* Store the current undo_no of the transaction so that we know where to roll back if we have to roll back the next SQL statement */ diff --git a/handler/ha_innodb.h b/handler/ha_innodb.h index 95fa895c857..61b2d6084ef 100644 --- a/handler/ha_innodb.h +++ b/handler/ha_innodb.h @@ -32,7 +32,10 @@ typedef struct st_innobase_share { } INNOBASE_SHARE; +struct dict_index_struct; struct row_prebuilt_struct; + +typedef struct dict_index_struct dict_index_t; typedef struct row_prebuilt_struct row_prebuilt_t; /* The class defining a handle to an Innodb table */ @@ -54,7 +57,7 @@ class ha_innobase: public handler ulong upd_and_key_val_buff_len; /* the length of each of the previous two buffers */ - ulong int_table_flags; + Table_flags int_table_flags; uint primary_key; ulong start_of_scan; /* this is set to 1 when we are starting a table scan but have not @@ -70,6 +73,11 @@ class ha_innobase: public handler int change_active_index(uint keynr); int general_fetch(uchar* buf, uint direction, uint match_mode); int innobase_read_and_init_auto_inc(longlong* ret); + ulong innobase_autoinc_lock(); + ulong innobase_set_max_autoinc(ulonglong auto_inc); + ulong innobase_reset_autoinc(ulonglong auto_inc); + ulong innobase_get_auto_increment(ulonglong* value); + dict_index_t* innobase_get_index(uint keynr); /* Init values for the class: */ public: @@ -84,7 +92,7 @@ class ha_innobase: public handler const char* table_type() const { return("InnoDB");} const char *index_type(uint key_number) { return "BTREE"; } const char** bas_ext() const; - ulonglong table_flags() const { return int_table_flags; } + Table_flags table_flags() const; ulong index_flags(uint idx, uint part, bool all_parts) const { return(HA_READ_NEXT | HA_READ_PREV | HA_READ_ORDER | HA_READ_RANGE | HA_KEYREAD_ONLY); @@ -191,6 +199,52 @@ class ha_innobase: public handler uint table_changes); }; +/* Some accessor functions which the InnoDB plugin needs, but which +can not be added to mysql/plugin.h as part of the public interface; +the definitions are bracketed with #ifdef INNODB_COMPATIBILITY_HOOKS */ + +#ifndef INNODB_COMPATIBILITY_HOOKS +#error InnoDB needs MySQL to be built with #define INNODB_COMPATIBILITY_HOOKS +#endif + +extern "C" { +struct charset_info_st *thd_charset(MYSQL_THD thd); +char **thd_query(MYSQL_THD thd); + +/** Get the file name of the MySQL binlog. + * @return the name of the binlog file + */ +const char* mysql_bin_log_file_name(void); + +/** Get the current position of the MySQL binlog. + * @return byte offset from the beginning of the binlog + */ +ulonglong mysql_bin_log_file_pos(void); + +/** + Check if a user thread is a replication slave thread + @param thd user thread + @retval 0 the user thread is not a replication slave thread + @retval 1 the user thread is a replication slave thread +*/ +int thd_slave_thread(const MYSQL_THD thd); + +/** + Check if a user thread is running a non-transactional update + @param thd user thread + @retval 0 the user thread is not running a non-transactional update + @retval 1 the user thread is running a non-transactional update +*/ +int thd_non_transactional_update(const MYSQL_THD thd); + +/** + Get the user thread's binary logging format + @param thd user thread + @return Value to be used as index into the binlog_format_names array +*/ +int thd_binlog_format(const MYSQL_THD thd); +} + /* don't delete it - it may be re-enabled later as an optimization for the most common case InnoDB+binlog diff --git a/include/buf0buf.h b/include/buf0buf.h index 664b4017efe..c1695828739 100644 --- a/include/buf0buf.h +++ b/include/buf0buf.h @@ -382,11 +382,11 @@ of dropping from the buffer pool. NOTE: does not reserve the buffer pool mutex. */ UNIV_INLINE ibool -buf_block_peek_if_too_old( -/*======================*/ +buf_page_peek_if_too_old( +/*=====================*/ /* out: TRUE if should be made younger */ - const buf_block_t* block); /* in: block to make younger */ + const buf_page_t* bpage); /* in: block to make younger */ /************************************************************************ Returns the current state of is_hashed of a page. FALSE if the page is not in the pool. NOTE that this operation does not fix the page in the diff --git a/include/buf0buf.ic b/include/buf0buf.ic index 2a8ebe7bb51..73dd6ed4571 100644 --- a/include/buf0buf.ic +++ b/include/buf0buf.ic @@ -41,15 +41,15 @@ of dropping from the buffer pool. NOTE: does not reserve the buffer pool mutex. */ UNIV_INLINE ibool -buf_block_peek_if_too_old( -/*======================*/ +buf_page_peek_if_too_old( +/*=====================*/ /* out: TRUE if should be made younger */ - const buf_block_t* block) /* in: block to make younger */ + const buf_page_t* bpage) /* in: block to make younger */ { return(buf_pool->freed_page_clock - >= buf_block_get_freed_page_clock(block) - + 1 + (buf_pool->curr_size / 1024)); + >= buf_page_get_freed_page_clock(bpage) + + 1 + (buf_pool->curr_size / 4)); } /************************************************************************* diff --git a/include/dict0dict.h b/include/dict0dict.h index e1432f31805..f2fbf448f79 100644 --- a/include/dict0dict.h +++ b/include/dict0dict.h @@ -164,6 +164,13 @@ dict_col_name_is_reserved( /* out: TRUE if name is reserved */ const char* name); /* in: column name */ /************************************************************************ +Acquire the autoinc lock.*/ + +void +dict_table_autoinc_lock( +/*====================*/ + dict_table_t* table); /* in: table */ +/************************************************************************ Initializes the autoinc counter. It is not an error to initialize an already initialized counter. */ @@ -173,22 +180,6 @@ dict_table_autoinc_initialize( dict_table_t* table, /* in: table */ ib_longlong value); /* in: next value to assign to a row */ /************************************************************************ -Gets the next autoinc value (== autoinc counter value), 0 if not yet -initialized. If initialized, increments the counter by 1. */ - -ib_longlong -dict_table_autoinc_get( -/*===================*/ - /* out: value for a new row, or 0 */ - dict_table_t* table); /* in: table */ -/************************************************************************ -Decrements the autoinc counter value by 1. */ - -void -dict_table_autoinc_decrement( -/*=========================*/ - dict_table_t* table); /* in: table */ -/************************************************************************ Reads the next autoinc value (== autoinc counter value), 0 if not yet initialized. */ @@ -198,15 +189,6 @@ dict_table_autoinc_read( /* out: value for a new row, or 0 */ dict_table_t* table); /* in: table */ /************************************************************************ -Peeks the autoinc counter value, 0 if not yet initialized. Does not -increment the counter. The read not protected by any mutex! */ - -ib_longlong -dict_table_autoinc_peek( -/*====================*/ - /* out: value of the counter */ - dict_table_t* table); /* in: table */ -/************************************************************************ Updates the autoinc counter if the value supplied is equal or bigger than the current value. If not inited, does nothing. */ @@ -216,6 +198,13 @@ dict_table_autoinc_update( dict_table_t* table, /* in: table */ ib_longlong value); /* in: value which was assigned to a row */ +/************************************************************************ +Release the autoinc lock.*/ + +void +dict_table_autoinc_unlock( +/*======================*/ + dict_table_t* table); /* in: table */ /************************************************************************** Adds system columns to a table object. */ diff --git a/include/dict0mem.h b/include/dict0mem.h index 56281e585ec..4e4252d3043 100644 --- a/include/dict0mem.h +++ b/include/dict0mem.h @@ -433,6 +433,10 @@ struct dict_table_struct{ SELECT MAX(auto inc column) */ ib_longlong autoinc;/* autoinc counter value to give to the next inserted row */ + ib_longlong autoinc_increment; + /* The increment step of the auto increment + column. Value must be greater than or equal + to 1 */ /*----------------------*/ UT_LIST_BASE_NODE_T(row_prebuilt_t) prebuilts; /* base node for the prebuilts defined diff --git a/include/fsp0fsp.h b/include/fsp0fsp.h index 0ab829bda48..666e5e602ec 100644 --- a/include/fsp0fsp.h +++ b/include/fsp0fsp.h @@ -257,7 +257,7 @@ will be able to insert new data to the database without running out the tablespace. Only free extents are taken into account and we also subtract the safety margin required by the above function fsp_reserve_free_extents. */ -ulint +ullint fsp_get_available_space_in_free_extents( /*====================================*/ /* out: available space in kB */ diff --git a/include/lock0iter.h b/include/lock0iter.h new file mode 100644 index 00000000000..38fbae43e1f --- /dev/null +++ b/include/lock0iter.h @@ -0,0 +1,52 @@ +/****************************************************** +Lock queue iterator type and function prototypes. + +(c) 2007 Innobase Oy + +Created July 16, 2007 Vasil Dimov +*******************************************************/ + +#ifndef lock0iter_h +#define lock0iter_h + +#include "univ.i" +#include "lock0types.h" + +typedef struct lock_queue_iterator_struct { + const lock_t* current_lock; + /* In case this is a record lock queue (not table lock queue) + then bit_no is the record number within the heap in which the + record is stored. */ + ulint bit_no; +} lock_queue_iterator_t; + +/*********************************************************************** +Initialize lock queue iterator so that it starts to iterate from +"lock". bit_no specifies the record number within the heap where the +record is stored. It can be undefined (ULINT_UNDEFINED) in two cases: +1. If the lock is a table lock, thus we have a table lock queue; +2. If the lock is a record lock and it is a wait lock. In this case + bit_no is calculated in this function by using + lock_rec_find_set_bit(). There is exactly one bit set in the bitmap + of a wait lock. */ + +void +lock_queue_iterator_reset( +/*======================*/ + lock_queue_iterator_t* iter, /* out: iterator */ + const lock_t* lock, /* in: lock to start from */ + ulint bit_no);/* in: record number in the + heap */ + +/*********************************************************************** +Gets the previous lock in the lock queue, returns NULL if there are no +more locks (i.e. the current lock is the first one). The iterator is +receded (if not-NULL is returned). */ + +const lock_t* +lock_queue_iterator_get_prev( +/*=========================*/ + /* out: previous lock or NULL */ + lock_queue_iterator_t* iter); /* in/out: iterator */ + +#endif /* lock0iter_h */ diff --git a/include/lock0lock.h b/include/lock0lock.h index d58f63970ca..c1f59c85128 100644 --- a/include/lock0lock.h +++ b/include/lock0lock.h @@ -597,6 +597,19 @@ lock_is_table_exclusive( dict_table_t* table, /* in: table */ trx_t* trx); /* in: transaction */ /************************************************************************* +Checks if a lock request lock1 has to wait for request lock2. */ + +ibool +lock_has_to_wait( +/*=============*/ + /* out: TRUE if lock1 has to wait for + lock2 to be removed */ + const lock_t* lock1, /* in: waiting lock */ + const lock_t* lock2); /* in: another lock; NOTE that it is + assumed that this has a lock bit set + on the same record as in lock1 if the + locks are record locks */ +/************************************************************************* Checks that a transaction id is sensible, i.e., not in the future. */ ibool @@ -641,7 +654,7 @@ lock_print_info_all_transactions( FILE* file); /* in: file where to print */ /************************************************************************* Return approximate number or record locks (bits set in the bitmap) for -this transaction. Since delete-marked records ma ybe removed, the +this transaction. Since delete-marked records may be removed, the record count will not be precise. */ ulint diff --git a/include/lock0priv.h b/include/lock0priv.h new file mode 100644 index 00000000000..76b59d1968c --- /dev/null +++ b/include/lock0priv.h @@ -0,0 +1,101 @@ +/****************************************************** +Lock module internal structures and methods. + +(c) 2007 Innobase Oy + +Created July 12, 2007 Vasil Dimov +*******************************************************/ + +#ifndef lock0priv_h +#define lock0priv_h + +#ifndef LOCK_MODULE_IMPLEMENTATION +/* If you need to access members of the structures defined in this +file, please write appropriate functions that retrieve them and put +those functions in lock/ */ +#error Do not include lock0priv.h outside of the lock/ module +#endif + +#include "univ.i" +#include "dict0types.h" +#include "hash0hash.h" +#include "trx0types.h" +#include "ut0lst.h" + +/* A table lock */ +typedef struct lock_table_struct lock_table_t; +struct lock_table_struct { + dict_table_t* table; /* database table in dictionary + cache */ + UT_LIST_NODE_T(lock_t) + locks; /* list of locks on the same + table */ +}; + +/* Record lock for a page */ +typedef struct lock_rec_struct lock_rec_t; +struct lock_rec_struct { + ulint space; /* space id */ + ulint page_no; /* page number */ + ulint n_bits; /* number of bits in the lock + bitmap; NOTE: the lock bitmap is + placed immediately after the + lock struct */ +}; + +/* Lock struct */ +struct lock_struct { + trx_t* trx; /* transaction owning the + lock */ + UT_LIST_NODE_T(lock_t) + trx_locks; /* list of the locks of the + transaction */ + ulint type_mode; /* lock type, mode, LOCK_GAP or + LOCK_REC_NOT_GAP, + LOCK_INSERT_INTENTION, + wait flag, ORed */ + hash_node_t hash; /* hash chain node for a record + lock */ + dict_index_t* index; /* index for a record lock */ + union { + lock_table_t tab_lock;/* table lock */ + lock_rec_t rec_lock;/* record lock */ + } un_member; +}; + +/************************************************************************* +Gets the type of a lock. */ +UNIV_INLINE +ulint +lock_get_type( +/*==========*/ + /* out: LOCK_TABLE or LOCK_REC */ + const lock_t* lock); /* in: lock */ + +/************************************************************************** +Looks for a set bit in a record lock bitmap. Returns ULINT_UNDEFINED, +if none found. */ + +ulint +lock_rec_find_set_bit( +/*==================*/ + /* out: bit index == heap number of + the record, or ULINT_UNDEFINED if none found */ + const lock_t* lock); /* in: record lock with at least one bit set */ + +/************************************************************************* +Gets the previous record lock set on a record. */ + +const lock_t* +lock_rec_get_prev( +/*==============*/ + /* out: previous lock on the same + record, NULL if none exists */ + const lock_t* in_lock,/* in: record lock */ + ulint heap_no);/* in: heap number of the record */ + +#ifndef UNIV_NONINL +#include "lock0priv.ic" +#endif + +#endif /* lock0priv_h */ diff --git a/include/lock0priv.ic b/include/lock0priv.ic new file mode 100644 index 00000000000..4bc8397509d --- /dev/null +++ b/include/lock0priv.ic @@ -0,0 +1,32 @@ +/****************************************************** +Lock module internal inline methods. + +(c) 2007 Innobase Oy + +Created July 16, 2007 Vasil Dimov +*******************************************************/ + +/* This file contains only methods which are used in +lock/lock0* files, other than lock/lock0lock.c. +I.e. lock/lock0lock.c contains more internal inline +methods but they are used only in that file. */ + +#ifndef LOCK_MODULE_IMPLEMENTATION +#error Do not include lock0priv.ic outside of the lock/ module +#endif + +/************************************************************************* +Gets the type of a lock. */ +UNIV_INLINE +ulint +lock_get_type( +/*==========*/ + /* out: LOCK_TABLE or LOCK_REC */ + const lock_t* lock) /* in: lock */ +{ + ut_ad(lock); + + return(lock->type_mode & LOCK_TYPE_MASK); +} + +/* vim: set filetype=c: */ diff --git a/include/row0mysql.h b/include/row0mysql.h index e1dbf9283be..2a615a1b81d 100644 --- a/include/row0mysql.h +++ b/include/row0mysql.h @@ -766,6 +766,7 @@ struct row_prebuilt_struct { to this heap */ mem_heap_t* old_vers_heap; /* memory heap where a previous version is built in consistent read */ + ulonglong last_value; /* last value of AUTO-INC interval */ UT_LIST_NODE_T(row_prebuilt_t) prebuilts; /* list node of table->prebuilts */ ulint magic_n2; /* this should be the same as diff --git a/include/row0sel.h b/include/row0sel.h index 96273a18cd5..4bde648f18e 100644 --- a/include/row0sel.h +++ b/include/row0sel.h @@ -171,7 +171,17 @@ row_search_check_if_query_cache_permitted( trx_t* trx, /* in: transaction object */ const char* norm_name); /* in: concatenation of database name, '/' char, table name */ +/*********************************************************************** +Read the max AUTOINC value from an index. */ +ulint +row_search_max_autoinc( +/*===================*/ + /* out: DB_SUCCESS if all OK else + error code */ + dict_index_t* index, /* in: index to search */ + const char* col_name, /* in: autoinc column name */ + ib_longlong* value); /* out: AUTOINC value read */ /* A structure for caching column values for prefetched rows */ struct sel_buf_struct{ diff --git a/include/trx0trx.h b/include/trx0trx.h index 970258b1315..4c24e47a4a0 100644 --- a/include/trx0trx.h +++ b/include/trx0trx.h @@ -694,6 +694,9 @@ struct trx_struct{ trx_undo_arr_t* undo_no_arr; /* array of undo numbers of undo log records which are currently processed by a rollback operation */ + ulint n_autoinc_rows; /* no. of AUTO-INC rows required for + an SQL statement. This is useful for + multi-row INSERTs */ /*------------------------------*/ char detailed_error[256]; /* detailed error message for last error, or empty. */ diff --git a/include/univ.i b/include/univ.i index a7639b2dcd9..214e8e33020 100644 --- a/include/univ.i +++ b/include/univ.i @@ -231,6 +231,8 @@ typedef longlong ib_longlong; typedef ulonglong ib_uint64_t; #endif +typedef unsigned long long int ullint; + #ifndef __WIN__ #if SIZEOF_LONG != SIZEOF_VOIDP #error "Error: InnoDB's ulint must be of the same size as void*" diff --git a/lock/lock0iter.c b/lock/lock0iter.c new file mode 100644 index 00000000000..49001bd4bd3 --- /dev/null +++ b/lock/lock0iter.c @@ -0,0 +1,90 @@ +/****************************************************** +Lock queue iterator. Can iterate over table and record +lock queues. + +(c) 2007 Innobase Oy + +Created July 16, 2007 Vasil Dimov +*******************************************************/ + +#define LOCK_MODULE_IMPLEMENTATION + +#include "univ.i" +#include "lock0iter.h" +#include "lock0lock.h" +#include "lock0priv.h" +#include "ut0dbg.h" +#include "ut0lst.h" + +/*********************************************************************** +Initialize lock queue iterator so that it starts to iterate from +"lock". bit_no specifies the record number within the heap where the +record is stored. It can be undefined (ULINT_UNDEFINED) in two cases: +1. If the lock is a table lock, thus we have a table lock queue; +2. If the lock is a record lock and it is a wait lock. In this case + bit_no is calculated in this function by using + lock_rec_find_set_bit(). There is exactly one bit set in the bitmap + of a wait lock. */ + +void +lock_queue_iterator_reset( +/*======================*/ + lock_queue_iterator_t* iter, /* out: iterator */ + const lock_t* lock, /* in: lock to start from */ + ulint bit_no) /* in: record number in the + heap */ +{ + iter->current_lock = lock; + + if (bit_no != ULINT_UNDEFINED) { + + iter->bit_no = bit_no; + } else { + + switch (lock_get_type(lock)) { + case LOCK_TABLE: + iter->bit_no = ULINT_UNDEFINED; + break; + case LOCK_REC: + iter->bit_no = lock_rec_find_set_bit(lock); + ut_a(iter->bit_no != ULINT_UNDEFINED); + break; + default: + ut_error; + } + } +} + +/*********************************************************************** +Gets the previous lock in the lock queue, returns NULL if there are no +more locks (i.e. the current lock is the first one). The iterator is +receded (if not-NULL is returned). */ + +const lock_t* +lock_queue_iterator_get_prev( +/*=========================*/ + /* out: previous lock or NULL */ + lock_queue_iterator_t* iter) /* in/out: iterator */ +{ + const lock_t* prev_lock; + + switch (lock_get_type(iter->current_lock)) { + case LOCK_REC: + prev_lock = lock_rec_get_prev( + iter->current_lock, iter->bit_no); + break; + case LOCK_TABLE: + prev_lock = UT_LIST_GET_PREV( + un_member.tab_lock.locks, iter->current_lock); + break; + default: + ut_error; + } + + if (prev_lock != NULL) { + + iter->current_lock = prev_lock; + } + + return(prev_lock); +} diff --git a/lock/lock0lock.c b/lock/lock0lock.c index fbbb91b030c..110146c2e21 100644 --- a/lock/lock0lock.c +++ b/lock/lock0lock.c @@ -6,10 +6,14 @@ The transaction lock system Created 5/7/1996 Heikki Tuuri *******************************************************/ +#define LOCK_MODULE_IMPLEMENTATION + #include "lock0lock.h" +#include "lock0priv.h" #ifdef UNIV_NONINL #include "lock0lock.ic" +#include "lock0priv.ic" #endif #include "usr0sess.h" @@ -373,42 +377,6 @@ lock_rec_validate_page( /* The lock system */ lock_sys_t* lock_sys = NULL; -/* A table lock */ -typedef struct lock_table_struct lock_table_t; -struct lock_table_struct{ - dict_table_t* table; /* database table in dictionary cache */ - UT_LIST_NODE_T(lock_t) - locks; /* list of locks on the same table */ -}; - -/* Record lock for a page */ -typedef struct lock_rec_struct lock_rec_t; -struct lock_rec_struct{ - ulint space; /* space id */ - ulint page_no; /* page number */ - ulint n_bits; /* number of bits in the lock bitmap */ - /* NOTE: the lock bitmap is placed immediately - after the lock struct */ -}; - -/* Lock struct */ -struct lock_struct{ - trx_t* trx; /* transaction owning the lock */ - UT_LIST_NODE_T(lock_t) - trx_locks; /* list of the locks of the - transaction */ - ulint type_mode; /* lock type, mode, LOCK_GAP or - LOCK_REC_NOT_GAP, - LOCK_INSERT_INTENTION, - wait flag, ORed */ - hash_node_t hash; /* hash chain node for a record lock */ - dict_index_t* index; /* index for a record lock */ - union { - lock_table_t tab_lock;/* table lock */ - lock_rec_t rec_lock;/* record lock */ - } un_member; -}; - /* We store info on the latest deadlock error to this buffer. InnoDB Monitor will then fetch it and print */ ibool lock_deadlock_found = FALSE; @@ -454,20 +422,6 @@ lock_deadlock_recursive( LOCK_MAX_DEPTH_IN_DEADLOCK_CHECK, we return LOCK_VICTIM_IS_START */ -/************************************************************************* -Gets the type of a lock. */ -UNIV_INLINE -ulint -lock_get_type( -/*==========*/ - /* out: LOCK_TABLE or LOCK_REC */ - const lock_t* lock) /* in: lock */ -{ - ut_ad(lock); - - return(lock->type_mode & LOCK_TYPE_MASK); -} - /************************************************************************* Gets the nth bit of a record lock. */ UNIV_INLINE @@ -1021,7 +975,7 @@ lock_rec_has_to_wait( /************************************************************************* Checks if a lock request lock1 has to wait for request lock2. */ -static + ibool lock_has_to_wait( /*=============*/ @@ -1095,7 +1049,7 @@ lock_rec_set_nth_bit( /************************************************************************** Looks for a set bit in a record lock bitmap. Returns ULINT_UNDEFINED, if none found. */ -static + ulint lock_rec_find_set_bit( /*==================*/ @@ -1359,7 +1313,7 @@ lock_rec_copy( /************************************************************************* Gets the previous record lock set on a record. */ -static + const lock_t* lock_rec_get_prev( /*==============*/ diff --git a/log/log0recv.c b/log/log0recv.c index a57aef70a1f..1addc6d6118 100644 --- a/log/log0recv.c +++ b/log/log0recv.c @@ -58,6 +58,16 @@ ibool recv_needed_recovery = FALSE; ibool recv_lsn_checks_on = FALSE; +/* There are two conditions under which we scan the logs, the first +is normal startup and the second is when we do a recovery from an +archive. +This flag is set if we are doing a scan from the last checkpoint during +startup. If we find log entries that were written after the last checkpoint +we know that the server was not cleanly shutdown. We must then initialize +the crash recovery environment before attempting to store these entries in +the log hash table. */ +ibool recv_log_scan_is_startup_type = FALSE; + /* If the following is TRUE, the buffer pool file pages must be invalidated after recovery and no ibuf operations are allowed; this becomes TRUE if the log record hash table becomes too full, and log records must be merged @@ -100,6 +110,16 @@ the recovery failed and the database may be corrupt. */ ib_uint64_t recv_max_page_lsn; +/* prototypes */ + +/*********************************************************** +Initialize crash recovery environment. Can be called iff +recv_needed_recovery == FALSE. */ +static +void +recv_init_crash_recovery(void); +/*===========================*/ + /************************************************************ Creates the recovery system. */ @@ -2339,6 +2359,20 @@ recv_scan_log_recs( if (scanned_lsn > recv_sys->scanned_lsn) { + /* We have found more entries. If this scan is + of startup type, we must initiate crash recovery + environment before parsing these log records. */ + + if (recv_log_scan_is_startup_type + && !recv_needed_recovery) { + + fprintf(stderr, + "InnoDB: Log scan progressed" + " past the checkpoint lsn %llu\n", + recv_sys->scanned_lsn); + recv_init_crash_recovery(); + } + /* We were able to find more log data: add it to the parsing buffer if parse_start_lsn is already non-zero */ @@ -2460,6 +2494,47 @@ recv_group_scan_log_recs( #endif /* UNIV_DEBUG */ } +/*********************************************************** +Initialize crash recovery environment. Can be called iff +recv_needed_recovery == FALSE. */ +static +void +recv_init_crash_recovery(void) +/*==========================*/ +{ + ut_a(!recv_needed_recovery); + + recv_needed_recovery = TRUE; + + ut_print_timestamp(stderr); + + fprintf(stderr, + " InnoDB: Database was not" + " shut down normally!\n" + "InnoDB: Starting crash recovery.\n"); + + fprintf(stderr, + "InnoDB: Reading tablespace information" + " from the .ibd files...\n"); + + fil_load_single_table_tablespaces(); + + /* If we are using the doublewrite method, we will + check if there are half-written pages in data files, + and restore them from the doublewrite buffer if + possible */ + + if (srv_force_recovery < SRV_FORCE_NO_LOG_REDO) { + + fprintf(stderr, + "InnoDB: Restoring possible" + " half-written data pages from" + " the doublewrite\n" + "InnoDB: buffer...\n"); + trx_sys_doublewrite_init_or_restore_pages(TRUE); + } +} + /************************************************************ Recovers from a checkpoint. When this function returns, the database is able to start processing of new user transactions, but the function @@ -2589,81 +2664,6 @@ recv_recovery_from_checkpoint_start( recv_sys->recovered_lsn = checkpoint_lsn; srv_start_lsn = checkpoint_lsn; - - /* NOTE: we always do a 'recovery' at startup, but only if - there is something wrong we will print a message to the - user about recovery: */ - - if (checkpoint_lsn != max_flushed_lsn - || checkpoint_lsn != min_flushed_lsn) { - - if (checkpoint_lsn < max_flushed_lsn) { - fprintf(stderr, - "InnoDB: #########################" - "#################################\n" - "InnoDB: " - "WARNING!\n" - "InnoDB: The log sequence number" - " in ibdata files is higher\n" - "InnoDB: than the log sequence number" - " in the ib_logfiles! Are you sure\n" - "InnoDB: you are using the right" - " ib_logfiles to start up" - " the database?\n" - "InnoDB: Log sequence number in" - " ib_logfiles is %llu, log\n" - "InnoDB: sequence numbers stamped" - " to ibdata file headers are between\n" - "InnoDB: %llu and %llu.\n" - "InnoDB: #########################" - "#################################\n", - checkpoint_lsn, - min_flushed_lsn, - max_flushed_lsn); - } - - recv_needed_recovery = TRUE; - - ut_print_timestamp(stderr); - - fprintf(stderr, - " InnoDB: Database was not" - " shut down normally!\n" - "InnoDB: Starting crash recovery.\n"); - - fprintf(stderr, - "InnoDB: Reading tablespace information" - " from the .ibd files...\n"); - - fil_load_single_table_tablespaces(); - - /* If we are using the doublewrite method, we will - check if there are half-written pages in data files, - and restore them from the doublewrite buffer if - possible */ - - if (srv_force_recovery < SRV_FORCE_NO_LOG_REDO) { - - fprintf(stderr, - "InnoDB: Restoring possible" - " half-written data pages from" - " the doublewrite\n" - "InnoDB: buffer...\n"); - trx_sys_doublewrite_init_or_restore_pages( - TRUE); - } - - ut_print_timestamp(stderr); - - fprintf(stderr, - " InnoDB: Starting log scan" - " based on checkpoint at\n" - "InnoDB: log sequence number %llu.\n", - checkpoint_lsn); - } else { - /* Init the doublewrite buffer memory structure */ - trx_sys_doublewrite_init_or_restore_pages(FALSE); - } } contiguous_lsn = ut_uint64_align_down(recv_sys->scanned_lsn, @@ -2713,6 +2713,8 @@ recv_recovery_from_checkpoint_start( group = UT_LIST_GET_NEXT(log_groups, group); } + /* Set the flag to publish that we are doing startup scan. */ + recv_log_scan_is_startup_type = (type == LOG_CHECKPOINT); while (group) { old_scanned_lsn = recv_sys->scanned_lsn; @@ -2734,6 +2736,57 @@ recv_recovery_from_checkpoint_start( group = UT_LIST_GET_NEXT(log_groups, group); } + /* Done with startup scan. Clear the flag. */ + recv_log_scan_is_startup_type = FALSE; + if (type == LOG_CHECKPOINT) { + /* NOTE: we always do a 'recovery' at startup, but only if + there is something wrong we will print a message to the + user about recovery: */ + + if (checkpoint_lsn != max_flushed_lsn + || checkpoint_lsn != min_flushed_lsn) { + + if (checkpoint_lsn < max_flushed_lsn) { + fprintf(stderr, + "InnoDB: #########################" + "#################################\n" + "InnoDB: " + "WARNING!\n" + "InnoDB: The log sequence number" + " in ibdata files is higher\n" + "InnoDB: than the log sequence number" + " in the ib_logfiles! Are you sure\n" + "InnoDB: you are using the right" + " ib_logfiles to start up" + " the database?\n" + "InnoDB: Log sequence number in" + " ib_logfiles is %llu, log\n" + "InnoDB: sequence numbers stamped" + " to ibdata file headers are between\n" + "InnoDB: %llu and %llu.\n" + "InnoDB: #########################" + "#################################\n", + checkpoint_lsn, + min_flushed_lsn, + max_flushed_lsn); + } + + if (!recv_needed_recovery) { + fprintf(stderr, + "InnoDB: The log sequence number" + " in ibdata files does not match\n" + "InnoDB: the log sequence number" + " in the ib_logfiles!\n"); + recv_init_crash_recovery(); + } + } + + if (!recv_needed_recovery) { + /* Init the doublewrite buffer memory structure */ + trx_sys_doublewrite_init_or_restore_pages(FALSE); + } + } + /* We currently have only one log group */ if (group_scanned_lsn < checkpoint_lsn) { ut_print_timestamp(stderr); @@ -2786,15 +2839,7 @@ recv_recovery_from_checkpoint_start( recv_synchronize_groups(up_to_date_group); if (!recv_needed_recovery) { - if (checkpoint_lsn != recv_sys->recovered_lsn) { - fprintf(stderr, - "InnoDB: Warning: we did not need to do" - " crash recovery, but log scan\n" - "InnoDB: progressed past the checkpoint" - " lsn %llu up to lsn %llu\n", - checkpoint_lsn, - recv_sys->recovered_lsn); - } + ut_a(checkpoint_lsn == recv_sys->recovered_lsn); } else { srv_start_lsn = recv_sys->recovered_lsn; } diff --git a/mysql-test/have_innodb.inc b/mysql-test/have_innodb.inc index be8850725e5..cbffe6a2574 100644 --- a/mysql-test/have_innodb.inc +++ b/mysql-test/have_innodb.inc @@ -1,4 +1,4 @@ disable_query_log; --require r/true.require -select support = 'Enabled' as `TRUE` from information_schema.engines where engine = 'innodb'; +select (support = 'YES' or support = 'DEFAULT') as `TRUE` from information_schema.engines where engine = 'innodb'; enable_query_log; diff --git a/mysql-test/innodb.result b/mysql-test/innodb.result index c64388c0783..c29fe55a934 100644 --- a/mysql-test/innodb.result +++ b/mysql-test/innodb.result @@ -501,7 +501,7 @@ ERROR 23000: Duplicate entry 'test2' for key 'ggid' select * from t1; id ggid email passwd 1 this will work -3 test2 this will work +4 test2 this will work select * from t1 where id=1; id ggid email passwd 1 this will work @@ -1086,6 +1086,39 @@ n d 1 30 2 20 drop table t1,t2; +CREATE TABLE `t1` ( +`a` int(11) NOT NULL auto_increment, +`b` int(11) default NULL, +PRIMARY KEY (`a`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 ; +CREATE TABLE `t2` ( +`a` int(11) NOT NULL auto_increment, +`b` int(11) default NULL, +PRIMARY KEY (`a`) +) ENGINE=INNODB DEFAULT CHARSET=latin1 ; +insert into t1 values (1,1),(2,2); +insert into t2 values (1,1),(4,4); +reset master; +UPDATE t2,t1 SET t2.a=t1.a+2; +ERROR 23000: Duplicate entry '3' for key 'PRIMARY' +select * from t2 /* must be (3,1), (4,4) */; +a b +1 1 +4 4 +show master status /* there must no UPDATE in binlog */; +File Position Binlog_Do_DB Binlog_Ignore_DB +master-bin.000001 106 +delete from t1; +delete from t2; +insert into t1 values (1,2),(3,4),(4,4); +insert into t2 values (1,2),(3,4),(4,4); +reset master; +UPDATE t2,t1 SET t2.a=t2.b where t2.a=t1.a; +ERROR 23000: Duplicate entry '4' for key 'PRIMARY' +show master status /* there must be no UPDATE query event */; +File Position Binlog_Do_DB Binlog_Ignore_DB +master-bin.000001 106 +drop table t1, t2; create table t1 (a int, b int) engine=innodb; insert into t1 values(20,null); select t2.b, ifnull(t2.b,"this is null") from t1 as t2 left join t1 as t3 on @@ -1607,7 +1640,7 @@ t2 CREATE TABLE `t2` ( ) ENGINE=InnoDB DEFAULT CHARSET=latin1 drop table t2; create table t2 (id int(11) not null, id2 int(11) not null, constraint t1_id_fk foreign key (id2,id) references t1 (id)) engine = innodb; -ERROR HY000: Can't create table 'test.t2' (errno: 150) +ERROR 42000: Incorrect foreign key definition for 't1_id_fk': Key reference and table reference don't match create table t2 (a int auto_increment primary key, b int, index(b), foreign key (b) references t1(id), unique(b)) engine=innodb; show create table t2; Table Create Table @@ -1632,30 +1665,6 @@ t2 CREATE TABLE `t2` ( CONSTRAINT `t2_ibfk_2` FOREIGN KEY (`b`) REFERENCES `t1` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 drop table t2, t1; -flush status; -show status like "binlog_cache_use"; -Variable_name Value -Binlog_cache_use 0 -show status like "binlog_cache_disk_use"; -Variable_name Value -Binlog_cache_disk_use 0 -create table t1 (a int) engine=innodb; -show status like "binlog_cache_use"; -Variable_name Value -Binlog_cache_use 1 -show status like "binlog_cache_disk_use"; -Variable_name Value -Binlog_cache_disk_use 1 -begin; -delete from t1; -commit; -show status like "binlog_cache_use"; -Variable_name Value -Binlog_cache_use 2 -show status like "binlog_cache_disk_use"; -Variable_name Value -Binlog_cache_disk_use 1 -drop table t1; create table t1 (c char(10), index (c,c)) engine=innodb; ERROR 42S21: Duplicate column name 'c' create table t1 (c1 char(10), c2 char(10), index (c1,c2,c1)) engine=innodb; @@ -1775,13 +1784,13 @@ Variable_name Value Innodb_page_size 16384 show status like "Innodb_rows_deleted"; Variable_name Value -Innodb_rows_deleted 2070 +Innodb_rows_deleted 72 show status like "Innodb_rows_inserted"; Variable_name Value -Innodb_rows_inserted 3083 +Innodb_rows_inserted 1088 show status like "Innodb_rows_updated"; Variable_name Value -Innodb_rows_updated 886 +Innodb_rows_updated 888 show status like "Innodb_row_lock_waits"; Variable_name Value Innodb_row_lock_waits 0 diff --git a/mysql-test/innodb.test b/mysql-test/innodb.test index 50d73ef5464..c806bc8593a 100644 --- a/mysql-test/innodb.test +++ b/mysql-test/innodb.test @@ -13,6 +13,11 @@ -- source include/not_embedded.inc -- source include/have_innodb.inc +-- source include/have_log_bin.inc + +# Disabling it temporarily for statement-based logging since some +# tests are not safe while binlog is on. +-- source include/have_binlog_format_mixed_or_row.inc # # Small basic test with ignore @@ -53,7 +58,7 @@ INSERT INTO t1 VALUES (1,0,0),(3,1,1),(4,1,1),(8,2,2),(9,2,2),(17,3,2),(22,4,2), update t1 set parent_id=parent_id+100; select * from t1 where parent_id=102; update t1 set id=id+1000; --- error ER_DUP_ENTRY_WITH_KEY_NAME,1022 +-- error ER_DUP_ENTRY,1022 update t1 set id=1024 where id=1009; select * from t1; update ignore t1 set id=id+1; # This will change all rows @@ -134,13 +139,13 @@ commit; select n, "after commit" from t1; commit; insert into t1 values (5); --- error ER_DUP_ENTRY_WITH_KEY_NAME +-- error ER_DUP_ENTRY insert into t1 values (4); commit; select n, "after commit" from t1; set autocommit=1; insert into t1 values (6); --- error ER_DUP_ENTRY_WITH_KEY_NAME +-- error ER_DUP_ENTRY insert into t1 values (4); select n from t1; set autocommit=0; @@ -214,7 +219,7 @@ drop table t1; CREATE TABLE t1 (id char(8) not null primary key, val int not null) engine=innodb; insert into t1 values ('pippo', 12); --- error ER_DUP_ENTRY_WITH_KEY_NAME +-- error ER_DUP_ENTRY insert into t1 values ('pippo', 12); # Gives error delete from t1; delete from t1 where id = 'pippo'; @@ -342,9 +347,9 @@ CREATE TABLE t1 ( insert into t1 (ggid,passwd) values ('test1','xxx'); insert into t1 (ggid,passwd) values ('test2','yyy'); --- error ER_DUP_ENTRY_WITH_KEY_NAME +-- error ER_DUP_ENTRY insert into t1 (ggid,passwd) values ('test2','this will fail'); --- error ER_DUP_ENTRY_WITH_KEY_NAME +-- error ER_DUP_ENTRY insert into t1 (ggid,id) values ('this will fail',1); select * from t1 where ggid='test1'; @@ -353,7 +358,7 @@ select * from t1 where id=2; replace into t1 (ggid,id) values ('this will work',1); replace into t1 (ggid,passwd) values ('test2','this will work'); --- error ER_DUP_ENTRY_WITH_KEY_NAME +-- error ER_DUP_ENTRY update t1 set id=100,ggid='test2' where id=1; select * from t1; select * from t1 where id=1; @@ -524,7 +529,7 @@ drop table t1; create table t1 (id int NOT NULL,id2 int NOT NULL,id3 int NOT NULL,dummy1 char(30),primary key (id,id2),index index_id3 (id3)) engine=innodb; insert into t1 values (0,0,0,'ABCDEFGHIJ'),(2,2,2,'BCDEFGHIJK'),(1,1,1,'CDEFGHIJKL'); LOCK TABLES t1 WRITE; ---error ER_DUP_ENTRY_WITH_KEY_NAME +--error ER_DUP_ENTRY insert into t1 values (99,1,2,'D'),(1,1,2,'D'); select id from t1; select id from t1; @@ -535,7 +540,7 @@ create table t1 (id int NOT NULL,id2 int NOT NULL,id3 int NOT NULL,dummy1 char(3 insert into t1 values (0,0,0,'ABCDEFGHIJ'),(2,2,2,'BCDEFGHIJK'),(1,1,1,'CDEFGHIJKL'); LOCK TABLES t1 WRITE; begin; ---error ER_DUP_ENTRY_WITH_KEY_NAME +--error ER_DUP_ENTRY insert into t1 values (99,1,2,'D'),(1,1,2,'D'); select id from t1; insert ignore into t1 values (100,1,2,'D'),(1,1,99,'D'); @@ -754,6 +759,45 @@ select * from t1; select * from t2; drop table t1,t2; +# +# Bug#27716 multi-update did partially and has not binlogged +# + +CREATE TABLE `t1` ( + `a` int(11) NOT NULL auto_increment, + `b` int(11) default NULL, + PRIMARY KEY (`a`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 ; + +CREATE TABLE `t2` ( + `a` int(11) NOT NULL auto_increment, + `b` int(11) default NULL, + PRIMARY KEY (`a`) +) ENGINE=INNODB DEFAULT CHARSET=latin1 ; + +# A. testing multi_update::send_eof() execution branch +insert into t1 values (1,1),(2,2); +insert into t2 values (1,1),(4,4); +reset master; +--error ER_DUP_ENTRY +UPDATE t2,t1 SET t2.a=t1.a+2; +# check +select * from t2 /* must be (3,1), (4,4) */; +show master status /* there must no UPDATE in binlog */; + +# B. testing multi_update::send_error() execution branch +delete from t1; +delete from t2; +insert into t1 values (1,2),(3,4),(4,4); +insert into t2 values (1,2),(3,4),(4,4); +reset master; +--error ER_DUP_ENTRY +UPDATE t2,t1 SET t2.a=t2.b where t2.a=t1.a; +show master status /* there must be no UPDATE query event */; + +# cleanup bug#27716 +drop table t1, t2; + # # Testing of IFNULL # @@ -1134,7 +1178,7 @@ drop table t2; # Clean up filename -- embedded server reports whole path without .frm, # regular server reports relative path with .frm (argh!) --replace_result \\ / $MYSQL_TEST_DIR . /var/master-data/ / t2.frm t2 ---error 1005 +--error ER_WRONG_FK_DEF create table t2 (id int(11) not null, id2 int(11) not null, constraint t1_id_fk foreign key (id2,id) references t1 (id)) engine = innodb; # bug#3749 @@ -1147,41 +1191,6 @@ show create table t2; drop table t2, t1; -# -# Let us test binlog_cache_use and binlog_cache_disk_use status vars. -# Actually this test has nothing to do with innodb per se, it just requires -# transactional table. -# -flush status; -show status like "binlog_cache_use"; -show status like "binlog_cache_disk_use"; - -create table t1 (a int) engine=innodb; - -# Now we are going to create transaction which is long enough so its -# transaction binlog will be flushed to disk... -let $1=2000; -disable_query_log; -begin; -while ($1) -{ - eval insert into t1 values( $1 ); - dec $1; -} -commit; -enable_query_log; -show status like "binlog_cache_use"; -show status like "binlog_cache_disk_use"; - -# Transaction which should not be flushed to disk and so should not -# increase binlog_cache_disk_use. -begin; -delete from t1; -commit; -show status like "binlog_cache_use"; -show status like "binlog_cache_disk_use"; -drop table t1; - # # Bug #6126: Duplicate columns in keys gives misleading error message # @@ -1408,7 +1417,7 @@ create table t1 (rowid int not null auto_increment, val int not null,primary key (rowid), unique(val)) engine=innodb; replace into t1 (val) values ('1'),('2'); replace into t1 (val) values ('1'),('2'); ---error ER_DUP_ENTRY_WITH_KEY_NAME +--error ER_DUP_ENTRY insert into t1 (val) values ('1'),('2'); select * from t1; drop table t1; @@ -1421,7 +1430,7 @@ create table t1 (a int not null auto_increment primary key, val int) engine=Inno insert into t1 (val) values (1); update t1 set a=2 where a=1; # We should get the following error because InnoDB does not update the counter ---error ER_DUP_ENTRY_WITH_KEY_NAME +--error ER_DUP_ENTRY insert into t1 (val) values (1); select * from t1; drop table t1; @@ -1744,13 +1753,13 @@ create table t3 (s1 varchar(2) binary,primary key (s1)) engine=innodb; create table t4 (s1 char(2) binary,primary key (s1)) engine=innodb; insert into t1 values (0x41),(0x4120),(0x4100); --- error ER_DUP_ENTRY_WITH_KEY_NAME +-- error ER_DUP_ENTRY insert into t2 values (0x41),(0x4120),(0x4100); insert into t2 values (0x41),(0x4120); --- error ER_DUP_ENTRY_WITH_KEY_NAME +-- error ER_DUP_ENTRY insert into t3 values (0x41),(0x4120),(0x4100); insert into t3 values (0x41),(0x4100); --- error ER_DUP_ENTRY_WITH_KEY_NAME +-- error ER_DUP_ENTRY insert into t4 values (0x41),(0x4120),(0x4100); insert into t4 values (0x41),(0x4100); select hex(s1) from t1; diff --git a/mysql-test/innodb_trx_weight.test b/mysql-test/innodb_trx_weight.test index 27698323ce0..b72eaad345f 100644 --- a/mysql-test/innodb_trx_weight.test +++ b/mysql-test/innodb_trx_weight.test @@ -10,12 +10,18 @@ SET storage_engine=InnoDB; -# we do not really care about what gets output-ed, we are only +# we do not really care about what gets printed, we are only # interested in getting the deadlock resolved according to our # expectations -- disable_query_log -- disable_result_log +# we want to use "-- eval statement1; statement2" which does not work with +# prepared statements. Because this test should not behave differently with +# or without prepared statements we disable them so the test does not fail +# if someone runs ./mysql-test-run.pl --ps-protocol +-- disable_ps_protocol + -- disable_warnings DROP TABLE IF EXISTS t1, t2, t3, t4, t5_nontrans; -- enable_warnings diff --git a/os/os0file.c b/os/os0file.c index 7b1f70e0a9b..99d81a9919b 100644 --- a/os/os0file.c +++ b/os/os0file.c @@ -456,10 +456,9 @@ os_file_handle_error_no_exit( #undef USE_FILE_LOCK #define USE_FILE_LOCK -#if defined(UNIV_HOTBACKUP) || defined(__WIN__) || defined(__FreeBSD__) || defined(__NETWARE__) +#if defined(UNIV_HOTBACKUP) || defined(__WIN__) || defined(__NETWARE__) /* InnoDB Hot Backup does not lock the data files. * On Windows, mandatory locking is used. - * On FreeBSD with LinuxThreads, advisory locking does not work properly. */ # undef USE_FILE_LOCK #endif diff --git a/plug.in b/plug.in index 3ae8a37ab22..6e26a7d3376 100644 --- a/plug.in +++ b/plug.in @@ -3,7 +3,6 @@ MYSQL_STORAGE_ENGINE(innobase, innodb, [InnoDB Storage Engine], MYSQL_PLUGIN_DIRECTORY(innobase, [storage/innobase]) MYSQL_PLUGIN_STATIC(innobase, [libinnobase.a]) MYSQL_PLUGIN_DYNAMIC(innobase, [ha_innodb.la]) -MYSQL_PLUGIN_DEPENDS_ON_MYSQL_INTERNALS(innobase, [handler/ha_innodb.cc]) MYSQL_PLUGIN_ACTIONS(innobase, [ AC_CHECK_LIB(rt, aio_read, [innodb_system_libs="-lrt"]) AC_SUBST(innodb_system_libs) @@ -41,5 +40,4 @@ MYSQL_PLUGIN_ACTIONS(innobase, [ CFLAGS="$CFLAGS -DUNIV_MUST_NOT_INLINE";; esac ]) -MYSQL_PLUGIN_DEPENDS_ON_MYSQL_INTERNALS(innobase, [handler/ha_innodb.cc]) diff --git a/row/row0mysql.c b/row/row0mysql.c index 7a2046baa32..8d69c246541 100644 --- a/row/row0mysql.c +++ b/row/row0mysql.c @@ -3143,6 +3143,8 @@ next_rec: dict_table_change_id_in_cache(table, new_id); } + /* MySQL calls ha_innobase::reset_auto_increment() which does + the same thing. */ dict_table_autoinc_initialize(table, 0); dict_update_statistics(table); diff --git a/row/row0sel.c b/row/row0sel.c index 9800ccdd485..311b4916060 100644 --- a/row/row0sel.c +++ b/row/row0sel.c @@ -4577,3 +4577,169 @@ row_search_check_if_query_cache_permitted( return(ret); } + +/*********************************************************************** +Read the AUTOINC column from the current row. */ +static +ib_longlong +row_search_autoinc_read_column( +/*===========================*/ + /* out: value read from the column */ + dict_index_t* index, /* in: index to read from */ + const rec_t* rec, /* in: current rec */ + ulint col_no, /* in: column number */ + ibool unsigned_type) /* in: signed or unsigned flag */ +{ + ulint len; + byte* ptr; + const byte* data; + ib_longlong value; + mem_heap_t* heap = NULL; + byte dest[sizeof(value)]; + ulint offsets_[REC_OFFS_NORMAL_SIZE]; + ulint* offsets = offsets_; + + *offsets_ = sizeof offsets_ / sizeof *offsets_; + + /* TODO: We have to cast away the const of rec for now. This needs + to be fixed later.*/ + offsets = rec_get_offsets( + (rec_t*) rec, index, offsets, ULINT_UNDEFINED, &heap); + + /* TODO: We have to cast away the const of rec for now. This needs + to be fixed later.*/ + data = rec_get_nth_field((rec_t*)rec, offsets, col_no, &len); + + ut_a(len != UNIV_SQL_NULL); + ut_a(len <= sizeof value); + + /* Convert integer data from Innobase to a little-endian format, + sign bit restored to normal */ + + for (ptr = dest + len; ptr != dest; ++data) { + --ptr; + *ptr = *data; + } + + if (!unsigned_type) { + dest[len - 1] ^= 128; + } + + /* The assumption here is that the AUTOINC value can't be negative.*/ + switch (len) { + case 8: + value = *(ib_longlong*) ptr; + break; + + case 4: + value = *(ib_uint32_t*) ptr; + break; + + case 2: + value = *(uint16 *) ptr; + break; + + case 1: + value = *ptr; + break; + + default: + ut_error; + } + + if (UNIV_LIKELY_NULL(heap)) { + mem_heap_free(heap); + } + + ut_a(value >= 0); + + return(value); +} + +/*********************************************************************** +Get the last row. */ +static +const rec_t* +row_search_autoinc_get_rec( +/*=======================*/ + /* out: current rec or NULL */ + btr_pcur_t* pcur, /* in: the current cursor */ + mtr_t* mtr) /* in: mini transaction */ +{ + do { + const rec_t* rec = btr_pcur_get_rec(pcur); + + if (page_rec_is_user_rec(rec)) { + return(rec); + } + } while (btr_pcur_move_to_prev(pcur, mtr)); + + return(NULL); +} + +/*********************************************************************** +Read the max AUTOINC value from an index. */ + +ulint +row_search_max_autoinc( +/*===================*/ + /* out: DB_SUCCESS if all OK else + error code, DB_RECORD_NOT_FOUND if + column name can't be found in index */ + dict_index_t* index, /* in: index to search */ + const char* col_name, /* in: name of autoinc column */ + ib_longlong* value) /* out: AUTOINC value read */ +{ + ulint i; + ulint n_cols; + dict_field_t* dfield = NULL; + ulint error = DB_SUCCESS; + + n_cols = dict_index_get_n_ordering_defined_by_user(index); + + /* Search the index for the AUTOINC column name */ + for (i = 0; i < n_cols; ++i) { + dfield = dict_index_get_nth_field(index, i); + + if (strcmp(col_name, dfield->name) == 0) { + break; + } + } + + *value = 0; + + /* Must find the AUTOINC column name */ + if (i < n_cols && dfield) { + mtr_t mtr; + btr_pcur_t pcur; + + mtr_start(&mtr); + + /* Open at the high/right end (FALSE), and INIT + cursor (TRUE) */ + btr_pcur_open_at_index_side( + FALSE, index, BTR_SEARCH_LEAF, &pcur, TRUE, &mtr); + + if (page_get_n_recs(btr_pcur_get_page(&pcur)) > 0) { + const rec_t* rec; + + rec = row_search_autoinc_get_rec(&pcur, &mtr); + + if (rec != NULL) { + ibool unsigned_type = ( + dfield->col->prtype & DATA_UNSIGNED); + + *value = row_search_autoinc_read_column( + index, rec, i, unsigned_type); + } + } + + btr_pcur_close(&pcur); + + mtr_commit(&mtr); + } else { + error = DB_RECORD_NOT_FOUND; + } + + return(error); +} diff --git a/sync/sync0rw.c b/sync/sync0rw.c index 34b45e2c1c3..4db780c8b3f 100644 --- a/sync/sync0rw.c +++ b/sync/sync0rw.c @@ -15,16 +15,34 @@ Created 9/11/1995 Heikki Tuuri #include "mem0mem.h" #include "srv0srv.h" +/* number of system calls made during shared latching */ ulint rw_s_system_call_count = 0; + +/* number of spin waits on rw-latches, +resulted during shared (read) locks */ ulint rw_s_spin_wait_count = 0; + +/* number of OS waits on rw-latches, +resulted during shared (read) locks */ ulint rw_s_os_wait_count = 0; +/* number of unlocks (that unlock shared locks), +set only when UNIV_SYNC_PERF_STAT is defined */ ulint rw_s_exit_count = 0; +/* number of system calls made during exclusive latching */ ulint rw_x_system_call_count = 0; + +/* number of spin waits on rw-latches, +resulted during exclusive (write) locks */ ulint rw_x_spin_wait_count = 0; + +/* number of OS waits on rw-latches, +resulted during exclusive (write) locks */ ulint rw_x_os_wait_count = 0; +/* number of unlocks (that unlock exclusive locks), +set only when UNIV_SYNC_PERF_STAT is defined */ ulint rw_x_exit_count = 0; /* The global list of rw-locks */ diff --git a/sync/sync0sync.c b/sync/sync0sync.c index 3324a6f8ba5..0179b89b61c 100644 --- a/sync/sync0sync.c +++ b/sync/sync0sync.c @@ -115,6 +115,7 @@ ulint mutex_system_call_count = 0; /* Number of spin waits on mutexes: for performance monitoring */ +/* round=one iteration of a spin loop */ ulint mutex_spin_round_count = 0; ulint mutex_spin_wait_count = 0; ulint mutex_os_wait_count = 0; diff --git a/trx/trx0trx.c b/trx/trx0trx.c index b2f9e515030..58bdf7a681e 100644 --- a/trx/trx0trx.c +++ b/trx/trx0trx.c @@ -182,6 +182,8 @@ trx_create( memset(&trx->xid, 0, sizeof(trx->xid)); trx->xid.formatID = -1; + trx->n_autoinc_rows = 0; + trx_reset_new_rec_lock_info(trx); return(trx);