summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorheikki@donna.mysql.fi <>2001-06-03 22:58:03 +0300
committerheikki@donna.mysql.fi <>2001-06-03 22:58:03 +0300
commitd9c18caa20ab8944f44ab1a2cc8ccd8659aac886 (patch)
treece963973f9fcd0dfa6b854cc66c3c0d53fc93ea0
parentf02ed5fe0b1456daf87155d5ef7b56b9ce66dd5c (diff)
downloadmariadb-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.texi2
-rw-r--r--innobase/btr/btr0btr.c123
-rw-r--r--innobase/btr/btr0cur.c9
-rw-r--r--innobase/btr/btr0sea.c11
-rw-r--r--innobase/include/btr0btr.h3
-rw-r--r--innobase/include/btr0cur.h14
-rw-r--r--innobase/include/btr0pcur.h6
-rw-r--r--innobase/include/btr0pcur.ic6
-rw-r--r--innobase/include/row0mysql.h14
-rw-r--r--innobase/include/trx0trx.h14
-rw-r--r--innobase/page/page0page.c142
-rw-r--r--innobase/rem/rem0cmp.c4
-rw-r--r--innobase/row/row0mysql.c143
-rw-r--r--innobase/row/row0sel.c121
-rw-r--r--innobase/trx/trx0trx.c22
-rw-r--r--mysql-test/r/innodb.result2
-rw-r--r--sql/ha_innobase.cc56
-rw-r--r--sql/ha_innobase.h2
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,