Commit 8638150c authored by marko's avatar marko

branches/innodb+: Merge 2579:2637 from branches/zip.

parent f208759f
2008-09-17 The InnoDB Team
* btr/btr0cur.c, data/data0data.c, include/page0zip.h,
include/page0zip.ic, page/page0zip.c,
mysql-test/innodb_bug36172.test:
Prevent infinite B-tree page splits in compressed tables by
ensuring that there will always be enough space for two node
pointer records in an empty B-tree page. Also, require that at
least one data record will fit in an empty compressed page. This
will reduce the maximum size of records in compressed tables.
This was reported as Mantis issue #73.
2008-09-09 The InnoDB Team
* mysql-test/innodb.result:
Fix the failing innodb test by merging changes that MySQL made to
that file (r2646.12.1 in MySQL BZR repository)
2008-09-09 The InnoDB Team
* handler/ha_innodb.cc, mysql-test/innodb-autoinc.result,
mysql-test/innodb-autoinc.test:
Fix Bug#38839 auto increment does not work properly with InnoDB after
update
2008-09-09 The InnoDB Team
* dict/dict0dict.c, handler/handler0alter.cc, include/dict0dict.h,
mysql-test/innodb-index.result, mysql-test/innodb-index.test:
Fix Bug#38786 InnoDB plugin crashes on drop table/create table with FK
2008-08-21 The InnoDB Team
* handler/ha_innodb.cc, include/ha_prototypes.h, row/row0sel.c:
Fix Bug#37885 row_search_for_mysql may gap lock unnecessarily with SQL
comments in query
2008-08-21 The InnoDB Team
* handler/ha_innodb.cc:
Fix Bug#38185 ha_innobase::info can hold locks even when called with
HA_STATUS_NO_LOCK
2008-08-18 The InnoDB Team
* buf/buf0buf.c, buf/buf0lru.c, include/buf0buf.ic, include/univ.i:
Introduce UNIV_LRU_DEBUG for debugging the LRU buffer pool cache
2008-08-08 The InnoDB Team
* buf/buf0lru.c, include/buf0buf.h:
Fix two recovery bugs that could lead to a crash in debug builds with
small buffer size
2008-08-07 The InnoDB Team
* btr/btr0cur.c, handler/ha_innodb.cc, include/srv0srv.h,
srv/srv0srv.c:
Add a parameter innodb_stats_sample_pages to allow users to control
the number of index dives when InnoDB estimates the cardinality of
an index (ANALYZE TABLE, SHOW TABLE STATUS etc)
2008-08-07 The InnoDB Team
* trx/trx0i_s.c:
Fix a bug that would lead to a crash if a SELECT was issued from the
INFORMATION_SCHEMA tables and there are rolling back transactions at
the same time
2008-08-06 The InnoDB Team
* btr/btr0btr.c, btr/btr0cur.c, ibuf/ibuf0ibuf.c, include/btr0cur.h,
include/trx0roll.h, include/trx0types.h, row/row0purge.c,
row/row0uins.c, row/row0umod.c, trx/trx0roll.c:
In the rollback of incomplete transactions after crash recovery,
tolerate clustered index records whose externally stored columns
have not been written.
2008-07-30 The InnoDB Team
* trx/trx0trx.c:
Fixes a race in recovery where the recovery thread recovering a
PREPARED trx and the background rollback thread can both try
to free the trx after its status is set to COMMITTED_IN_MEMORY.
2008-07-29 The InnoDB Team
* include/trx0rec.h, row/row0purge.c, row/row0vers.c, trx/trx0rec.c:
Fix a BLOB corruption bug
2008-07-15 The InnoDB Team
* btr/btr0sea.c, dict/dict0dict.c, include/btr0sea.h:
Fixed a timing hole where a thread dropping an index can free the
in-memory index struct while another thread is still using that
structure to remove entries from adaptive hash index belonging
to one of the pages that belongs to the index being dropped.
2008-07-04 The InnoDB Team
* mysql-test/innodb-index.result:
Fix the failing innodb-index test by adjusting the result to a new
MySQL behavior (the change occured in BZR-r2667)
2008-07-03 The InnoDB Team
* mysql-test/innodb-zip.result, mysql-test/innodb-zip.test:
Remove the negative test cases that produce warnings
2008-07-02 The InnoDB Team
* mysql-test/innodb-replace.result, mysql-test/innodb-index.test:
Disable part of innodb-index test because MySQL changed its behavior
and is not calling ::add_index() anymore when adding primary index on
non-NULL column
2008-07-01 The InnoDB Team
* mysql-test/innodb-replace.result, mysql-test/innodb-replace.test:
Fix the failing innodb-replace test by merging changes that MySQL
made to that file (r2659 in MySQL BZR repository)
2008-07-01 The InnoDB Team
* lock/lock0lock.c:
Fix Bug#36942 Performance problem in lock_get_n_rec_locks (SHOW INNODB
STATUS)
2008-07-01 The InnoDB Team
* ha/ha0ha.c:
Fix Bug#36941 Performance problem in ha_print_info (SHOW INNODB
STATUS)
2008-07-01 The InnoDB Team
* handler/ha_innodb.cc, mysql-test/innodb-autoinc.result,
mysql-test/innodb-autoinc.test:
Fix Bug#37531 After truncate, auto_increment behaves incorrectly for
InnoDB
2008-06-19 The InnoDB Team
* handler/ha_innodb.cc:
Rewrite the function innodb_plugin_init() to support parameters in
different order (in static and dynamic InnoDB) and to support more
parameters in the static InnoDB
2008-06-19 The InnoDB Team
* handler/handler0alter.cc:
Fix a bug in ::add_index() which set the transaction state to "active"
but never restored it to the original value. This bug caused warnings
to be printed by the rpl.rpl_ddl mysql-test.
2008-06-19 The InnoDB Team
* mysql-test/patches:
Add a directory which contains patches, which need to be applied to
MySQL source in order to get some mysql-tests to succeed. The patches
cannot be committed in MySQL repository because they are specific to
the InnoDB plugin.
2008-06-19 The InnoDB Team
* mysql-test/innodb-zip.result, mysql-test/innodb-zip.test,
row/row0row.c:
Fix an anomaly when updating a record with BLOB prefix
2008-06-18 The InnoDB Team
* include/trx0sys.h, srv/srv0start.c, trx/trx0sys.c:
Fix a bug in recovery which was a side effect of the file_format_check
changes
2008-06-09 The InnoDB Team
* mysql-test/innodb.result:
......
......@@ -78,6 +78,26 @@ make them consecutive on disk if possible. From the other file segment
we allocate pages for the non-leaf levels of the tree.
*/
#ifdef UNIV_BTR_DEBUG
/******************************************************************
Checks a file segment header within a B-tree root page. */
static
ibool
btr_root_fseg_validate(
/*===================*/
/* out: TRUE if valid */
const fseg_header_t* seg_header, /* in: segment header */
ulint space) /* in: tablespace identifier */
{
ulint offset = mach_read_from_2(seg_header + FSEG_HDR_OFFSET);
ut_a(mach_read_from_4(seg_header + FSEG_HDR_SPACE) == space);
ut_a(offset >= FIL_PAGE_DATA);
ut_a(offset <= UNIV_PAGE_SIZE - FIL_PAGE_DATA_END);
return(TRUE);
}
#endif /* UNIV_BTR_DEBUG */
/******************************************************************
Gets the root node of a tree and x-latches it. */
static
......@@ -100,6 +120,12 @@ btr_root_block_get(
block = btr_block_get(space, zip_size, root_page_no, RW_X_LATCH, mtr);
ut_a((ibool)!!page_is_comp(buf_block_get_frame(block))
== dict_table_is_comp(index->table));
#ifdef UNIV_BTR_DEBUG
ut_a(btr_root_fseg_validate(FIL_PAGE_DATA + PAGE_BTR_SEG_LEAF
+ buf_block_get_frame(block), space));
ut_a(btr_root_fseg_validate(FIL_PAGE_DATA + PAGE_BTR_SEG_TOP
+ buf_block_get_frame(block), space));
#endif /* UNIV_BTR_DEBUG */
return(block);
}
......@@ -833,6 +859,12 @@ leaf_loop:
mtr_start(&mtr);
root = btr_page_get(space, zip_size, root_page_no, RW_X_LATCH, &mtr);
#ifdef UNIV_BTR_DEBUG
ut_a(btr_root_fseg_validate(FIL_PAGE_DATA + PAGE_BTR_SEG_LEAF
+ root, space));
ut_a(btr_root_fseg_validate(FIL_PAGE_DATA + PAGE_BTR_SEG_TOP
+ root, space));
#endif /* UNIV_BTR_DEBUG */
/* NOTE: page hash indexes are dropped when a page is freed inside
fsp0fsp. */
......@@ -849,6 +881,10 @@ top_loop:
mtr_start(&mtr);
root = btr_page_get(space, zip_size, root_page_no, RW_X_LATCH, &mtr);
#ifdef UNIV_BTR_DEBUG
ut_a(btr_root_fseg_validate(FIL_PAGE_DATA + PAGE_BTR_SEG_TOP
+ root, space));
#endif /* UNIV_BTR_DEBUG */
finished = fseg_free_step_not_header(
root + PAGE_HEADER + PAGE_BTR_SEG_TOP, &mtr);
......@@ -881,6 +917,9 @@ btr_free_root(
btr_search_drop_page_hash_index(block);
header = buf_block_get_frame(block) + PAGE_HEADER + PAGE_BTR_SEG_TOP;
#ifdef UNIV_BTR_DEBUG
ut_a(btr_root_fseg_validate(header, space));
#endif /* UNIV_BTR_DEBUG */
while (!fseg_free_step(header, mtr));
}
......@@ -1117,8 +1156,13 @@ btr_root_raise_and_insert(
ut_a(!root_page_zip || page_zip_validate(root_page_zip, root));
#endif /* UNIV_ZIP_DEBUG */
index = btr_cur_get_index(cursor);
ut_ad(dict_index_get_page(index) == page_get_page_no(root));
#ifdef UNIV_BTR_DEBUG
ut_a(btr_root_fseg_validate(FIL_PAGE_DATA + PAGE_BTR_SEG_LEAF
+ root, dict_index_get_space(index)));
ut_a(btr_root_fseg_validate(FIL_PAGE_DATA + PAGE_BTR_SEG_TOP
+ root, dict_index_get_space(index)));
ut_a(dict_index_get_page(index) == page_get_page_no(root));
#endif /* UNIV_BTR_DEBUG */
ut_ad(mtr_memo_contains(mtr, dict_index_get_lock(index),
MTR_MEMO_X_LOCK));
ut_ad(mtr_memo_contains(mtr, root_block, MTR_MEMO_PAGE_X_FIX));
......@@ -2664,6 +2708,14 @@ btr_discard_only_page_on_level(
== dict_index_get_page(index))) {
/* The father is the root page */
#ifdef UNIV_BTR_DEBUG
const page_t* root = buf_block_get_frame(father_block);
const ulint space = dict_index_get_space(index);
ut_a(btr_root_fseg_validate(FIL_PAGE_DATA + PAGE_BTR_SEG_LEAF
+ root, space));
ut_a(btr_root_fseg_validate(FIL_PAGE_DATA + PAGE_BTR_SEG_TOP
+ root, space));
#endif /* UNIV_BTR_DEBUG */
btr_page_empty(father_block, father_page_zip, mtr, index);
/* We play safe and reset the free bits for the father */
......
......@@ -969,7 +969,7 @@ btr_cur_open_at_rnd_pos(
/*****************************************************************
Inserts a record if there is enough space, or if enough space can
be freed by reorganizing. Differs from _optimistic_insert because
be freed by reorganizing. Differs from btr_cur_optimistic_insert because
no heuristics is applied to whether it pays to use CPU time for
reorganizing the page or not. */
static
......@@ -1173,7 +1173,8 @@ btr_cur_optimistic_insert(
/* Calculate the record size when entry is converted to a record */
rec_size = rec_get_converted_size(index, entry, n_ext);
if (page_zip_rec_needs_ext(rec_size, page_is_comp(page), zip_size)) {
if (page_zip_rec_needs_ext(rec_size, page_is_comp(page),
dtuple_get_n_fields(entry), zip_size)) {
/* The record is so big that we have to store some fields
externally on separate database pages */
......@@ -1187,6 +1188,46 @@ btr_cur_optimistic_insert(
rec_size = rec_get_converted_size(index, entry, n_ext);
}
if (UNIV_UNLIKELY(zip_size)) {
/* Estimate the free space of an empty compressed page.
Subtract one byte for the encoded heap_no in the
modification log. */
ulint free_space_zip = page_zip_empty_size(
cursor->index->n_fields, zip_size) - 1;
ulint extra;
ulint n_uniq = dict_index_get_n_unique_in_tree(index);
ut_ad(dict_table_is_comp(index->table));
/* There should be enough room for two node pointer
records on an empty non-leaf page. This prevents
infinite page splits. */
if (UNIV_LIKELY(entry->n_fields >= n_uniq)
&& UNIV_UNLIKELY(rec_get_converted_size_comp(
index, REC_STATUS_NODE_PTR,
entry->fields, n_uniq,
&extra)
/* On a compressed page, there is
a two-byte entry in the dense
page directory for every record.
But there is no record header. */
- (REC_N_NEW_EXTRA_BYTES - 2)
> free_space_zip / 2)) {
if (big_rec_vec) {
dtuple_convert_back_big_rec(
index, entry, big_rec_vec);
}
if (heap) {
mem_heap_free(heap);
}
return(DB_TOO_BIG_RECORD);
}
}
/* If there have been many consecutive inserts, and we are on the leaf
level, check if we have to split the page to reserve enough free space
for future updates of records. */
......@@ -1418,6 +1459,7 @@ btr_cur_pessimistic_insert(
if (page_zip_rec_needs_ext(rec_get_converted_size(index, entry, n_ext),
dict_table_is_comp(index->table),
dict_index_get_n_fields(index),
zip_size)) {
/* The record is so big that we have to store some fields
externally on separate database pages */
......@@ -1441,45 +1483,6 @@ btr_cur_pessimistic_insert(
}
}
if (UNIV_UNLIKELY(zip_size)) {
/* Estimate the free space of an empty compressed page. */
ulint free_space_zip = page_zip_empty_size(
cursor->index->n_fields, zip_size);
if (UNIV_UNLIKELY(rec_get_converted_size(index, entry, n_ext)
> free_space_zip)) {
/* Try to insert the record by itself on a new page.
If it fails, no amount of splitting will help. */
buf_block_t* temp_block
= buf_block_alloc(zip_size);
page_t* temp_page
= page_create_zip(temp_block, index, 0, NULL);
page_cur_t temp_cursor;
rec_t* temp_rec;
page_cur_position(temp_page + PAGE_NEW_INFIMUM,
temp_block, &temp_cursor);
temp_rec = page_cur_tuple_insert(&temp_cursor,
entry, index,
n_ext, NULL);
buf_block_free(temp_block);
if (UNIV_UNLIKELY(!temp_rec)) {
if (big_rec_vec) {
dtuple_convert_back_big_rec(
index, entry, big_rec_vec);
}
if (heap) {
mem_heap_free(heap);
}
return(DB_TOO_BIG_RECORD);
}
}
}
if (dict_index_get_page(index)
== buf_block_get_page_no(btr_cur_get_block(cursor))) {
......@@ -2289,10 +2292,20 @@ btr_cur_pessimistic_update(
offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, heap);
n_ext += btr_push_update_extern_fields(new_entry, update, *heap);
if (page_zip_rec_needs_ext(rec_get_converted_size(index, new_entry,
n_ext),
page_is_comp(page), page_zip
? page_zip_get_size(page_zip) : 0)) {
if (UNIV_LIKELY_NULL(page_zip)) {
ut_ad(page_is_comp(page));
if (page_zip_rec_needs_ext(
rec_get_converted_size(index, new_entry, n_ext),
TRUE,
dict_index_get_n_fields(index),
page_zip_get_size(page_zip))) {
goto make_external;
}
} else if (page_zip_rec_needs_ext(
rec_get_converted_size(index, new_entry, n_ext),
page_is_comp(page), 0, 0)) {
make_external:
big_rec_vec = dtuple_convert_big_rec(index, new_entry, &n_ext);
if (UNIV_UNLIKELY(big_rec_vec == NULL)) {
......@@ -3270,6 +3283,7 @@ btr_estimate_number_of_different_key_vals(
ulint matched_fields;
ulint matched_bytes;
ib_int64_t* n_diff;
ullint n_sample_pages; /* number of pages to sample */
ulint not_empty_flag = 0;
ulint total_external_size = 0;
ulint i;
......@@ -3288,9 +3302,21 @@ btr_estimate_number_of_different_key_vals(
n_diff = mem_zalloc((n_cols + 1) * sizeof(ib_int64_t));
/* It makes no sense to test more pages than are contained
in the index, thus we lower the number if it is too high */
if (srv_stats_sample_pages > index->stat_index_size) {
if (index->stat_index_size > 0) {
n_sample_pages = index->stat_index_size;
} else {
n_sample_pages = 1;
}
} else {
n_sample_pages = srv_stats_sample_pages;
}
/* We sample some pages in the index to get an estimate */
for (i = 0; i < srv_stats_sample_pages; i++) {
for (i = 0; i < n_sample_pages; i++) {
rec_t* supremum;
mtr_start(&mtr);
......@@ -3379,7 +3405,7 @@ btr_estimate_number_of_different_key_vals(
}
/* If we saw k borders between different key values on
srv_stats_sample_pages leaf pages, we can estimate how many
n_sample_pages leaf pages, we can estimate how many
there will be in index->stat_n_leaf_pages */
/* We must take into account that our sample actually represents
......@@ -3390,26 +3416,26 @@ btr_estimate_number_of_different_key_vals(
index->stat_n_diff_key_vals[j]
= ((n_diff[j]
* (ib_int64_t)index->stat_n_leaf_pages
+ srv_stats_sample_pages - 1
+ n_sample_pages - 1
+ total_external_size
+ not_empty_flag)
/ (srv_stats_sample_pages
/ (n_sample_pages
+ total_external_size));
/* If the tree is small, smaller than
10 * srv_stats_sample_pages + total_external_size, then
10 * n_sample_pages + total_external_size, then
the above estimate is ok. For bigger trees it is common that we
do not see any borders between key values in the few pages
we pick. But still there may be srv_stats_sample_pages
we pick. But still there may be n_sample_pages
different key values, or even more. Let us try to approximate
that: */
add_on = index->stat_n_leaf_pages
/ (10 * (srv_stats_sample_pages
/ (10 * (n_sample_pages
+ total_external_size));
if (add_on > srv_stats_sample_pages) {
add_on = srv_stats_sample_pages;
if (add_on > n_sample_pages) {
add_on = n_sample_pages;
}
index->stat_n_diff_key_vals[j] += add_on;
......
......@@ -971,21 +971,21 @@ btr_search_drop_page_hash_index(
for which we know that
block->buf_fix_count == 0 */
{
hash_table_t* table;
ulint n_fields;
ulint n_bytes;
page_t* page;
rec_t* rec;
ulint fold;
ulint prev_fold;
dulint index_id;
ulint n_cached;
ulint n_recs;
ulint* folds;
ulint i;
mem_heap_t* heap;
dict_index_t* index;
ulint* offsets;
hash_table_t* table;
ulint n_fields;
ulint n_bytes;
const page_t* page;
const rec_t* rec;
ulint fold;
ulint prev_fold;
dulint index_id;
ulint n_cached;
ulint n_recs;
ulint* folds;
ulint i;
mem_heap_t* heap;
const dict_index_t* index;
ulint* offsets;
#ifdef UNIV_SYNC_DEBUG
ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_SHARED));
......@@ -1034,7 +1034,7 @@ retry:
n_cached = 0;
rec = page_get_infimum_rec(page);
rec = page_rec_get_next(rec);
rec = page_rec_get_next_low(rec, page_is_comp(page));
index_id = btr_page_get_index_id(page);
......@@ -1062,7 +1062,7 @@ retry:
folds[n_cached] = fold;
n_cached++;
next_rec:
rec = page_rec_get_next(rec);
rec = page_rec_get_next_low(rec, page_rec_is_comp(rec));
prev_fold = fold;
}
......
......@@ -1053,6 +1053,14 @@ buf_relocate(
if (UNIV_UNLIKELY(buf_pool->LRU_old == bpage)) {
buf_pool->LRU_old = dpage;
#ifdef UNIV_LRU_DEBUG
/* buf_pool->LRU_old must be the first item in the LRU list
whose "old" flag is set. */
ut_a(!UT_LIST_GET_PREV(LRU, buf_pool->LRU_old)
|| !UT_LIST_GET_PREV(LRU, buf_pool->LRU_old)->old);
ut_a(!UT_LIST_GET_NEXT(LRU, buf_pool->LRU_old)
|| UT_LIST_GET_NEXT(LRU, buf_pool->LRU_old)->old);
#endif /* UNIV_LRU_DEBUG */
}
ut_d(UT_LIST_VALIDATE(LRU, buf_page_t, buf_pool->LRU));
......
......@@ -712,7 +712,7 @@ loop:
if (n_iterations > 30) {
ut_print_timestamp(stderr);
fprintf(stderr,
"InnoDB: Warning: difficult to find free blocks from\n"
" InnoDB: Warning: difficult to find free blocks in\n"
"InnoDB: the buffer pool (%lu search iterations)!"
" Consider\n"
"InnoDB: increasing the buffer pool size.\n"
......@@ -790,12 +790,25 @@ buf_LRU_old_adjust_len(void)
#if 3 * (BUF_LRU_OLD_MIN_LEN / 8) <= BUF_LRU_OLD_TOLERANCE + 5
# error "3 * (BUF_LRU_OLD_MIN_LEN / 8) <= BUF_LRU_OLD_TOLERANCE + 5"
#endif
#ifdef UNIV_LRU_DEBUG
/* buf_pool->LRU_old must be the first item in the LRU list
whose "old" flag is set. */
ut_a(buf_pool->LRU_old->old);
ut_a(!UT_LIST_GET_PREV(LRU, buf_pool->LRU_old)
|| !UT_LIST_GET_PREV(LRU, buf_pool->LRU_old)->old);
ut_a(!UT_LIST_GET_NEXT(LRU, buf_pool->LRU_old)
|| UT_LIST_GET_NEXT(LRU, buf_pool->LRU_old)->old);
#endif /* UNIV_LRU_DEBUG */
for (;;) {
old_len = buf_pool->LRU_old_len;
new_len = 3 * (UT_LIST_GET_LEN(buf_pool->LRU) / 8);
ut_ad(buf_pool->LRU_old->in_LRU_list);
ut_a(buf_pool->LRU_old);
#ifdef UNIV_LRU_DEBUG
ut_a(buf_pool->LRU_old->old);
#endif /* UNIV_LRU_DEBUG */
/* Update the LRU_old pointer if necessary */
......@@ -803,6 +816,9 @@ buf_LRU_old_adjust_len(void)
buf_pool->LRU_old = UT_LIST_GET_PREV(
LRU, buf_pool->LRU_old);
#ifdef UNIV_LRU_DEBUG
ut_a(!buf_pool->LRU_old->old);
#endif /* UNIV_LRU_DEBUG */
buf_page_set_old(buf_pool->LRU_old, TRUE);
buf_pool->LRU_old_len++;
......@@ -813,8 +829,6 @@ buf_LRU_old_adjust_len(void)
LRU, buf_pool->LRU_old);
buf_pool->LRU_old_len--;
} else {
ut_a(buf_pool->LRU_old); /* Check that we did not
fall out of the LRU list */
return;
}
}
......@@ -901,6 +915,9 @@ buf_LRU_remove_block(
buf_pool->LRU_old = UT_LIST_GET_PREV(LRU, bpage);
ut_a(buf_pool->LRU_old);
#ifdef UNIV_LRU_DEBUG
ut_a(!buf_pool->LRU_old->old);
#endif /* UNIV_LRU_DEBUG */
buf_page_set_old(buf_pool->LRU_old, TRUE);
buf_pool->LRU_old_len++;
......@@ -974,8 +991,6 @@ buf_LRU_add_block_to_end_low(
ut_a(buf_page_in_file(bpage));
buf_page_set_old(bpage, TRUE);
last_bpage = UT_LIST_GET_LAST(buf_pool->LRU);
if (last_bpage) {
......@@ -988,6 +1003,8 @@ buf_LRU_add_block_to_end_low(
UT_LIST_ADD_LAST(LRU, buf_pool->LRU, bpage);
ut_d(bpage->in_LRU_list = TRUE);
buf_page_set_old(bpage, TRUE);
if (UT_LIST_GET_LEN(buf_pool->LRU) >= BUF_LRU_OLD_MIN_LEN) {
buf_pool->LRU_old_len++;
......@@ -1035,8 +1052,6 @@ buf_LRU_add_block_low(
ut_a(buf_page_in_file(bpage));
ut_ad(!bpage->in_LRU_list);
buf_page_set_old(bpage, old);
if (!old || (UT_LIST_GET_LEN(buf_pool->LRU) < BUF_LRU_OLD_MIN_LEN)) {
UT_LIST_ADD_FIRST(LRU, buf_pool->LRU, bpage);
......@@ -1044,6 +1059,15 @@ buf_LRU_add_block_low(
bpage->LRU_position = buf_pool_clock_tic();
bpage->freed_page_clock = buf_pool->freed_page_clock;
} else {
#ifdef UNIV_LRU_DEBUG
/* buf_pool->LRU_old must be the first item in the LRU list
whose "old" flag is set. */
ut_a(buf_pool->LRU_old->old);
ut_a(!UT_LIST_GET_PREV(LRU, buf_pool->LRU_old)
|| !UT_LIST_GET_PREV(LRU, buf_pool->LRU_old)->old);
ut_a(!UT_LIST_GET_NEXT(LRU, buf_pool->LRU_old)
|| UT_LIST_GET_NEXT(LRU, buf_pool->LRU_old)->old);
#endif /* UNIV_LRU_DEBUG */
UT_LIST_INSERT_AFTER(LRU, buf_pool->LRU, buf_pool->LRU_old,
bpage);
buf_pool->LRU_old_len++;
......@@ -1056,6 +1080,8 @@ buf_LRU_add_block_low(
ut_d(bpage->in_LRU_list = TRUE);
buf_page_set_old(bpage, old);
if (UT_LIST_GET_LEN(buf_pool->LRU) > BUF_LRU_OLD_MIN_LEN) {
ut_ad(buf_pool->LRU_old);
......@@ -1252,6 +1278,15 @@ alloc:
buf_pool->LRU_old = b;
}
#ifdef UNIV_LRU_DEBUG
ut_a(prev_b->old
|| !UT_LIST_GET_NEXT(LRU, b)
|| UT_LIST_GET_NEXT(LRU, b)->old);
} else {
ut_a(!prev_b->old
|| !UT_LIST_GET_NEXT(LRU, b)
|| !UT_LIST_GET_NEXT(LRU, b)->old);
#endif /* UNIV_LRU_DEBUG */
}
lru_len = UT_LIST_GET_LEN(buf_pool->LRU);
......
......@@ -607,6 +607,7 @@ dtuple_convert_big_rec(
while (page_zip_rec_needs_ext(rec_get_converted_size(index, entry,
*n_ext),
dict_table_is_comp(index->table),
dict_index_get_n_fields(index),
dict_table_zip_size(index->table))) {
ulint i;
ulint longest = 0;
......
......@@ -543,11 +543,7 @@ dict_build_index_def_step(
ut_ad((UT_LIST_GET_LEN(table->indexes) > 0)
|| dict_index_is_clust(index));
/* For fast index creation we have already allocated an index id
for this index so that we could write an UNDO log record for it.*/
if (ut_dulint_is_zero(index->id)) {
index->id = dict_hdr_get_new_id(DICT_HDR_INDEX_ID);
}
index->id = dict_hdr_get_new_id(DICT_HDR_INDEX_ID);
/* Inherit the space id from the table; we store all indexes of a
table in the same tablespace */
......
......@@ -55,56 +55,6 @@ UNIV_INTERN rw_lock_t dict_operation_lock;
/* Identifies generated InnoDB foreign key names */
static char dict_ibfk[] = "_ibfk_";
#ifndef UNIV_HOTBACKUP
/**********************************************************************
Converts an identifier to a table name.
NOTE: the prototype of this function is copied from ha_innodb.cc! If you change
this function, you MUST change also the prototype here! */
UNIV_INTERN
void
innobase_convert_from_table_id(
/*===========================*/
char* to, /* out: converted identifier */
const char* from, /* in: identifier to convert */
ulint len); /* in: length of 'to', in bytes;
should be at least 5 * strlen(to) + 1 */
/**********************************************************************
Converts an identifier to UTF-8.
NOTE: the prototype of this function is copied from ha_innodb.cc! If you change
this function, you MUST change also the prototype here! */
UNIV_INTERN
void
innobase_convert_from_id(
/*=====================*/
char* to, /* out: converted identifier */
const char* from, /* in: identifier to convert */
ulint len); /* in: length of 'to', in bytes;
should be at least 3 * strlen(to) + 1 */
/**********************************************************************
Makes all characters in a NUL-terminated UTF-8 string lower case.
NOTE: the prototype of this function is copied from ha_innodb.cc! If you change
this function, you MUST change also the prototype here! */
UNIV_INTERN
void
innobase_casedn_str(
/*================*/
char* a); /* in/out: string to put in lower case */
/**************************************************************************
Determines the connection character set.
NOTE: the prototype of this function is copied from ha_innodb.cc! If you change
this function, you MUST change also the prototype here! */
struct charset_info_st*
innobase_get_charset(
/*=================*/
/* out: connection character set */
void* mysql_thd); /* in: MySQL thread handle */
#endif /* !UNIV_HOTBACKUP */
/***********************************************************************
Tries to find column names for the index and sets the col field of the
index. */
......@@ -1948,27 +1898,19 @@ dict_table_get_referenced_constraint(
dict_table_t* table, /* in: InnoDB table */
dict_index_t* index) /* in: InnoDB index */
{
dict_foreign_t* foreign = NULL;
ut_ad(index && table);
dict_foreign_t* foreign;
/* If the referenced list is empty, nothing to do */
ut_ad(index != NULL);
ut_ad(table != NULL);
if (UT_LIST_GET_LEN(table->referenced_list) == 0) {
for (foreign = UT_LIST_GET_FIRST(table->referenced_list);
foreign;
foreign = UT_LIST_GET_NEXT(referenced_list, foreign)) {
return(NULL);
}
foreign = UT_LIST_GET_FIRST(table->referenced_list);
while (foreign) {
if (foreign->referenced_index == index
|| foreign->referenced_index == index) {
if (foreign->referenced_index == index) {
return(foreign);
}
foreign = UT_LIST_GET_NEXT(referenced_list, foreign);
}
return(NULL);
......@@ -1987,29 +1929,20 @@ dict_table_get_foreign_constraint(
dict_table_t* table, /* in: InnoDB table */
dict_index_t* index) /* in: InnoDB index */
{
dict_foreign_t* foreign = NULL;
ut_ad(index && table);
/* If list empty then nothgin to do */
if (UT_LIST_GET_LEN(table->foreign_list) == 0) {
return(NULL);
}
dict_foreign_t* foreign;
/* Check whether this index is defined for a foreign key */
ut_ad(index != NULL);
ut_ad(table != NULL);
foreign = UT_LIST_GET_FIRST(table->foreign_list);
for (foreign = UT_LIST_GET_FIRST(table->foreign_list);
foreign;
foreign = UT_LIST_GET_NEXT(foreign_list, foreign)) {
while (foreign) {
if (foreign->foreign_index == index
|| foreign->referenced_index == index) {
return(foreign);
}
foreign = UT_LIST_GET_NEXT(foreign_list, foreign);
}
return(NULL);
......@@ -2179,6 +2112,30 @@ next_rec:
return(NULL);
}
/**************************************************************************
Find an index that is equivalent to the one passed in and is not marked
for deletion. */
UNIV_INTERN
dict_index_t*
dict_foreign_find_equiv_index(
/*==========================*/
/* out: index equivalent to
foreign->foreign_index, or NULL */
dict_foreign_t* foreign)/* in: foreign key */
{
ut_a(foreign != NULL);
/* Try to find an index which contains the columns as the
first fields and in the right order, and the types are the
same as in foreign->foreign_index */
return(dict_foreign_find_index(
foreign->foreign_table,
foreign->foreign_col_names, foreign->n_fields,
foreign->foreign_index, TRUE, /* check types */
FALSE/* allow columns to be NULL */));
}
/**************************************************************************
Returns an index object by matching on the name and column names and
if more than one index matches return the index with the max id */
......@@ -2409,7 +2366,7 @@ dict_foreign_add_to_cache(
Scans from pointer onwards. Stops if is at the start of a copy of
'string' where characters are compared without case sensitivity, and
only outside `` or "" quotes. Stops also at '\0'. */
UNIV_INTERN
static
const char*
dict_scan_to(
/*=========*/
......@@ -2584,7 +2541,7 @@ convert_id:
len = 3 * len + 1;
*id = dst = mem_heap_alloc(heap, len);
innobase_convert_from_id(dst, str, len);
innobase_convert_from_id(cs, dst, str, len);
} else if (!strncmp(str, srv_mysql50_table_name_prefix,
sizeof srv_mysql50_table_name_prefix)) {
/* This is a pre-5.1 table name
......@@ -2598,7 +2555,7 @@ convert_id:
len = 5 * len + 1;
*id = dst = mem_heap_alloc(heap, len);
innobase_convert_from_table_id(dst, str, len);
innobase_convert_from_table_id(cs, dst, str, len);
}
return(ptr);
......@@ -4502,41 +4459,6 @@ dict_table_get_index_on_name(
}
/**************************************************************************
Find and index that is equivalent to the one passed in. */
UNIV_INTERN
dict_index_t*
dict_table_find_equivalent_index(
/*=============================*/
dict_table_t* table, /* in/out: table */
dict_index_t* index) /* in: index to match */
{
ulint i;
const char** column_names;
dict_index_t* equiv_index;
if (UT_LIST_GET_LEN(table->foreign_list) == 0) {
return(NULL);
}
column_names = mem_alloc(index->n_fields * sizeof *column_names);
/* Convert the column names to the format & type accepted by the find
index function */
for (i = 0; i < index->n_fields; i++) {
column_names[i] = index->fields[i].name;
}
equiv_index = dict_foreign_find_index(
table, column_names, index->n_fields,
index, TRUE, FALSE);
mem_free((void*) column_names);
return(equiv_index);
}
/**************************************************************************
Replace the index passed in with another equivalent index in the tables
foreign key list. */
......@@ -4547,30 +4469,18 @@ dict_table_replace_index_in_foreign_list(
dict_table_t* table, /* in/out: table */
dict_index_t* index) /* in: index to be replaced */
{
dict_index_t* new_index;
new_index = dict_table_find_equivalent_index(table, index);
/* If match found */
if (new_index) {
dict_foreign_t* foreign;
ut_a(new_index != index);
foreign = UT_LIST_GET_FIRST(table->foreign_list);
/* If the list is not empty then this should hold */
ut_a(foreign);
dict_foreign_t* foreign;
/* Iterate over the foreign index list and replace the index
passed in with the new index */
while (foreign) {
for (foreign = UT_LIST_GET_FIRST(table->foreign_list);
foreign;
foreign = UT_LIST_GET_NEXT(foreign_list, foreign)) {
if (foreign->foreign_index == index) {
foreign->foreign_index = new_index;
}
if (foreign->foreign_index == index) {
dict_index_t* new_index
= dict_foreign_find_equiv_index(foreign);
ut_a(new_index);
foreign = UT_LIST_GET_NEXT(foreign_list, foreign);
foreign->foreign_index = new_index;
}
}
}
......
......@@ -304,8 +304,7 @@ static MYSQL_THDVAR_BOOL(table_locks, PLUGIN_VAR_OPCMDARG,
/* check_func */ NULL, /* update_func */ NULL,
/* default */ TRUE);
static MYSQL_THDVAR_BOOL(strict_mode,
PLUGIN_VAR_NOCMDARG,
static MYSQL_THDVAR_BOOL(strict_mode, PLUGIN_VAR_OPCMDARG,
"Use strict mode when evaluating create options.",
NULL, NULL, FALSE);
......@@ -641,6 +640,18 @@ thd_has_edited_nontrans_tables(
return((ibool) thd_non_transactional_update((THD*) thd));
}
/**********************************************************************
Returns true if the thread is executing a SELECT statement. */
extern "C" UNIV_INTERN
ibool
thd_is_select(
/*==========*/
/* out: true if thd is executing SELECT */
const void* thd) /* in: thread handle (THD*) */
{
return(thd_sql_command((const THD*) thd) == SQLCOM_SELECT);
}
/************************************************************************
Obtain the InnoDB transaction of a MySQL thread. */
inline
......@@ -894,41 +905,35 @@ innobase_get_cset_width(
}
/**********************************************************************
Converts an identifier to a table name.
NOTE that the exact prototype of this function has to be in
/innobase/dict/dict0dict.c! */
Converts an identifier to a table name. */
extern "C" UNIV_INTERN
void
innobase_convert_from_table_id(
/*===========================*/
char* to, /* out: converted identifier */
const char* from, /* in: identifier to convert */
ulint len) /* in: length of 'to', in bytes */
struct charset_info_st* cs, /* in: the 'from' character set */
char* to, /* out: converted identifier */
const char* from, /* in: identifier to convert */
ulint len) /* in: length of 'to', in bytes */
{
uint errors;
strconvert(thd_charset(current_thd), from,
&my_charset_filename, to, (uint) len, &errors);
strconvert(cs, from, &my_charset_filename, to, (uint) len, &errors);
}
/**********************************************************************
Converts an identifier to UTF-8.
NOTE that the exact prototype of this function has to be in
/innobase/dict/dict0dict.c! */
Converts an identifier to UTF-8. */
extern "C" UNIV_INTERN
void
innobase_convert_from_id(
/*=====================*/
char* to, /* out: converted identifier */
const char* from, /* in: identifier to convert */
ulint len) /* in: length of 'to', in bytes */
struct charset_info_st* cs, /* in: the 'from' character set */
char* to, /* out: converted identifier */
const char* from, /* in: identifier to convert */
ulint len) /* in: length of 'to', in bytes */
{
uint errors;
strconvert(thd_charset(current_thd), from,
system_charset_info, to, (uint) len, &errors);
strconvert(cs, from, system_charset_info, to, (uint) len, &errors);
}
/**********************************************************************
......@@ -945,10 +950,7 @@ innobase_strcasecmp(
}
/**********************************************************************
Makes all characters in a NUL-terminated UTF-8 string lower case.
NOTE that the exact prototype of this function has to be in
/innobase/dict/dict0dict.c! */
Makes all characters in a NUL-terminated UTF-8 string lower case. */
extern "C" UNIV_INTERN
void
innobase_casedn_str(
......@@ -959,10 +961,7 @@ innobase_casedn_str(
}
/**************************************************************************
Determines the connection character set.
NOTE that the exact prototype of this function has to be in
/innobase/dict/dict0dict.c! */
Determines the connection character set. */
extern "C" UNIV_INTERN
struct charset_info_st*
innobase_get_charset(
......@@ -2709,6 +2708,14 @@ ha_innobase::open(
UT_NOT_USED(test_if_locked);
thd = ha_thd();
/* Under some cases MySQL seems to call this function while
holding btr_search_latch. This breaks the latching order as
we acquire dict_sys->mutex below and leads to a deadlock. */
if (thd != NULL) {
innobase_release_temporary_latches(ht, thd);
}
normalize_table_name(norm_name, name);
user_thd = NULL;
......@@ -6659,9 +6666,21 @@ ha_innobase::info(
stats.index_file_length = ((ulonglong)
ib_table->stat_sum_of_other_index_sizes)
* UNIV_PAGE_SIZE;
stats.delete_length =
fsp_get_available_space_in_free_extents(
ib_table->space) * 1024;
/* Since fsp_get_available_space_in_free_extents() is
acquiring latches inside InnoDB, we do not call it if we
are asked by MySQL to avoid locking. Another reason to
avoid the call is that it uses quite a lot of CPU.
See Bug#38185.
We do not update delete_length if no locking is requested
so the "old" value can remain. delete_length is initialized
to 0 in the ha_statistics' constructor. */
if (!(flag & HA_STATUS_NO_LOCK)) {
stats.delete_length =
fsp_get_available_space_in_free_extents(
ib_table->space) * 1024;
}
stats.check_time = 0;
if (stats.records == 0) {
......@@ -7242,13 +7261,20 @@ UNIV_INTERN
int
ha_innobase::reset()
{
if (prebuilt->blob_heap) {
row_mysql_prebuilt_free_blob_heap(prebuilt);
}
reset_template(prebuilt);
return 0;
}
if (prebuilt->blob_heap) {
row_mysql_prebuilt_free_blob_heap(prebuilt);
}
reset_template(prebuilt);
/* TODO: This should really be reset in reset_template() but for now
it's safer to do it explicitly here. */
/* This is a statement level counter. */
prebuilt->last_value = 0;
return(0);
}
/**********************************************************************
MySQL calls this function at the start of each SQL statement inside LOCK
......
......@@ -983,15 +983,16 @@ ha_innobase::prepare_drop_index(
if (trx->check_foreigns
&& thd_sql_command(user_thd) != SQLCOM_CREATE_INDEX) {
dict_index_t* index
= dict_table_get_first_index(prebuilt->table);
dict_index_t* index;
do {
for (index = dict_table_get_first_index(prebuilt->table);
index;
index = dict_table_get_next_index(index)) {
dict_foreign_t* foreign;
if (!index->to_be_dropped) {
goto next_index;
continue;
}
/* Check if the index is referenced. */
......@@ -1019,20 +1020,61 @@ index_needed:
ut_a(foreign->foreign_index == index);
/* Search for an equivalent index that
the foreign key contraint could use
the foreign key constraint could use
if this index were to be deleted. */
if (!dict_table_find_equivalent_index(
prebuilt->table,
foreign->foreign_index)) {
if (!dict_foreign_find_equiv_index(
foreign)) {
goto index_needed;
}
}
}
}
} else if (thd_sql_command(user_thd) == SQLCOM_CREATE_INDEX) {
/* This is a drop of a foreign key constraint index that
was created by MySQL when the constraint was added. MySQL
does this when the user creates an index explicitly which
can be used in place of the automatically generated index. */
next_index:
index = dict_table_get_next_index(index);
} while (index);
dict_index_t* index;
for (index = dict_table_get_first_index(prebuilt->table);
index;
index = dict_table_get_next_index(index)) {
dict_foreign_t* foreign;
if (!index->to_be_dropped) {
continue;
}
/* Check if this index references some other table */
foreign = dict_table_get_foreign_constraint(
prebuilt->table, index);
if (foreign == NULL) {
continue;
}
ut_a(foreign->foreign_index == index);
/* Search for an equivalent index that the
foreign key constraint could use if this index
were to be deleted. */
if (!dict_foreign_find_equiv_index(foreign)) {
trx_set_detailed_error(
trx,
"Index needed in foreign key "
"constraint");
trx->error_info = foreign->foreign_index;
err = HA_ERR_DROP_INDEX_FK;
break;
}
}
}
func_exit:
......
......@@ -852,9 +852,13 @@ ibuf_set_free_bits_func(
/****************************************************************************
Resets the free bits of the page in the ibuf bitmap. This is done in a
separate mini-transaction, hence this operation does not restrict further
work to only ibuf bitmap operations, which would result if the latch to the
bitmap page were kept. */
separate mini-transaction, hence this operation does not restrict
further work to only ibuf bitmap operations, which would result if the
latch to the bitmap page were kept. NOTE: The free bits in the insert
buffer bitmap must never exceed the free space on a page. It is safe
to decrement or reset the bits in the bitmap in a mini-transaction
that is committed before the mini-transaction that affects the free
space. */
UNIV_INTERN
void
ibuf_reset_free_bits(
......@@ -867,9 +871,13 @@ ibuf_reset_free_bits(
}
/**************************************************************************
Updates the free bits for an uncompressed page to reflect the present state.
Does this in the mtr given, which means that the latching order rules virtually
prevent any further operations for this OS thread until mtr is committed. */
Updates the free bits for an uncompressed page to reflect the present
state. Does this in the mtr given, which means that the latching
order rules virtually prevent any further operations for this OS
thread until mtr is committed. NOTE: The free bits in the insert
buffer bitmap must never exceed the free space on a page. It is safe
to set the free bits in the same mini-transaction that updated the
page. */
UNIV_INTERN
void
ibuf_update_free_bits_low(
......@@ -899,9 +907,13 @@ ibuf_update_free_bits_low(
}
/**************************************************************************
Updates the free bits for a compressed page to reflect the present state.
Does this in the mtr given, which means that the latching order rules virtually
prevent any further operations for this OS thread until mtr is committed. */
Updates the free bits for a compressed page to reflect the present
state. Does this in the mtr given, which means that the latching
order rules virtually prevent any further operations for this OS
thread until mtr is committed. NOTE: The free bits in the insert
buffer bitmap must never exceed the free space on a page. It is safe
to set the free bits in the same mini-transaction that updated the
page. */
UNIV_INTERN
void
ibuf_update_free_bits_zip(
......@@ -940,9 +952,12 @@ ibuf_update_free_bits_zip(
}
/**************************************************************************
Updates the free bits for the two pages to reflect the present state. Does
this in the mtr given, which means that the latching order rules virtually
prevent any further operations until mtr is committed. */
Updates the free bits for the two pages to reflect the present state.
Does this in the mtr given, which means that the latching order rules
virtually prevent any further operations until mtr is committed.
NOTE: The free bits in the insert buffer bitmap must never exceed the
free space on a page. It is safe to set the free bits in the same
mini-transaction that updated the pages. */
UNIV_INTERN
void
ibuf_update_free_bits_for_two_pages_low(
......
......@@ -444,6 +444,15 @@ buf_page_set_old(
{
ut_a(buf_page_in_file(bpage));
ut_ad(buf_pool_mutex_own());
ut_ad(bpage->in_LRU_list);
#ifdef UNIV_LRU_DEBUG
if (UT_LIST_GET_PREV(LRU, bpage) && UT_LIST_GET_NEXT(LRU, bpage)
&& UT_LIST_GET_PREV(LRU, bpage)->old
== UT_LIST_GET_NEXT(LRU, bpage)->old) {
ut_a(UT_LIST_GET_PREV(LRU, bpage)->old == old);
}
#endif /* UNIV_LRU_DEBUG */
bpage->old = old;
}
......
......@@ -420,6 +420,16 @@ dict_table_get_on_id_low(
/* out: table, NULL if does not exist */
dulint table_id); /* in: table id */
/**************************************************************************
Find an index that is equivalent to the one passed in and is not marked
for deletion. */
UNIV_INTERN
dict_index_t*
dict_foreign_find_equiv_index(
/*==========================*/
/* out: index equivalent to
foreign->foreign_index, or NULL */
dict_foreign_t* foreign);/* in: foreign key */
/**************************************************************************
Returns an index object by matching on the name and column names and if
more than index is found return the index with the higher id.*/
UNIV_INTERN
......@@ -1068,17 +1078,6 @@ dict_tables_have_same_db(
const char* name2); /* in: table name in the form
dbname '/' tablename */
/*************************************************************************
Scans from pointer onwards. Stops if is at the start of a copy of
'string' where characters are compared without case sensitivity. Stops
also at '\0'. */
const char*
dict_scan_to(
/*=========*/
/* out: scanned up to this */
const char* ptr, /* in: scan from */
const char* string);/* in: look for this */
/*************************************************************************
Removes an index from the cache */
UNIV_INTERN
void
......@@ -1096,15 +1095,6 @@ dict_table_get_index_on_name(
dict_table_t* table, /* in: table */
const char* name); /* in: name of the index to find */
/**************************************************************************
Find and index that is equivalent to the one passed in and is not marked
for deletion. */
UNIV_INTERN
dict_index_t*
dict_table_find_equivalent_index(
/*=============================*/
dict_table_t* table, /* in/out: table */
dict_index_t* index); /* in: index to match */
/**************************************************************************
In case there is more than one index with the same name return the index
with the min(id). */
UNIV_INTERN
......
......@@ -38,6 +38,8 @@ flst_write_addr(
{
ut_ad(faddr && mtr);
ut_ad(mtr_memo_contains_page(mtr, faddr, MTR_MEMO_PAGE_X_FIX));
ut_a(addr.page == FIL_NULL || addr.boffset >= FIL_PAGE_DATA);
ut_a(ut_align_offset(faddr, UNIV_PAGE_SIZE) >= FIL_PAGE_DATA);
mlog_write_ulint(faddr + FIL_ADDR_PAGE, addr.page, MLOG_4BYTES, mtr);
mlog_write_ulint(faddr + FIL_ADDR_BYTE, addr.boffset,
......@@ -61,6 +63,8 @@ flst_read_addr(
addr.page = mtr_read_ulint(faddr + FIL_ADDR_PAGE, MLOG_4BYTES, mtr);
addr.boffset = mtr_read_ulint(faddr + FIL_ADDR_BYTE, MLOG_2BYTES,
mtr);
ut_a(addr.page == FIL_NULL || addr.boffset >= FIL_PAGE_DATA);
ut_a(ut_align_offset(faddr, UNIV_PAGE_SIZE) >= FIL_PAGE_DATA);
return(addr);
}
......
......@@ -158,5 +158,53 @@ innobase_strcasecmp(
/* out: 0 if a=b, <0 if a<b, >1 if a>b */
const char* a, /* in: first string to compare */
const char* b); /* in: second string to compare */
/**********************************************************************
Returns true if the thread is executing a SELECT statement. */
ibool
thd_is_select(
/*==========*/
/* out: true if thd is executing SELECT */
const void* thd); /* in: thread handle (THD*) */
/**********************************************************************
Converts an identifier to a table name. */
UNIV_INTERN
void
innobase_convert_from_table_id(
/*===========================*/
struct charset_info_st* cs, /* in: the 'from' character set */
char* to, /* out: converted identifier */
const char* from, /* in: identifier to convert */
ulint len); /* in: length of 'to', in bytes; should
be at least 5 * strlen(to) + 1 */
/**********************************************************************
Converts an identifier to UTF-8. */
UNIV_INTERN
void
innobase_convert_from_id(
/*=====================*/
struct charset_info_st* cs, /* in: the 'from' character set */
char* to, /* out: converted identifier */
const char* from, /* in: identifier to convert */
ulint len); /* in: length of 'to', in bytes; should
be at least 3 * strlen(to) + 1 */
/**********************************************************************
Makes all characters in a NUL-terminated UTF-8 string lower case. */
UNIV_INTERN
void
innobase_casedn_str(
/*================*/
char* a); /* in/out: string to put in lower case */
/**************************************************************************
Determines the connection character set. */
struct charset_info_st*
innobase_get_charset(
/*=================*/
/* out: connection character set */
void* mysql_thd); /* in: MySQL thread handle */
#endif
#endif
......@@ -90,7 +90,7 @@ do {\
if (cell3333->node == NULL) {\
cell3333->node = DATA;\
} else {\
struct3333 = cell3333->node;\
struct3333 = (TYPE*) cell3333->node;\
\
while (struct3333->NAME != NULL) {\
\
......
......@@ -31,6 +31,26 @@ typedef enum {
extern ibuf_t* ibuf;
/* The purpose of the insert buffer is to reduce random disk access.
When we wish to insert a record into a non-unique secondary index and
the B-tree leaf page where the record belongs to is not in the buffer
pool, we insert the record into the insert buffer B-tree, indexed by
(space_id, page_no). When the page is eventually read into the buffer
pool, we look up the insert buffer B-tree for any modifications to the
page, and apply these upon the completion of the read operation. This
is called the insert buffer merge. */
/* The insert buffer merge must always succeed. To guarantee this,
the insert buffer subsystem keeps track of the free space in pages for
which it can buffer operations. Two bits per page in the insert
buffer bitmap indicate the available space in coarse increments. The
free bits in the insert buffer bitmap must never exceed the free space
on a page. It is safe to decrement or reset the bits in the bitmap in
a mini-transaction that is committed before the mini-transaction that
affects the free space. It is unsafe to increment the bits in a
separately committed mini-transaction, because in crash recovery, the
free bits could momentarily be set too high. */
/**********************************************************************
Creates the insert buffer data structure at a database startup. */
UNIV_INTERN
......@@ -54,9 +74,13 @@ ibuf_bitmap_page_init(
mtr_t* mtr); /* in: mtr */
/****************************************************************************
Resets the free bits of the page in the ibuf bitmap. This is done in a
separate mini-transaction, hence this operation does not restrict further
work to only ibuf bitmap operations, which would result if the latch to the
bitmap page were kept. */
separate mini-transaction, hence this operation does not restrict
further work to only ibuf bitmap operations, which would result if the
latch to the bitmap page were kept. NOTE: The free bits in the insert
buffer bitmap must never exceed the free space on a page. It is safe
to decrement or reset the bits in the bitmap in a mini-transaction
that is committed before the mini-transaction that affects the free
space. */
UNIV_INTERN
void
ibuf_reset_free_bits(
......@@ -66,10 +90,17 @@ ibuf_reset_free_bits(
non-unique, and page level is 0 */
/****************************************************************************
Updates the free bits of an uncompressed page in the ibuf bitmap if
there is not enough free on the page any more. This is done in a
there is not enough free on the page any more. This is done in a
separate mini-transaction, hence this operation does not restrict
further work to only ibuf bitmap operations, which would result if the
latch to the bitmap page were kept. */
latch to the bitmap page were kept. NOTE: The free bits in the insert
buffer bitmap must never exceed the free space on a page. It is
unsafe to increment the bits in a separately committed
mini-transaction, because in crash recovery, the free bits could
momentarily be set too high. It is only safe to use this function for
decrementing the free bits. Should more free space become available,
we must not update the free bits here, because that would break crash
recovery. */
UNIV_INLINE
void
ibuf_update_free_bits_if_full(
......@@ -86,9 +117,13 @@ ibuf_update_free_bits_if_full(
used in the latest operation, if known, or
ULINT_UNDEFINED */
/**************************************************************************
Updates the free bits for an uncompressed page to reflect the present state.
Does this in the mtr given, which means that the latching order rules virtually
prevent any further operations for this OS thread until mtr is committed. */
Updates the free bits for an uncompressed page to reflect the present
state. Does this in the mtr given, which means that the latching
order rules virtually prevent any further operations for this OS
thread until mtr is committed. NOTE: The free bits in the insert
buffer bitmap must never exceed the free space on a page. It is safe
to set the free bits in the same mini-transaction that updated the
page. */
UNIV_INTERN
void
ibuf_update_free_bits_low(
......@@ -101,9 +136,13 @@ ibuf_update_free_bits_low(
performed to the page */
mtr_t* mtr); /* in/out: mtr */
/**************************************************************************
Updates the free bits for a compressed page to reflect the present state.
Does this in the mtr given, which means that the latching order rules virtually
prevent any further operations for this OS thread until mtr is committed. */
Updates the free bits for a compressed page to reflect the present
state. Does this in the mtr given, which means that the latching
order rules virtually prevent any further operations for this OS
thread until mtr is committed. NOTE: The free bits in the insert
buffer bitmap must never exceed the free space on a page. It is safe
to set the free bits in the same mini-transaction that updated the
page. */
UNIV_INTERN
void
ibuf_update_free_bits_zip(
......@@ -111,9 +150,12 @@ ibuf_update_free_bits_zip(
buf_block_t* block, /* in/out: index page */
mtr_t* mtr); /* in/out: mtr */
/**************************************************************************
Updates the free bits for the two pages to reflect the present state. Does
this in the mtr given, which means that the latching order rules virtually
prevent any further operations until mtr is committed. */
Updates the free bits for the two pages to reflect the present state.
Does this in the mtr given, which means that the latching order rules
virtually prevent any further operations until mtr is committed.
NOTE: The free bits in the insert buffer bitmap must never exceed the
free space on a page. It is safe to set the free bits in the same
mini-transaction that updated the pages. */
UNIV_INTERN
void
ibuf_update_free_bits_for_two_pages_low(
......
......@@ -251,10 +251,17 @@ ibuf_index_page_calc_free(
/****************************************************************************
Updates the free bits of an uncompressed page in the ibuf bitmap if
there is not enough free on the page any more. This is done in a
there is not enough free on the page any more. This is done in a
separate mini-transaction, hence this operation does not restrict
further work to only ibuf bitmap operations, which would result if the
latch to the bitmap page were kept. */
latch to the bitmap page were kept. NOTE: The free bits in the insert
buffer bitmap must never exceed the free space on a page. It is
unsafe to increment the bits in a separately committed
mini-transaction, because in crash recovery, the free bits could
momentarily be set too high. It is only safe to use this function for
decrementing the free bits. Should more free space become available,
we must not update the free bits here, because that would break crash
recovery. */
UNIV_INLINE
void
ibuf_update_free_bits_if_full(
......
......@@ -244,21 +244,25 @@ page_header_reset_last_insert(
uncompressed part will be updated, or NULL */
mtr_t* mtr); /* in: mtr */
/****************************************************************
Gets the first record on the page. */
Gets the offset of the first record on the page. */
UNIV_INLINE
rec_t*
page_get_infimum_rec(
/*=================*/
/* out: the first record in record list */
page_t* page); /* in: page which must have record(s) */
ulint
page_get_infimum_offset(
/*====================*/
/* out: offset of the first record
in record list, relative from page */
const page_t* page); /* in: page which must have record(s) */
/****************************************************************
Gets the last record on the page. */
Gets the offset of the last record on the page. */
UNIV_INLINE
rec_t*
page_get_supremum_rec(
/*==================*/
/* out: the last record in record list */
page_t* page); /* in: page which must have record(s) */
ulint
page_get_supremum_offset(
/*=====================*/
/* out: offset of the last record in
record list, relative from page */
const page_t* page); /* in: page which must have record(s) */
#define page_get_infimum_rec(page) ((page) + page_get_infimum_offset(page))
#define page_get_supremum_rec(page) ((page) + page_get_supremum_offset(page))
/****************************************************************
Returns the middle record of record list. If there are an even number
of records in the list, returns the first record of upper half-list. */
......
......@@ -246,38 +246,42 @@ page_is_leaf(
}
/****************************************************************
Gets the first record on the page. */
Gets the offset of the first record on the page. */
UNIV_INLINE
rec_t*
page_get_infimum_rec(
/*=================*/
/* out: the first record in record list */
page_t* page) /* in: page which must have record(s) */
ulint
page_get_infimum_offset(
/*====================*/
/* out: offset of the first record
in record list, relative from page */
const page_t* page) /* in: page which must have record(s) */
{
ut_ad(page);
ut_ad(!page_offset(page));
if (page_is_comp(page)) {
return(page + PAGE_NEW_INFIMUM);
return(PAGE_NEW_INFIMUM);
} else {
return(page + PAGE_OLD_INFIMUM);
return(PAGE_OLD_INFIMUM);
}
}
/****************************************************************
Gets the last record on the page. */
Gets the offset of the last record on the page. */
UNIV_INLINE
rec_t*
page_get_supremum_rec(
/*==================*/
/* out: the last record in record list */
page_t* page) /* in: page which must have record(s) */
ulint
page_get_supremum_offset(
/*=====================*/
/* out: offset of the last record in
record list, relative from page */
const page_t* page) /* in: page which must have record(s) */
{
ut_ad(page);
ut_ad(!page_offset(page));
if (page_is_comp(page)) {
return(page + PAGE_NEW_SUPREMUM);
return(PAGE_NEW_SUPREMUM);
} else {
return(page + PAGE_OLD_SUPREMUM);
return(PAGE_OLD_SUPREMUM);
}
}
......
......@@ -48,6 +48,8 @@ page_zip_rec_needs_ext(
can be stored locally on the page */
ulint rec_size, /* in: length of the record in bytes */
ulint comp, /* in: nonzero=compact format */
ulint n_fields, /* in: number of fields in the record;
ignored if zip_size == 0 */
ulint zip_size) /* in: compressed page size in bytes, or 0 */
__attribute__((const));
......
......@@ -148,10 +148,13 @@ page_zip_rec_needs_ext(
can be stored locally on the page */
ulint rec_size, /* in: length of the record in bytes */
ulint comp, /* in: nonzero=compact format */
ulint n_fields, /* in: number of fields in the record;
ignored if zip_size == 0 */
ulint zip_size) /* in: compressed page size in bytes, or 0 */
{
ut_ad(rec_size > comp ? REC_N_NEW_EXTRA_BYTES : REC_N_OLD_EXTRA_BYTES);
ut_ad(ut_is_2pow(zip_size));
ut_ad(comp || !zip_size);
#if UNIV_PAGE_SIZE > REC_MAX_DATA_SIZE
if (UNIV_UNLIKELY(rec_size >= REC_MAX_DATA_SIZE)) {
......@@ -159,21 +162,18 @@ page_zip_rec_needs_ext(
}
#endif
if (UNIV_UNLIKELY(!comp)) {
ut_ad(!zip_size);
return(rec_size >= page_get_free_space_of_empty(FALSE) / 2);
if (UNIV_UNLIKELY(zip_size)) {
ut_ad(comp);
/* On a compressed page, there is a two-byte entry in
the dense page directory for every record. But there
is no record header. There should be enough room for
one record on an empty leaf page. Subtract 1 byte for
the encoded heap number. */
return(rec_size - (REC_N_NEW_EXTRA_BYTES - 2)
>= (page_zip_empty_size(n_fields, zip_size) - 1));
}
/* If zip_size != 0, the record should fit on the compressed page.
If not, the right-hand-side of the comparison will overwrap
and the condition will not hold. Thus, we do not need to test
for zip_size != 0. We subtract the size of the page header and
assume that compressing the index information takes 50 bytes. */
if (rec_size >= zip_size - (PAGE_DATA + 50)) {
return(TRUE);
}
return(rec_size >= page_get_free_space_of_empty(TRUE) / 2);
return(rec_size >= page_get_free_space_of_empty(comp) / 2);
}
#ifdef UNIV_DEBUG
......@@ -355,15 +355,7 @@ page_zip_write_header(
{
ulint pos;
#if 0
/* In btr_cur_pessimistic_insert(), we allocate temp_page
from the buffer pool to see if a record fits on a compressed
page by itself. The buf_block_align() call in
buf_frame_get_page_zip() only works for file pages, not
temporarily allocated blocks. Thus, we must unfortunately
disable the following assertion. */
ut_ad(buf_frame_get_page_zip(str) == page_zip);
#endif
ut_ad(page_zip_simple_validate(page_zip));
UNIV_MEM_ASSERT_RW(page_zip->data, page_zip_get_size(page_zip));
......
......@@ -137,6 +137,7 @@ command. Not tested on Windows. */
#define UNIV_DEBUG_FILE_ACCESSES /* Debug .ibd file access
(field file_page_was_freed
in buf_page_t) */
#define UNIV_LRU_DEBUG /* debug the buffer pool LRU */
#define UNIV_HASH_DEBUG /* debug HASH_ macros */
#define UNIV_LIST_DEBUG /* debug UT_LIST_ macros */
#define UNIV_MEM_DEBUG /* detect memory leaks etc */
......
Variable_name Value
innodb_stats_sample_pages 1
#
# Test that mysqld does not crash when running ANALYZE TABLE with
# different values of the parameter innodb_stats_sample_pages.
#
-- source include/have_innodb.inc
# we care only that the following SQL commands do not produce errors
# and do not crash the server
-- disable_query_log
-- disable_result_log
-- enable_warnings
SET GLOBAL innodb_stats_sample_pages=0;
# check that the value has been adjusted to 1
-- enable_result_log
SHOW VARIABLES LIKE 'innodb_stats_sample_pages';
-- disable_result_log
CREATE TABLE innodb_analyze (
a INT,
b INT,
KEY(a),
KEY(b,a)
) ENGINE=InnoDB;
# test with empty table
ANALYZE TABLE innodb_analyze;
SET GLOBAL innodb_stats_sample_pages=2;
ANALYZE TABLE innodb_analyze;
SET GLOBAL innodb_stats_sample_pages=4;
ANALYZE TABLE innodb_analyze;
SET GLOBAL innodb_stats_sample_pages=8;
ANALYZE TABLE innodb_analyze;
SET GLOBAL innodb_stats_sample_pages=16;
ANALYZE TABLE innodb_analyze;
INSERT INTO innodb_analyze VALUES
(1,1), (1,1), (1,2), (1,3), (1,4), (1,5),
(8,1), (8,8), (8,2), (7,1), (1,4), (3,5);
SET GLOBAL innodb_stats_sample_pages=1;
ANALYZE TABLE innodb_analyze;
SET GLOBAL innodb_stats_sample_pages=2;
ANALYZE TABLE innodb_analyze;
SET GLOBAL innodb_stats_sample_pages=4;
ANALYZE TABLE innodb_analyze;
SET GLOBAL innodb_stats_sample_pages=8;
ANALYZE TABLE innodb_analyze;
SET GLOBAL innodb_stats_sample_pages=16;
ANALYZE TABLE innodb_analyze;
DROP TABLE innodb_analyze;
......@@ -169,3 +169,30 @@ t1 CREATE TABLE `t1` (
PRIMARY KEY (`c1`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=latin1
DROP TABLE t1;
DROP TABLE IF EXISTS t1;
Warnings:
Note 1051 Unknown table 't1'
CREATE TABLE t1 (c1 INT AUTO_INCREMENT, c2 INT, PRIMARY KEY(c1)) ENGINE=InnoDB;
INSERT INTO t1 VALUES (NULL, 1);
DELETE FROM t1 WHERE c1 = 1;
INSERT INTO t1 VALUES (2,1);
INSERT INTO t1 VALUES (NULL,8);
SELECT * FROM t1;
c1 c2
2 1
3 8
DROP TABLE t1;
DROP TABLE IF EXISTS t1;
Warnings:
Note 1051 Unknown table 't1'
CREATE TABLE t1 (c1 INT AUTO_INCREMENT, c2 INT, PRIMARY KEY(c1)) ENGINE=InnoDB;
INSERT INTO t1 VALUES (NULL, 1);
DELETE FROM t1 WHERE c1 = 1;
INSERT INTO t1 VALUES (2,1), (NULL, 8);
INSERT INTO t1 VALUES (NULL,9);
SELECT * FROM t1;
c1 c2
2 1
3 8
5 9
DROP TABLE t1;
......@@ -139,3 +139,24 @@ SELECT c1 FROM t1;
SHOW CREATE TABLE t1;
DROP TABLE t1;
#
# Bug 38839
# Reset the last value generated at end of statement
#
DROP TABLE IF EXISTS t1;
CREATE TABLE t1 (c1 INT AUTO_INCREMENT, c2 INT, PRIMARY KEY(c1)) ENGINE=InnoDB;
INSERT INTO t1 VALUES (NULL, 1);
DELETE FROM t1 WHERE c1 = 1;
INSERT INTO t1 VALUES (2,1);
INSERT INTO t1 VALUES (NULL,8);
SELECT * FROM t1;
DROP TABLE t1;
# Bug 38839 -- same as above but for multi value insert
DROP TABLE IF EXISTS t1;
CREATE TABLE t1 (c1 INT AUTO_INCREMENT, c2 INT, PRIMARY KEY(c1)) ENGINE=InnoDB;
INSERT INTO t1 VALUES (NULL, 1);
DELETE FROM t1 WHERE c1 = 1;
INSERT INTO t1 VALUES (2,1), (NULL, 8);
INSERT INTO t1 VALUES (NULL,9);
SELECT * FROM t1;
DROP TABLE t1;
......@@ -960,3 +960,167 @@ t1 CREATE TABLE `t1` (
KEY `t1st` (`s`(1),`t`(1))
) ENGINE=InnoDB DEFAULT CHARSET=latin1
drop table t1;
SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
CREATE TABLE t1(
c1 BIGINT(12) NOT NULL,
PRIMARY KEY (c1)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE t2(
c1 BIGINT(16) NOT NULL,
c2 BIGINT(12) NOT NULL,
c3 BIGINT(12) NOT NULL,
PRIMARY KEY (c1)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE t2 ADD CONSTRAINT fk_t2_ca
FOREIGN KEY (c3) REFERENCES t1(c1);
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`c1` bigint(16) NOT NULL,
`c2` bigint(12) NOT NULL,
`c3` bigint(12) NOT NULL,
PRIMARY KEY (`c1`),
KEY `fk_t2_ca` (`c3`),
CONSTRAINT `fk_t2_ca` FOREIGN KEY (`c3`) REFERENCES `t1` (`c1`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
CREATE INDEX i_t2_c3_c2 ON t2(c3, c2);
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`c1` bigint(16) NOT NULL,
`c2` bigint(12) NOT NULL,
`c3` bigint(12) NOT NULL,
PRIMARY KEY (`c1`),
KEY `i_t2_c3_c2` (`c3`,`c2`),
CONSTRAINT `fk_t2_ca` FOREIGN KEY (`c3`) REFERENCES `t1` (`c1`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
INSERT INTO t2 VALUES(0,0,0);
ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `fk_t2_ca` FOREIGN KEY (`c3`) REFERENCES `t1` (`c1`))
INSERT INTO t1 VALUES(0);
INSERT INTO t2 VALUES(0,0,0);
DROP TABLE t2;
CREATE TABLE t2(
c1 BIGINT(16) NOT NULL,
c2 BIGINT(12) NOT NULL,
c3 BIGINT(12) NOT NULL,
PRIMARY KEY (c1,c2,c3)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE t2 ADD CONSTRAINT fk_t2_ca
FOREIGN KEY (c3) REFERENCES t1(c1);
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`c1` bigint(16) NOT NULL,
`c2` bigint(12) NOT NULL,
`c3` bigint(12) NOT NULL,
PRIMARY KEY (`c1`,`c2`,`c3`),
KEY `fk_t2_ca` (`c3`),
CONSTRAINT `fk_t2_ca` FOREIGN KEY (`c3`) REFERENCES `t1` (`c1`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
CREATE INDEX i_t2_c3_c2 ON t2(c3, c2);
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`c1` bigint(16) NOT NULL,
`c2` bigint(12) NOT NULL,
`c3` bigint(12) NOT NULL,
PRIMARY KEY (`c1`,`c2`,`c3`),
KEY `i_t2_c3_c2` (`c3`,`c2`),
CONSTRAINT `fk_t2_ca` FOREIGN KEY (`c3`) REFERENCES `t1` (`c1`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
INSERT INTO t2 VALUES(0,0,1);
ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `fk_t2_ca` FOREIGN KEY (`c3`) REFERENCES `t1` (`c1`))
INSERT INTO t2 VALUES(0,0,0);
DELETE FROM t1;
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `fk_t2_ca` FOREIGN KEY (`c3`) REFERENCES `t1` (`c1`))
DELETE FROM t2;
DROP TABLE t2;
DROP TABLE t1;
CREATE TABLE t1(
c1 BIGINT(12) NOT NULL,
c2 INT(4) NOT NULL,
PRIMARY KEY (c2,c1)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE t2(
c1 BIGINT(16) NOT NULL,
c2 BIGINT(12) NOT NULL,
c3 BIGINT(12) NOT NULL,
PRIMARY KEY (c1)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE t2 ADD CONSTRAINT fk_t2_ca
FOREIGN KEY (c3,c2) REFERENCES t1(c1,c1);
ERROR HY000: Can't create table '#sql-temporary' (errno: 150)
ALTER TABLE t2 ADD CONSTRAINT fk_t2_ca
FOREIGN KEY (c3,c2) REFERENCES t1(c1,c2);
ERROR HY000: Can't create table '#sql-temporary' (errno: 150)
ALTER TABLE t2 ADD CONSTRAINT fk_t2_ca
FOREIGN KEY (c3,c2) REFERENCES t1(c2,c1);
ERROR HY000: Can't create table '#sql-temporary' (errno: 150)
ALTER TABLE t1 MODIFY COLUMN c2 BIGINT(12) NOT NULL;
ALTER TABLE t2 ADD CONSTRAINT fk_t2_ca
FOREIGN KEY (c3,c2) REFERENCES t1(c1,c2);
ERROR HY000: Can't create table '#sql-temporary' (errno: 150)
ALTER TABLE t2 ADD CONSTRAINT fk_t2_ca
FOREIGN KEY (c3,c2) REFERENCES t1(c2,c1);
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`c1` bigint(12) NOT NULL,
`c2` bigint(12) NOT NULL,
PRIMARY KEY (`c2`,`c1`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`c1` bigint(16) NOT NULL,
`c2` bigint(12) NOT NULL,
`c3` bigint(12) NOT NULL,
PRIMARY KEY (`c1`),
KEY `fk_t2_ca` (`c3`,`c2`),
CONSTRAINT `fk_t2_ca` FOREIGN KEY (`c3`, `c2`) REFERENCES `t1` (`c2`, `c1`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
CREATE INDEX i_t2_c2_c1 ON t2(c2, c1);
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`c1` bigint(16) NOT NULL,
`c2` bigint(12) NOT NULL,
`c3` bigint(12) NOT NULL,
PRIMARY KEY (`c1`),
KEY `fk_t2_ca` (`c3`,`c2`),
KEY `i_t2_c2_c1` (`c2`,`c1`),
CONSTRAINT `fk_t2_ca` FOREIGN KEY (`c3`, `c2`) REFERENCES `t1` (`c2`, `c1`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
CREATE INDEX i_t2_c3_c1_c2 ON t2(c3, c1, c2);
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`c1` bigint(16) NOT NULL,
`c2` bigint(12) NOT NULL,
`c3` bigint(12) NOT NULL,
PRIMARY KEY (`c1`),
KEY `fk_t2_ca` (`c3`,`c2`),
KEY `i_t2_c2_c1` (`c2`,`c1`),
KEY `i_t2_c3_c1_c2` (`c3`,`c1`,`c2`),
CONSTRAINT `fk_t2_ca` FOREIGN KEY (`c3`, `c2`) REFERENCES `t1` (`c2`, `c1`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
CREATE INDEX i_t2_c3_c2 ON t2(c3, c2);
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`c1` bigint(16) NOT NULL,
`c2` bigint(12) NOT NULL,
`c3` bigint(12) NOT NULL,
PRIMARY KEY (`c1`),
KEY `i_t2_c2_c1` (`c2`,`c1`),
KEY `i_t2_c3_c1_c2` (`c3`,`c1`,`c2`),
KEY `i_t2_c3_c2` (`c3`,`c2`),
CONSTRAINT `fk_t2_ca` FOREIGN KEY (`c3`, `c2`) REFERENCES `t1` (`c2`, `c1`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
DROP TABLE t2;
DROP TABLE t1;
......@@ -387,3 +387,114 @@ create index t1ut on t1 (u(1), t(1));
create index t1st on t1 (s(1), t(1));
show create table t1;
drop table t1;
#
# Test to check whether CREATE INDEX handles implicit foreign key
# constraint modifications (Issue #70, Bug #38786)
#
SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
CREATE TABLE t1(
c1 BIGINT(12) NOT NULL,
PRIMARY KEY (c1)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE t2(
c1 BIGINT(16) NOT NULL,
c2 BIGINT(12) NOT NULL,
c3 BIGINT(12) NOT NULL,
PRIMARY KEY (c1)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE t2 ADD CONSTRAINT fk_t2_ca
FOREIGN KEY (c3) REFERENCES t1(c1);
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
SHOW CREATE TABLE t2;
CREATE INDEX i_t2_c3_c2 ON t2(c3, c2);
SHOW CREATE TABLE t2;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
--error ER_NO_REFERENCED_ROW_2
INSERT INTO t2 VALUES(0,0,0);
INSERT INTO t1 VALUES(0);
INSERT INTO t2 VALUES(0,0,0);
DROP TABLE t2;
CREATE TABLE t2(
c1 BIGINT(16) NOT NULL,
c2 BIGINT(12) NOT NULL,
c3 BIGINT(12) NOT NULL,
PRIMARY KEY (c1,c2,c3)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE t2 ADD CONSTRAINT fk_t2_ca
FOREIGN KEY (c3) REFERENCES t1(c1);
SHOW CREATE TABLE t2;
CREATE INDEX i_t2_c3_c2 ON t2(c3, c2);
SHOW CREATE TABLE t2;
--error ER_NO_REFERENCED_ROW_2
INSERT INTO t2 VALUES(0,0,1);
INSERT INTO t2 VALUES(0,0,0);
--error ER_ROW_IS_REFERENCED_2
DELETE FROM t1;
DELETE FROM t2;
DROP TABLE t2;
DROP TABLE t1;
CREATE TABLE t1(
c1 BIGINT(12) NOT NULL,
c2 INT(4) NOT NULL,
PRIMARY KEY (c2,c1)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE t2(
c1 BIGINT(16) NOT NULL,
c2 BIGINT(12) NOT NULL,
c3 BIGINT(12) NOT NULL,
PRIMARY KEY (c1)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--replace_regex /'test\.#sql-[0-9a-f-]*_1'/'#sql-temporary'/
--error ER_CANT_CREATE_TABLE
ALTER TABLE t2 ADD CONSTRAINT fk_t2_ca
FOREIGN KEY (c3,c2) REFERENCES t1(c1,c1);
--replace_regex /'test\.#sql-[0-9a-f-]*_1'/'#sql-temporary'/
--error ER_CANT_CREATE_TABLE
ALTER TABLE t2 ADD CONSTRAINT fk_t2_ca
FOREIGN KEY (c3,c2) REFERENCES t1(c1,c2);
--replace_regex /'test\.#sql-[0-9a-f-]*_1'/'#sql-temporary'/
--error ER_CANT_CREATE_TABLE
ALTER TABLE t2 ADD CONSTRAINT fk_t2_ca
FOREIGN KEY (c3,c2) REFERENCES t1(c2,c1);
ALTER TABLE t1 MODIFY COLUMN c2 BIGINT(12) NOT NULL;
--replace_regex /'test\.#sql-[0-9a-f-]*_1'/'#sql-temporary'/
--error ER_CANT_CREATE_TABLE
ALTER TABLE t2 ADD CONSTRAINT fk_t2_ca
FOREIGN KEY (c3,c2) REFERENCES t1(c1,c2);
ALTER TABLE t2 ADD CONSTRAINT fk_t2_ca
FOREIGN KEY (c3,c2) REFERENCES t1(c2,c1);
SHOW CREATE TABLE t1;
SHOW CREATE TABLE t2;
CREATE INDEX i_t2_c2_c1 ON t2(c2, c1);
SHOW CREATE TABLE t2;
CREATE INDEX i_t2_c3_c1_c2 ON t2(c3, c1, c2);
SHOW CREATE TABLE t2;
CREATE INDEX i_t2_c3_c2 ON t2(c3, c2);
SHOW CREATE TABLE t2;
DROP TABLE t2;
DROP TABLE t1;
......@@ -174,6 +174,11 @@ set global innodb_file_format=``;
ERROR HY000: Incorrect arguments to SET
set global innodb_file_per_table = on;
set global innodb_file_format = `1`;
set innodb_strict_mode = off;
create table t1 (id int primary key) engine = innodb key_block_size = 0;
Warnings:
Warning 1478 InnoDB: ignoring KEY_BLOCK_SIZE=0.
drop table t1;
set innodb_strict_mode = on;
create table t1 (id int primary key) engine = innodb key_block_size = 0;
ERROR HY000: Can't create table 'test.t1' (errno: 1478)
......@@ -376,7 +381,6 @@ test t9 Redundant
drop table t8, t9;
set global innodb_file_per_table=0;
set global innodb_file_format=Antelope;
set innodb_strict_mode=0;
set global innodb_file_per_table=on;
set global innodb_file_format=`Barracuda`;
set global innodb_file_format_check=`Antelope`;
......
......@@ -2,7 +2,6 @@
let $per_table=`select @@innodb_file_per_table`;
let $format=`select @@innodb_file_format`;
let $mode=`select @@innodb_strict_mode`;
set global innodb_file_per_table=off;
set global innodb_file_format=`0`;
......@@ -152,6 +151,10 @@ set global innodb_file_format=``;
set global innodb_file_per_table = on;
set global innodb_file_format = `1`;
set innodb_strict_mode = off;
create table t1 (id int primary key) engine = innodb key_block_size = 0;
drop table t1;
#set strict_mode
set innodb_strict_mode = on;
......@@ -294,7 +297,6 @@ drop table t8, t9;
eval set global innodb_file_per_table=$per_table;
eval set global innodb_file_format=$format;
eval set innodb_strict_mode=$mode;
#
# Testing of tablespace tagging
#
......
......@@ -166,6 +166,7 @@ level id parent_id
1 1007 101
optimize table t1;
Table Op Msg_type Msg_text
test.t1 optimize note Table does not support optimize, doing recreate + analyze instead
test.t1 optimize status OK
show keys from t1;
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment
......@@ -190,6 +191,7 @@ create table t1 (a int) engine=innodb;
insert into t1 values (1), (2);
optimize table t1;
Table Op Msg_type Msg_text
test.t1 optimize note Table does not support optimize, doing recreate + analyze instead
test.t1 optimize status OK
delete from t1 where a = 1;
select * from t1;
......@@ -738,6 +740,7 @@ world 2
hello 1
optimize table t1;
Table Op Msg_type Msg_text
test.t1 optimize note Table does not support optimize, doing recreate + analyze instead
test.t1 optimize status OK
show keys from t1;
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment
......@@ -3111,6 +3114,7 @@ BEGIN;
INSERT INTO t1 VALUES (1);
OPTIMIZE TABLE t1;
Table Op Msg_type Msg_text
test.t1 optimize note Table does not support optimize, doing recreate + analyze instead
test.t1 optimize status OK
DROP TABLE t1;
CREATE TABLE t1 (id int PRIMARY KEY, f int NOT NULL, INDEX(f)) ENGINE=InnoDB;
......
This diff is collapsed.
......@@ -3203,15 +3203,7 @@ page_zip_write_rec(
ulint heap_no;
byte* slot;
#if 0
/* In btr_cur_pessimistic_insert(), we allocate temp_page
from the buffer pool to see if a record fits on a compressed
page by itself. The buf_block_align() call in
buf_frame_get_page_zip() only works for file pages, not
temporarily allocated blocks. Thus, we must unfortunately
disable the following assertion. */
ut_ad(buf_frame_get_page_zip(rec) == page_zip);
#endif
ut_ad(page_zip_simple_validate(page_zip));
ut_ad(page_zip_get_size(page_zip)
> PAGE_DATA + page_zip_dir_size(page_zip));
......@@ -4263,7 +4255,7 @@ page_zip_reorganize(
/* Recreate the page: note that global data on page (possible
segment headers, next page-field, etc.) is preserved intact */
page_create(block, mtr, dict_table_is_comp(index->table));
page_create(block, mtr, TRUE);
block->check_index_page_at_flush = TRUE;
/* Copy the records from the temporary space to the recreated page;
......
......@@ -2181,14 +2181,6 @@ row_merge_create_index(
ut_a(index);
/* Create the index id, as it will be required when we build
the index. We assign the id here because we want to write an
UNDO record before we insert the entry into SYS_INDEXES. */
ut_a(ut_dulint_is_zero(index->id));
index->id = dict_hdr_get_new_id(DICT_HDR_INDEX_ID);
index->table = table;
for (i = 0; i < n_fields; i++) {
merge_index_field_t* ifield = &index_def->fields[i];
......@@ -2196,8 +2188,7 @@ row_merge_create_index(
ifield->prefix_len);
}
/* Add the index to SYS_INDEXES, this will use the prototype
to create an entry in SYS_INDEXES. */
/* Add the index to SYS_INDEXES, using the index prototype. */
err = row_merge_create_index_graph(trx, table, index);
if (err == DB_SUCCESS) {
......
......@@ -32,6 +32,7 @@ Created 12/19/1997 Heikki Tuuri
#include "row0mysql.h"
#include "read0read.h"
#include "buf0lru.h"
#include "ha_prototypes.h"
/* Maximum number of rows to prefetch; MySQL interface has another parameter */
#define SEL_MAX_N_PREFETCH 16
......@@ -3699,19 +3700,11 @@ shortcut_fails_too_big_rec:
if (trx->isolation_level <= TRX_ISO_READ_COMMITTED
&& prebuilt->select_lock_type != LOCK_NONE
&& trx->mysql_thd != NULL
&& trx->mysql_query_str != NULL
&& *trx->mysql_query_str != NULL) {
&& thd_is_select(trx->mysql_thd)) {
/* It is a plain locking SELECT and the isolation
level is low: do not lock gaps */
/* Scan the MySQL query string; check if SELECT is the first
word there */
if (dict_str_starts_with_keyword(
trx->mysql_thd, *trx->mysql_query_str, "SELECT")) {
/* It is a plain locking SELECT and the isolation
level is low: do not lock gaps */
set_also_gap_locks = FALSE;
}
set_also_gap_locks = FALSE;
}
/* Note that if the search mode was GE or G, then the cursor
......
......@@ -1470,7 +1470,7 @@ innobase_start_or_create_for_mysql(void)
ensure that we return the system to a state where normal
recovery is guaranteed to work. We do this by
invalidating the buffer cache, this will force the
reread of the page and restoration to it's last known
reread of the page and restoration to its last known
consistent state, this is REQUIRED for the recovery
process to work. */
err = trx_sys_file_format_max_check(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment