Commit 9d4272bb authored by unknown's avatar unknown

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


sql/ha_innobase.cc:
  CHECK TABLE now also for InnoDB, a join speed optimization
sql/ha_innobase.h:
  CHECK TABLE now also for InnoDB, a join speed optimization
mysql-test/r/innodb.result:
  CHECK TABLE now also for InnoDB, a join speed optimization
innobase/btr/btr0btr.c:
  CHECK TABLE now also for InnoDB, a join speed optimization
innobase/btr/btr0cur.c:
  CHECK TABLE now also for InnoDB, a join speed optimization
innobase/btr/btr0sea.c:
  CHECK TABLE now also for InnoDB, a join speed optimization
innobase/include/btr0btr.h:
  CHECK TABLE now also for InnoDB, a join speed optimization
innobase/include/btr0cur.h:
  CHECK TABLE now also for InnoDB, a join speed optimization
innobase/include/btr0pcur.h:
  CHECK TABLE now also for InnoDB, a join speed optimization
innobase/include/btr0pcur.ic:
  CHECK TABLE now also for InnoDB, a join speed optimization
innobase/include/row0mysql.h:
  CHECK TABLE now also for InnoDB, a join speed optimization
innobase/include/trx0trx.h:
  CHECK TABLE now also for InnoDB, a join speed optimization
innobase/page/page0page.c:
  CHECK TABLE now also for InnoDB, a join speed optimization
innobase/rem/rem0cmp.c:
  CHECK TABLE now also for InnoDB, a join speed optimization
innobase/row/row0mysql.c:
  CHECK TABLE now also for InnoDB, a join speed optimization
innobase/row/row0sel.c:
  CHECK TABLE now also for InnoDB, a join speed optimization
innobase/trx/trx0trx.c:
  CHECK TABLE now also for InnoDB, a join speed optimization
Docs/manual.texi:
  website address change
parent 5ec76be4
......@@ -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.
......@@ -2238,12 +2238,93 @@ btr_check_node_ptr(
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. */
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);
}
......@@ -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 */
......
......@@ -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;
}
......
......@@ -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
......
......@@ -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 */
......
......@@ -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:
......
......@@ -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:
......
......@@ -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"
......
......@@ -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
......
......@@ -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);
}
/*******************************************************************
......
......@@ -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);
......
......@@ -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);
}
......@@ -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,
......
......@@ -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;
......@@ -132,6 +136,21 @@ trx_allocate_for_mysql(void)
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. */
......@@ -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);
}
......
......@@ -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
......
......@@ -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. */
......
......@@ -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,
......
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