diff options
author | heikki@donna.mysql.fi <> | 2001-06-03 22:58:03 +0300 |
---|---|---|
committer | heikki@donna.mysql.fi <> | 2001-06-03 22:58:03 +0300 |
commit | d9c18caa20ab8944f44ab1a2cc8ccd8659aac886 (patch) | |
tree | ce963973f9fcd0dfa6b854cc66c3c0d53fc93ea0 | |
parent | f02ed5fe0b1456daf87155d5ef7b56b9ce66dd5c (diff) | |
download | mariadb-git-d9c18caa20ab8944f44ab1a2cc8ccd8659aac886.tar.gz |
manual.texi website address change
row0sel.c CHECK TABLE now also for InnoDB, a join speed optimization
trx0trx.c CHECK TABLE now also for InnoDB, a join speed optimization
rem0cmp.c CHECK TABLE now also for InnoDB, a join speed optimization
row0mysql.c CHECK TABLE now also for InnoDB, a join speed optimization
page0page.c CHECK TABLE now also for InnoDB, a join speed optimization
row0mysql.h CHECK TABLE now also for InnoDB, a join speed optimization
trx0trx.h CHECK TABLE now also for InnoDB, a join speed optimization
btr0btr.h CHECK TABLE now also for InnoDB, a join speed optimization
btr0cur.h CHECK TABLE now also for InnoDB, a join speed optimization
btr0pcur.h CHECK TABLE now also for InnoDB, a join speed optimization
btr0pcur.ic CHECK TABLE now also for InnoDB, a join speed optimization
btr0btr.c CHECK TABLE now also for InnoDB, a join speed optimization
btr0cur.c CHECK TABLE now also for InnoDB, a join speed optimization
btr0sea.c CHECK TABLE now also for InnoDB, a join speed optimization
innodb.result CHECK TABLE now also for InnoDB, a join speed optimization
ha_innobase.cc CHECK TABLE now also for InnoDB, a join speed optimization
ha_innobase.h CHECK TABLE now also for InnoDB, a join speed optimization
-rw-r--r-- | Docs/manual.texi | 2 | ||||
-rw-r--r-- | innobase/btr/btr0btr.c | 123 | ||||
-rw-r--r-- | innobase/btr/btr0cur.c | 9 | ||||
-rw-r--r-- | innobase/btr/btr0sea.c | 11 | ||||
-rw-r--r-- | innobase/include/btr0btr.h | 3 | ||||
-rw-r--r-- | innobase/include/btr0cur.h | 14 | ||||
-rw-r--r-- | innobase/include/btr0pcur.h | 6 | ||||
-rw-r--r-- | innobase/include/btr0pcur.ic | 6 | ||||
-rw-r--r-- | innobase/include/row0mysql.h | 14 | ||||
-rw-r--r-- | innobase/include/trx0trx.h | 14 | ||||
-rw-r--r-- | innobase/page/page0page.c | 142 | ||||
-rw-r--r-- | innobase/rem/rem0cmp.c | 4 | ||||
-rw-r--r-- | innobase/row/row0mysql.c | 143 | ||||
-rw-r--r-- | innobase/row/row0sel.c | 121 | ||||
-rw-r--r-- | innobase/trx/trx0trx.c | 22 | ||||
-rw-r--r-- | mysql-test/r/innodb.result | 2 | ||||
-rw-r--r-- | sql/ha_innobase.cc | 56 | ||||
-rw-r--r-- | sql/ha_innobase.h | 2 |
18 files changed, 639 insertions, 55 deletions
diff --git a/Docs/manual.texi b/Docs/manual.texi index 30d4ae23663..8f45392d3d4 100644 --- a/Docs/manual.texi +++ b/Docs/manual.texi @@ -26712,7 +26712,7 @@ the maximum size for a table. The minimum tablespace size is 10 MB. Contact information of Innobase Oy, producer of the InnoDB engine: @example -Website: www.innobase.fi +Website: www.innodb.com Heikki.Tuuri@@innobase.inet.fi phone: 358-9-6969 3250 (office) 358-40-5617367 (mobile) InnoDB Oy Inc. diff --git a/innobase/btr/btr0btr.c b/innobase/btr/btr0btr.c index 63e70eb1b83..2507f805cd6 100644 --- a/innobase/btr/btr0btr.c +++ b/innobase/btr/btr0btr.c @@ -2239,11 +2239,92 @@ btr_check_node_ptr( } /**************************************************************** +Checks the size and number of fields in a record based on the definition of +the index. */ +static +ibool +btr_index_rec_validate( +/*====================*/ + /* out: TRUE if ok */ + rec_t* rec, /* in: index record */ + dict_index_t* index) /* in: index */ +{ + dtype_t* type; + byte* data; + ulint len; + ulint n; + ulint i; + + n = dict_index_get_n_fields(index); + + if (rec_get_n_fields(rec) != n) { + fprintf(stderr, "Record has %lu fields, should have %lu\n", + rec_get_n_fields(rec), n); + + return(FALSE); + } + + for (i = 0; i < n; i++) { + data = rec_get_nth_field(rec, i, &len); + + type = dict_index_get_nth_type(index, i); + + if (len != UNIV_SQL_NULL && dtype_is_fixed_size(type) + && len != dtype_get_fixed_size(type)) { + fprintf(stderr, + "Record field %lu len is %lu, should be %lu\n", + i, len, dtype_get_fixed_size(type)); + + return(FALSE); + } + } + + return(TRUE); +} + +/**************************************************************** +Checks the size and number of fields in records based on the definition of +the index. */ +static +ibool +btr_index_page_validate( +/*====================*/ + /* out: TRUE if ok */ + page_t* page, /* in: index page */ + dict_index_t* index) /* in: index */ +{ + rec_t* rec; + page_cur_t cur; + ibool ret = TRUE; + + page_cur_set_before_first(page, &cur); + page_cur_move_to_next(&cur); + + for (;;) { + rec = (&cur)->rec; + + if (page_cur_is_after_last(&cur)) { + break; + } + + if (!btr_index_rec_validate(rec, index)) { + + ret = FALSE; + } + + page_cur_move_to_next(&cur); + } + + return(ret); +} + +/**************************************************************** Validates index tree level. */ static -void +ibool btr_validate_level( /*===============*/ + /* out: TRUE if ok */ dict_tree_t* tree, /* in: index tree */ ulint level) /* in: level number */ { @@ -2260,7 +2341,9 @@ btr_validate_level( page_cur_t cursor; mem_heap_t* heap; dtuple_t* node_ptr_tuple; - + ibool ret = TRUE; + dict_index_t* index; + mtr_start(&mtr); page = btr_root_get(tree, &mtr); @@ -2278,13 +2361,31 @@ btr_validate_level( page = btr_node_ptr_get_child(node_ptr, &mtr); } + index = UT_LIST_GET_FIRST(tree->tree_indexes); + /* Now we are on the desired level */ loop: mtr_x_lock(dict_tree_get_lock(tree), &mtr); - /* Check ordering of records */ - page_validate(page, UT_LIST_GET_FIRST(tree->tree_indexes)); + /* Check ordering etc. of records */ + + if (!page_validate(page, index)) { + fprintf(stderr, "Error in page %lu in index %s\n", + buf_frame_get_page_no(page), index->name); + ret = FALSE; + } + + if (level == 0) { + if (!btr_index_page_validate(page, index)) { + fprintf(stderr, + "Error in page %lu in index %s\n", + buf_frame_get_page_no(page), index->name); + + ret = FALSE; + } + } + ut_a(btr_page_get_level(page, &mtr) == level); right_page_no = btr_page_get_next(page, &mtr); @@ -2374,14 +2475,17 @@ loop: goto loop; } + + return(ret); } /****************************************************************** Checks the consistency of an index tree. */ -void +ibool btr_validate_tree( /*==============*/ + /* out: TRUE if ok */ dict_tree_t* tree) /* in: tree */ { mtr_t mtr; @@ -2397,8 +2501,15 @@ btr_validate_tree( for (i = 0; i <= n; i++) { - btr_validate_level(tree, n - i); + if (!btr_validate_level(tree, n - i)) { + + mtr_commit(&mtr); + + return(FALSE); + } } mtr_commit(&mtr); + + return(TRUE); } diff --git a/innobase/btr/btr0cur.c b/innobase/btr/btr0cur.c index e0e59152895..a8680c6b380 100644 --- a/innobase/btr/btr0cur.c +++ b/innobase/btr/btr0cur.c @@ -163,9 +163,14 @@ btr_cur_search_to_nth_level( BTR_INSERT and BTR_ESTIMATE; cursor->left_page is used to store a pointer to the left neighbor page, in the cases - BTR_SEARCH_PREV and BTR_MODIFY_PREV */ + BTR_SEARCH_PREV and BTR_MODIFY_PREV; + NOTE that if has_search_latch + is != 0, we maybe do not have a latch set + on the cursor page, we assume + the caller uses his search latch + to protect the record! */ btr_cur_t* cursor, /* in/out: tree cursor; the cursor page is - s- or x-latched */ + s- or x-latched, but see also above! */ ulint has_search_latch,/* in: info on the latch mode the caller currently has on btr_search_latch: RW_S_LATCH, or 0 */ diff --git a/innobase/btr/btr0sea.c b/innobase/btr/btr0sea.c index 318bf97e7d2..ac4e7c5ba3f 100644 --- a/innobase/btr/btr0sea.c +++ b/innobase/btr/btr0sea.c @@ -601,7 +601,12 @@ btr_search_guess_on_hash( btr_search_t* info, /* in: index search info */ dtuple_t* tuple, /* in: logical record */ ulint mode, /* in: PAGE_CUR_L, ... */ - ulint latch_mode, /* in: BTR_SEARCH_LEAF, ... */ + ulint latch_mode, /* in: BTR_SEARCH_LEAF, ...; + NOTE that only if has_search_latch + is 0, we will have a latch set on + the cursor page, otherwise we assume + the caller uses his search latch + to protect the record! */ btr_cur_t* cursor, /* out: tree cursor */ ulint has_search_latch,/* in: latch mode the caller currently has on btr_search_latch: @@ -722,7 +727,9 @@ btr_search_guess_on_hash( } if (!success) { - btr_leaf_page_release(page, latch_mode, mtr); + if (!has_search_latch) { + btr_leaf_page_release(page, latch_mode, mtr); + } goto failure; } diff --git a/innobase/include/btr0btr.h b/innobase/include/btr0btr.h index d2ac9952695..f8a3000ca8a 100644 --- a/innobase/include/btr0btr.h +++ b/innobase/include/btr0btr.h @@ -376,9 +376,10 @@ btr_print_tree( /****************************************************************** Checks the consistency of an index tree. */ -void +ibool btr_validate_tree( /*==============*/ + /* out: TRUE if ok */ dict_tree_t* tree); /* in: tree */ #define BTR_N_LEAF_PAGES 1 diff --git a/innobase/include/btr0cur.h b/innobase/include/btr0cur.h index 79ec56c8e50..4ce2177bfe8 100644 --- a/innobase/include/btr0cur.h +++ b/innobase/include/btr0cur.h @@ -98,12 +98,18 @@ btr_cur_search_to_nth_level( the previous page of the record! Inserts should always be made using PAGE_CUR_LE to search the position! */ - ulint latch_mode, /* in: BTR_SEARCH_LEAF, ...; + ulint latch_mode, /* in: BTR_SEARCH_LEAF, ..., ORed with + BTR_INSERT and BTR_ESTIMATE; cursor->left_page is used to store a pointer to the left neighbor page, in the cases - BTR_SEARCH_PREV and BTR_MODIFY_PREV */ - btr_cur_t* cursor, /* out: tree cursor; the cursor page is s- or - x-latched */ + BTR_SEARCH_PREV and BTR_MODIFY_PREV; + NOTE that if has_search_latch + is != 0, we maybe do not have a latch set + on the cursor page, we assume + the caller uses his search latch + to protect the record! */ + btr_cur_t* cursor, /* in/out: tree cursor; the cursor page is + s- or x-latched, but see also above! */ ulint has_search_latch,/* in: latch mode the caller currently has on btr_search_latch: RW_S_LATCH, or 0 */ diff --git a/innobase/include/btr0pcur.h b/innobase/include/btr0pcur.h index c07d5199d8c..6465093e3c1 100644 --- a/innobase/include/btr0pcur.h +++ b/innobase/include/btr0pcur.h @@ -87,7 +87,11 @@ btr_pcur_open_with_no_init( PAGE_CUR_LE, not PAGE_CUR_GE, as the latter may end up on the previous page of the record! */ - ulint latch_mode,/* in: BTR_SEARCH_LEAF, ... */ + ulint latch_mode,/* in: BTR_SEARCH_LEAF, ...; + NOTE that if has_search_latch != 0 then + we maybe do not acquire a latch on the cursor + page, but assume that the caller uses his + btr search latch to protect the record! */ btr_pcur_t* cursor, /* in: memory buffer for persistent cursor */ ulint has_search_latch,/* in: latch mode the caller currently has on btr_search_latch: diff --git a/innobase/include/btr0pcur.ic b/innobase/include/btr0pcur.ic index 7f31f8fe502..8e927689208 100644 --- a/innobase/include/btr0pcur.ic +++ b/innobase/include/btr0pcur.ic @@ -492,7 +492,11 @@ btr_pcur_open_with_no_init( PAGE_CUR_LE, not PAGE_CUR_GE, as the latter may end up on the previous page of the record! */ - ulint latch_mode,/* in: BTR_SEARCH_LEAF, ... */ + ulint latch_mode,/* in: BTR_SEARCH_LEAF, ...; + NOTE that if has_search_latch != 0 then + we maybe do not acquire a latch on the cursor + page, but assume that the caller uses his + btr search latch to protect the record! */ btr_pcur_t* cursor, /* in: memory buffer for persistent cursor */ ulint has_search_latch,/* in: latch mode the caller currently has on btr_search_latch: diff --git a/innobase/include/row0mysql.h b/innobase/include/row0mysql.h index d47fa729dce..554da2c035c 100644 --- a/innobase/include/row0mysql.h +++ b/innobase/include/row0mysql.h @@ -229,6 +229,15 @@ row_rename_table_for_mysql( char* old_name, /* in: old table name */ char* new_name, /* in: new table name */ trx_t* trx); /* in: transaction handle */ +/************************************************************************* +Checks a table for corruption. */ + +ulint +row_check_table_for_mysql( +/*======================*/ + /* out: DB_ERROR or DB_SUCCESS */ + row_prebuilt_t* prebuilt); /* in: prebuilt struct in MySQL + handle */ /* A struct describing a place for an individual column in the MySQL row format which is presented to the table handler in ha_innobase. @@ -281,7 +290,8 @@ struct row_prebuilt_struct { is set to TRUE */ dict_index_t* index; /* current index for a search, if any */ ulint template_type; /* ROW_MYSQL_WHOLE_ROW, - ROW_MYSQL_REC_FIELDS or + ROW_MYSQL_REC_FIELDS, + ROW_MYSQL_DUMMY_TEMPLATE, or ROW_MYSQL_NO_TEMPLATE */ ulint n_template; /* number of elements in the template */ @@ -359,6 +369,8 @@ struct row_prebuilt_struct { #define ROW_MYSQL_WHOLE_ROW 0 #define ROW_MYSQL_REC_FIELDS 1 #define ROW_MYSQL_NO_TEMPLATE 2 +#define ROW_MYSQL_DUMMY_TEMPLATE 3 /* dummy template used in + row_scan_and_check_index */ #ifndef UNIV_NONINL #include "row0mysql.ic" diff --git a/innobase/include/trx0trx.h b/innobase/include/trx0trx.h index 52be0b1d992..f67ba43162d 100644 --- a/innobase/include/trx0trx.h +++ b/innobase/include/trx0trx.h @@ -24,6 +24,13 @@ saving CPU time. The kernel mutex contention is increased, however. */ extern ulint trx_n_mysql_transactions; +/************************************************************************ +Releases the search latch if trx has reserved it. */ + +void +trx_search_latch_release_if_reserved( +/*=================================*/ + trx_t* trx); /* in: transaction */ /******************************************************************** Retrieves the error_info field from a trx. */ @@ -282,6 +289,13 @@ struct trx_struct{ ulint n_mysql_tables_in_use; /* number of Innobase tables used in the processing of the current SQL statement in MySQL */ + ulint mysql_n_tables_locked; + /* how many tables the current SQL + statement uses, except those + in consistent read */ + ibool has_search_latch; + /* TRUE if this trx has latched the + search system latch in S-mode */ ibool ignore_duplicates_in_insert; /* in an insert roll back only insert of the latest row in case diff --git a/innobase/page/page0page.c b/innobase/page/page0page.c index 7986684fd07..511191ecd89 100644 --- a/innobase/page/page0page.c +++ b/innobase/page/page0page.c @@ -1199,8 +1199,16 @@ page_rec_validate( n_owned = rec_get_n_owned(rec); heap_no = rec_get_heap_no(rec); - ut_a(n_owned <= PAGE_DIR_SLOT_MAX_N_OWNED); - ut_a(heap_no < page_header_get_field(page, PAGE_N_HEAP)); + if (!(n_owned <= PAGE_DIR_SLOT_MAX_N_OWNED)) { + fprintf(stderr, "Dir slot n owned too big %lu\n", n_owned); + return(FALSE); + } + + if (!(heap_no < page_header_get_field(page, PAGE_N_HEAP))) { + fprintf(stderr, "Heap no too big %lu %lu\n", heap_no, + page_header_get_field(page, PAGE_N_HEAP)); + return(FALSE); + } return(TRUE); } @@ -1216,20 +1224,21 @@ page_validate( dict_index_t* index) /* in: data dictionary index containing the page record type definition */ { + page_dir_slot_t* slot; mem_heap_t* heap; + page_cur_t cur; byte* buf; ulint i; ulint count; ulint own_count; ulint slot_no; ulint data_size; - page_cur_t cur; rec_t* rec; rec_t* old_rec = NULL; - page_dir_slot_t* slot; ulint offs; ulint n_slots; - + ibool ret = FALSE; + heap = mem_heap_create(UNIV_PAGE_SIZE); /* The following buffer is used to check that the @@ -1244,8 +1253,16 @@ page_validate( overlap. */ n_slots = page_dir_get_n_slots(page); - ut_ad(page_header_get_ptr(page, PAGE_HEAP_TOP) <= - page_dir_get_nth_slot(page, n_slots - 1)); + + if (!(page_header_get_ptr(page, PAGE_HEAP_TOP) <= + page_dir_get_nth_slot(page, n_slots - 1))) { + fprintf(stderr, + "Record heap and dir overlap on a page in index %s, %lu, %lu\n", + index->name, page_header_get_ptr(page, PAGE_HEAP_TOP), + page_dir_get_nth_slot(page, n_slots - 1)); + + goto func_exit; + } /* Validate the record list in a loop checking also that it is consistent with the directory. */ @@ -1259,11 +1276,20 @@ page_validate( for (;;) { rec = (&cur)->rec; - page_rec_validate(rec); + + if (!page_rec_validate(rec)) { + goto func_exit; + } /* Check that the records are in the ascending order */ if ((count >= 2) && (!page_cur_is_after_last(&cur))) { - ut_a(1 == cmp_rec_rec(rec, old_rec, index)); + if (!(1 == cmp_rec_rec(rec, old_rec, index))) { + fprintf(stderr, + "Records in wrong order in index %s\n", + index->name); + + goto func_exit; + } } if ((rec != page_get_supremum_rec(page)) @@ -1275,16 +1301,38 @@ page_validate( offs = rec_get_start(rec) - page; for (i = 0; i < rec_get_size(rec); i++) { - ut_a(buf[offs + i] == 0); /* No other record may - overlap this */ + if (!buf[offs + i] == 0) { + /* No other record may overlap this */ + + fprintf(stderr, + "Record overlaps another in index %s \n", + index->name); + + goto func_exit; + } + buf[offs + i] = 1; } if (rec_get_n_owned(rec) != 0) { /* This is a record pointed to by a dir slot */ - ut_a(rec_get_n_owned(rec) == own_count); + if (rec_get_n_owned(rec) != own_count) { + fprintf(stderr, + "Wrong owned count %lu, %lu, in index %s\n", + rec_get_n_owned(rec), own_count, + index->name); - ut_a(page_dir_slot_get_rec(slot) == rec); + goto func_exit; + } + + if (page_dir_slot_get_rec(slot) != rec) { + fprintf(stderr, + "Dir slot does not point to right rec in %s\n", + index->name); + + goto func_exit; + } + page_dir_slot_check(slot); own_count = 0; @@ -1297,45 +1345,89 @@ page_validate( if (page_cur_is_after_last(&cur)) { break; } - - count++; + + if (rec_get_next_offs(rec) < FIL_PAGE_DATA + || rec_get_next_offs(rec) >= UNIV_PAGE_SIZE) { + fprintf(stderr, + "Next record offset wrong %lu in index %s\n", + rec_get_next_offs(rec), index->name); + + goto func_exit; + } + + count++; page_cur_move_to_next(&cur); own_count++; old_rec = rec; } - ut_a(rec_get_n_owned(rec) != 0); - ut_a(slot_no == n_slots - 1); - ut_a(page_header_get_field(page, PAGE_N_RECS) + 2 == count + 1); + if (rec_get_n_owned(rec) == 0) { + fprintf(stderr, "n owned is zero in index %s\n", index->name); + + goto func_exit; + } + + if (slot_no != n_slots - 1) { + fprintf(stderr, "n slots wrong %lu %lu in index %s\n", + slot_no, n_slots - 1, index->name); + goto func_exit; + } + + if (page_header_get_field(page, PAGE_N_RECS) + 2 != count + 1) { + fprintf(stderr, "n recs wrong %lu %lu in index %s\n", + page_header_get_field(page, PAGE_N_RECS) + 2, count + 1, + index->name); + + goto func_exit; + } if (data_size != page_get_data_size(page)) { - printf("Summed data size %lu, returned by func %lu\n", + fprintf(stderr, "Summed data size %lu, returned by func %lu\n", data_size, page_get_data_size(page)); - ut_error; + goto func_exit; } /* Check then the free list */ rec = page_header_get_ptr(page, PAGE_FREE); while (rec != NULL) { - page_rec_validate(rec); + if (!page_rec_validate(rec)) { + + goto func_exit; + } count++; offs = rec_get_start(rec) - page; for (i = 0; i < rec_get_size(rec); i++) { - ut_a(buf[offs + i] == 0); + + if (buf[offs + i] != 0) { + fprintf(stderr, + "Record overlaps another in free list, index %s \n", + index->name); + + goto func_exit; + } + buf[offs + i] = 1; } rec = page_rec_get_next(rec); } - ut_a(page_header_get_field(page, PAGE_N_HEAP) == count + 1); - + if (page_header_get_field(page, PAGE_N_HEAP) != count + 1) { + + fprintf(stderr, "N heap is wrong %lu %lu in index %s\n", + page_header_get_field(page, PAGE_N_HEAP), count + 1, + index->name); + } + + ret = TRUE; + +func_exit: mem_heap_free(heap); - return(TRUE); + return(ret); } /******************************************************************* diff --git a/innobase/rem/rem0cmp.c b/innobase/rem/rem0cmp.c index d5208f2d486..78f4e450269 100644 --- a/innobase/rem/rem0cmp.c +++ b/innobase/rem/rem0cmp.c @@ -177,7 +177,9 @@ cmp_whole_field( (int)(type->prtype & ~DATA_NOT_NULL), a, a_length, b, b_length)); default: - assert(0); + fprintf(stderr, + "InnoDB: unknown type number %lu\n", data_type); + ut_a(0); } return(0); diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c index ec24b40f5c2..b40e026c675 100644 --- a/innobase/row/row0mysql.c +++ b/innobase/row/row0mysql.c @@ -1129,3 +1129,146 @@ funct_exit: return((int) err); } + +/************************************************************************* +Checks that the index contains entries in an ascending order, unique +constraint is not broken, and calculates the number of index entries +in the read view of the current transaction. */ +static +ibool +row_scan_and_check_index( +/*=====================*/ + /* out: TRUE if ok */ + row_prebuilt_t* prebuilt, /* in: prebuilt struct in MySQL */ + dict_index_t* index, /* in: index */ + ulint* n_rows) /* out: number of entries seen in the + current consistent read */ +{ + mem_heap_t* heap; + dtuple_t* prev_entry = NULL; + ulint matched_fields; + ulint matched_bytes; + byte* buf; + ulint ret; + rec_t* rec; + ibool is_ok = TRUE; + int cmp; + + *n_rows = 0; + + buf = mem_alloc(UNIV_PAGE_SIZE); + heap = mem_heap_create(100); + + /* Make a dummy template in prebuilt, which we will use + in scanning the index entries */ + + prebuilt->index = index; + prebuilt->sql_stat_start = TRUE; + prebuilt->template_type = ROW_MYSQL_DUMMY_TEMPLATE; + prebuilt->n_template = 0; + prebuilt->need_to_access_clustered = FALSE; + + dtuple_set_n_fields(prebuilt->search_tuple, 0); + + prebuilt->select_lock_type = LOCK_NONE; + + ret = row_search_for_mysql(buf, PAGE_CUR_G, prebuilt, 0, 0); +loop: + if (ret != DB_SUCCESS) { + + mem_free(buf); + mem_heap_free(heap); + + return(is_ok); + } + + *n_rows = *n_rows + 1; + + /* row_search... returns the index record in buf, record origin offset + within buf stored in the first 4 bytes, because we have built a dummy + template */ + + rec = buf + mach_read_from_4(buf); + + if (prev_entry != NULL) { + matched_fields = 0; + matched_bytes = 0; + + cmp = cmp_dtuple_rec_with_match(prev_entry, rec, + &matched_fields, + &matched_bytes); + if (cmp > 0) { + fprintf(stderr, + "Error: index records in a wrong order in index %s\n", + index->name); + + is_ok = FALSE; + } else if ((index->type & DICT_UNIQUE) + && matched_fields >= + dict_index_get_n_ordering_defined_by_user(index)) { + fprintf(stderr, + "Error: duplicate key in index %s\n", + index->name); + + is_ok = FALSE; + } + } + + mem_heap_empty(heap); + + prev_entry = row_rec_to_index_entry(ROW_COPY_DATA, index, rec, heap); + + ret = row_search_for_mysql(buf, PAGE_CUR_G, prebuilt, 0, ROW_SEL_NEXT); + + goto loop; +} + +/************************************************************************* +Checks a table for corruption. */ + +ulint +row_check_table_for_mysql( +/*======================*/ + /* out: DB_ERROR or DB_SUCCESS */ + row_prebuilt_t* prebuilt) /* in: prebuilt struct in MySQL + handle */ +{ + dict_table_t* table = prebuilt->table; + dict_index_t* index; + ulint n_rows; + ulint n_rows_in_table; + ulint ret = DB_SUCCESS; + + index = dict_table_get_first_index(table); + + while (index != NULL) { + /* fprintf(stderr, "Validating index %s\n", index->name); */ + + if (!btr_validate_tree(index->tree)) { + ret = DB_ERROR; + } else { + if (!row_scan_and_check_index(prebuilt, + index, &n_rows)) { + ret = DB_ERROR; + } + + /* fprintf(stderr, "%lu entries in index %s\n", n_rows, + index->name); */ + + if (index == dict_table_get_first_index(table)) { + n_rows_in_table = n_rows; + } else if (n_rows != n_rows_in_table) { + + ret = DB_ERROR; + + fprintf(stderr, + "Error: index %s contains %lu entries, should be %lu\n", + index->name, n_rows, n_rows_in_table); + } + } + + index = dict_table_get_next_index(index); + } + + return(ret); +} diff --git a/innobase/row/row0sel.c b/innobase/row/row0sel.c index 58e0d053947..e3bab021669 100644 --- a/innobase/row/row0sel.c +++ b/innobase/row/row0sel.c @@ -2341,6 +2341,65 @@ row_sel_push_cache_row_for_mysql( prebuilt->n_fetch_cached++; } +/************************************************************************* +Tries to do a shortcut to fetch a clustered index record with a unique key, +using the hash index if possible (not always). We assume that the search +mode is PAGE_CUR_GE, it is a consistent read, trx has already a read view, +btr search latch has been locked in S-mode. */ +static +ulint +row_sel_try_search_shortcut_for_mysql( +/*==================================*/ + /* out: SEL_FOUND, SEL_EXHAUSTED, SEL_RETRY */ + rec_t** out_rec,/* out: record if found */ + row_prebuilt_t* prebuilt,/* in: prebuilt struct */ + mtr_t* mtr) /* in: started mtr */ +{ + dict_index_t* index = prebuilt->index; + dtuple_t* search_tuple = prebuilt->search_tuple; + btr_pcur_t* pcur = prebuilt->pcur; + trx_t* trx = prebuilt->trx; + rec_t* rec; + + ut_ad(index->type & DICT_CLUSTERED); + + btr_pcur_open_with_no_init(index, search_tuple, PAGE_CUR_GE, + BTR_SEARCH_LEAF, pcur, + RW_S_LATCH, mtr); + rec = btr_pcur_get_rec(pcur); + + if (!page_rec_is_user_rec(rec)) { + + return(SEL_RETRY); + } + + /* As the cursor is now placed on a user record after a search with + the mode PAGE_CUR_GE, the up_match field in the cursor tells how many + fields in the user record matched to the search tuple */ + + if (btr_pcur_get_up_match(pcur) < dtuple_get_n_fields(search_tuple)) { + + return(SEL_EXHAUSTED); + } + + /* This is a non-locking consistent read: if necessary, fetch + a previous version of the record */ + + if (!lock_clust_rec_cons_read_sees(rec, index, trx->read_view)) { + + return(SEL_RETRY); + } + + if (rec_get_deleted_flag(rec)) { + + return(SEL_EXHAUSTED); + } + + *out_rec = rec; + + return(SEL_FOUND); +} + /************************************************************************ Searches for rows in the database. This is used in the interface to MySQL. This function opens a cursor, and also implements fetch next @@ -2387,6 +2446,7 @@ row_search_for_mysql( ibool cons_read_requires_clust_rec; ibool was_lock_wait; ulint ret; + ulint shortcut; ibool unique_search_from_clust_index = FALSE; ibool mtr_has_extra_clust_latch = FALSE; ibool moves_up = FALSE; @@ -2452,6 +2512,8 @@ row_search_for_mysql( mode = pcur->search_mode; } + mtr_start(&mtr); + if (match_mode == ROW_SEL_EXACT && index->type & DICT_UNIQUE && index->type & DICT_CLUSTERED && dtuple_get_n_fields(search_tuple) @@ -2464,6 +2526,8 @@ row_search_for_mysql( restore cursor position, and must return immediately */ + mtr_commit(&mtr); + return(DB_RECORD_NOT_FOUND); } @@ -2472,8 +2536,51 @@ row_search_for_mysql( mode = PAGE_CUR_GE; unique_search_from_clust_index = TRUE; + + if (trx->mysql_n_tables_locked == 0 + && !prebuilt->sql_stat_start) { + + /* This is a SELECT query done as a consistent read, + and the read view has already been allocated: + let us try a search shortcut through the hash + index */ + + if (!trx->has_search_latch) { + rw_lock_s_lock(&btr_search_latch); + trx->has_search_latch = TRUE; + + } else if (btr_search_latch.writer_is_wait_ex) { + /* There is an x-latch request waiting: + release the s-latch for a moment to reduce + starvation */ + + rw_lock_s_unlock(&btr_search_latch); + rw_lock_s_lock(&btr_search_latch); + } + + shortcut = row_sel_try_search_shortcut_for_mysql(&rec, + prebuilt, &mtr); + if (shortcut == SEL_FOUND) { + row_sel_store_mysql_rec(buf, prebuilt, rec); + + mtr_commit(&mtr); + + return(DB_SUCCESS); + + } else if (shortcut == SEL_EXHAUSTED) { + + mtr_commit(&mtr); + + return(DB_RECORD_NOT_FOUND); + } + } } + if (trx->has_search_latch) { + rw_lock_s_unlock(&btr_search_latch); + trx->has_search_latch = FALSE; + } + /* Note that if the search mode was GE or G, then the cursor naturally moves upward (in fetch next) in alphabetical order, otherwise downward */ @@ -2485,8 +2592,6 @@ row_search_for_mysql( } else if (direction == ROW_SEL_NEXT) { moves_up = TRUE; } - - mtr_start(&mtr); thr = que_fork_get_first_thr(prebuilt->sel_graph); @@ -2711,7 +2816,9 @@ rec_loop: if (prebuilt->n_rows_fetched >= MYSQL_FETCH_CACHE_THRESHOLD && !prebuilt->templ_contains_blob && prebuilt->select_lock_type == LOCK_NONE - && !prebuilt->clust_index_was_generated) { + && !prebuilt->clust_index_was_generated + && prebuilt->template_type + != ROW_MYSQL_DUMMY_TEMPLATE) { /* Inside an update, for example, we do not cache rows, since we may use the cursor position to do the actual @@ -2726,7 +2833,13 @@ rec_loop: goto next_rec; } else { - row_sel_store_mysql_rec(buf, prebuilt, rec); + if (prebuilt->template_type == ROW_MYSQL_DUMMY_TEMPLATE) { + ut_memcpy(buf + 4, rec - rec_get_extra_size(rec), + rec_get_size(rec)); + mach_write_to_4(buf, rec_get_extra_size(rec) + 4); + } else { + row_sel_store_mysql_rec(buf, prebuilt, rec); + } if (prebuilt->clust_index_was_generated) { row_sel_store_row_id_to_prebuilt(prebuilt, index_rec, diff --git a/innobase/trx/trx0trx.c b/innobase/trx/trx0trx.c index 4841711551b..14108c677eb 100644 --- a/innobase/trx/trx0trx.c +++ b/innobase/trx/trx0trx.c @@ -22,6 +22,7 @@ Created 3/26/1996 Heikki Tuuri #include "read0read.h" #include "srv0srv.h" #include "thr0loc.h" +#include "btr0sea.h" /* Dummy session used currently in MySQL interface */ sess_t* trx_dummy_sess = NULL; @@ -63,6 +64,7 @@ trx_create( trx->dict_operation = FALSE; trx->n_mysql_tables_in_use = 0; + trx->mysql_n_tables_locked = 0; trx->ignore_duplicates_in_insert = FALSE; @@ -96,6 +98,8 @@ trx_create( trx->lock_heap = mem_heap_create_in_buffer(256); UT_LIST_INIT(trx->trx_locks); + trx->has_search_latch = FALSE; + trx->read_view_heap = mem_heap_create(256); trx->read_view = NULL; @@ -133,6 +137,21 @@ trx_allocate_for_mysql(void) } /************************************************************************ +Releases the search latch if trx has reserved it. */ + +void +trx_search_latch_release_if_reserved( +/*=================================*/ + trx_t* trx) /* in: transaction */ +{ + if (trx->has_search_latch) { + rw_lock_s_unlock(&btr_search_latch); + + trx->has_search_latch = FALSE; + } +} + +/************************************************************************ Frees a transaction object. */ void @@ -149,6 +168,7 @@ trx_free( ut_a(trx->update_undo == NULL); ut_a(trx->n_mysql_tables_in_use == 0); + ut_a(trx->mysql_n_tables_locked == 0); if (trx->undo_no_arr) { trx_undo_arr_free(trx->undo_no_arr); @@ -160,6 +180,8 @@ trx_free( ut_a(trx->wait_lock == NULL); ut_a(UT_LIST_GET_LEN(trx->wait_thrs) == 0); + ut_a(!trx->has_search_latch); + if (trx->lock_heap) { mem_heap_free(trx->lock_heap); } diff --git a/mysql-test/r/innodb.result b/mysql-test/r/innodb.result index d21863e7231..52251e8f87a 100644 --- a/mysql-test/r/innodb.result +++ b/mysql-test/r/innodb.result @@ -144,7 +144,7 @@ test.t1 optimize error The handler for the table doesn't support check/repair a 2 Table Op Msg_type Msg_text -test.t1 check error The handler for the table doesn't support check/repair +test.t1 check status OK a b 2 testing Table Op Msg_type Msg_text diff --git a/sql/ha_innobase.cc b/sql/ha_innobase.cc index fa44cebe19d..4a69056a9e2 100644 --- a/sql/ha_innobase.cc +++ b/sql/ha_innobase.cc @@ -2142,6 +2142,7 @@ ha_innobase::external_lock( prebuilt->in_update_remember_pos = TRUE; if (lock_type == F_WRLCK) { + /* If this is a SELECT, then it is in UPDATE TABLE ... or SELECT ... FOR UPDATE */ prebuilt->select_lock_type = LOCK_X; @@ -2153,13 +2154,27 @@ ha_innobase::external_lock( } trx->n_mysql_tables_in_use++; + + if (prebuilt->select_lock_type != LOCK_NONE) { + + trx->mysql_n_tables_locked++; + } } else { trx->n_mysql_tables_in_use--; - if (trx->n_mysql_tables_in_use == 0 && - !(thd->options - & (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN))) { - innobase_commit(thd, trx); + if (trx->n_mysql_tables_in_use == 0) { + + trx->mysql_n_tables_locked = 0; + + if (trx->has_search_latch) { + + trx_search_latch_release_if_reserved(trx); + } + + if (!(thd->options + & (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN))) { + innobase_commit(thd, trx); + } } } @@ -2690,6 +2705,39 @@ ha_innobase::info( DBUG_VOID_RETURN; } +/*********************************************************************** +Tries to check that an InnoDB table is not corrupted. If corruption is +noticed, prints to stderr information about it. In case of corruption +may also assert a failure and crash the server. */ + +int +ha_innobase::check( +/*===============*/ + /* out: HA_ADMIN_CORRUPT or + HA_ADMIN_OK */ + THD* thd, /* in: user thread handle */ + HA_CHECK_OPT* check_opt) /* in: check options, currently + ignored */ +{ + row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; + ulint ret; + + if (prebuilt->mysql_template == NULL) { + /* Build the template; we will use a dummy template + in index scans done in checking */ + + build_template(prebuilt, NULL, table, ROW_MYSQL_WHOLE_ROW); + } + + ret = row_check_table_for_mysql(prebuilt); + + if (ret == DB_SUCCESS) { + return(HA_ADMIN_OK); + } + + return(HA_ADMIN_CORRUPT); +} + /***************************************************************** Adds information about free space in the InnoDB tablespace to a table comment which is printed out when a user calls SHOW TABLE STATUS. */ diff --git a/sql/ha_innobase.h b/sql/ha_innobase.h index 258e34cbf86..d832ac93d0f 100644 --- a/sql/ha_innobase.h +++ b/sql/ha_innobase.h @@ -142,7 +142,7 @@ class ha_innobase: public handler HA_CREATE_INFO *create_info); int delete_table(const char *name); int rename_table(const char* from, const char* to); - + int check(THD* thd, HA_CHECK_OPT* check_opt); char* update_table_comment(const char* comment); THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, |