diff --git a/ChangeLog b/ChangeLog index 03daaec0201..dd906230d19 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,63 @@ +2009-01-13 The InnoDB Team + + * include/hash0hash.h, include/dict0dict.ic, dict/dict0dict.c, + include/buf0buf.ic, buf/buf0buddy.c, trx/trx0i_s.c, + handler/ha_innodb.cc, handler/win_delay_loader.cc, + dict/dict0mem.c, ha/ha0storage.c, thr/thr0loc.c, fil/fil0fil.c: + Add the parameter ASSERTION to HASH_SEARCH() macro, and use it for + light validation of the traversed items in hash table lookups when + UNIV_DEBUG is enabled. + +2009-01-09 The InnoDB Team + + * include/buf0flu.h, include/buf0flu.ic, buf/buf0flu.c: + Remove unused code from the functions + buf_flush_insert_into_flush_list() and + buf_flush_insert_sorted_into_flush_list(). + +2009-01-09 The InnoDB Team + + * buf/buf0flu.c: Simplify the functions buf_flush_try_page() and + buf_flush_batch(). Add debug assertions and an explanation to + buf_flush_write_block_low(). + +2009-01-07 The InnoDB Team + + * row/row0merge.c: + Fix a bug in recovery when dropping temporary indexes + +2009-01-07 The InnoDB Team + + * handler/ha_innodb.cc, handler/ha_innodb.h, handler/handler0alter.cc: + Fix Bug#41680 calls to trx_allocate_for_mysql are not consistent + +2009-01-07 The InnoDB Team + + * mysql-test/innodb_bug41904.result, mysql-test/innodb_bug41904.test, + row/row0merge.c: + Fix Bug#41904 create unique index problem + +2009-01-02 The InnoDB Team + + * handler/ha_innodb.cc, include/srv0srv.h, srv/srv0srv.c, + srv/srv0start.c, mem/mem0pool.c, + mysql-test/innodb-use-sys-malloc-master.opt, + mysql-test/innodb-use-sys-malloc.result, + mysql-test/innodb-use-sys-malloc.test: + Implement the configuration parameter innodb_use_sys_malloc + (false by default), for disabling InnoDB's internal memory allocator + and using system malloc/free instead. + +2008-12-30 The InnoDB Team + + * btr/btr0btr.c: + When setting the PAGE_LEVEL of a compressed B-tree page from or to + 0, compress the page at the same time. This is necessary, because + the column information stored on the compressed page will differ + between leaf and non-leaf pages. Leaf pages are identified by + PAGE_LEVEL=0. This bug can make InnoDB crash when all rows of a + compressed table are deleted. + 2008-12-17 The InnoDB Team * include/row0upd.h, include/row0sel.h, pars/pars0pars.c, diff --git a/buf/buf0buddy.c b/buf/buf0buddy.c index e7ffd7e64b7..c6a2c2dacf9 100644 --- a/buf/buf0buddy.c +++ b/buf/buf0buddy.c @@ -172,6 +172,8 @@ buf_buddy_block_free( ut_a(!ut_align_offset(buf, UNIV_PAGE_SIZE)); HASH_SEARCH(hash, buf_pool->zip_hash, fold, buf_page_t*, bpage, + ut_ad(buf_page_get_state(bpage) == BUF_BLOCK_MEMORY + && bpage->in_zip_hash && !bpage->in_page_hash), ((buf_block_t*) bpage)->frame == buf); ut_a(bpage); ut_a(buf_page_get_state(bpage) == BUF_BLOCK_MEMORY); diff --git a/buf/buf0buf.c b/buf/buf0buf.c index a3436415cb8..c9976182358 100644 --- a/buf/buf0buf.c +++ b/buf/buf0buf.c @@ -245,7 +245,7 @@ static ulint buf_dbg_counter = 0; /* This is used to insert validation operations in excution in the debug version */ /** Flag to forbid the release of the buffer pool mutex. -Protected by buf_pool->mutex. */ +Protected by buf_pool_mutex. */ UNIV_INTERN ulint buf_pool_mutex_exit_forbidden = 0; #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ #ifdef UNIV_DEBUG diff --git a/buf/buf0flu.c b/buf/buf0flu.c index 5e1f2f3d29c..9585cb2238b 100644 --- a/buf/buf0flu.c +++ b/buf/buf0flu.c @@ -179,44 +179,27 @@ UNIV_INTERN void buf_flush_insert_into_flush_list( /*=============================*/ - buf_page_t* bpage) /* in: block which is modified */ + buf_block_t* block) /* in/out: block which is modified */ { ut_ad(buf_pool_mutex_own()); ut_ad((UT_LIST_GET_FIRST(buf_pool->flush_list) == NULL) || (UT_LIST_GET_FIRST(buf_pool->flush_list)->oldest_modification - <= bpage->oldest_modification)); + <= block->page.oldest_modification)); /* If we are in the recovery then we need to update the flush red-black tree as well. */ if (UNIV_LIKELY_NULL(buf_pool->flush_rbt)) { - buf_flush_insert_sorted_into_flush_list(bpage); + buf_flush_insert_sorted_into_flush_list(block); return; } - switch (buf_page_get_state(bpage)) { - case BUF_BLOCK_ZIP_PAGE: - mutex_enter(&buf_pool_zip_mutex); - buf_page_set_state(bpage, BUF_BLOCK_ZIP_DIRTY); - mutex_exit(&buf_pool_zip_mutex); - UT_LIST_REMOVE(list, buf_pool->zip_clean, bpage); - /* fall through */ - case BUF_BLOCK_ZIP_DIRTY: - case BUF_BLOCK_FILE_PAGE: - ut_ad(bpage->in_LRU_list); - ut_ad(bpage->in_page_hash); - ut_ad(!bpage->in_zip_hash); - ut_ad(!bpage->in_flush_list); - ut_d(bpage->in_flush_list = TRUE); - UT_LIST_ADD_FIRST(list, buf_pool->flush_list, bpage); - break; - case BUF_BLOCK_ZIP_FREE: - case BUF_BLOCK_NOT_USED: - case BUF_BLOCK_READY_FOR_USE: - case BUF_BLOCK_MEMORY: - case BUF_BLOCK_REMOVE_HASH: - ut_error; - return; - } + ut_ad(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE); + ut_ad(block->page.in_LRU_list); + ut_ad(block->page.in_page_hash); + ut_ad(!block->page.in_zip_hash); + ut_ad(!block->page.in_flush_list); + ut_d(block->page.in_flush_list = TRUE); + UT_LIST_ADD_FIRST(list, buf_pool->flush_list, &block->page); #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG ut_a(buf_flush_validate_low()); @@ -231,36 +214,19 @@ UNIV_INTERN void buf_flush_insert_sorted_into_flush_list( /*====================================*/ - buf_page_t* bpage) /* in: block which is modified */ + buf_block_t* block) /* in/out: block which is modified */ { buf_page_t* prev_b; buf_page_t* b; ut_ad(buf_pool_mutex_own()); + ut_ad(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE); - switch (buf_page_get_state(bpage)) { - case BUF_BLOCK_ZIP_PAGE: - mutex_enter(&buf_pool_zip_mutex); - buf_page_set_state(bpage, BUF_BLOCK_ZIP_DIRTY); - mutex_exit(&buf_pool_zip_mutex); - UT_LIST_REMOVE(list, buf_pool->zip_clean, bpage); - /* fall through */ - case BUF_BLOCK_ZIP_DIRTY: - case BUF_BLOCK_FILE_PAGE: - ut_ad(bpage->in_LRU_list); - ut_ad(bpage->in_page_hash); - ut_ad(!bpage->in_zip_hash); - ut_ad(!bpage->in_flush_list); - ut_d(bpage->in_flush_list = TRUE); - break; - case BUF_BLOCK_ZIP_FREE: - case BUF_BLOCK_NOT_USED: - case BUF_BLOCK_READY_FOR_USE: - case BUF_BLOCK_MEMORY: - case BUF_BLOCK_REMOVE_HASH: - ut_error; - return; - } + ut_ad(block->page.in_LRU_list); + ut_ad(block->page.in_page_hash); + ut_ad(!block->page.in_zip_hash); + ut_ad(!block->page.in_flush_list); + ut_d(block->page.in_flush_list = TRUE); prev_b = NULL; @@ -272,14 +238,14 @@ buf_flush_insert_sorted_into_flush_list( linear search in the else block. */ if (buf_pool->flush_rbt) { - prev_b = buf_flush_insert_in_flush_rbt(bpage); + prev_b = buf_flush_insert_in_flush_rbt(&block->page); } else { b = UT_LIST_GET_FIRST(buf_pool->flush_list); while (b && b->oldest_modification - > bpage->oldest_modification) { + > block->page.oldest_modification) { ut_ad(b->in_flush_list); prev_b = b; b = UT_LIST_GET_NEXT(list, b); @@ -287,10 +253,10 @@ buf_flush_insert_sorted_into_flush_list( } if (prev_b == NULL) { - UT_LIST_ADD_FIRST(list, buf_pool->flush_list, bpage); + UT_LIST_ADD_FIRST(list, buf_pool->flush_list, &block->page); } else { UT_LIST_INSERT_AFTER(list, buf_pool->flush_list, - prev_b, bpage); + prev_b, &block->page); } #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG @@ -345,6 +311,7 @@ buf_flush_ready_for_flush( ut_a(buf_page_in_file(bpage)); ut_ad(buf_pool_mutex_own()); ut_ad(mutex_own(buf_page_get_mutex(bpage))); + ut_ad(flush_type == BUF_FLUSH_LRU || BUF_FLUSH_LIST); if (bpage->oldest_modification != 0 && buf_page_get_io_fix(bpage) == BUF_IO_NONE) { @@ -910,6 +877,16 @@ buf_flush_write_block_low( ut_ad(buf_page_in_file(bpage)); + /* We are not holding buf_pool_mutex or block_mutex here. + Nevertheless, it is safe to access bpage, because it is + io_fixed and oldest_modification != 0. Thus, it cannot be + relocated in the buffer pool or removed from flush_list or + LRU_list. */ + ut_ad(!buf_pool_mutex_own()); + ut_ad(!mutex_own(buf_page_get_mutex(bpage))); + ut_ad(buf_page_get_io_fix(bpage) == BUF_IO_WRITE); + ut_ad(bpage->oldest_modification != 0); + #ifdef UNIV_IBUF_COUNT_DEBUG ut_a(ibuf_count_get(bpage->space, bpage->offset) == 0); #endif @@ -971,68 +948,55 @@ buf_flush_write_block_low( } /************************************************************************ -Writes a page asynchronously from the buffer buf_pool to a file, if it can be -found in the buf_pool and it is in a flushable state. NOTE: in simulated aio -we must call os_aio_simulated_wake_handler_threads after we have posted a batch -of writes! */ +Writes a flushable page asynchronously from the buffer pool to a file. +NOTE: in simulated aio we must call +os_aio_simulated_wake_handler_threads after we have posted a batch of +writes! NOTE: buf_pool_mutex and buf_page_get_mutex(bpage) must be +held upon entering this function, and they will be released by this +function. */ static -ulint -buf_flush_try_page( -/*===============*/ - /* out: 1 if a page was - flushed, 0 otherwise */ - ulint space, /* in: space id */ - ulint offset, /* in: page offset */ - enum buf_flush flush_type) /* in: BUF_FLUSH_LRU, BUF_FLUSH_LIST, - or BUF_FLUSH_SINGLE_PAGE */ +void +buf_flush_page( +/*===========*/ + buf_page_t* bpage, /* in: buffer control block */ + enum buf_flush flush_type) /* in: BUF_FLUSH_LRU + or BUF_FLUSH_LIST */ { - buf_page_t* bpage; mutex_t* block_mutex; - ibool locked; + ibool is_uncompressed; - ut_ad(flush_type == BUF_FLUSH_LRU || flush_type == BUF_FLUSH_LIST - || flush_type == BUF_FLUSH_SINGLE_PAGE); + ut_ad(flush_type == BUF_FLUSH_LRU || flush_type == BUF_FLUSH_LIST); + ut_ad(buf_pool_mutex_own()); + ut_ad(buf_page_in_file(bpage)); - buf_pool_mutex_enter(); - - bpage = buf_page_hash_get(space, offset); - - if (!bpage) { - buf_pool_mutex_exit(); - return(0); - } - - ut_a(buf_page_in_file(bpage)); block_mutex = buf_page_get_mutex(bpage); + ut_ad(mutex_own(block_mutex)); - mutex_enter(block_mutex); + ut_ad(buf_flush_ready_for_flush(bpage, flush_type)); - if (!buf_flush_ready_for_flush(bpage, flush_type)) { - mutex_exit(block_mutex); - buf_pool_mutex_exit(); - return(0); + buf_page_set_io_fix(bpage, BUF_IO_WRITE); + + buf_page_set_flush_type(bpage, flush_type); + + if (buf_pool->n_flush[flush_type] == 0) { + + os_event_reset(buf_pool->no_flush[flush_type]); } + buf_pool->n_flush[flush_type]++; + + is_uncompressed = (buf_page_get_state(bpage) == BUF_BLOCK_FILE_PAGE); + ut_ad(is_uncompressed == (block_mutex != &buf_pool_zip_mutex)); + switch (flush_type) { + ibool is_s_latched; case BUF_FLUSH_LIST: - buf_page_set_io_fix(bpage, BUF_IO_WRITE); - - buf_page_set_flush_type(bpage, flush_type); - - if (buf_pool->n_flush[flush_type] == 0) { - - os_event_reset(buf_pool->no_flush[flush_type]); - } - - buf_pool->n_flush[flush_type]++; - /* If the simulated aio thread is not running, we must not wait for any latch, as we may end up in a deadlock: if buf_fix_count == 0, then we know we need not wait */ - locked = bpage->buf_fix_count == 0; - if (locked - && buf_page_get_state(bpage) == BUF_BLOCK_FILE_PAGE) { + is_s_latched = (bpage->buf_fix_count == 0); + if (is_s_latched && is_uncompressed) { rw_lock_s_lock_gen(&((buf_block_t*) bpage)->lock, BUF_IO_WRITE); } @@ -1040,10 +1004,16 @@ buf_flush_try_page( mutex_exit(block_mutex); buf_pool_mutex_exit(); - if (!locked) { + /* Even though bpage is not protected by any mutex at + this point, it is safe to access bpage, because it is + io_fixed and oldest_modification != 0. Thus, it + cannot be relocated in the buffer pool or removed from + flush_list or LRU_list. */ + + if (!is_s_latched) { buf_flush_buffered_writes(); - if (buf_page_get_state(bpage) == BUF_BLOCK_FILE_PAGE) { + if (is_uncompressed) { rw_lock_s_lock_gen(&((buf_block_t*) bpage) ->lock, BUF_IO_WRITE); } @@ -1056,22 +1026,10 @@ buf_flush_try_page( Because any thread may call the LRU flush, even when owning locks on pages, to avoid deadlocks, we must make sure that the s-lock is acquired on the page without waiting: this is - accomplished because in the if-condition above we require - the page not to be bufferfixed (in function - ..._ready_for_flush). */ + accomplished because buf_flush_ready_for_flush() must hold, + and that requires the page not to be bufferfixed. */ - buf_page_set_io_fix(bpage, BUF_IO_WRITE); - - buf_page_set_flush_type(bpage, flush_type); - - if (buf_pool->n_flush[flush_type] == 0) { - - os_event_reset(buf_pool->no_flush[flush_type]); - } - - buf_pool->n_flush[flush_type]++; - - if (buf_page_get_state(bpage) == BUF_BLOCK_FILE_PAGE) { + if (is_uncompressed) { rw_lock_s_lock_gen(&((buf_block_t*) bpage)->lock, BUF_IO_WRITE); } @@ -1084,31 +1042,15 @@ buf_flush_try_page( buf_pool_mutex_exit(); break; - case BUF_FLUSH_SINGLE_PAGE: - buf_page_set_io_fix(bpage, BUF_IO_WRITE); - - buf_page_set_flush_type(bpage, flush_type); - - if (buf_pool->n_flush[flush_type] == 0) { - - os_event_reset(buf_pool->no_flush[flush_type]); - } - - buf_pool->n_flush[flush_type]++; - - mutex_exit(block_mutex); - buf_pool_mutex_exit(); - - if (buf_page_get_state(bpage) == BUF_BLOCK_FILE_PAGE) { - rw_lock_s_lock_gen(&((buf_block_t*) bpage)->lock, - BUF_IO_WRITE); - } - break; - default: ut_error; } + /* Even though bpage is not protected by any mutex at this + point, it is safe to access bpage, because it is io_fixed and + oldest_modification != 0. Thus, it cannot be relocated in the + buffer pool or removed from flush_list or LRU_list. */ + #ifdef UNIV_DEBUG if (buf_debug_prints) { fprintf(stderr, @@ -1117,8 +1059,6 @@ buf_flush_try_page( } #endif /* UNIV_DEBUG */ buf_flush_write_block_low(bpage); - - return(1); } /*************************************************************** @@ -1168,21 +1108,20 @@ buf_flush_try_neighbors( for (i = low; i < high; i++) { bpage = buf_page_hash_get(space, i); - ut_a(!bpage || buf_page_in_file(bpage)); if (!bpage) { continue; + } - } else if (flush_type == BUF_FLUSH_LRU && i != offset - && !buf_page_is_old(bpage)) { + ut_a(buf_page_in_file(bpage)); - /* We avoid flushing 'non-old' blocks in an LRU flush, - because the flushed blocks are soon freed */ - - continue; - } else { + /* We avoid flushing 'non-old' blocks in an LRU flush, + because the flushed blocks are soon freed */ + if (flush_type != BUF_FLUSH_LRU + || i == offset + || buf_page_is_old(bpage)) { mutex_t* block_mutex = buf_page_get_mutex(bpage); mutex_enter(block_mutex); @@ -1197,18 +1136,9 @@ buf_flush_try_neighbors( flush the doublewrite buffer before we start waiting. */ - buf_pool_mutex_exit(); - - mutex_exit(block_mutex); - - /* Note: as we release the buf_pool mutex - above, in buf_flush_try_page we cannot be sure - the page is still in a flushable state: - therefore we check it again inside that - function. */ - - count += buf_flush_try_page(space, i, - flush_type); + buf_flush_page(bpage, flush_type); + ut_ad(!mutex_own(block_mutex)); + count++; buf_pool_mutex_enter(); } else { @@ -1308,19 +1238,20 @@ flush_next: function a pointer to a block in the list! */ do { - mutex_t* block_mutex = buf_page_get_mutex(bpage); + mutex_t*block_mutex = buf_page_get_mutex(bpage); + ibool ready; ut_a(buf_page_in_file(bpage)); mutex_enter(block_mutex); + ready = buf_flush_ready_for_flush(bpage, flush_type); + mutex_exit(block_mutex); - if (buf_flush_ready_for_flush(bpage, flush_type)) { - + if (ready) { space = buf_page_get_space(bpage); offset = buf_page_get_page_no(bpage); buf_pool_mutex_exit(); - mutex_exit(block_mutex); old_page_count = page_count; @@ -1336,15 +1267,10 @@ flush_next: goto flush_next; } else if (flush_type == BUF_FLUSH_LRU) { - - mutex_exit(block_mutex); - bpage = UT_LIST_GET_PREV(LRU, bpage); } else { ut_ad(flush_type == BUF_FLUSH_LIST); - mutex_exit(block_mutex); - bpage = UT_LIST_GET_PREV(list, bpage); ut_ad(!bpage || bpage->in_flush_list); } @@ -1357,8 +1283,7 @@ flush_next: buf_pool->init_flush[flush_type] = FALSE; - if ((buf_pool->n_flush[flush_type] == 0) - && (buf_pool->init_flush[flush_type] == FALSE)) { + if (buf_pool->n_flush[flush_type] == 0) { /* The running flush batch has ended */ diff --git a/buf/buf0lru.c b/buf/buf0lru.c index e495cc51a31..b90433a7087 100644 --- a/buf/buf0lru.c +++ b/buf/buf0lru.c @@ -251,7 +251,7 @@ scan_again: if (num_entries < BUF_LRU_DROP_SEARCH_HASH_SIZE) { goto next_page; } - /* Array full. We release the buf_pool->mutex to + /* Array full. We release the buf_pool_mutex to obey the latching order. */ buf_pool_mutex_exit(); diff --git a/dict/dict0dict.c b/dict/dict0dict.c index 0f5eef60059..000e3d1017f 100644 --- a/dict/dict0dict.c +++ b/dict/dict0dict.c @@ -751,18 +751,34 @@ dict_table_add_to_cache( { dict_table_t* table2; HASH_SEARCH(name_hash, dict_sys->table_hash, fold, - dict_table_t*, table2, - (ut_strcmp(table2->name, table->name) == 0)); + dict_table_t*, table2, ut_ad(table2->cached), + ut_strcmp(table2->name, table->name) == 0); ut_a(table2 == NULL); + +#ifdef UNIV_DEBUG + /* Look for the same table pointer with a different name */ + HASH_SEARCH_ALL(name_hash, dict_sys->table_hash, + dict_table_t*, table2, ut_ad(table2->cached), + table2 == table); + ut_ad(table2 == NULL); +#endif /* UNIV_DEBUG */ } /* Look for a table with the same id: error if such exists */ { dict_table_t* table2; HASH_SEARCH(id_hash, dict_sys->table_id_hash, id_fold, - dict_table_t*, table2, - (ut_dulint_cmp(table2->id, table->id) == 0)); + dict_table_t*, table2, ut_ad(table2->cached), + ut_dulint_cmp(table2->id, table->id) == 0); ut_a(table2 == NULL); + +#ifdef UNIV_DEBUG + /* Look for the same table pointer with a different id */ + HASH_SEARCH_ALL(id_hash, dict_sys->table_id_hash, + dict_table_t*, table2, ut_ad(table2->cached), + table2 == table); + ut_ad(table2 == NULL); +#endif /* UNIV_DEBUG */ } /* Add table to hash table of tables */ @@ -844,7 +860,7 @@ dict_table_rename_in_cache( { dict_table_t* table2; HASH_SEARCH(name_hash, dict_sys->table_hash, fold, - dict_table_t*, table2, + dict_table_t*, table2, ut_ad(table2->cached), (ut_strcmp(table2->name, new_name) == 0)); if (UNIV_LIKELY_NULL(table2)) { ut_print_timestamp(stderr); diff --git a/dict/dict0load.c b/dict/dict0load.c index e51b1376be3..521c9d656ec 100644 --- a/dict/dict0load.c +++ b/dict/dict0load.c @@ -949,11 +949,11 @@ err_exit: of the error condition, since the user may want to dump data from the clustered index. However we load the foreign key information only if all indexes were loaded. */ - if (err != DB_SUCCESS && !srv_force_recovery) { - dict_mem_table_free(table); - table = NULL; - } else if (err == DB_SUCCESS) { + if (err == DB_SUCCESS) { err = dict_load_foreigns(table->name, TRUE); + } else if (!srv_force_recovery) { + dict_table_remove_from_cache(table); + table = NULL; } # if 0 if (err != DB_SUCCESS && table != NULL) { diff --git a/dict/dict0mem.c b/dict/dict0mem.c index 15372d8e261..dff2ad3f552 100644 --- a/dict/dict0mem.c +++ b/dict/dict0mem.c @@ -84,6 +84,7 @@ dict_mem_table_free( { ut_ad(table); ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); + ut_d(table->cached = FALSE); mutex_free(&(table->autoinc_mutex)); mem_heap_free(table->heap); diff --git a/fil/fil0fil.c b/fil/fil0fil.c index 107a81b85a8..d413cb7d31d 100644 --- a/fil/fil0fil.c +++ b/fil/fil0fil.c @@ -363,7 +363,9 @@ fil_space_get_by_id( ut_ad(mutex_own(&fil_system->mutex)); HASH_SEARCH(hash, fil_system->spaces, id, - fil_space_t*, space, space->id == id); + fil_space_t*, space, + ut_ad(space->magic_n == FIL_SPACE_MAGIC_N), + space->id == id); return(space); } @@ -384,7 +386,9 @@ fil_space_get_by_name( fold = ut_fold_string(name); HASH_SEARCH(name_hash, fil_system->name_hash, fold, - fil_space_t*, space, !strcmp(name, space->name)); + fil_space_t*, space, + ut_ad(space->magic_n == FIL_SPACE_MAGIC_N), + !strcmp(name, space->name)); return(space); } diff --git a/ha/ha0storage.c b/ha/ha0storage.c index ca2e644954c..046ab9b9346 100644 --- a/ha/ha0storage.c +++ b/ha/ha0storage.c @@ -45,6 +45,7 @@ ha_storage_get( fold, /* key */ ha_storage_node_t*, /* type of node->next */ node, /* auxiliary variable */ + , /* assertion */ IS_FOUND); /* search criteria */ if (node == NULL) { diff --git a/handler/ha_innodb.cc b/handler/ha_innodb.cc index 829be82b821..1ce0e0e30a4 100644 --- a/handler/ha_innodb.cc +++ b/handler/ha_innodb.cc @@ -1211,6 +1211,53 @@ innobase_next_autoinc( return(next_value); } +/************************************************************************* +Initializes some fields in an InnoDB transaction object. */ +static +void +innobase_trx_init( +/*==============*/ + THD* thd, /* in: user thread handle */ + trx_t* trx) /* in/out: InnoDB transaction handle */ +{ + DBUG_ENTER("innobase_trx_init"); + DBUG_ASSERT(EQ_CURRENT_THD(thd)); + DBUG_ASSERT(thd == trx->mysql_thd); + + trx->check_foreigns = !thd_test_options( + thd, OPTION_NO_FOREIGN_KEY_CHECKS); + + trx->check_unique_secondary = !thd_test_options( + thd, OPTION_RELAXED_UNIQUE_CHECKS); + + DBUG_VOID_RETURN; +} + +/************************************************************************* +Allocates an InnoDB transaction for a MySQL handler object. */ +extern "C" UNIV_INTERN +trx_t* +innobase_trx_allocate( +/*==================*/ + /* out: InnoDB transaction handle */ + THD* thd) /* in: user thread handle */ +{ + trx_t* trx; + + DBUG_ENTER("innobase_trx_allocate"); + DBUG_ASSERT(thd != NULL); + DBUG_ASSERT(EQ_CURRENT_THD(thd)); + + trx = trx_allocate_for_mysql(); + + trx->mysql_thd = thd; + trx->mysql_query_str = thd_query(thd); + + innobase_trx_init(thd, trx); + + DBUG_RETURN(trx); +} + /************************************************************************* Gets the InnoDB transaction handle for a MySQL handler object, creates an InnoDB transaction struct if the corresponding MySQL thread struct still @@ -1227,31 +1274,13 @@ check_trx_exists( ut_ad(EQ_CURRENT_THD(thd)); if (trx == NULL) { - DBUG_ASSERT(thd != NULL); - trx = trx_allocate_for_mysql(); - - trx->mysql_thd = thd; - trx->mysql_query_str = thd_query(thd); - - } else { - if (trx->magic_n != TRX_MAGIC_N) { - mem_analyze_corruption(trx); - - ut_error; - } + trx = innobase_trx_allocate(thd); + } else if (UNIV_UNLIKELY(trx->magic_n != TRX_MAGIC_N)) { + mem_analyze_corruption(trx); + ut_error; } - if (thd_test_options(thd, OPTION_NO_FOREIGN_KEY_CHECKS)) { - trx->check_foreigns = FALSE; - } else { - trx->check_foreigns = TRUE; - } - - if (thd_test_options(thd, OPTION_RELAXED_UNIQUE_CHECKS)) { - trx->check_unique_secondary = FALSE; - } else { - trx->check_unique_secondary = TRUE; - } + innobase_trx_init(thd, trx); return(trx); } @@ -4621,7 +4650,8 @@ ha_innobase::unlock_row(void) switch (prebuilt->row_read_type) { case ROW_READ_WITH_LOCKS: if (!srv_locks_unsafe_for_binlog - || prebuilt->trx->isolation_level == TRX_ISO_READ_COMMITTED) { + && prebuilt->trx->isolation_level + != TRX_ISO_READ_COMMITTED) { break; } /* fall through */ @@ -5480,9 +5510,19 @@ create_table_def( charset_no = (ulint)field->charset()->number; - ut_a(charset_no < 256); /* in data0type.h we assume - that the number fits in one - byte */ + if (UNIV_UNLIKELY(charset_no >= 256)) { + /* in data0type.h we assume that the + number fits in one byte in prtype */ + push_warning_printf( + (THD*) trx->mysql_thd, + MYSQL_ERROR::WARN_LEVEL_ERROR, + ER_CANT_CREATE_TABLE, + "In InnoDB, charset-collation codes" + " must be below 256." + " Unsupported code %lu.", + (ulong) charset_no); + DBUG_RETURN(ER_CANT_CREATE_TABLE); + } } ut_a(field->type() < 256); /* we assume in dtype_form_prtype() @@ -5927,18 +5967,7 @@ ha_innobase::create( trx_search_latch_release_if_reserved(parent_trx); - trx = trx_allocate_for_mysql(); - - trx->mysql_thd = thd; - trx->mysql_query_str = thd_query(thd); - - if (thd_test_options(thd, OPTION_NO_FOREIGN_KEY_CHECKS)) { - trx->check_foreigns = FALSE; - } - - if (thd_test_options(thd, OPTION_RELAXED_UNIQUE_CHECKS)) { - trx->check_unique_secondary = FALSE; - } + trx = innobase_trx_allocate(thd); if (lower_case_table_names) { srv_lower_case_table_names = TRUE; @@ -6344,25 +6373,14 @@ ha_innobase::delete_table( trx_search_latch_release_if_reserved(parent_trx); + trx = innobase_trx_allocate(thd); + if (lower_case_table_names) { srv_lower_case_table_names = TRUE; } else { srv_lower_case_table_names = FALSE; } - trx = trx_allocate_for_mysql(); - - trx->mysql_thd = thd; - trx->mysql_query_str = thd_query(thd); - - if (thd_test_options(thd, OPTION_NO_FOREIGN_KEY_CHECKS)) { - trx->check_foreigns = FALSE; - } - - if (thd_test_options(thd, OPTION_RELAXED_UNIQUE_CHECKS)) { - trx->check_unique_secondary = FALSE; - } - name_len = strlen(name); ut_a(name_len < 1000); @@ -6445,19 +6463,14 @@ innobase_drop_database( #ifdef __WIN__ innobase_casedn_str(namebuf); #endif +#if defined __WIN__ && !defined MYSQL_SERVER + /* In the Windows plugin, thd = current_thd is always NULL */ trx = trx_allocate_for_mysql(); - trx->mysql_thd = thd; - if (thd) { - trx->mysql_query_str = thd_query(thd); - - if (thd_test_options(thd, OPTION_NO_FOREIGN_KEY_CHECKS)) { - trx->check_foreigns = FALSE; - } - } else { - /* In the Windows plugin, thd = current_thd is always NULL */ - trx->mysql_query_str = NULL; - } - + trx->mysql_thd = NULL; + trx->mysql_query_str = NULL; +#else + trx = innobase_trx_allocate(thd); +#endif error = row_drop_database_for_mysql(namebuf, trx); my_free(namebuf, MYF(0)); @@ -6567,13 +6580,7 @@ ha_innobase::rename_table( trx_search_latch_release_if_reserved(parent_trx); - trx = trx_allocate_for_mysql(); - trx->mysql_thd = thd; - trx->mysql_query_str = thd_query(thd); - - if (thd_test_options(thd, OPTION_NO_FOREIGN_KEY_CHECKS)) { - trx->check_foreigns = FALSE; - } + trx = innobase_trx_allocate(thd); error = innobase_rename_table(trx, from, to, TRUE); @@ -8164,6 +8171,7 @@ static INNOBASE_SHARE* get_share(const char* table_name) HASH_SEARCH(table_name_hash, innobase_open_tables, fold, INNOBASE_SHARE*, share, + ut_ad(share->use_count > 0), !my_strnncoll(system_charset_info, share->table_name, share->table_name_length, @@ -8205,6 +8213,7 @@ static void free_share(INNOBASE_SHARE* share) HASH_SEARCH(table_name_hash, innobase_open_tables, fold, INNOBASE_SHARE*, share2, + ut_ad(share->use_count > 0), !my_strnncoll(system_charset_info, share->table_name, share->table_name_length, @@ -9580,6 +9589,11 @@ static MYSQL_SYSVAR_STR(version, innodb_version_str, PLUGIN_VAR_NOCMDOPT | PLUGIN_VAR_READONLY, "InnoDB version", NULL, NULL, INNODB_VERSION_STR); +static MYSQL_SYSVAR_BOOL(use_sys_malloc, srv_use_sys_malloc, + PLUGIN_VAR_NOCMDARG | PLUGIN_VAR_READONLY, + "Use OS memory allocator instead of InnoDB's internal memory allocator", + NULL, NULL, FALSE); + static MYSQL_SYSVAR_BOOL(use_native_aio, srv_use_native_aio, PLUGIN_VAR_NOCMDARG | PLUGIN_VAR_READONLY, "Use native AIO if supported on this platform.", @@ -9631,6 +9645,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= { MYSQL_SYSVAR(thread_sleep_delay), MYSQL_SYSVAR(autoinc_lock_mode), MYSQL_SYSVAR(version), + MYSQL_SYSVAR(use_sys_malloc), MYSQL_SYSVAR(use_native_aio), NULL }; diff --git a/handler/ha_innodb.h b/handler/ha_innodb.h index 9b18af7feaa..e403eff6ddb 100644 --- a/handler/ha_innodb.h +++ b/handler/ha_innodb.h @@ -269,3 +269,12 @@ convert_error_code_to_mysql( int error, /* in: InnoDB error code */ ulint flags, /* in: InnoDB table flags, or 0 */ MYSQL_THD thd); /* in: user thread handle or NULL */ + +/************************************************************************* +Allocates an InnoDB transaction for a MySQL handler object. */ +extern "C" +trx_t* +innobase_trx_allocate( +/*==================*/ + /* out: InnoDB transaction handle */ + MYSQL_THD thd); /* in: user thread handle */ diff --git a/handler/handler0alter.cc b/handler/handler0alter.cc index 4ffacb8d5e8..9691e10ba2d 100644 --- a/handler/handler0alter.cc +++ b/handler/handler0alter.cc @@ -633,12 +633,9 @@ ha_innobase::add_index( /* Create a background transaction for the operations on the data dictionary tables. */ - trx = trx_allocate_for_mysql(); + trx = innobase_trx_allocate(user_thd); trx_start_if_not_started(trx); - trx->mysql_thd = user_thd; - trx->mysql_query_str = thd_query(user_thd); - innodb_table = indexed_table = dict_table_get(prebuilt->table->name, FALSE); @@ -1125,12 +1122,9 @@ ha_innobase::final_drop_index( /* Create a background transaction for the operations on the data dictionary tables. */ - trx = trx_allocate_for_mysql(); + trx = innobase_trx_allocate(user_thd); trx_start_if_not_started(trx); - trx->mysql_thd = user_thd; - trx->mysql_query_str = thd_query(user_thd); - /* Flag this transaction as a dictionary operation, so that the data dictionary will be locked in crash recovery. */ trx_set_dict_operation(trx, TRX_DICT_OP_INDEX); diff --git a/handler/win_delay_loader.cc b/handler/win_delay_loader.cc index 8b69f4a6e51..da1714c92d0 100644 --- a/handler/win_delay_loader.cc +++ b/handler/win_delay_loader.cc @@ -453,6 +453,7 @@ wdl_get_procaddr_from_map( map_fold, map_hash_chain_t*, hash_chain, + , (ut_strcmp(hash_chain->symbol, import_proc) == 0)); if (hash_chain == NULL) { @@ -472,6 +473,7 @@ wdl_get_procaddr_from_map( map_fold, map_hash_chain_t*, hash_chain, + , (ut_strcmp(hash_chain->symbol, import_proc) == 0)); if (hash_chain == NULL) { @@ -515,6 +517,7 @@ wdl_get_varaddr_from_map( map_fold, map_hash_chain_t*, hash_chain, + , (ut_strcmp(hash_chain->symbol, import_variable) == 0)); if (hash_chain == NULL) { @@ -534,6 +537,7 @@ wdl_get_varaddr_from_map( map_fold, map_hash_chain_t*, hash_chain, + , (ut_strcmp(hash_chain->symbol, import_variable) == 0)); if (hash_chain == NULL) { diff --git a/include/buf0buf.h b/include/buf0buf.h index 451c9d8e96d..e7bf6faed1d 100644 --- a/include/buf0buf.h +++ b/include/buf0buf.h @@ -1010,8 +1010,10 @@ struct buf_page_struct{ since they can be stored in the same machine word. Some of them are additionally protected by buf_pool_mutex. */ - unsigned space:32; /* tablespace id */ - unsigned offset:32; /* page number */ + unsigned space:32; /* tablespace id; also protected + by buf_pool_mutex. */ + unsigned offset:32; /* page number; also protected + by buf_pool_mutex. */ unsigned state:3; /* state of the control block (@see enum buf_page_state); also @@ -1080,7 +1082,8 @@ struct buf_page_struct{ not yet been flushed on disk; zero if all modifications are on disk */ - /* 3. LRU replacement algorithm fields; protected by buf_pool_mutex */ + /* 3. LRU replacement algorithm fields; protected by + buf_pool_mutex only (not buf_pool_zip_mutex or block->mutex) */ UT_LIST_NODE_T(buf_page_t) LRU; /* node of the LRU list */ diff --git a/include/buf0buf.ic b/include/buf0buf.ic index 6758599eff5..3f71b8feea2 100644 --- a/include/buf0buf.ic +++ b/include/buf0buf.ic @@ -20,6 +20,7 @@ buf_page_get_freed_page_clock( /* out: freed_page_clock */ const buf_page_t* bpage) /* in: block */ { + /* This is sometimes read without holding buf_pool_mutex. */ return(bpage->freed_page_clock); } @@ -89,6 +90,9 @@ buf_pool_get_oldest_modification(void) buf_pool_mutex_exit(); + /* The returned answer may be out of date: the flush_list can + change after the mutex has been released. */ + return(lsn); } @@ -261,6 +265,7 @@ buf_page_get_LRU_position( const buf_page_t* bpage) /* in: control block */ { ut_ad(buf_page_in_file(bpage)); + ut_ad(buf_pool_mutex_own()); return(bpage->LRU_position); } @@ -429,6 +434,7 @@ buf_page_is_old( const buf_page_t* bpage) /* in: control block */ { ut_ad(buf_page_in_file(bpage)); + ut_ad(buf_pool_mutex_own()); return(bpage->old); } @@ -805,8 +811,9 @@ buf_page_get_newest_modification( page frame */ { ib_uint64_t lsn; + mutex_t* block_mutex = buf_page_get_mutex(bpage); - buf_pool_mutex_enter(); + mutex_enter(block_mutex); if (buf_page_in_file(bpage)) { lsn = bpage->newest_modification; @@ -814,7 +821,7 @@ buf_page_get_newest_modification( lsn = 0; } - buf_pool_mutex_exit(); + mutex_exit(block_mutex); return(lsn); } @@ -921,6 +928,8 @@ buf_page_hash_get( fold = buf_page_address_fold(space, offset); HASH_SEARCH(hash, buf_pool->page_hash, fold, buf_page_t*, bpage, + ut_ad(bpage->in_page_hash && !bpage->in_zip_hash + && buf_page_in_file(bpage)), bpage->space == space && bpage->offset == offset); if (bpage) { ut_a(buf_page_in_file(bpage)); @@ -1040,6 +1049,10 @@ buf_page_release( #endif block->page.buf_fix_count--; + /* Dirty blocks should be in the flush list. */ + ut_ad(!block->page.oldest_modification + || block->page.in_flush_list); + mutex_exit(&block->mutex); if (rw_latch == RW_S_LATCH) { diff --git a/include/buf0flu.h b/include/buf0flu.h index da02f3c915b..e21ada3bb9f 100644 --- a/include/buf0flu.h +++ b/include/buf0flu.h @@ -14,13 +14,6 @@ Created 11/5/1995 Heikki Tuuri #include "ut0byte.h" #include "mtr0types.h" -/************************************************************************ -Inserts a modified block into the flush list. */ -UNIV_INTERN -void -buf_flush_insert_into_flush_list( -/*=============================*/ - buf_page_t* bpage); /* in: block which is modified */ /************************************************************************ Remove a block from the flush list of modified blocks. */ UNIV_INTERN diff --git a/include/buf0flu.ic b/include/buf0flu.ic index d02c07ec5aa..fa056a52ae9 100644 --- a/include/buf0flu.ic +++ b/include/buf0flu.ic @@ -9,6 +9,13 @@ Created 11/5/1995 Heikki Tuuri #include "buf0buf.h" #include "mtr0mtr.h" +/************************************************************************ +Inserts a modified block into the flush list. */ +UNIV_INTERN +void +buf_flush_insert_into_flush_list( +/*=============================*/ + buf_block_t* block); /* in/out: block which is modified */ /************************************************************************ Inserts a modified block into the flush list in the right sorted position. This function is used by recovery, because there the modifications do not @@ -17,7 +24,7 @@ UNIV_INTERN void buf_flush_insert_sorted_into_flush_list( /*====================================*/ - buf_page_t* bpage); /* in: block which is modified */ + buf_block_t* block); /* in/out: block which is modified */ /************************************************************************ This function should be called at a mini-transaction commit, if a page was @@ -49,7 +56,7 @@ buf_flush_note_modification( block->page.oldest_modification = mtr->start_lsn; ut_ad(block->page.oldest_modification != 0); - buf_flush_insert_into_flush_list(&block->page); + buf_flush_insert_into_flush_list(block); } else { ut_ad(block->page.oldest_modification <= mtr->start_lsn); } @@ -88,7 +95,7 @@ buf_flush_recv_note_modification( ut_ad(block->page.oldest_modification != 0); - buf_flush_insert_sorted_into_flush_list(&block->page); + buf_flush_insert_sorted_into_flush_list(block); } else { ut_ad(block->page.oldest_modification <= start_lsn); } diff --git a/include/dict0dict.ic b/include/dict0dict.ic index e94a96da872..1bed2a538ee 100644 --- a/include/dict0dict.ic +++ b/include/dict0dict.ic @@ -720,7 +720,8 @@ dict_table_check_if_in_cache_low( table_fold = ut_fold_string(table_name); HASH_SEARCH(name_hash, dict_sys->table_hash, table_fold, - dict_table_t*, table, !strcmp(table->name, table_name)); + dict_table_t*, table, ut_ad(table->cached), + !strcmp(table->name, table_name)); return(table); } @@ -745,6 +746,8 @@ dict_table_get_low( table = dict_load_table(table_name); } + ut_ad(!table || table->cached); + return(table); } @@ -766,11 +769,14 @@ dict_table_get_on_id_low( fold = ut_fold_dulint(table_id); HASH_SEARCH(id_hash, dict_sys->table_id_hash, fold, - dict_table_t*, table, !ut_dulint_cmp(table->id, table_id)); + dict_table_t*, table, ut_ad(table->cached), + !ut_dulint_cmp(table->id, table_id)); if (table == NULL) { table = dict_load_table_on_id(table_id); } + ut_ad(!table || table->cached); + /* TODO: should get the type information from MySQL */ return(table); diff --git a/include/hash0hash.h b/include/hash0hash.h index cb88d53a56a..9a40a6a1cee 100644 --- a/include/hash0hash.h +++ b/include/hash0hash.h @@ -66,12 +66,8 @@ hash_calc_hash( hash_table_t* table); /* in: hash table */ /************************************************************************ Assert that the mutex for the table in a hash operation is owned. */ -#ifdef UNIV_SYNC_DEBUG -# define HASH_ASSERT_OWNED(TABLE, FOLD) \ +#define HASH_ASSERT_OWNED(TABLE, FOLD) \ ut_ad(!(TABLE)->mutexes || mutex_own(hash_get_mutex(TABLE, FOLD))); -#else -# define HASH_ASSERT_OWNED(TABLE, FOLD) -#endif /*********************************************************************** Inserts a struct to a hash table. */ @@ -151,7 +147,7 @@ Gets the next struct in a hash chain, NULL if none. */ /************************************************************************ Looks for a struct in a hash table. */ -#define HASH_SEARCH(NAME, TABLE, FOLD, TYPE, DATA, TEST)\ +#define HASH_SEARCH(NAME, TABLE, FOLD, TYPE, DATA, ASSERTION, TEST)\ {\ \ HASH_ASSERT_OWNED(TABLE, FOLD)\ @@ -160,6 +156,7 @@ Looks for a struct in a hash table. */ HASH_ASSERT_VALID(DATA);\ \ while ((DATA) != NULL) {\ + ASSERTION;\ if (TEST) {\ break;\ } else {\ @@ -169,6 +166,32 @@ Looks for a struct in a hash table. */ }\ } +/************************************************************************ +Looks for an item in all hash buckets. */ +#define HASH_SEARCH_ALL(NAME, TABLE, TYPE, DATA, ASSERTION, TEST) \ +do { \ + ulint i3333; \ + \ + for (i3333 = (TABLE)->n_cells; i3333--; ) { \ + (DATA) = (TYPE) HASH_GET_FIRST(TABLE, i3333); \ + \ + while ((DATA) != NULL) { \ + HASH_ASSERT_VALID(DATA); \ + ASSERTION; \ + \ + if (TEST) { \ + break; \ + } \ + \ + (DATA) = (TYPE) HASH_GET_NEXT(NAME, DATA); \ + } \ + \ + if ((DATA) != NULL) { \ + break; \ + } \ + } \ +} while (0) + /**************************************************************** Gets the nth cell in a hash table. */ UNIV_INLINE diff --git a/include/mtr0mtr.h b/include/mtr0mtr.h index 645d56c55c5..321164495b9 100644 --- a/include/mtr0mtr.h +++ b/include/mtr0mtr.h @@ -344,10 +344,14 @@ struct mtr_struct{ this mtr */ ib_uint64_t end_lsn;/* end lsn of the possible log entry for this mtr */ +#ifdef UNIV_DEBUG ulint magic_n; +#endif /* UNIV_DEBUG */ }; -#define MTR_MAGIC_N 54551 +#ifdef UNIV_DEBUG +# define MTR_MAGIC_N 54551 +#endif /* UNIV_DEBUG */ #define MTR_ACTIVE 12231 #define MTR_COMMITTING 56456 diff --git a/include/mtr0mtr.ic b/include/mtr0mtr.ic index 7bccf65537f..f6460ededc1 100644 --- a/include/mtr0mtr.ic +++ b/include/mtr0mtr.ic @@ -28,10 +28,9 @@ mtr_start( mtr->modifications = FALSE; mtr->n_log_recs = 0; -#ifdef UNIV_DEBUG - mtr->state = MTR_ACTIVE; - mtr->magic_n = MTR_MAGIC_N; -#endif + ut_d(mtr->state = MTR_ACTIVE); + ut_d(mtr->magic_n = MTR_MAGIC_N); + return(mtr); } diff --git a/include/srv0srv.h b/include/srv0srv.h index ff9caefd989..ef18cdffd16 100644 --- a/include/srv0srv.h +++ b/include/srv0srv.h @@ -98,6 +98,7 @@ extern ulong srv_flush_log_at_trx_commit; /* The sort order table of the MySQL latin1_swedish_ci character set collation */ extern const byte* srv_latin1_ordering; +extern my_bool srv_use_sys_malloc; extern ulint srv_buf_pool_size; /* requested size in bytes */ extern ulint srv_buf_pool_old_size; /* previously requested size */ extern ulint srv_buf_pool_curr_size; /* current size in bytes */ diff --git a/include/trx0roll.h b/include/trx0roll.h index f86b600cce8..dc89931ee20 100644 --- a/include/trx0roll.h +++ b/include/trx0roll.h @@ -15,6 +15,8 @@ Created 3/26/1996 Heikki Tuuri #include "mtr0mtr.h" #include "trx0sys.h" +#define trx_roll_free_all_savepoints(s) trx_roll_savepoints_free((s), NULL) + /*********************************************************************** Determines if this transaction is rolling back an incomplete transaction in crash recovery. */ @@ -249,8 +251,18 @@ trx_release_savepoint_for_mysql( const char* savepoint_name); /* in: savepoint name */ /*********************************************************************** -Frees savepoint structs. */ +Frees a single savepoint struct. */ UNIV_INTERN +void +trx_roll_savepoint_free( +/*=====================*/ + trx_t* trx, /* in: transaction handle */ + trx_named_savept_t* savep); /* in: savepoint to free */ + +/*********************************************************************** +Frees savepoint structs starting from savep, if savep == NULL then +free all savepoints. */ + void trx_roll_savepoints_free( /*=====================*/ diff --git a/mem/mem0pool.c b/mem/mem0pool.c index d3bf4d04853..4f26ec560bf 100644 --- a/mem/mem0pool.c +++ b/mem/mem0pool.c @@ -11,6 +11,7 @@ Created 5/12/1997 Heikki Tuuri #include "mem0pool.ic" #endif +#include "srv0srv.h" #include "sync0sync.h" #include "ut0mem.h" #include "ut0lst.h" @@ -336,6 +337,12 @@ mem_area_alloc( ulint n; ibool ret; + /* If we are using os allocator just make a simple call + to malloc */ + if (srv_use_sys_malloc) { + return(malloc(*psize)); + } + size = *psize; n = ut_2_log(ut_max(size + MEM_AREA_EXTRA_SIZE, MEM_AREA_MIN_SIZE)); @@ -470,6 +477,10 @@ mem_area_free( ulint size; ulint n; + if (srv_use_sys_malloc) { + return(free(ptr)); + } + /* It may be that the area was really allocated from the OS with regular malloc: check if ptr points within our memory pool */ diff --git a/mtr/mtr0mtr.c b/mtr/mtr0mtr.c index 9cc82d1f73b..92b917fd829 100644 --- a/mtr/mtr0mtr.c +++ b/mtr/mtr0mtr.c @@ -158,9 +158,8 @@ mtr_commit( ut_ad(mtr); ut_ad(mtr->magic_n == MTR_MAGIC_N); ut_ad(mtr->state == MTR_ACTIVE); -#ifdef UNIV_DEBUG - mtr->state = MTR_COMMITTING; -#endif + ut_d(mtr->state = MTR_COMMITTING); + write_log = mtr->modifications && mtr->n_log_recs; if (write_log) { @@ -181,9 +180,7 @@ mtr_commit( log_release(); } -#ifdef UNIV_DEBUG - mtr->state = MTR_COMMITTED; -#endif + ut_d(mtr->state = MTR_COMMITTED); dyn_array_free(&(mtr->memo)); dyn_array_free(&(mtr->log)); } diff --git a/mysql-test/innodb-semi-consistent-master.opt b/mysql-test/innodb-semi-consistent-master.opt index 2746e4e184e..e76299453d3 100644 --- a/mysql-test/innodb-semi-consistent-master.opt +++ b/mysql-test/innodb-semi-consistent-master.opt @@ -1 +1 @@ ---innodb_locks_unsafe_for_binlog=true --innodb_lock_wait_timeout=2 +--innodb_lock_wait_timeout=2 diff --git a/mysql-test/innodb-semi-consistent.result b/mysql-test/innodb-semi-consistent.result index 6173048c320..55e3cb5c7b4 100644 --- a/mysql-test/innodb-semi-consistent.result +++ b/mysql-test/innodb-semi-consistent.result @@ -1,6 +1,6 @@ drop table if exists t1; set binlog_format=mixed; -set session transaction isolation level read committed; +set session transaction isolation level repeatable read; create table t1(a int not null) engine=innodb DEFAULT CHARSET=latin1; insert into t1 values (1),(2),(3),(4),(5),(6),(7); set autocommit=0; @@ -8,11 +8,12 @@ select * from t1 where a=3 lock in share mode; a 3 set binlog_format=mixed; -set session transaction isolation level read committed; +set session transaction isolation level repeatable read; set autocommit=0; update t1 set a=10 where a=5; ERROR HY000: Lock wait timeout exceeded; try restarting transaction commit; +set session transaction isolation level read committed; update t1 set a=10 where a=5; select * from t1 where a=2 for update; ERROR HY000: Lock wait timeout exceeded; try restarting transaction diff --git a/mysql-test/innodb-semi-consistent.test b/mysql-test/innodb-semi-consistent.test index a3496625e95..6d3020bb560 100644 --- a/mysql-test/innodb-semi-consistent.test +++ b/mysql-test/innodb-semi-consistent.test @@ -11,7 +11,7 @@ connect (a,localhost,root,,); connect (b,localhost,root,,); connection a; set binlog_format=mixed; -set session transaction isolation level read committed; +set session transaction isolation level repeatable read; create table t1(a int not null) engine=innodb DEFAULT CHARSET=latin1; insert into t1 values (1),(2),(3),(4),(5),(6),(7); set autocommit=0; @@ -19,13 +19,15 @@ set autocommit=0; select * from t1 where a=3 lock in share mode; connection b; set binlog_format=mixed; -set session transaction isolation level read committed; +set session transaction isolation level repeatable read; set autocommit=0; -- error ER_LOCK_WAIT_TIMEOUT update t1 set a=10 where a=5; connection a; commit; connection b; +# perform a semi-consisent read (and unlock non-matching rows) +set session transaction isolation level read committed; update t1 set a=10 where a=5; connection a; -- error ER_LOCK_WAIT_TIMEOUT @@ -33,6 +35,7 @@ select * from t1 where a=2 for update; # this should lock the records (1),(2) select * from t1 where a=2 limit 1 for update; connection b; +# semi-consistent read will skip non-matching locked rows a=1, a=2 update t1 set a=11 where a=6; -- error ER_LOCK_WAIT_TIMEOUT update t1 set a=12 where a=2; diff --git a/mysql-test/innodb-use-sys-malloc-master.opt b/mysql-test/innodb-use-sys-malloc-master.opt new file mode 100644 index 00000000000..889834add01 --- /dev/null +++ b/mysql-test/innodb-use-sys-malloc-master.opt @@ -0,0 +1,2 @@ +--innodb-use-sys-malloc=true +--innodb-use-sys-malloc=true diff --git a/mysql-test/innodb-use-sys-malloc.result b/mysql-test/innodb-use-sys-malloc.result new file mode 100644 index 00000000000..2ec4c7c8130 --- /dev/null +++ b/mysql-test/innodb-use-sys-malloc.result @@ -0,0 +1,48 @@ +SELECT @@GLOBAL.innodb_use_sys_malloc; +@@GLOBAL.innodb_use_sys_malloc +1 +1 Expected +SET @@GLOBAL.innodb_use_sys_malloc=0; +ERROR HY000: Variable 'innodb_use_sys_malloc' is a read only variable +Expected error 'Read only variable' +SELECT @@GLOBAL.innodb_use_sys_malloc; +@@GLOBAL.innodb_use_sys_malloc +1 +1 Expected +drop table if exists t1; +create table t1(a int not null) engine=innodb DEFAULT CHARSET=latin1; +insert into t1 values (1),(2),(3),(4),(5),(6),(7); +select * from t1; +a +1 +2 +3 +4 +5 +6 +7 +drop table t1; +SELECT @@GLOBAL.innodb_use_sys_malloc; +@@GLOBAL.innodb_use_sys_malloc +1 +1 Expected +SET @@GLOBAL.innodb_use_sys_malloc=0; +ERROR HY000: Variable 'innodb_use_sys_malloc' is a read only variable +Expected error 'Read only variable' +SELECT @@GLOBAL.innodb_use_sys_malloc; +@@GLOBAL.innodb_use_sys_malloc +1 +1 Expected +drop table if exists t1; +create table t1(a int not null) engine=innodb DEFAULT CHARSET=latin1; +insert into t1 values (1),(2),(3),(4),(5),(6),(7); +select * from t1; +a +1 +2 +3 +4 +5 +6 +7 +drop table t1; diff --git a/mysql-test/innodb-use-sys-malloc.test b/mysql-test/innodb-use-sys-malloc.test new file mode 100644 index 00000000000..325dd19d086 --- /dev/null +++ b/mysql-test/innodb-use-sys-malloc.test @@ -0,0 +1,48 @@ +--source include/have_innodb.inc + +#display current value of innodb_use_sys_malloc +SELECT @@GLOBAL.innodb_use_sys_malloc; +--echo 1 Expected + +#try changing it. Should fail. +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +SET @@GLOBAL.innodb_use_sys_malloc=0; +--echo Expected error 'Read only variable' + +SELECT @@GLOBAL.innodb_use_sys_malloc; +--echo 1 Expected + + +#do some stuff to see if it works. +--disable_warnings +drop table if exists t1; +--enable_warnings + +create table t1(a int not null) engine=innodb DEFAULT CHARSET=latin1; +insert into t1 values (1),(2),(3),(4),(5),(6),(7); +select * from t1; +drop table t1; +--source include/have_innodb.inc + +#display current value of innodb_use_sys_malloc +SELECT @@GLOBAL.innodb_use_sys_malloc; +--echo 1 Expected + +#try changing it. Should fail. +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +SET @@GLOBAL.innodb_use_sys_malloc=0; +--echo Expected error 'Read only variable' + +SELECT @@GLOBAL.innodb_use_sys_malloc; +--echo 1 Expected + + +#do some stuff to see if it works. +--disable_warnings +drop table if exists t1; +--enable_warnings + +create table t1(a int not null) engine=innodb DEFAULT CHARSET=latin1; +insert into t1 values (1),(2),(3),(4),(5),(6),(7); +select * from t1; +drop table t1; diff --git a/mysql-test/innodb_bug41904.result b/mysql-test/innodb_bug41904.result new file mode 100644 index 00000000000..6070d32d181 --- /dev/null +++ b/mysql-test/innodb_bug41904.result @@ -0,0 +1,4 @@ +CREATE TABLE bug41904 (id INT PRIMARY KEY, uniquecol CHAR(15)) ENGINE=InnoDB; +INSERT INTO bug41904 VALUES (1,NULL), (2,NULL); +CREATE UNIQUE INDEX ui ON bug41904 (uniquecol); +DROP TABLE bug41904; diff --git a/mysql-test/innodb_bug41904.test b/mysql-test/innodb_bug41904.test new file mode 100644 index 00000000000..365c5229adc --- /dev/null +++ b/mysql-test/innodb_bug41904.test @@ -0,0 +1,14 @@ +# +# Make sure http://bugs.mysql.com/41904 remains fixed. +# + +-- source include/not_embedded.inc +-- source include/have_innodb.inc + +CREATE TABLE bug41904 (id INT PRIMARY KEY, uniquecol CHAR(15)) ENGINE=InnoDB; + +INSERT INTO bug41904 VALUES (1,NULL), (2,NULL); + +CREATE UNIQUE INDEX ui ON bug41904 (uniquecol); + +DROP TABLE bug41904; diff --git a/mysql-test/patches/bug41893.diff b/mysql-test/patches/bug41893.diff new file mode 100644 index 00000000000..f42f4ae71cb --- /dev/null +++ b/mysql-test/patches/bug41893.diff @@ -0,0 +1,87 @@ +=== modified file 'mysql-test/r/variables.result' +--- mysql-test/r/variables.result 2008-11-27 10:50:28 +0000 ++++ mysql-test/r/variables.result 2009-01-06 07:33:27 +0000 +@@ -297,14 +297,14 @@ + select ROUND(RAND(),5); + ROUND(RAND(),5) + 0.02887 +-show variables like '%alloc%'; ++show variables where variable_name in ('query_alloc_block_size', 'query_prealloc_size', 'range_alloc_block_size', 'transaction_alloc_block_size', 'transaction_prealloc_size'); + Variable_name Value + query_alloc_block_size 8192 + query_prealloc_size 8192 + range_alloc_block_size 4096 + transaction_alloc_block_size 8192 + transaction_prealloc_size 4096 +-select * from information_schema.session_variables where variable_name like '%alloc%' order by 1; ++select * from information_schema.session_variables where variable_name in ('query_alloc_block_size', 'query_prealloc_size', 'range_alloc_block_size', 'transaction_alloc_block_size', 'transaction_prealloc_size') order by 1; + VARIABLE_NAME VARIABLE_VALUE + QUERY_ALLOC_BLOCK_SIZE 8192 + QUERY_PREALLOC_SIZE 8192 +@@ -319,14 +319,14 @@ + select @@query_alloc_block_size; + @@query_alloc_block_size + 17408 +-show variables like '%alloc%'; ++show variables where variable_name in ('query_alloc_block_size', 'query_prealloc_size', 'range_alloc_block_size', 'transaction_alloc_block_size', 'transaction_prealloc_size'); + Variable_name Value + query_alloc_block_size 17408 + query_prealloc_size 18432 + range_alloc_block_size 16384 + transaction_alloc_block_size 19456 + transaction_prealloc_size 20480 +-select * from information_schema.session_variables where variable_name like '%alloc%' order by 1; ++select * from information_schema.session_variables where variable_name in ('query_alloc_block_size', 'query_prealloc_size', 'range_alloc_block_size', 'transaction_alloc_block_size', 'transaction_prealloc_size') order by 1; + VARIABLE_NAME VARIABLE_VALUE + QUERY_ALLOC_BLOCK_SIZE 17408 + QUERY_PREALLOC_SIZE 18432 +@@ -336,14 +336,14 @@ + set @@range_alloc_block_size=default; + set @@query_alloc_block_size=default, @@query_prealloc_size=default; + set transaction_alloc_block_size=default, @@transaction_prealloc_size=default; +-show variables like '%alloc%'; ++show variables where variable_name in ('query_alloc_block_size', 'query_prealloc_size', 'range_alloc_block_size', 'transaction_alloc_block_size', 'transaction_prealloc_size'); + Variable_name Value + query_alloc_block_size 8192 + query_prealloc_size 8192 + range_alloc_block_size 4096 + transaction_alloc_block_size 8192 + transaction_prealloc_size 4096 +-select * from information_schema.session_variables where variable_name like '%alloc%' order by 1; ++select * from information_schema.session_variables where variable_name in ('query_alloc_block_size', 'query_prealloc_size', 'range_alloc_block_size', 'transaction_alloc_block_size', 'transaction_prealloc_size') order by 1; + VARIABLE_NAME VARIABLE_VALUE + QUERY_ALLOC_BLOCK_SIZE 8192 + QUERY_PREALLOC_SIZE 8192 + +=== modified file 'mysql-test/t/variables.test' +--- mysql-test/t/variables.test 2008-11-27 10:50:28 +0000 ++++ mysql-test/t/variables.test 2009-01-06 07:28:12 +0000 +@@ -172,21 +172,21 @@ + set @@rand_seed1=10000000,@@rand_seed2=1000000; + select ROUND(RAND(),5); + +-show variables like '%alloc%'; +-select * from information_schema.session_variables where variable_name like '%alloc%' order by 1; ++show variables where variable_name in ('query_alloc_block_size', 'query_prealloc_size', 'range_alloc_block_size', 'transaction_alloc_block_size', 'transaction_prealloc_size'); ++select * from information_schema.session_variables where variable_name in ('query_alloc_block_size', 'query_prealloc_size', 'range_alloc_block_size', 'transaction_alloc_block_size', 'transaction_prealloc_size') order by 1; + set @@range_alloc_block_size=1024*16; + set @@query_alloc_block_size=1024*17+2; + set @@query_prealloc_size=1024*18; + set @@transaction_alloc_block_size=1024*20-1; + set @@transaction_prealloc_size=1024*21-1; + select @@query_alloc_block_size; +-show variables like '%alloc%'; +-select * from information_schema.session_variables where variable_name like '%alloc%' order by 1; ++show variables where variable_name in ('query_alloc_block_size', 'query_prealloc_size', 'range_alloc_block_size', 'transaction_alloc_block_size', 'transaction_prealloc_size'); ++select * from information_schema.session_variables where variable_name in ('query_alloc_block_size', 'query_prealloc_size', 'range_alloc_block_size', 'transaction_alloc_block_size', 'transaction_prealloc_size') order by 1; + set @@range_alloc_block_size=default; + set @@query_alloc_block_size=default, @@query_prealloc_size=default; + set transaction_alloc_block_size=default, @@transaction_prealloc_size=default; +-show variables like '%alloc%'; +-select * from information_schema.session_variables where variable_name like '%alloc%' order by 1; ++show variables where variable_name in ('query_alloc_block_size', 'query_prealloc_size', 'range_alloc_block_size', 'transaction_alloc_block_size', 'transaction_prealloc_size'); ++select * from information_schema.session_variables where variable_name in ('query_alloc_block_size', 'query_prealloc_size', 'range_alloc_block_size', 'transaction_alloc_block_size', 'transaction_prealloc_size') order by 1; + + # + # Bug #10904 Illegal mix of collations between + diff --git a/row/row0merge.c b/row/row0merge.c index 6d9dfa9feb4..91b03043697 100644 --- a/row/row0merge.c +++ b/row/row0merge.c @@ -447,14 +447,29 @@ row_merge_tuple_cmp( int cmp; const dfield_t* field = a; + /* Compare the fields of the tuples until a difference is + found or we run out of fields to compare. If !cmp at the + end, the tuples are equal. */ do { cmp = cmp_dfield_dfield(a++, b++); } while (!cmp && --n_field); if (UNIV_UNLIKELY(!cmp) && UNIV_LIKELY_NULL(dup)) { + /* Report a duplicate value error if the tuples are + logically equal. NULL columns are logically inequal, + although they are equal in the sorting order. Find + out if any of the fields are NULL. */ + for (b = field; b != a; b++) { + if (dfield_is_null(b)) { + + goto func_exit; + } + } + row_merge_dup_report(dup, field); } +func_exit: return(cmp); } @@ -1839,7 +1854,7 @@ row_merge_drop_temp_indexes(void) "PROCEDURE DROP_TEMP_INDEXES_PROC () IS\n" "indexid CHAR;\n" "DECLARE CURSOR c IS SELECT ID FROM SYS_INDEXES\n" - "WHERE SUBSTR(NAME,0,1)='\377' FOR UPDATE;\n" + "WHERE SUBSTR(NAME,0,1)='\377';\n" "BEGIN\n" "\tOPEN c;\n" "\tWHILE 1=1 LOOP\n" @@ -1848,7 +1863,7 @@ row_merge_drop_temp_indexes(void) "\t\t\tEXIT;\n" "\t\tEND IF;\n" "\t\tDELETE FROM SYS_FIELDS WHERE INDEX_ID = indexid;\n" - "\t\tDELETE FROM SYS_INDEXES WHERE CURRENT OF c;\n" + "\t\tDELETE FROM SYS_INDEXES WHERE ID = indexid;\n" "\tEND LOOP;\n" "\tCLOSE c;\n" "\tCOMMIT WORK;\n" @@ -1858,6 +1873,15 @@ row_merge_drop_temp_indexes(void) trx->op_info = "dropping partially created indexes"; row_mysql_lock_data_dictionary(trx); + /* Incomplete transactions may be holding some locks on the + data dictionary tables. However, they should never have been + able to lock the records corresponding to the partially + created indexes that we are attempting to delete, because the + table was locked when the indexes were being created. We will + drop the partially created indexes before the rollback of + incomplete transactions is initiated. Thus, this should not + interfere with the incomplete transactions. */ + trx->isolation_level = TRX_ISO_READ_UNCOMMITTED; err = que_eval_sql(NULL, drop_temp_indexes, FALSE, trx); ut_a(err == DB_SUCCESS); @@ -1974,7 +1998,6 @@ row_merge_create_temporary_table( if (error != DB_SUCCESS) { trx->error_state = error; - dict_mem_table_free(new_table); new_table = NULL; } diff --git a/row/row0mysql.c b/row/row0mysql.c index 24ef5526a99..6c7319e5b2d 100644 --- a/row/row0mysql.c +++ b/row/row0mysql.c @@ -1443,12 +1443,13 @@ row_unlock_for_mysql( ut_ad(prebuilt && trx); ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); - if (!(srv_locks_unsafe_for_binlog - || trx->isolation_level == TRX_ISO_READ_COMMITTED)) { + if (UNIV_UNLIKELY + (!srv_locks_unsafe_for_binlog + && trx->isolation_level != TRX_ISO_READ_COMMITTED)) { fprintf(stderr, "InnoDB: Error: calling row_unlock_for_mysql though\n" - "InnoDB: srv_locks_unsafe_for_binlog is FALSE and\n" + "InnoDB: innodb_locks_unsafe_for_binlog is FALSE and\n" "InnoDB: this session is not using" " READ COMMITTED isolation level.\n"); diff --git a/srv/srv0srv.c b/srv/srv0srv.c index 2dea4dad943..4e6d633ebf8 100644 --- a/srv/srv0srv.c +++ b/srv/srv0srv.c @@ -142,6 +142,8 @@ UNIV_INTERN ulong srv_flush_log_at_trx_commit = 1; collation */ UNIV_INTERN const byte* srv_latin1_ordering; +/* use os/external memory allocator */ +UNIV_INTERN my_bool srv_use_sys_malloc = FALSE; /* requested size in kilobytes */ UNIV_INTERN ulint srv_buf_pool_size = ULINT_MAX; /* previously requested size */ @@ -1463,10 +1465,13 @@ srv_suspend_mysql_thread( ut_a(trx->dict_operation_lock_mode == 0); - /* Wait for the release */ + /* Suspend this thread and wait for the event. */ os_event_wait(event); + /* After resuming, reacquire the data dictionary latch if + necessary. */ + switch (had_dict_lock) { case RW_S_LATCH: row_mysql_freeze_data_dictionary(trx); diff --git a/srv/srv0start.c b/srv/srv0start.c index 696c4a51c8f..28c40989ddf 100644 --- a/srv/srv0start.c +++ b/srv/srv0start.c @@ -1048,6 +1048,11 @@ innobase_start_or_create_for_mysql(void) "InnoDB: !!!!!!!! UNIV_MEM_DEBUG switched on !!!!!!!!!\n"); #endif + if (srv_use_sys_malloc) { + fprintf(stderr, + "InnoDB: The InnoDB memory heap is disabled\n"); + } + /* Since InnoDB does not currently clean up all its internal data structures in MySQL Embedded Server Library server_end(), we print an error message if someone tries to start up InnoDB a diff --git a/thr/thr0loc.c b/thr/thr0loc.c index 5de171696ef..5957b14562d 100644 --- a/thr/thr0loc.c +++ b/thr/thr0loc.c @@ -71,7 +71,7 @@ try_again: local = NULL; HASH_SEARCH(hash, thr_local_hash, os_thread_pf(id), - thr_local_t*, local, os_thread_eq(local->id, id)); + thr_local_t*, local,, os_thread_eq(local->id, id)); if (local == NULL) { mutex_exit(&thr_local_mutex); @@ -195,7 +195,7 @@ thr_local_free( /* Look for the local struct in the hash table */ HASH_SEARCH(hash, thr_local_hash, os_thread_pf(id), - thr_local_t*, local, os_thread_eq(local->id, id)); + thr_local_t*, local,, os_thread_eq(local->id, id)); if (local == NULL) { mutex_exit(&thr_local_mutex); diff --git a/trx/trx0i_s.c b/trx/trx0i_s.c index 9290891d892..1dfe0071137 100644 --- a/trx/trx0i_s.c +++ b/trx/trx0i_s.c @@ -821,6 +821,8 @@ search_innodb_locks( i_s_hash_chain_t*, /* auxiliary variable */ hash_chain, + /* assertion on every traversed item */ + , /* this determines if we have found the lock */ locks_row_eq_lock(hash_chain->value, lock, heap_no)); diff --git a/trx/trx0roll.c b/trx/trx0roll.c index b1079eff01d..6a478aa850b 100644 --- a/trx/trx0roll.c +++ b/trx/trx0roll.c @@ -171,8 +171,26 @@ trx_rollback_last_sql_stat_for_mysql( } /*********************************************************************** -Frees savepoint structs. */ +Frees a single savepoint struct. */ UNIV_INTERN +void +trx_roll_savepoint_free( +/*=====================*/ + trx_t* trx, /* in: transaction handle */ + trx_named_savept_t* savep) /* in: savepoint to free */ +{ + ut_a(savep != NULL); + ut_a(UT_LIST_GET_LEN(trx->trx_savepoints) > 0); + + UT_LIST_REMOVE(trx_savepoints, trx->trx_savepoints, savep); + mem_free(savep->name); + mem_free(savep); +} + +/*********************************************************************** +Frees savepoint structs starting from savep, if savep == NULL then +free all savepoints. */ + void trx_roll_savepoints_free( /*=====================*/ @@ -192,9 +210,7 @@ trx_roll_savepoints_free( while (savep != NULL) { next_savep = UT_LIST_GET_NEXT(trx_savepoints, savep); - UT_LIST_REMOVE(trx_savepoints, trx->trx_savepoints, savep); - mem_free(savep->name); - mem_free(savep); + trx_roll_savepoint_free(trx, savep); savep = next_savep; } @@ -329,8 +345,8 @@ trx_savepoint_for_mysql( } /*********************************************************************** -Releases a named savepoint. Savepoints which -were set after this savepoint are deleted. */ +Releases only the named savepoint. Savepoints which were set after this +savepoint are left as is. */ UNIV_INTERN ulint trx_release_savepoint_for_mysql( @@ -346,31 +362,16 @@ trx_release_savepoint_for_mysql( savep = UT_LIST_GET_FIRST(trx->trx_savepoints); + /* Search for the savepoint by name and free if found. */ while (savep != NULL) { if (0 == ut_strcmp(savep->name, savepoint_name)) { - /* Found */ - break; + trx_roll_savepoint_free(trx, savep); + return(DB_SUCCESS); } savep = UT_LIST_GET_NEXT(trx_savepoints, savep); } - if (savep == NULL) { - - return(DB_NO_SAVEPOINT); - } - - /* We can now free all savepoints strictly later than this one */ - - trx_roll_savepoints_free(trx, savep); - - /* Now we can free this savepoint too */ - - UT_LIST_REMOVE(trx_savepoints, trx->trx_savepoints, savep); - - mem_free(savep->name); - mem_free(savep); - - return(DB_SUCCESS); + return(DB_NO_SAVEPOINT); } /*********************************************************************** diff --git a/trx/trx0trx.c b/trx/trx0trx.c index 20bfff32b2e..d7e40c07201 100644 --- a/trx/trx0trx.c +++ b/trx/trx0trx.c @@ -917,8 +917,8 @@ trx_commit_off_kernel( mutex_enter(&kernel_mutex); } - /* Free savepoints */ - trx_roll_savepoints_free(trx, NULL); + /* Free all savepoints */ + trx_roll_free_all_savepoints(trx); trx->conc_state = TRX_NOT_STARTED; trx->rseg = NULL;