Commit d9c18caa authored by heikki@donna.mysql.fi's avatar heikki@donna.mysql.fi

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
parent f02ed5fe
...@@ -26712,7 +26712,7 @@ the maximum size for a table. The minimum tablespace size is 10 MB. ...@@ -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: Contact information of Innobase Oy, producer of the InnoDB engine:
@example @example
Website: www.innobase.fi Website: www.innodb.com
Heikki.Tuuri@@innobase.inet.fi Heikki.Tuuri@@innobase.inet.fi
phone: 358-9-6969 3250 (office) 358-40-5617367 (mobile) phone: 358-9-6969 3250 (office) 358-40-5617367 (mobile)
InnoDB Oy Inc. InnoDB Oy Inc.
...@@ -2238,12 +2238,93 @@ btr_check_node_ptr( ...@@ -2238,12 +2238,93 @@ btr_check_node_ptr(
return(TRUE); return(TRUE);
} }
/****************************************************************
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. */ Validates index tree level. */
static static
void ibool
btr_validate_level( btr_validate_level(
/*===============*/ /*===============*/
/* out: TRUE if ok */
dict_tree_t* tree, /* in: index tree */ dict_tree_t* tree, /* in: index tree */
ulint level) /* in: level number */ ulint level) /* in: level number */
{ {
...@@ -2260,7 +2341,9 @@ btr_validate_level( ...@@ -2260,7 +2341,9 @@ btr_validate_level(
page_cur_t cursor; page_cur_t cursor;
mem_heap_t* heap; mem_heap_t* heap;
dtuple_t* node_ptr_tuple; dtuple_t* node_ptr_tuple;
ibool ret = TRUE;
dict_index_t* index;
mtr_start(&mtr); mtr_start(&mtr);
page = btr_root_get(tree, &mtr); page = btr_root_get(tree, &mtr);
...@@ -2278,13 +2361,31 @@ btr_validate_level( ...@@ -2278,13 +2361,31 @@ btr_validate_level(
page = btr_node_ptr_get_child(node_ptr, &mtr); page = btr_node_ptr_get_child(node_ptr, &mtr);
} }
index = UT_LIST_GET_FIRST(tree->tree_indexes);
/* Now we are on the desired level */ /* Now we are on the desired level */
loop: loop:
mtr_x_lock(dict_tree_get_lock(tree), &mtr); mtr_x_lock(dict_tree_get_lock(tree), &mtr);
/* Check ordering of records */ /* Check ordering etc. of records */
page_validate(page, UT_LIST_GET_FIRST(tree->tree_indexes));
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); ut_a(btr_page_get_level(page, &mtr) == level);
right_page_no = btr_page_get_next(page, &mtr); right_page_no = btr_page_get_next(page, &mtr);
...@@ -2374,14 +2475,17 @@ loop: ...@@ -2374,14 +2475,17 @@ loop:
goto loop; goto loop;
} }
return(ret);
} }
/****************************************************************** /******************************************************************
Checks the consistency of an index tree. */ Checks the consistency of an index tree. */
void ibool
btr_validate_tree( btr_validate_tree(
/*==============*/ /*==============*/
/* out: TRUE if ok */
dict_tree_t* tree) /* in: tree */ dict_tree_t* tree) /* in: tree */
{ {
mtr_t mtr; mtr_t mtr;
...@@ -2397,8 +2501,15 @@ btr_validate_tree( ...@@ -2397,8 +2501,15 @@ btr_validate_tree(
for (i = 0; i <= n; i++) { 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); mtr_commit(&mtr);
return(TRUE);
} }
...@@ -163,9 +163,14 @@ btr_cur_search_to_nth_level( ...@@ -163,9 +163,14 @@ btr_cur_search_to_nth_level(
BTR_INSERT and BTR_ESTIMATE; BTR_INSERT and BTR_ESTIMATE;
cursor->left_page is used to store a pointer cursor->left_page is used to store a pointer
to the left neighbor page, in the cases 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 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 ulint has_search_latch,/* in: info on the latch mode the
caller currently has on btr_search_latch: caller currently has on btr_search_latch:
RW_S_LATCH, or 0 */ RW_S_LATCH, or 0 */
......
...@@ -601,7 +601,12 @@ btr_search_guess_on_hash( ...@@ -601,7 +601,12 @@ btr_search_guess_on_hash(
btr_search_t* info, /* in: index search info */ btr_search_t* info, /* in: index search info */
dtuple_t* tuple, /* in: logical record */ dtuple_t* tuple, /* in: logical record */
ulint mode, /* in: PAGE_CUR_L, ... */ 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 */ btr_cur_t* cursor, /* out: tree cursor */
ulint has_search_latch,/* in: latch mode the caller ulint has_search_latch,/* in: latch mode the caller
currently has on btr_search_latch: currently has on btr_search_latch:
...@@ -722,7 +727,9 @@ btr_search_guess_on_hash( ...@@ -722,7 +727,9 @@ btr_search_guess_on_hash(
} }
if (!success) { if (!success) {
btr_leaf_page_release(page, latch_mode, mtr); if (!has_search_latch) {
btr_leaf_page_release(page, latch_mode, mtr);
}
goto failure; goto failure;
} }
......
...@@ -376,9 +376,10 @@ btr_print_tree( ...@@ -376,9 +376,10 @@ btr_print_tree(
/****************************************************************** /******************************************************************
Checks the consistency of an index tree. */ Checks the consistency of an index tree. */
void ibool
btr_validate_tree( btr_validate_tree(
/*==============*/ /*==============*/
/* out: TRUE if ok */
dict_tree_t* tree); /* in: tree */ dict_tree_t* tree); /* in: tree */
#define BTR_N_LEAF_PAGES 1 #define BTR_N_LEAF_PAGES 1
......
...@@ -98,12 +98,18 @@ btr_cur_search_to_nth_level( ...@@ -98,12 +98,18 @@ btr_cur_search_to_nth_level(
the previous page of the record! Inserts the previous page of the record! Inserts
should always be made using PAGE_CUR_LE to should always be made using PAGE_CUR_LE to
search the position! */ 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 cursor->left_page is used to store a pointer
to the left neighbor page, in the cases to the left neighbor page, in the cases
BTR_SEARCH_PREV and BTR_MODIFY_PREV */ BTR_SEARCH_PREV and BTR_MODIFY_PREV;
btr_cur_t* cursor, /* out: tree cursor; the cursor page is s- or NOTE that if has_search_latch
x-latched */ 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 ulint has_search_latch,/* in: latch mode the caller
currently has on btr_search_latch: currently has on btr_search_latch:
RW_S_LATCH, or 0 */ RW_S_LATCH, or 0 */
......
...@@ -87,7 +87,11 @@ btr_pcur_open_with_no_init( ...@@ -87,7 +87,11 @@ btr_pcur_open_with_no_init(
PAGE_CUR_LE, not PAGE_CUR_GE, as the latter PAGE_CUR_LE, not PAGE_CUR_GE, as the latter
may end up on the previous page of the may end up on the previous page of the
record! */ 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 */ btr_pcur_t* cursor, /* in: memory buffer for persistent cursor */
ulint has_search_latch,/* in: latch mode the caller ulint has_search_latch,/* in: latch mode the caller
currently has on btr_search_latch: currently has on btr_search_latch:
......
...@@ -492,7 +492,11 @@ btr_pcur_open_with_no_init( ...@@ -492,7 +492,11 @@ btr_pcur_open_with_no_init(
PAGE_CUR_LE, not PAGE_CUR_GE, as the latter PAGE_CUR_LE, not PAGE_CUR_GE, as the latter
may end up on the previous page of the may end up on the previous page of the
record! */ 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 */ btr_pcur_t* cursor, /* in: memory buffer for persistent cursor */
ulint has_search_latch,/* in: latch mode the caller ulint has_search_latch,/* in: latch mode the caller
currently has on btr_search_latch: currently has on btr_search_latch:
......
...@@ -229,6 +229,15 @@ row_rename_table_for_mysql( ...@@ -229,6 +229,15 @@ row_rename_table_for_mysql(
char* old_name, /* in: old table name */ char* old_name, /* in: old table name */
char* new_name, /* in: new table name */ char* new_name, /* in: new table name */
trx_t* trx); /* in: transaction handle */ 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 /* A struct describing a place for an individual column in the MySQL
row format which is presented to the table handler in ha_innobase. row format which is presented to the table handler in ha_innobase.
...@@ -281,7 +290,8 @@ struct row_prebuilt_struct { ...@@ -281,7 +290,8 @@ struct row_prebuilt_struct {
is set to TRUE */ is set to TRUE */
dict_index_t* index; /* current index for a search, if any */ dict_index_t* index; /* current index for a search, if any */
ulint template_type; /* ROW_MYSQL_WHOLE_ROW, 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 */ ROW_MYSQL_NO_TEMPLATE */
ulint n_template; /* number of elements in the ulint n_template; /* number of elements in the
template */ template */
...@@ -359,6 +369,8 @@ struct row_prebuilt_struct { ...@@ -359,6 +369,8 @@ struct row_prebuilt_struct {
#define ROW_MYSQL_WHOLE_ROW 0 #define ROW_MYSQL_WHOLE_ROW 0
#define ROW_MYSQL_REC_FIELDS 1 #define ROW_MYSQL_REC_FIELDS 1
#define ROW_MYSQL_NO_TEMPLATE 2 #define ROW_MYSQL_NO_TEMPLATE 2
#define ROW_MYSQL_DUMMY_TEMPLATE 3 /* dummy template used in
row_scan_and_check_index */
#ifndef UNIV_NONINL #ifndef UNIV_NONINL
#include "row0mysql.ic" #include "row0mysql.ic"
......
...@@ -24,6 +24,13 @@ saving CPU time. The kernel mutex contention is increased, however. */ ...@@ -24,6 +24,13 @@ saving CPU time. The kernel mutex contention is increased, however. */
extern ulint trx_n_mysql_transactions; 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. */ Retrieves the error_info field from a trx. */
...@@ -282,6 +289,13 @@ struct trx_struct{ ...@@ -282,6 +289,13 @@ struct trx_struct{
ulint n_mysql_tables_in_use; /* number of Innobase tables ulint n_mysql_tables_in_use; /* number of Innobase tables
used in the processing of the current used in the processing of the current
SQL statement in MySQL */ 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; ibool ignore_duplicates_in_insert;
/* in an insert roll back only insert /* in an insert roll back only insert
of the latest row in case of the latest row in case
......
...@@ -1199,8 +1199,16 @@ page_rec_validate( ...@@ -1199,8 +1199,16 @@ page_rec_validate(
n_owned = rec_get_n_owned(rec); n_owned = rec_get_n_owned(rec);
heap_no = rec_get_heap_no(rec); heap_no = rec_get_heap_no(rec);
ut_a(n_owned <= PAGE_DIR_SLOT_MAX_N_OWNED); if (!(n_owned <= PAGE_DIR_SLOT_MAX_N_OWNED)) {
ut_a(heap_no < page_header_get_field(page, PAGE_N_HEAP)); 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); return(TRUE);
} }
...@@ -1216,20 +1224,21 @@ page_validate( ...@@ -1216,20 +1224,21 @@ page_validate(
dict_index_t* index) /* in: data dictionary index containing dict_index_t* index) /* in: data dictionary index containing
the page record type definition */ the page record type definition */
{ {
page_dir_slot_t* slot;
mem_heap_t* heap; mem_heap_t* heap;
page_cur_t cur;
byte* buf; byte* buf;
ulint i; ulint i;
ulint count; ulint count;
ulint own_count; ulint own_count;
ulint slot_no; ulint slot_no;
ulint data_size; ulint data_size;
page_cur_t cur;
rec_t* rec; rec_t* rec;
rec_t* old_rec = NULL; rec_t* old_rec = NULL;
page_dir_slot_t* slot;
ulint offs; ulint offs;
ulint n_slots; ulint n_slots;
ibool ret = FALSE;
heap = mem_heap_create(UNIV_PAGE_SIZE); heap = mem_heap_create(UNIV_PAGE_SIZE);
/* The following buffer is used to check that the /* The following buffer is used to check that the
...@@ -1244,8 +1253,16 @@ page_validate( ...@@ -1244,8 +1253,16 @@ page_validate(
overlap. */ overlap. */
n_slots = page_dir_get_n_slots(page); 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 /* Validate the record list in a loop checking also that
it is consistent with the directory. */ it is consistent with the directory. */
...@@ -1259,11 +1276,20 @@ page_validate( ...@@ -1259,11 +1276,20 @@ page_validate(
for (;;) { for (;;) {
rec = (&cur)->rec; rec = (&cur)->rec;
page_rec_validate(rec);
if (!page_rec_validate(rec)) {
goto func_exit;
}
/* Check that the records are in the ascending order */ /* Check that the records are in the ascending order */
if ((count >= 2) && (!page_cur_is_after_last(&cur))) { 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)) if ((rec != page_get_supremum_rec(page))
...@@ -1275,16 +1301,38 @@ page_validate( ...@@ -1275,16 +1301,38 @@ page_validate(
offs = rec_get_start(rec) - page; offs = rec_get_start(rec) - page;
for (i = 0; i < rec_get_size(rec); i++) { for (i = 0; i < rec_get_size(rec); i++) {
ut_a(buf[offs + i] == 0); /* No other record may if (!buf[offs + i] == 0) {
overlap this */ /* No other record may overlap this */
fprintf(stderr,
"Record overlaps another in index %s \n",
index->name);
goto func_exit;
}
buf[offs + i] = 1; buf[offs + i] = 1;
} }
if (rec_get_n_owned(rec) != 0) { if (rec_get_n_owned(rec) != 0) {
/* This is a record pointed to by a dir slot */ /* 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); page_dir_slot_check(slot);
own_count = 0; own_count = 0;
...@@ -1297,45 +1345,89 @@ page_validate( ...@@ -1297,45 +1345,89 @@ page_validate(
if (page_cur_is_after_last(&cur)) { if (page_cur_is_after_last(&cur)) {
break; 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); page_cur_move_to_next(&cur);
own_count++; own_count++;
old_rec = rec; old_rec = rec;
} }
ut_a(rec_get_n_owned(rec) != 0); if (rec_get_n_owned(rec) == 0) {
ut_a(slot_no == n_slots - 1); fprintf(stderr, "n owned is zero in index %s\n", index->name);
ut_a(page_header_get_field(page, PAGE_N_RECS) + 2 == count + 1);
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)) { 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)); data_size, page_get_data_size(page));
ut_error; goto func_exit;
} }
/* Check then the free list */ /* Check then the free list */
rec = page_header_get_ptr(page, PAGE_FREE); rec = page_header_get_ptr(page, PAGE_FREE);
while (rec != NULL) { while (rec != NULL) {
page_rec_validate(rec); if (!page_rec_validate(rec)) {
goto func_exit;
}
count++; count++;
offs = rec_get_start(rec) - page; offs = rec_get_start(rec) - page;
for (i = 0; i < rec_get_size(rec); i++) { 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; buf[offs + i] = 1;
} }
rec = page_rec_get_next(rec); 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); mem_heap_free(heap);
return(TRUE); return(ret);
} }
/******************************************************************* /*******************************************************************
......
...@@ -177,7 +177,9 @@ cmp_whole_field( ...@@ -177,7 +177,9 @@ cmp_whole_field(
(int)(type->prtype & ~DATA_NOT_NULL), (int)(type->prtype & ~DATA_NOT_NULL),
a, a_length, b, b_length)); a, a_length, b, b_length));
default: default:
assert(0); fprintf(stderr,
"InnoDB: unknown type number %lu\n", data_type);
ut_a(0);
} }
return(0); return(0);
......
...@@ -1129,3 +1129,146 @@ funct_exit: ...@@ -1129,3 +1129,146 @@ funct_exit:
return((int) err); 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);
}
...@@ -2341,6 +2341,65 @@ row_sel_push_cache_row_for_mysql( ...@@ -2341,6 +2341,65 @@ row_sel_push_cache_row_for_mysql(
prebuilt->n_fetch_cached++; 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 Searches for rows in the database. This is used in the interface to
MySQL. This function opens a cursor, and also implements fetch next MySQL. This function opens a cursor, and also implements fetch next
...@@ -2387,6 +2446,7 @@ row_search_for_mysql( ...@@ -2387,6 +2446,7 @@ row_search_for_mysql(
ibool cons_read_requires_clust_rec; ibool cons_read_requires_clust_rec;
ibool was_lock_wait; ibool was_lock_wait;
ulint ret; ulint ret;
ulint shortcut;
ibool unique_search_from_clust_index = FALSE; ibool unique_search_from_clust_index = FALSE;
ibool mtr_has_extra_clust_latch = FALSE; ibool mtr_has_extra_clust_latch = FALSE;
ibool moves_up = FALSE; ibool moves_up = FALSE;
...@@ -2452,6 +2512,8 @@ row_search_for_mysql( ...@@ -2452,6 +2512,8 @@ row_search_for_mysql(
mode = pcur->search_mode; mode = pcur->search_mode;
} }
mtr_start(&mtr);
if (match_mode == ROW_SEL_EXACT && index->type & DICT_UNIQUE if (match_mode == ROW_SEL_EXACT && index->type & DICT_UNIQUE
&& index->type & DICT_CLUSTERED && index->type & DICT_CLUSTERED
&& dtuple_get_n_fields(search_tuple) && dtuple_get_n_fields(search_tuple)
...@@ -2464,6 +2526,8 @@ row_search_for_mysql( ...@@ -2464,6 +2526,8 @@ row_search_for_mysql(
restore cursor position, and must return restore cursor position, and must return
immediately */ immediately */
mtr_commit(&mtr);
return(DB_RECORD_NOT_FOUND); return(DB_RECORD_NOT_FOUND);
} }
...@@ -2472,8 +2536,51 @@ row_search_for_mysql( ...@@ -2472,8 +2536,51 @@ row_search_for_mysql(
mode = PAGE_CUR_GE; mode = PAGE_CUR_GE;
unique_search_from_clust_index = TRUE; 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 /* Note that if the search mode was GE or G, then the cursor
naturally moves upward (in fetch next) in alphabetical order, naturally moves upward (in fetch next) in alphabetical order,
otherwise downward */ otherwise downward */
...@@ -2485,8 +2592,6 @@ row_search_for_mysql( ...@@ -2485,8 +2592,6 @@ row_search_for_mysql(
} else if (direction == ROW_SEL_NEXT) { } else if (direction == ROW_SEL_NEXT) {
moves_up = TRUE; moves_up = TRUE;
} }
mtr_start(&mtr);
thr = que_fork_get_first_thr(prebuilt->sel_graph); thr = que_fork_get_first_thr(prebuilt->sel_graph);
...@@ -2711,7 +2816,9 @@ rec_loop: ...@@ -2711,7 +2816,9 @@ rec_loop:
if (prebuilt->n_rows_fetched >= MYSQL_FETCH_CACHE_THRESHOLD if (prebuilt->n_rows_fetched >= MYSQL_FETCH_CACHE_THRESHOLD
&& !prebuilt->templ_contains_blob && !prebuilt->templ_contains_blob
&& prebuilt->select_lock_type == LOCK_NONE && 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, /* Inside an update, for example, we do not cache rows,
since we may use the cursor position to do the actual since we may use the cursor position to do the actual
...@@ -2726,7 +2833,13 @@ rec_loop: ...@@ -2726,7 +2833,13 @@ rec_loop:
goto next_rec; goto next_rec;
} else { } 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) { if (prebuilt->clust_index_was_generated) {
row_sel_store_row_id_to_prebuilt(prebuilt, index_rec, row_sel_store_row_id_to_prebuilt(prebuilt, index_rec,
......
...@@ -22,6 +22,7 @@ Created 3/26/1996 Heikki Tuuri ...@@ -22,6 +22,7 @@ Created 3/26/1996 Heikki Tuuri
#include "read0read.h" #include "read0read.h"
#include "srv0srv.h" #include "srv0srv.h"
#include "thr0loc.h" #include "thr0loc.h"
#include "btr0sea.h"
/* Dummy session used currently in MySQL interface */ /* Dummy session used currently in MySQL interface */
sess_t* trx_dummy_sess = NULL; sess_t* trx_dummy_sess = NULL;
...@@ -63,6 +64,7 @@ trx_create( ...@@ -63,6 +64,7 @@ trx_create(
trx->dict_operation = FALSE; trx->dict_operation = FALSE;
trx->n_mysql_tables_in_use = 0; trx->n_mysql_tables_in_use = 0;
trx->mysql_n_tables_locked = 0;
trx->ignore_duplicates_in_insert = FALSE; trx->ignore_duplicates_in_insert = FALSE;
...@@ -96,6 +98,8 @@ trx_create( ...@@ -96,6 +98,8 @@ trx_create(
trx->lock_heap = mem_heap_create_in_buffer(256); trx->lock_heap = mem_heap_create_in_buffer(256);
UT_LIST_INIT(trx->trx_locks); UT_LIST_INIT(trx->trx_locks);
trx->has_search_latch = FALSE;
trx->read_view_heap = mem_heap_create(256); trx->read_view_heap = mem_heap_create(256);
trx->read_view = NULL; trx->read_view = NULL;
...@@ -132,6 +136,21 @@ trx_allocate_for_mysql(void) ...@@ -132,6 +136,21 @@ trx_allocate_for_mysql(void)
return(trx); return(trx);
} }
/************************************************************************
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. */ Frees a transaction object. */
...@@ -149,6 +168,7 @@ trx_free( ...@@ -149,6 +168,7 @@ trx_free(
ut_a(trx->update_undo == NULL); ut_a(trx->update_undo == NULL);
ut_a(trx->n_mysql_tables_in_use == 0); ut_a(trx->n_mysql_tables_in_use == 0);
ut_a(trx->mysql_n_tables_locked == 0);
if (trx->undo_no_arr) { if (trx->undo_no_arr) {
trx_undo_arr_free(trx->undo_no_arr); trx_undo_arr_free(trx->undo_no_arr);
...@@ -160,6 +180,8 @@ trx_free( ...@@ -160,6 +180,8 @@ trx_free(
ut_a(trx->wait_lock == NULL); ut_a(trx->wait_lock == NULL);
ut_a(UT_LIST_GET_LEN(trx->wait_thrs) == 0); ut_a(UT_LIST_GET_LEN(trx->wait_thrs) == 0);
ut_a(!trx->has_search_latch);
if (trx->lock_heap) { if (trx->lock_heap) {
mem_heap_free(trx->lock_heap); mem_heap_free(trx->lock_heap);
} }
......
...@@ -144,7 +144,7 @@ test.t1 optimize error The handler for the table doesn't support check/repair ...@@ -144,7 +144,7 @@ test.t1 optimize error The handler for the table doesn't support check/repair
a a
2 2
Table Op Msg_type Msg_text 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 a b
2 testing 2 testing
Table Op Msg_type Msg_text Table Op Msg_type Msg_text
......
...@@ -2142,6 +2142,7 @@ ha_innobase::external_lock( ...@@ -2142,6 +2142,7 @@ ha_innobase::external_lock(
prebuilt->in_update_remember_pos = TRUE; prebuilt->in_update_remember_pos = TRUE;
if (lock_type == F_WRLCK) { if (lock_type == F_WRLCK) {
/* If this is a SELECT, then it is in UPDATE TABLE ... /* If this is a SELECT, then it is in UPDATE TABLE ...
or SELECT ... FOR UPDATE */ or SELECT ... FOR UPDATE */
prebuilt->select_lock_type = LOCK_X; prebuilt->select_lock_type = LOCK_X;
...@@ -2153,13 +2154,27 @@ ha_innobase::external_lock( ...@@ -2153,13 +2154,27 @@ ha_innobase::external_lock(
} }
trx->n_mysql_tables_in_use++; trx->n_mysql_tables_in_use++;
if (prebuilt->select_lock_type != LOCK_NONE) {
trx->mysql_n_tables_locked++;
}
} else { } else {
trx->n_mysql_tables_in_use--; trx->n_mysql_tables_in_use--;
if (trx->n_mysql_tables_in_use == 0 && if (trx->n_mysql_tables_in_use == 0) {
!(thd->options
& (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN))) { trx->mysql_n_tables_locked = 0;
innobase_commit(thd, trx);
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( ...@@ -2690,6 +2705,39 @@ ha_innobase::info(
DBUG_VOID_RETURN; 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 Adds information about free space in the InnoDB tablespace to a
table comment which is printed out when a user calls SHOW TABLE STATUS. */ table comment which is printed out when a user calls SHOW TABLE STATUS. */
......
...@@ -142,7 +142,7 @@ class ha_innobase: public handler ...@@ -142,7 +142,7 @@ class ha_innobase: public handler
HA_CREATE_INFO *create_info); HA_CREATE_INFO *create_info);
int delete_table(const char *name); int delete_table(const char *name);
int rename_table(const char* from, const char* to); 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); char* update_table_comment(const char* comment);
THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
......
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