summaryrefslogtreecommitdiff
path: root/innobase
diff options
context:
space:
mode:
Diffstat (limited to 'innobase')
-rw-r--r--innobase/btr/btr0cur.c20
-rw-r--r--innobase/buf/buf0lru.c1
-rw-r--r--innobase/dict/dict0crea.c21
-rw-r--r--innobase/dict/dict0dict.c54
-rw-r--r--innobase/dict/dict0mem.c3
-rw-r--r--innobase/fil/fil0fil.c10
-rw-r--r--innobase/include/dict0dict.h17
-rw-r--r--innobase/include/dict0mem.h6
-rw-r--r--innobase/include/log0recv.h1
-rw-r--r--innobase/include/mem0dbg.h14
-rw-r--r--innobase/include/mem0mem.h112
-rw-r--r--innobase/include/mem0mem.ic57
-rw-r--r--innobase/include/mem0pool.h12
-rw-r--r--innobase/include/page0page.ic2
-rw-r--r--innobase/include/que0que.h5
-rw-r--r--innobase/include/row0mysql.h27
-rw-r--r--innobase/include/srv0srv.h32
-rw-r--r--innobase/include/trx0sys.h35
-rw-r--r--innobase/include/trx0trx.h39
-rw-r--r--innobase/log/log0recv.c23
-rw-r--r--innobase/mem/mem0dbg.c94
-rw-r--r--innobase/mem/mem0mem.c94
-rw-r--r--innobase/mem/mem0pool.c36
-rw-r--r--innobase/os/os0file.c51
-rw-r--r--innobase/pars/lexyy.c2
-rw-r--r--innobase/que/que0que.c40
-rw-r--r--innobase/rem/rem0cmp.c10
-rw-r--r--innobase/rem/rem0rec.c11
-rw-r--r--innobase/row/row0mysql.c473
-rw-r--r--innobase/row/row0sel.c11
-rw-r--r--innobase/srv/srv0srv.c219
-rw-r--r--innobase/srv/srv0start.c24
-rw-r--r--innobase/sync/sync0arr.c11
-rw-r--r--innobase/trx/trx0purge.c4
-rw-r--r--innobase/trx/trx0sys.c111
-rw-r--r--innobase/trx/trx0trx.c65
-rw-r--r--innobase/trx/trx0undo.c19
-rw-r--r--innobase/ut/ut0mem.c6
38 files changed, 1531 insertions, 241 deletions
diff --git a/innobase/btr/btr0cur.c b/innobase/btr/btr0cur.c
index 9f3d02525d8..d6944ecadc8 100644
--- a/innobase/btr/btr0cur.c
+++ b/innobase/btr/btr0cur.c
@@ -2552,6 +2552,7 @@ btr_estimate_number_of_different_key_vals(
ulint total_external_size = 0;
ulint i;
ulint j;
+ ulint add_on;
mtr_t mtr;
n_cols = dict_index_get_n_unique(index);
@@ -2624,8 +2625,25 @@ btr_estimate_number_of_different_key_vals(
+ not_empty_flag)
/ (BTR_KEY_VAL_ESTIMATE_N_PAGES
+ total_external_size);
- }
+ /* If the tree is small, smaller than <
+ 10 * BTR_KEY_VAL_ESTIMATE_N_PAGES + total_external_size, then
+ the above estimate is ok. For bigger trees it is common that we
+ do not see any borders between key values in the few pages
+ we pick. But still there may be BTR_KEY_VAL_ESTIMATE_N_PAGES
+ different key values, or even more. Let us try to approximate
+ that: */
+
+ add_on = index->stat_n_leaf_pages /
+ (10 * (BTR_KEY_VAL_ESTIMATE_N_PAGES + total_external_size));
+
+ if (add_on > BTR_KEY_VAL_ESTIMATE_N_PAGES) {
+ add_on = BTR_KEY_VAL_ESTIMATE_N_PAGES;
+ }
+
+ index->stat_n_diff_key_vals[j] += add_on;
+ }
+
mem_free(n_diff);
}
diff --git a/innobase/buf/buf0lru.c b/innobase/buf/buf0lru.c
index a3bb673dfc8..5ab045212c2 100644
--- a/innobase/buf/buf0lru.c
+++ b/innobase/buf/buf0lru.c
@@ -243,7 +243,6 @@ loop:
if (n_iterations > 30) {
ut_print_timestamp(stderr);
fprintf(stderr,
- " ***********************************************\n"
"InnoDB: Warning: difficult to find free blocks from\n"
"InnoDB: the buffer pool (%lu search iterations)! Consider\n"
"InnoDB: increasing the buffer pool size.\n",
diff --git a/innobase/dict/dict0crea.c b/innobase/dict/dict0crea.c
index 7b9255b4e27..69b1e7c61fd 100644
--- a/innobase/dict/dict0crea.c
+++ b/innobase/dict/dict0crea.c
@@ -1235,16 +1235,23 @@ loop:
if (error != DB_SUCCESS) {
fprintf(stderr,
- "InnoDB: foreign constraint creation failed;\n"
+ "InnoDB: Foreign key constraint creation failed:\n"
"InnoDB: internal error number %lu\n", error);
- ut_a(error == DB_OUT_OF_FILE_SPACE);
-
- fprintf(stderr, "InnoDB: tablespace is full\n");
-
- trx_general_rollback_for_mysql(trx, FALSE, NULL);
+ if (error == DB_DUPLICATE_KEY) {
+ fprintf(stderr,
+ "InnoDB: Duplicate key error in system table %s index %s\n",
+ ((dict_index_t*)trx->error_info)->table_name,
+ ((dict_index_t*)trx->error_info)->name);
- error = DB_MUST_GET_MORE_FILE_SPACE;
+ fprintf(stderr, "%s\n", buf);
+
+ fprintf(stderr,
+ "InnoDB: Maybe the internal data dictionary of InnoDB is\n"
+ "InnoDB: out-of-sync from the .frm files of your tables.\n"
+ "InnoDB: See section 15.1 Troubleshooting data dictionary operations\n"
+ "InnoDB: at http://www.innodb.com/ibman.html\n");
+ }
return(error);
}
diff --git a/innobase/dict/dict0dict.c b/innobase/dict/dict0dict.c
index b5a826dde02..91bc770320f 100644
--- a/innobase/dict/dict0dict.c
+++ b/innobase/dict/dict0dict.c
@@ -196,6 +196,23 @@ dict_mutex_exit_for_mysql(void)
}
/************************************************************************
+Decrements the count of open MySQL handles to a table. */
+
+void
+dict_table_decrement_handle_count(
+/*==============================*/
+ dict_table_t* table) /* in: table */
+{
+ mutex_enter(&(dict_sys->mutex));
+
+ ut_a(table->n_mysql_handles_opened > 0);
+
+ table->n_mysql_handles_opened--;
+
+ mutex_exit(&(dict_sys->mutex));
+}
+
+/************************************************************************
Gets the nth column of a table. */
dict_col_t*
@@ -464,6 +481,41 @@ dict_table_get(
}
/**************************************************************************
+Returns a table object and increments MySQL open handle count on the table.
+*/
+
+dict_table_t*
+dict_table_get_and_increment_handle_count(
+/*======================================*/
+ /* out: table, NULL if does not exist */
+ char* table_name, /* in: table name */
+ trx_t* trx) /* in: transaction handle or NULL */
+{
+ dict_table_t* table;
+
+ UT_NOT_USED(trx);
+
+ mutex_enter(&(dict_sys->mutex));
+
+ table = dict_table_get_low(table_name);
+
+ if (table != NULL) {
+
+ table->n_mysql_handles_opened++;
+ }
+
+ mutex_exit(&(dict_sys->mutex));
+
+ if (table != NULL) {
+ if (!table->stat_initialized) {
+ dict_update_statistics(table);
+ }
+ }
+
+ return(table);
+}
+
+/**************************************************************************
Adds a table object to the dictionary cache. */
void
@@ -1935,7 +1987,7 @@ loop:
ptr = dict_accept(ptr, (char *) "FOREIGN", &success);
if (!isspace(*ptr)) {
- return(DB_CANNOT_ADD_CONSTRAINT);
+ goto loop;
}
ptr = dict_accept(ptr, (char *) "KEY", &success);
diff --git a/innobase/dict/dict0mem.c b/innobase/dict/dict0mem.c
index 019d6ef334f..1f9a44aca35 100644
--- a/innobase/dict/dict0mem.c
+++ b/innobase/dict/dict0mem.c
@@ -59,6 +59,9 @@ dict_mem_table_create(
table->n_def = 0;
table->n_cols = n_cols + DATA_N_SYS_COLS;
table->mem_fix = 0;
+
+ table->n_mysql_handles_opened = 0;
+
table->cached = FALSE;
table->cols = mem_heap_alloc(heap, (n_cols + DATA_N_SYS_COLS)
diff --git a/innobase/fil/fil0fil.c b/innobase/fil/fil0fil.c
index f1f2bc4fa17..727e52707e8 100644
--- a/innobase/fil/fil0fil.c
+++ b/innobase/fil/fil0fil.c
@@ -1088,7 +1088,15 @@ loop:
node = UT_LIST_GET_FIRST(space->chain);
for (;;) {
- ut_a(node);
+ if (node == NULL) {
+ fprintf(stderr,
+ "InnoDB: Error: trying to access page number %lu in space %lu\n"
+ "InnoDB: which is outside the tablespace bounds.\n"
+ "InnoDB: Byte offset %lu, len %lu, i/o type %lu\n",
+ block_offset, space_id, byte_offset, len, type);
+
+ ut_a(0);
+ }
if (node->size > block_offset) {
/* Found! */
diff --git a/innobase/include/dict0dict.h b/innobase/include/dict0dict.h
index ae313398c99..0f6f516c2cb 100644
--- a/innobase/include/dict0dict.h
+++ b/innobase/include/dict0dict.h
@@ -26,6 +26,13 @@ Created 1/8/1996 Heikki Tuuri
#include "ut0byte.h"
#include "trx0types.h"
+/************************************************************************
+Decrements the count of open MySQL handles to a table. */
+
+void
+dict_table_decrement_handle_count(
+/*==============================*/
+ dict_table_t* table); /* in: table */
/**************************************************************************
Inits the data dictionary module. */
@@ -181,6 +188,16 @@ dict_table_get(
char* table_name, /* in: table name */
trx_t* trx); /* in: transaction handle */
/**************************************************************************
+Returns a table object and increments MySQL open handle count on the table.
+*/
+
+dict_table_t*
+dict_table_get_and_increment_handle_count(
+/*======================================*/
+ /* out: table, NULL if does not exist */
+ char* table_name, /* in: table name */
+ trx_t* trx); /* in: transaction handle or NULL */
+/**************************************************************************
Returns a table object, based on table id, and memoryfixes it. */
dict_table_t*
diff --git a/innobase/include/dict0mem.h b/innobase/include/dict0mem.h
index 2f00ccfcf32..5ef0103087a 100644
--- a/innobase/include/dict0mem.h
+++ b/innobase/include/dict0mem.h
@@ -307,6 +307,12 @@ struct dict_table_struct{
ulint mem_fix;/* count of how many times the table
and its indexes has been fixed in memory;
currently NOT used */
+ ulint n_mysql_handles_opened;
+ /* count of how many handles MySQL has opened
+ to this table; dropping of the table is
+ NOT allowed until this count gets to zero;
+ MySQL does NOT itself check the number of
+ open handles at drop */
ibool cached; /* TRUE if the table object has been added
to the dictionary cache */
lock_t* auto_inc_lock;/* a buffer for an auto-inc lock
diff --git a/innobase/include/log0recv.h b/innobase/include/log0recv.h
index 51f14393d38..8f896756db9 100644
--- a/innobase/include/log0recv.h
+++ b/innobase/include/log0recv.h
@@ -258,6 +258,7 @@ struct recv_sys_struct{
extern recv_sys_t* recv_sys;
extern ibool recv_recovery_on;
extern ibool recv_no_ibuf_operations;
+extern ibool recv_needed_recovery;
/* States of recv_addr_struct */
#define RECV_NOT_PROCESSED 71
diff --git a/innobase/include/mem0dbg.h b/innobase/include/mem0dbg.h
index dda37626198..0b1aa53d694 100644
--- a/innobase/include/mem0dbg.h
+++ b/innobase/include/mem0dbg.h
@@ -10,11 +10,14 @@ Created 6/9/1994 Heikki Tuuri
/* In the debug version each allocated field is surrounded with
check fields whose sizes are given below */
+#ifdef UNIV_MEM_DEBUG
#define MEM_FIELD_HEADER_SIZE ut_calc_align(2 * sizeof(ulint),\
UNIV_MEM_ALIGNMENT)
#define MEM_FIELD_TRAILER_SIZE sizeof(ulint)
+#else
+#define MEM_FIELD_HEADER_SIZE 0
+#endif
-#define MEM_BLOCK_MAGIC_N 764741
/* Space needed when allocating for a user a field of
length N. The space is allocated only in multiples of
@@ -115,3 +118,12 @@ ibool
mem_validate(void);
/*===============*/
/* out: TRUE if ok */
+/****************************************************************
+Tries to find neigboring memory allocation blocks and dumps to stderr
+the neighborhood of a given pointer. */
+
+void
+mem_analyze_corruption(
+/*===================*/
+ byte* ptr); /* in: pointer to place of possible corruption */
+
diff --git a/innobase/include/mem0mem.h b/innobase/include/mem0mem.h
index 95024cf8011..89c5428f054 100644
--- a/innobase/include/mem0mem.h
+++ b/innobase/include/mem0mem.h
@@ -61,58 +61,41 @@ mem_init(
/******************************************************************
Use this macro instead of the corresponding function! Macro for memory
heap creation. */
-#ifdef UNIV_MEM_DEBUG
+
#define mem_heap_create(N) mem_heap_create_func(\
(N), NULL, MEM_HEAP_DYNAMIC,\
IB__FILE__, __LINE__)
-#else
-#define mem_heap_create(N) mem_heap_create_func(N, NULL, MEM_HEAP_DYNAMIC)
-#endif
/******************************************************************
Use this macro instead of the corresponding function! Macro for memory
heap creation. */
-#ifdef UNIV_MEM_DEBUG
+
#define mem_heap_create_in_buffer(N) mem_heap_create_func(\
(N), NULL, MEM_HEAP_BUFFER,\
IB__FILE__, __LINE__)
-#else
-#define mem_heap_create_in_buffer(N) mem_heap_create_func(N, NULL,\
- MEM_HEAP_BUFFER)
-#endif
/******************************************************************
Use this macro instead of the corresponding function! Macro for memory
heap creation. */
-#ifdef UNIV_MEM_DEBUG
+
#define mem_heap_create_in_btr_search(N) mem_heap_create_func(\
(N), NULL, MEM_HEAP_BTR_SEARCH |\
MEM_HEAP_BUFFER,\
IB__FILE__, __LINE__)
-#else
-#define mem_heap_create_in_btr_search(N) mem_heap_create_func(N, NULL,\
- MEM_HEAP_BTR_SEARCH | MEM_HEAP_BUFFER)
-#endif
/******************************************************************
Use this macro instead of the corresponding function! Macro for fast
memory heap creation. An initial block of memory B is given by the
caller, N is its size, and this memory block is not freed by
mem_heap_free. See the parameter comment in mem_heap_create_func below. */
-#ifdef UNIV_MEM_DEBUG
+
#define mem_heap_fast_create(N, B) mem_heap_create_func(\
(N), (B), MEM_HEAP_DYNAMIC,\
IB__FILE__, __LINE__)
-#else
-#define mem_heap_fast_create(N, B) mem_heap_create_func(N, (B),\
- MEM_HEAP_DYNAMIC)
-#endif
+
/******************************************************************
Use this macro instead of the corresponding function! Macro for memory
heap freeing. */
-#ifdef UNIV_MEM_DEBUG
+
#define mem_heap_free(heap) mem_heap_free_func(\
(heap), IB__FILE__, __LINE__)
-#else
-#define mem_heap_free(heap) mem_heap_free_func(heap)
-#endif
/*********************************************************************
NOTE: Use the corresponding macros instead of this function. Creates a
memory heap which allocates memory from dynamic space. For debugging
@@ -139,11 +122,9 @@ mem_heap_create_func(
block is not unintentionally erased
(if allocated in the stack), before
the memory heap is explicitly freed. */
- ulint type /* in: MEM_HEAP_DYNAMIC or MEM_HEAP_BUFFER */
- #ifdef UNIV_MEM_DEBUG
- ,char* file_name, /* in: file name where created */
+ ulint type, /* in: MEM_HEAP_DYNAMIC or MEM_HEAP_BUFFER */
+ char* file_name, /* in: file name where created */
ulint line /* in: line where created */
- #endif
);
/*********************************************************************
NOTE: Use the corresponding macro instead of this function.
@@ -152,11 +133,9 @@ UNIV_INLINE
void
mem_heap_free_func(
/*===============*/
- mem_heap_t* heap /* in, own: heap to be freed */
- #ifdef UNIV_MEM_DEBUG
- ,char* file_name, /* in: file name where freed */
- ulint line /* in: line where freed */
- #endif
+ mem_heap_t* heap, /* in, own: heap to be freed */
+ char* file_name, /* in: file name where freed */
+ ulint line /* in: line where freed */
);
/*******************************************************************
Allocates n bytes of memory from a memory heap. */
@@ -220,25 +199,18 @@ UNIV_INLINE
ulint
mem_heap_get_size(
/*==============*/
- mem_heap_t* heap); /* in: heap */
+ mem_heap_t* heap); /* in: heap */
/******************************************************************
Use this macro instead of the corresponding function!
Macro for memory buffer allocation */
-#ifdef UNIV_MEM_DEBUG
-#define mem_alloc(N) mem_alloc_func(\
- (N), IB__FILE__, __LINE__)
-#else
-#define mem_alloc(N) mem_alloc_func(N)
-#endif
+
+#define mem_alloc(N) mem_alloc_func((N), IB__FILE__, __LINE__)
/******************************************************************
Use this macro instead of the corresponding function!
Macro for memory buffer allocation */
-#ifdef UNIV_MEM_DEBUG
+
#define mem_alloc_noninline(N) mem_alloc_func_noninline(\
(N), IB__FILE__, __LINE__)
-#else
-#define mem_alloc_noninline(N) mem_alloc_func_noninline(N)
-#endif
/*******************************************************************
NOTE: Use the corresponding macro instead of this function.
Allocates a single buffer of memory from the dynamic memory of
@@ -250,11 +222,9 @@ mem_alloc_func(
/*===========*/
/* out, own: free storage, NULL
if did not succeed */
- ulint n /* in: desired number of bytes */
- #ifdef UNIV_MEM_DEBUG
- ,char* file_name, /* in: file name where created */
- ulint line /* in: line where created */
- #endif
+ ulint n, /* in: desired number of bytes */
+ char* file_name, /* in: file name where created */
+ ulint line /* in: line where created */
);
/*******************************************************************
NOTE: Use the corresponding macro instead of this function.
@@ -267,21 +237,15 @@ mem_alloc_func_noninline(
/*=====================*/
/* out, own: free storage, NULL if did not
succeed */
- ulint n /* in: desired number of bytes */
- #ifdef UNIV_MEM_DEBUG
- ,char* file_name, /* in: file name where created */
+ ulint n, /* in: desired number of bytes */
+ char* file_name, /* in: file name where created */
ulint line /* in: line where created */
- #endif
);
/******************************************************************
Use this macro instead of the corresponding function!
Macro for memory buffer freeing */
-#ifdef UNIV_MEM_DEBUG
-#define mem_free(PTR) mem_free_func(\
- (PTR), IB__FILE__, __LINE__)
-#else
-#define mem_free(PTR) mem_free_func(PTR)
-#endif
+
+#define mem_free(PTR) mem_free_func((PTR), IB__FILE__, __LINE__)
/*******************************************************************
NOTE: Use the corresponding macro instead of this function.
Frees a single buffer of storage from
@@ -290,11 +254,9 @@ UNIV_INLINE
void
mem_free_func(
/*==========*/
- void* ptr /* in, own: buffer to be freed */
- #ifdef UNIV_MEM_DEBUG
- ,char* file_name, /* in: file name where created */
- ulint line /* in: line where created */
- #endif
+ void* ptr, /* in, own: buffer to be freed */
+ char* file_name, /* in: file name where created */
+ ulint line /* in: line where created */
);
/*******************************************************************
Implements realloc. */
@@ -304,14 +266,27 @@ mem_realloc(
/*========*/
/* out, own: free storage, NULL if did not succeed */
void* buf, /* in: pointer to an old buffer */
- ulint n); /* in: desired number of bytes */
+ ulint n, /* in: desired number of bytes */
+ char* file_name,/* in: file name where called */
+ ulint line); /* in: line where called */
+#ifdef MEM_PERIODIC_CHECK
+/**********************************************************************
+Goes through the list of all allocated mem blocks, checks their magic
+numbers, and reports possible corruption. */
+void
+mem_validate_all_blocks(void);
+/*=========================*/
+#endif
/*#######################################################################*/
/* The info header of a block in a memory heap */
struct mem_block_info_struct {
+ ulint magic_n;/* magic number for debugging */
+ char file_name[8];/* file name where the mem heap was created */
+ ulint line; /* line number where the mem heap was created */
UT_LIST_BASE_NODE_T(mem_block_t) base; /* In the first block in the
the list this is the base node of the list of blocks;
in subsequent blocks this is undefined */
@@ -335,9 +310,16 @@ struct mem_block_info_struct {
allocated buffer frame, which can be appended as a
free block to the heap, if we need more space;
otherwise, this is NULL */
- ulint magic_n;/* magic number for debugging */
+#ifdef MEM_PERIODIC_CHECK
+ UT_LIST_NODE_T(mem_block_t) mem_block_list;
+ /* List of all mem blocks allocated; protected
+ by the mem_comm_pool mutex */
+#endif
};
+#define MEM_BLOCK_MAGIC_N 764741555
+#define MEM_FREED_BLOCK_MAGIC_N 547711122
+
/* Header size for a memory heap block */
#define MEM_BLOCK_HEADER_SIZE ut_calc_align(sizeof(mem_block_info_t),\
UNIV_MEM_ALIGNMENT)
diff --git a/innobase/include/mem0mem.ic b/innobase/include/mem0mem.ic
index edc3ab17f2a..a7abb93d91d 100644
--- a/innobase/include/mem0mem.ic
+++ b/innobase/include/mem0mem.ic
@@ -24,8 +24,10 @@ mem_heap_create_block(
if init_block is not NULL, its size in bytes */
void* init_block, /* in: init block in fast create, type must be
MEM_HEAP_DYNAMIC */
- ulint type); /* in: type of heap: MEM_HEAP_DYNAMIC or
+ ulint type, /* in: type of heap: MEM_HEAP_DYNAMIC or
MEM_HEAP_BUFFER */
+ char* file_name,/* in: file name where created */
+ ulint line); /* in: line where created */
/**********************************************************************
Frees a block from a memory heap. */
@@ -392,21 +394,20 @@ mem_heap_create_func(
block is not unintentionally erased
(if allocated in the stack), before
the memory heap is explicitly freed. */
- ulint type /* in: MEM_HEAP_DYNAMIC, or MEM_HEAP_BUFFER
+ ulint type, /* in: MEM_HEAP_DYNAMIC, or MEM_HEAP_BUFFER
possibly ORed to MEM_HEAP_BTR_SEARCH */
- #ifdef UNIV_MEM_DEBUG
- ,char* file_name, /* in: file name where created */
+ char* file_name, /* in: file name where created */
ulint line /* in: line where created */
- #endif
)
{
mem_block_t* block;
if (n > 0) {
- block = mem_heap_create_block(NULL, n, init_block, type);
+ block = mem_heap_create_block(NULL, n, init_block, type,
+ file_name, line);
} else {
block = mem_heap_create_block(NULL, MEM_BLOCK_START_SIZE,
- init_block, type);
+ init_block, type, file_name, line);
}
ut_ad(block);
@@ -438,11 +439,9 @@ UNIV_INLINE
void
mem_heap_free_func(
/*===============*/
- mem_heap_t* heap /* in, own: heap to be freed */
- #ifdef UNIV_MEM_DEBUG
- ,char* file_name, /* in: file name where freed */
+ mem_heap_t* heap, /* in, own: heap to be freed */
+ char* file_name, /* in: file name where freed */
ulint line /* in: line where freed */
- #endif
)
{
mem_block_t* block;
@@ -488,14 +487,12 @@ mem_alloc_func(
/*===========*/
/* out, own: free storage, NULL if did not
succeed */
- ulint n /* in: desired number of bytes */
- #ifdef UNIV_MEM_DEBUG
- ,char* file_name, /* in: file name where created */
+ ulint n, /* in: desired number of bytes */
+ char* file_name, /* in: file name where created */
ulint line /* in: line where created */
- #endif
)
{
- #ifndef UNIV_MEM_DEBUG
+#ifdef notdefined
void* buf;
buf = mem_area_alloc(n, mem_comm_pool);
@@ -505,7 +502,7 @@ mem_alloc_func(
#endif
return(buf);
- #else
+#else
mem_heap_t* heap;
void* buf;
@@ -524,11 +521,11 @@ mem_alloc_func(
buf = mem_heap_alloc(heap, n);
- ut_ad((byte*)heap == (byte*)buf - MEM_BLOCK_HEADER_SIZE
+ ut_a((byte*)heap == (byte*)buf - MEM_BLOCK_HEADER_SIZE
- MEM_FIELD_HEADER_SIZE);
return(buf);
- #endif
+#endif
}
/*******************************************************************
@@ -539,26 +536,22 @@ UNIV_INLINE
void
mem_free_func(
/*==========*/
- void* ptr /* in, own: buffer to be freed */
- #ifdef UNIV_MEM_DEBUG
- ,char* file_name, /* in: file name where created */
+ void* ptr, /* in, own: buffer to be freed */
+ char* file_name, /* in: file name where created */
ulint line /* in: line where created */
- #endif
)
{
- #ifndef UNIV_MEM_DEBUG
+#ifdef notdefined
mem_area_free(ptr, mem_comm_pool);
- #else
-
+#else
mem_heap_t* heap;
heap = (mem_heap_t*)((byte*)ptr - MEM_BLOCK_HEADER_SIZE
- MEM_FIELD_HEADER_SIZE);
mem_heap_free_func(heap, file_name, line);
-
- #endif
+#endif
}
/*********************************************************************
@@ -567,7 +560,7 @@ UNIV_INLINE
ulint
mem_heap_get_size(
/*==============*/
- mem_heap_t* heap) /* in: heap */
+ mem_heap_t* heap) /* in: heap */
{
mem_block_t* block;
ulint size = 0;
@@ -597,9 +590,11 @@ mem_realloc(
/*========*/
/* out, own: free storage, NULL if did not succeed */
void* buf, /* in: pointer to an old buffer */
- ulint n) /* in: desired number of bytes */
+ ulint n, /* in: desired number of bytes */
+ char* file_name,/* in: file name where called */
+ ulint line) /* in: line where called */
{
mem_free(buf);
- return(mem_alloc(n));
+ return(mem_alloc_func(n, file_name, line));
}
diff --git a/innobase/include/mem0pool.h b/innobase/include/mem0pool.h
index eb675b4a7f9..43707bd5f61 100644
--- a/innobase/include/mem0pool.h
+++ b/innobase/include/mem0pool.h
@@ -72,6 +72,18 @@ mem_pool_get_reserved(
/* out: reserved mmeory in bytes */
mem_pool_t* pool); /* in: memory pool */
/************************************************************************
+Reserves the mem pool mutex. */
+
+void
+mem_pool_mutex_enter(void);
+/*======================*/
+/************************************************************************
+Releases the mem pool mutex. */
+
+void
+mem_pool_mutex_exit(void);
+/*=====================*/
+/************************************************************************
Validates a memory pool. */
ibool
diff --git a/innobase/include/page0page.ic b/innobase/include/page0page.ic
index 6e33fe2ca5d..f84fe5a5606 100644
--- a/innobase/include/page0page.ic
+++ b/innobase/include/page0page.ic
@@ -512,6 +512,8 @@ page_dir_find_owner_slot(
slot = page_dir_get_nth_slot(page, i);
while (page_dir_slot_get_rec(slot) != rec) {
+ ut_a(i > 0);
+
i--;
slot = page_dir_get_nth_slot(page, i);
}
diff --git a/innobase/include/que0que.h b/innobase/include/que0que.h
index 4cbd888ba1d..cdaeeae1fde 100644
--- a/innobase/include/que0que.h
+++ b/innobase/include/que0que.h
@@ -327,6 +327,8 @@ mutex with the exceptions named below */
struct que_thr_struct{
que_common_t common; /* type: QUE_NODE_THR */
+ ulint magic_n; /* magic number to catch memory
+ corruption */
que_node_t* child; /* graph child node */
que_t* graph; /* graph where this node belongs */
ibool is_active; /* TRUE if the thread has been set
@@ -357,6 +359,9 @@ struct que_thr_struct{
thus far */
};
+#define QUE_THR_MAGIC_N 8476583
+#define QUE_THR_MAGIC_FREED 123461526
+
/* Query graph fork node: its fields are protected by the kernel mutex */
struct que_fork_struct{
que_common_t common; /* type: QUE_NODE_FORK */
diff --git a/innobase/include/row0mysql.h b/innobase/include/row0mysql.h
index 32354219e64..48b6ba8a715 100644
--- a/innobase/include/row0mysql.h
+++ b/innobase/include/row0mysql.h
@@ -251,6 +251,24 @@ row_table_add_foreign_constraints(
char* name); /* in: table full name in the normalized form
database_name/table_name */
/*************************************************************************
+The master thread in srv0srv.c calls this regularly to drop tables which
+we must drop in background after queries to them have ended. Such lazy
+dropping of tables is needed in ALTER TABLE on Unix. */
+
+ulint
+row_drop_tables_for_mysql_in_background(void);
+/*=========================================*/
+ /* out: how many tables dropped
+ + remaining tables in list */
+/*************************************************************************
+Get the background drop list length. NOTE: the caller must own the kernel
+mutex! */
+
+ulint
+row_get_background_drop_list_len_low(void);
+/*======================================*/
+ /* out: how many tables in list */
+/*************************************************************************
Drops a table for MySQL. If the name of the dropped table ends to
characters INNODB_MONITOR, then this also stops printing of monitor
output by the master thread. */
@@ -323,11 +341,18 @@ struct mysql_row_templ_struct {
/* After fetching this many rows, we start caching them in fetch_cache */
#define MYSQL_FETCH_CACHE_THRESHOLD 4
+#define ROW_PREBUILT_ALLOCATED 78540783
+#define ROW_PREBUILT_FREED 26423527
/* A struct for (sometimes lazily) prebuilt structures in an Innobase table
handle used within MySQL; these are used to save CPU time. */
struct row_prebuilt_struct {
+ ulint magic_n; /* this magic number is set to
+ ROW_PREBUILT_ALLOCATED when created
+ and to ROW_PREBUILT_FREED when the
+ struct has been freed; used in
+ debugging */
dict_table_t* table; /* Innobase table handle */
trx_t* trx; /* current transaction handle */
ibool sql_stat_start; /* TRUE when we start processing of
@@ -419,7 +444,7 @@ struct row_prebuilt_struct {
fetched row in fetch_cache */
ulint n_fetch_cached; /* number of not yet fetched rows
in fetch_cache */
- mem_heap_t* blob_heap; /* in SELECTS BLOB fields are copied
+ mem_heap_t* blob_heap; /* in SELECTS BLOB fie lds are copied
to this heap */
mem_heap_t* old_vers_heap; /* memory heap where a previous
version is built in consistent read */
diff --git a/innobase/include/srv0srv.h b/innobase/include/srv0srv.h
index bae0aa89da9..2b40852fe67 100644
--- a/innobase/include/srv0srv.h
+++ b/innobase/include/srv0srv.h
@@ -17,6 +17,8 @@ Created 10/10/1995 Heikki Tuuri
#include "que0types.h"
#include "trx0types.h"
+/* Buffer which can be used in printing fatal error messages */
+extern char srv_fatal_errbuf[];
/* When this event is set the lock timeout and InnoDB monitor
thread starts running */
@@ -248,6 +250,12 @@ mutex, for performace reasons). */
void
srv_active_wake_master_thread(void);
/*===============================*/
+/***********************************************************************
+Wakes up the master thread if it is suspended or being suspended. */
+
+void
+srv_wake_master_thread(void);
+/*========================*/
/*************************************************************************
Puts an OS thread to wait if there are too many concurrent threads
(>= srv_thread_concurrency) inside InnoDB. The threads wait in a FIFO queue. */
@@ -262,15 +270,27 @@ This lets a thread enter InnoDB regardless of the number of threads inside
InnoDB. This must be called when a thread ends a lock wait. */
void
-srv_conc_force_enter_innodb(void);
-/*=============================*/
+srv_conc_force_enter_innodb(
+/*========================*/
+ trx_t* trx); /* in: transaction object associated with the
+ thread */
+/*************************************************************************
+This must be called when a thread exits InnoDB in a lock wait or at the
+end of an SQL statement. */
+
+void
+srv_conc_force_exit_innodb(
+/*=======================*/
+ trx_t* trx); /* in: transaction object associated with the
+ thread */
/*************************************************************************
-This must be called when a thread exits InnoDB. This must also be called
-when a thread goes to wait for a lock. */
+This must be called when a thread exits InnoDB. */
void
-srv_conc_exit_innodb(void);
-/*======================*/
+srv_conc_exit_innodb(
+/*=================*/
+ trx_t* trx); /* in: transaction object associated with the
+ thread */
/*******************************************************************
Puts a MySQL OS thread to wait for a lock to be released. */
diff --git a/innobase/include/trx0sys.h b/innobase/include/trx0sys.h
index 0295cd6abff..f2eded697ec 100644
--- a/innobase/include/trx0sys.h
+++ b/innobase/include/trx0sys.h
@@ -218,6 +218,22 @@ trx_in_trx_list(
/*============*/
/* out: TRUE if is in */
trx_t* in_trx);/* in: trx */
+/*********************************************************************
+Updates the offset information about the end of the MySQL binlog entry
+which corresponds to the transaction just being committed. */
+
+void
+trx_sys_update_mysql_binlog_offset(
+/*===============================*/
+ trx_t* trx, /* in: transaction being committed */
+ mtr_t* mtr); /* in: mtr */
+/*********************************************************************
+Prints to stderr the MySQL binlog offset info in the trx system header if
+the magic number shows it valid. */
+
+void
+trx_sys_print_mysql_binlog_offset(void);
+/*===================================*/
/* The automatically created system rollback segment has this id */
#define TRX_SYS_SYSTEM_RSEG_ID 0
@@ -236,7 +252,7 @@ therefore 256 */
/* Transaction system header; protected by trx_sys->mutex */
/*-------------------------------------------------------------*/
-#define TRX_SYS_TRX_ID_STORE 0 /* The maximum trx id or trx number
+#define TRX_SYS_TRX_ID_STORE 0 /* the maximum trx id or trx number
modulo TRX_SYS_TRX_ID_UPDATE_MARGIN
written to a file page by any
transaction; the assignment of
@@ -252,6 +268,23 @@ therefore 256 */
segment specification slots */
/*-------------------------------------------------------------*/
+#define TRX_SYS_MYSQL_LOG_NAME_LEN 32
+#define TRX_SYS_MYSQL_LOG_MAGIC_N 873422344
+
+/* The offset of the MySQL binlog offset info on the trx system header page */
+#define TRX_SYS_MYSQL_LOG_INFO (UNIV_PAGE_SIZE - 300)
+#define TRX_SYS_MYSQL_LOG_MAGIC_N_FLD 0 /* magic number which shows
+ if we have valid data in the
+ MySQL binlog info; the value
+ is ..._MAGIC_N if yes */
+#define TRX_SYS_MYSQL_LOG_NAME 4 /* MySQL log file name */
+#define TRX_SYS_MYSQL_LOG_OFFSET_HIGH (4 + TRX_SYS_MYSQL_LOG_NAME_LEN)
+ /* high 4 bytes of the offset
+ within that file */
+#define TRX_SYS_MYSQL_LOG_OFFSET_LOW (8 + TRX_SYS_MYSQL_LOG_NAME_LEN)
+ /* low 4 bytes of the offset
+ within that file */
+
/* The offset of the doublewrite buffer header on the trx system header page */
#define TRX_SYS_DOUBLEWRITE (UNIV_PAGE_SIZE - 200)
/*-------------------------------------------------------------*/
diff --git a/innobase/include/trx0trx.h b/innobase/include/trx0trx.h
index 58cef01b376..26c9ace08b6 100644
--- a/innobase/include/trx0trx.h
+++ b/innobase/include/trx0trx.h
@@ -50,6 +50,13 @@ trx_allocate_for_mysql(void);
/*========================*/
/* out, own: transaction object */
/************************************************************************
+Creates a transaction object for background operations by the master thread. */
+
+trx_t*
+trx_allocate_for_background(void);
+/*=============================*/
+ /* out, own: transaction object */
+/************************************************************************
Frees a transaction object. */
void
@@ -63,6 +70,13 @@ void
trx_free_for_mysql(
/*===============*/
trx_t* trx); /* in, own: trx object */
+/************************************************************************
+Frees a transaction object of a background operation of the master thread. */
+
+void
+trx_free_for_background(
+/*====================*/
+ trx_t* trx); /* in, own: trx object */
/********************************************************************
Creates trx objects for transactions and initializes the trx list of
trx_sys at database start. Rollback segment and undo log lists must
@@ -266,11 +280,14 @@ struct trx_sig_struct{
transaction is waiting a reply */
};
+#define TRX_MAGIC_N 91118598
+
/* The transaction handle; every session has a trx object which is freed only
when the session is freed; in addition there may be session-less transactions
rolling back after a database recovery */
struct trx_struct{
+ ulint magic_n;
/* All the next fields are protected by the kernel mutex, except the
undo logs which are protected by undo_mutex */
char* op_info; /* English text describing the
@@ -290,10 +307,20 @@ struct trx_struct{
table */
dulint table_id; /* table id if the preceding field is
TRUE */
+ /*------------------------------*/
void* mysql_thd; /* MySQL thread handle corresponding
to this trx, or NULL */
+ char* mysql_log_file_name;
+ /* If MySQL binlog is used, this field
+ contains a pointer to the latest file
+ name; this is NULL if binlog is not
+ used */
+ ib_longlong mysql_log_offset;/* If MySQL binlog is used, this field
+ contains the end offset of the binlog
+ entry */
os_thread_id_t mysql_thread_id;/* id of the MySQL thread associated
with this transaction object */
+ /*------------------------------*/
ulint n_mysql_tables_in_use; /* number of Innobase tables
used in the processing of the current
SQL statement in MySQL */
@@ -314,6 +341,18 @@ struct trx_struct{
calls from MySQL; this is intended
to reduce contention on the search
latch */
+ /*------------------------------*/
+ ibool declared_to_be_inside_innodb;
+ /* this is TRUE if we have declared
+ this transaction in
+ srv_conc_enter_innodb to be inside the
+ InnoDB engine */
+ ulint n_tickets_to_enter_innodb;
+ /* this can be > 0 only when
+ declared_to_... is TRUE; when we come
+ to srv_conc_innodb_enter, if the value
+ here is > 0, we decrement this by 1 */
+ /*------------------------------*/
lock_t* auto_inc_lock; /* possible auto-inc lock reserved by
the transaction; note that it is also
in the lock list trx_locks */
diff --git a/innobase/log/log0recv.c b/innobase/log/log0recv.c
index 5cd5850d1a1..29e87c7572b 100644
--- a/innobase/log/log0recv.c
+++ b/innobase/log/log0recv.c
@@ -51,6 +51,8 @@ recv_sys_t* recv_sys = NULL;
ibool recv_recovery_on = FALSE;
ibool recv_recovery_from_backup_on = FALSE;
+ibool recv_needed_recovery = FALSE;
+
/* If the following is TRUE, the buffer pool file pages must be invalidated
after recovery and no ibuf operations are allowed; this becomes TRUE if
the log record hash table becomes too full, and log records must be merged
@@ -1018,9 +1020,10 @@ loop:
if (recv_addr->state == RECV_NOT_PROCESSED) {
if (!has_printed) {
+ ut_print_timestamp(stderr);
fprintf(stderr,
-"InnoDB: Starting an apply batch of log records to the database...\n"
-"InnoDB: Progress in percents:");
+" InnoDB: Starting an apply batch of log records to the database...\n"
+"InnoDB: Progress in percents: ");
has_printed = TRUE;
}
@@ -2032,12 +2035,16 @@ recv_recovery_from_checkpoint_start(
if (ut_dulint_cmp(checkpoint_lsn, max_flushed_lsn) != 0
|| ut_dulint_cmp(checkpoint_lsn, min_flushed_lsn) != 0) {
+ recv_needed_recovery = TRUE;
+
+ ut_print_timestamp(stderr);
+
fprintf(stderr,
- "InnoDB: Database was not shut down normally.\n"
- "InnoDB: Starting recovery from log files...\n");
+ " InnoDB: Database was not shut down normally.\n"
+ "InnoDB: Starting recovery from log files...\n");
fprintf(stderr,
- "InnoDB: Starting log scan based on checkpoint at\n"
- "InnoDB: log sequence number %lu %lu\n",
+ "InnoDB: Starting log scan based on checkpoint at\n"
+ "InnoDB: log sequence number %lu %lu\n",
ut_dulint_get_high(checkpoint_lsn),
ut_dulint_get_low(checkpoint_lsn));
}
@@ -2199,6 +2206,10 @@ recv_recovery_from_checkpoint_finish(void)
"InnoDB: Log records applied to the database\n");
}
+ if (recv_needed_recovery) {
+ trx_sys_print_mysql_binlog_offset();
+ }
+
/* Free the resources of the recovery system */
recv_recovery_on = FALSE;
diff --git a/innobase/mem/mem0dbg.c b/innobase/mem/mem0dbg.c
index 571674a506b..f94119b7f38 100644
--- a/innobase/mem/mem0dbg.c
+++ b/innobase/mem/mem0dbg.c
@@ -810,7 +810,7 @@ mem_validate_no_assert(void)
}
mutex_exit(&mem_hash_mutex);
-
+
return(error);
#else
@@ -834,3 +834,95 @@ mem_validate(void)
return(TRUE);
}
+
+/****************************************************************
+Tries to find neigboring memory allocation blocks and dumps to stderr
+the neighborhood of a given pointer. */
+
+void
+mem_analyze_corruption(
+/*===================*/
+ byte* ptr) /* in: pointer to place of possible corruption */
+{
+ byte* p;
+ ulint i;
+ ulint dist;
+
+ ut_sprintf_buf(srv_fatal_errbuf, ptr - 250, 500);
+ fprintf(stderr,
+ "InnoDB: Apparent memory corruption: mem dump %s\n", srv_fatal_errbuf);
+
+ fprintf(stderr,
+ "InnoDB: Scanning backward trying to find previous allocated mem blocks\n");
+
+ p = ptr;
+ dist = 0;
+
+ for (i = 0; i < 10; i++) {
+ for (;;) {
+ if (((ulint)p) % 4 == 0) {
+
+ if (*((ulint*)p) == MEM_BLOCK_MAGIC_N) {
+ fprintf(stderr,
+ "Mem block at - %lu, file %s, line %lu\n",
+ dist, p + sizeof(ulint),
+ *(ulint*)(p + 8 + sizeof(ulint)));
+
+ break;
+ }
+
+ if (*((ulint*)p) == MEM_FREED_BLOCK_MAGIC_N) {
+ fprintf(stderr,
+ "Freed mem block at - %lu, file %s, line %lu\n",
+ dist, p + sizeof(ulint),
+ *(ulint*)(p + 8 + sizeof(ulint)));
+
+ break;
+ }
+ }
+
+ p--;
+ dist++;
+ }
+
+ p--;
+ dist++;
+ }
+
+ fprintf(stderr,
+ "InnoDB: Scanning forward trying to find next allocated mem blocks\n");
+
+ p = ptr;
+ dist = 0;
+
+ for (i = 0; i < 10; i++) {
+ for (;;) {
+ if (((ulint)p) % 4 == 0) {
+
+ if (*((ulint*)p) == MEM_BLOCK_MAGIC_N) {
+ fprintf(stderr,
+ "Mem block at + %lu, file %s, line %lu\n",
+ dist, p + sizeof(ulint),
+ *(ulint*)(p + 8 + sizeof(ulint)));
+
+ break;
+ }
+
+ if (*((ulint*)p) == MEM_FREED_BLOCK_MAGIC_N) {
+ fprintf(stderr,
+ "Freed mem block at + %lu, file %s, line %lu\n",
+ dist, p + sizeof(ulint),
+ *(ulint*)(p + 8 + sizeof(ulint)));
+
+ break;
+ }
+ }
+
+ p++;
+ dist++;
+ }
+
+ p++;
+ dist++;
+ }
+}
diff --git a/innobase/mem/mem0mem.c b/innobase/mem/mem0mem.c
index 19a2c0d61a7..0680968a7eb 100644
--- a/innobase/mem/mem0mem.c
+++ b/innobase/mem/mem0mem.c
@@ -14,8 +14,9 @@ Created 6/9/1994 Heikki Tuuri
#include "mach0data.h"
#include "buf0buf.h"
-#include "mem0dbg.c"
#include "btr0sea.h"
+#include "srv0srv.h"
+#include "mem0dbg.c"
/*
THE MEMORY MANAGEMENT
@@ -49,7 +50,7 @@ of the blocks stay the same. An exception is, of course, the case
where the caller requests a memory buffer whose size is
bigger than the threshold. In that case a block big enough must
be allocated.
-
+
The heap is physically arranged so that if the current block
becomes full, a new block is allocated and always inserted in the
chain of blocks as the last block.
@@ -74,6 +75,14 @@ After freeing, all the blocks in the heap are set to random bytes
to help us discover errors which result from the use of
buffers in an already freed heap. */
+#ifdef MEM_PERIODIC_CHECK
+
+ibool mem_block_list_inited;
+/* List of all mem blocks allocated; protected by the mem_comm_pool mutex */
+UT_LIST_BASE_NODE_T(mem_block_t) mem_block_list;
+
+#endif
+
/*******************************************************************
NOTE: Use the corresponding macro instead of this function.
Allocates a single buffer of memory from the dynamic memory of
@@ -85,18 +94,12 @@ mem_alloc_func_noninline(
/*=====================*/
/* out, own: free storage, NULL if did not
succeed */
- ulint n /* in: desired number of bytes */
- #ifdef UNIV_MEM_DEBUG
- ,char* file_name, /* in: file name where created */
+ ulint n, /* in: desired number of bytes */
+ char* file_name, /* in: file name where created */
ulint line /* in: line where created */
- #endif
)
{
- return(mem_alloc_func(n
-#ifdef UNIV_MEM_DEBUG
- , file_name, line
-#endif
- ));
+ return(mem_alloc_func(n, file_name, line));
}
/*******************************************************************
@@ -113,8 +116,10 @@ mem_heap_create_block(
if init_block is not NULL, its size in bytes */
void* init_block, /* in: init block in fast create, type must be
MEM_HEAP_DYNAMIC */
- ulint type) /* in: type of heap: MEM_HEAP_DYNAMIC, or
+ ulint type, /* in: type of heap: MEM_HEAP_DYNAMIC, or
MEM_HEAP_BUFFER possibly ORed to MEM_HEAP_BTR_SEARCH */
+ char* file_name,/* in: file name where created */
+ ulint line) /* in: line where created */
{
mem_block_t* block;
ulint len;
@@ -122,6 +127,10 @@ mem_heap_create_block(
ut_ad((type == MEM_HEAP_DYNAMIC) || (type == MEM_HEAP_BUFFER)
|| (type == MEM_HEAP_BUFFER + MEM_HEAP_BTR_SEARCH));
+ if (heap && heap->magic_n != MEM_BLOCK_MAGIC_N) {
+ mem_analyze_corruption((byte*)heap);
+ }
+
/* In dynamic allocation, calculate the size: block header + data. */
if (init_block != NULL) {
@@ -164,7 +173,23 @@ mem_heap_create_block(
}
block->magic_n = MEM_BLOCK_MAGIC_N;
+ ut_memcpy(&(block->file_name), file_name + ut_strlen(file_name) - 7,
+ 7);
+ block->file_name[7]='\0';
+ block->line = line;
+
+#ifdef MEM_PERIODIC_CHECK
+ mem_pool_mutex_enter();
+
+ if (!mem_block_list_inited) {
+ mem_block_list_inited = TRUE;
+ UT_LIST_INIT(mem_block_list);
+ }
+
+ UT_LIST_ADD_LAST(mem_block_list, mem_block_list, block);
+ mem_pool_mutex_exit();
+#endif
mem_block_set_len(block, len);
mem_block_set_type(block, type);
mem_block_set_free(block, MEM_BLOCK_HEADER_SIZE);
@@ -223,8 +248,8 @@ mem_heap_add_block(
new_size = n;
}
- new_block = mem_heap_create_block(heap, new_size, NULL, heap->type);
-
+ new_block = mem_heap_create_block(heap, new_size, NULL, heap->type,
+ heap->file_name, heap->line);
if (new_block == NULL) {
return(NULL);
@@ -250,12 +275,24 @@ mem_heap_block_free(
ulint len;
ibool init_block;
+ if (block->magic_n != MEM_BLOCK_MAGIC_N) {
+ mem_analyze_corruption((byte*)block);
+ }
+
UT_LIST_REMOVE(list, heap->base, block);
+#ifdef MEM_PERIODIC_CHECK
+ mem_pool_mutex_enter();
+
+ UT_LIST_REMOVE(mem_block_list, mem_block_list, block);
+
+ mem_pool_mutex_exit();
+#endif
type = heap->type;
len = block->len;
init_block = block->init_block;
-
+ block->magic_n = MEM_FREED_BLOCK_MAGIC_N;
+
#ifdef UNIV_MEM_DEBUG
/* In the debug version we set the memory to a random combination
of hex 0xDE and 0xAD. */
@@ -296,3 +333,30 @@ mem_heap_free_block_free(
heap->free_block = NULL;
}
}
+
+#ifdef MEM_PERIODIC_CHECK
+/**********************************************************************
+Goes through the list of all allocated mem blocks, checks their magic
+numbers, and reports possible corruption. */
+
+void
+mem_validate_all_blocks(void)
+/*=========================*/
+{
+ mem_block_t* block;
+
+ mem_pool_mutex_enter();
+
+ block = UT_LIST_GET_FIRST(mem_block_list);
+
+ while (block) {
+ if (block->magic_n != MEM_BLOCK_MAGIC_N) {
+ mem_analyze_corruption((byte*)block);
+ }
+
+ block = UT_LIST_GET_NEXT(mem_block_list, block);
+ }
+
+ mem_pool_mutex_exit();
+}
+#endif
diff --git a/innobase/mem/mem0pool.c b/innobase/mem/mem0pool.c
index 48e7e686953..3681c8ef779 100644
--- a/innobase/mem/mem0pool.c
+++ b/innobase/mem/mem0pool.c
@@ -78,9 +78,9 @@ pool, and after that its locks will grow into the buffer pool. */
/* The smallest memory area total size */
#define MEM_AREA_MIN_SIZE (2 * MEM_AREA_EXTRA_SIZE)
+
/* Data structure for a memory pool. The space is allocated using the buddy
algorithm, where free list i contains areas of size 2 to power i. */
-
struct mem_pool_struct{
byte* buf; /* memory pool */
ulint size; /* memory common pool size */
@@ -99,6 +99,26 @@ mem_pool_t* mem_comm_pool = NULL;
ulint mem_out_of_mem_err_msg_count = 0;
/************************************************************************
+Reserves the mem pool mutex. */
+
+void
+mem_pool_mutex_enter(void)
+/*======================*/
+{
+ mutex_enter(&(mem_comm_pool->mutex));
+}
+
+/************************************************************************
+Releases the mem pool mutex. */
+
+void
+mem_pool_mutex_exit(void)
+/*=====================*/
+{
+ mutex_exit(&(mem_comm_pool->mutex));
+}
+
+/************************************************************************
Returns memory area size. */
UNIV_INLINE
ulint
@@ -240,15 +260,15 @@ mem_pool_fill_free_list(
if (mem_out_of_mem_err_msg_count % 1000000000 == 0) {
/* We do not print the message every time: */
+
+ ut_print_timestamp(stderr);
fprintf(stderr,
- "Innobase: Warning: out of memory in additional memory pool.\n");
- fprintf(stderr,
- "Innobase: Innobase will start allocating memory from the OS.\n");
- fprintf(stderr,
- "Innobase: You should restart the database with a bigger value in\n");
- fprintf(stderr,
- "Innobase: the MySQL .cnf file for innobase_additional_mem_pool_size.\n");
+ " InnoDB: Out of memory in additional memory pool.\n"
+ "InnoDB: InnoDB will start allocating memory from the OS.\n"
+ "InnoDB: You may get better performance if you configure a bigger\n"
+ "InnoDB: value in the MySQL my.cnf file for\n"
+ "InnoDB: innodb_additional_mem_pool_size.\n");
}
mem_out_of_mem_err_msg_count++;
diff --git a/innobase/os/os0file.c b/innobase/os/os0file.c
index ac29b292f6a..010716d8d17 100644
--- a/innobase/os/os0file.c
+++ b/innobase/os/os0file.c
@@ -169,8 +169,25 @@ os_file_get_last_error(void)
if (err != ERROR_FILE_EXISTS) {
fprintf(stderr,
- "InnoDB: Warning: operating system error number %li in a file operation.\n",
+ "InnoDB: Operating system error number %li in a file operation.\n"
+ "InnoDB: See http://www.innodb.com/ibman.html for installation help.\n",
(long) err);
+
+ if (err == ERROR_PATH_NOT_FOUND) {
+ fprintf(stderr,
+ "InnoDB: The error means the system cannot find the path specified.\n"
+ "InnoDB: In installation you must create directories yourself, InnoDB\n"
+ "InnoDB: does not create them.\n");
+ } else if (err == ERROR_ACCESS_DENIED) {
+ fprintf(stderr,
+ "InnoDB: The error means mysqld does not have the access rights to\n"
+ "InnoDB: the directory. It may also be you have created a subdirectory\n"
+ "InnoDB: of the same name as a data file.\n");
+ } else {
+ fprintf(stderr,
+ "InnoDB: Look from section 13.2 at http://www.innodb.com/ibman.html\n"
+ "InnoDB: what the error number means.\n");
+ }
}
if (err == ERROR_FILE_NOT_FOUND) {
@@ -186,9 +203,25 @@ os_file_get_last_error(void)
err = (ulint) errno;
if (err != EEXIST) {
- fprintf(stderr,
- "InnoDB: Warning: operating system error number %i in a file operation.\n",
- errno);
+ fprintf(stderr,
+ "InnoDB: Operating system error number %li in a file operation.\n"
+ "InnoDB: See http://www.innodb.com/ibman.html for installation help.\n",
+ (long) err);
+
+ if (err == ENOENT) {
+ fprintf(stderr,
+ "InnoDB: The error means the system cannot find the path specified.\n"
+ "InnoDB: In installation you must create directories yourself, InnoDB\n"
+ "InnoDB: does not create them.\n");
+ } else if (err == EACCES) {
+ fprintf(stderr,
+ "InnoDB: The error means mysqld does not have the access rights to\n"
+ "InnoDB: the directory.\n");
+ } else {
+ fprintf(stderr,
+ "InnoDB: Look from section 13.2 at http://www.innodb.com/ibman.html\n"
+ "InnoDB: what the error number means or use the perror program of MySQL.\n");
+ }
}
if (err == ENOSPC ) {
@@ -228,11 +261,11 @@ os_file_handle_error(
if (err == OS_FILE_DISK_FULL) {
fprintf(stderr, "\n");
if (name) {
- fprintf(stderr,
- "InnoDB: Encountered a problem with file %s.\n",
+ fprintf(stderr,
+ "InnoDB: Encountered a problem with file %s.\n",
name);
}
- fprintf(stderr,
+ fprintf(stderr,
"InnoDB: Cannot continue operation.\n"
"InnoDB: Disk is full. Try to clean the disk to free space.\n"
"InnoDB: Delete a possible created file and restart.\n");
@@ -245,6 +278,10 @@ os_file_handle_error(
} else if (err == OS_FILE_ALREADY_EXISTS) {
return(FALSE);
} else {
+ if (name) {
+ fprintf(stderr, "InnoDB: File name %s\n", name);
+ }
+
fprintf(stderr, "InnoDB: Cannot continue operation.\n");
exit(1);
diff --git a/innobase/pars/lexyy.c b/innobase/pars/lexyy.c
index 67bd12afa60..10bdcdd0990 100644
--- a/innobase/pars/lexyy.c
+++ b/innobase/pars/lexyy.c
@@ -7373,7 +7373,7 @@ void *ptr;
unsigned int size;
#endif
{
- return (void *) mem_realloc( ptr, size );
+ return (void *) mem_realloc( ptr, size, __FILE__, __LINE__ );
}
#ifdef YY_USE_PROTOS
diff --git a/innobase/que/que0que.c b/innobase/que/que0que.c
index 67f7b48187d..1cee316f32c 100644
--- a/innobase/que/que0que.c
+++ b/innobase/que/que0que.c
@@ -183,6 +183,8 @@ que_thr_create(
thr->common.type = QUE_NODE_THR;
thr->common.parent = parent;
+ thr->magic_n = QUE_THR_MAGIC_N;
+
thr->graph = parent->graph;
thr->state = QUE_THR_COMMAND_WAIT;
@@ -485,7 +487,6 @@ que_graph_free_recursive(
tab_node_t* cre_tab;
ind_node_t* cre_ind;
-
if (node == NULL) {
return;
@@ -509,6 +510,16 @@ que_graph_free_recursive(
thr = node;
+ if (thr->magic_n != QUE_THR_MAGIC_N) {
+ fprintf(stderr,
+ "que_thr struct appears corrupt; magic n %lu\n",
+ thr->magic_n);
+ mem_analyze_corruption((byte*)thr);
+ ut_a(0);
+ }
+
+ thr->magic_n = QUE_THR_MAGIC_FREED;
+
que_graph_free_recursive(thr->child);
break;
@@ -606,6 +617,10 @@ que_graph_free_recursive(
break;
default:
+ fprintf(stderr,
+ "que_node struct appears corrupt; type %lu\n",
+ que_node_get_type(node));
+ mem_analyze_corruption((byte*)node);
ut_a(0);
}
}
@@ -1068,20 +1083,29 @@ que_thr_stop_for_mysql(
mutex_exit(&kernel_mutex);
}
-
/**************************************************************************
Moves a thread from another state to the QUE_THR_RUNNING state. Increments
the n_active_thrs counters of the query graph and transaction if thr was
not active. */
+
void
que_thr_move_to_run_state_for_mysql(
/*================================*/
que_thr_t* thr, /* in: an query thread */
trx_t* trx) /* in: transaction */
{
+ if (thr->magic_n != QUE_THR_MAGIC_N) {
+ fprintf(stderr,
+ "que_thr struct appears corrupt; magic n %lu\n", thr->magic_n);
+
+ mem_analyze_corruption((byte*)thr);
+
+ ut_a(0);
+ }
+
if (!thr->is_active) {
- (thr->graph)->n_active_thrs++;
+ thr->graph->n_active_thrs++;
trx->n_active_thrs++;
@@ -1097,6 +1121,7 @@ que_thr_move_to_run_state_for_mysql(
/**************************************************************************
A patch for MySQL used to 'stop' a dummy query thread used in MySQL
select, when there is no error or lock wait. */
+
void
que_thr_stop_for_mysql_no_error(
/*============================*/
@@ -1105,6 +1130,15 @@ que_thr_stop_for_mysql_no_error(
{
ut_ad(thr->state == QUE_THR_RUNNING);
+ if (thr->magic_n != QUE_THR_MAGIC_N) {
+ fprintf(stderr,
+ "que_thr struct appears corrupt; magic n %lu\n", thr->magic_n);
+
+ mem_analyze_corruption((byte*)thr);
+
+ ut_a(0);
+ }
+
thr->state = QUE_THR_COMPLETED;
thr->is_active = FALSE;
diff --git a/innobase/rem/rem0cmp.c b/innobase/rem/rem0cmp.c
index 47b7021bf27..9a5a0eb6e8e 100644
--- a/innobase/rem/rem0cmp.c
+++ b/innobase/rem/rem0cmp.c
@@ -113,6 +113,16 @@ cmp_types_are_equal(
return(FALSE);
}
+ if (type1->mtype == DATA_INT
+ && (type1->prtype & DATA_UNSIGNED)
+ != (type2->prtype & DATA_UNSIGNED)) {
+ /* The storage format of an unsigned integer is different
+ from a signed integer: in a signed integer we OR
+ 0x8000... to the value of positive integers. */
+
+ return(FALSE);
+ }
+
if (type1->mtype == DATA_MYSQL
|| type1->mtype == DATA_VARMYSQL) {
diff --git a/innobase/rem/rem0rec.c b/innobase/rem/rem0rec.c
index f3b949ca06f..a151389798d 100644
--- a/innobase/rem/rem0rec.c
+++ b/innobase/rem/rem0rec.c
@@ -105,6 +105,17 @@ rec_get_nth_field(
ut_ad(rec && len);
ut_ad(n < rec_get_n_fields(rec));
+ if (n > 1024) {
+ fprintf(stderr, "Error: trying to access field %lu in rec\n",
+ n);
+ ut_a(0);
+ }
+
+ if (rec == NULL) {
+ fprintf(stderr, "Error: rec is NULL pointer\n");
+ ut_a(0);
+ }
+
if (rec_get_1byte_offs_flag(rec)) {
os = rec_1_get_field_start_offs(rec, n);
diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c
index 50ec88c595d..f5ed0ef65af 100644
--- a/innobase/row/row0mysql.c
+++ b/innobase/row/row0mysql.c
@@ -26,6 +26,19 @@ Created 9/17/2000 Heikki Tuuri
#include "trx0purge.h"
#include "lock0lock.h"
#include "rem0cmp.h"
+#include "log0log.h"
+
+/* List of tables we should drop in background. ALTER TABLE in MySQL requires
+that the table handler can drop the table in background when there are no
+queries to it any more. Protected by the kernel mutex. */
+typedef struct row_mysql_drop_struct row_mysql_drop_t;
+struct row_mysql_drop_struct{
+ char* table_name;
+ UT_LIST_NODE_T(row_mysql_drop_t) row_mysql_drop_list;
+};
+
+UT_LIST_BASE_NODE_T(row_mysql_drop_t) row_mysql_drop_list;
+ibool row_mysql_drop_list_inited = FALSE;
/***********************************************************************
Reads a MySQL format variable-length field (like VARCHAR) length and
@@ -172,10 +185,22 @@ handle_new_error:
trx_general_rollback_for_mysql(trx, TRUE, savept);
}
} else if (err == DB_TOO_BIG_RECORD) {
+ if (savept) {
+ /* Roll back the latest, possibly incomplete
+ insertion or update */
+
+ trx_general_rollback_for_mysql(trx, TRUE, savept);
+ }
/* MySQL will roll back the latest SQL statement */
} else if (err == DB_ROW_IS_REFERENCED
|| err == DB_NO_REFERENCED_ROW
|| err == DB_CANNOT_ADD_CONSTRAINT) {
+ if (savept) {
+ /* Roll back the latest, possibly incomplete
+ insertion or update */
+
+ trx_general_rollback_for_mysql(trx, TRUE, savept);
+ }
/* MySQL will roll back the latest SQL statement */
} else if (err == DB_LOCK_WAIT) {
@@ -200,6 +225,12 @@ handle_new_error:
trx_general_rollback_for_mysql(trx, FALSE, NULL);
} else if (err == DB_OUT_OF_FILE_SPACE) {
+ if (savept) {
+ /* Roll back the latest, possibly incomplete
+ insertion or update */
+
+ trx_general_rollback_for_mysql(trx, TRUE, savept);
+ }
/* MySQL will roll back the latest SQL statement */
} else if (err == DB_MUST_GET_MORE_FILE_SPACE) {
@@ -246,6 +277,8 @@ row_create_prebuilt(
prebuilt = mem_heap_alloc(heap, sizeof(row_prebuilt_t));
+ prebuilt->magic_n = ROW_PREBUILT_ALLOCATED;
+
prebuilt->table = table;
prebuilt->trx = NULL;
@@ -294,7 +327,7 @@ row_create_prebuilt(
prebuilt->blob_heap = NULL;
prebuilt->old_vers_heap = NULL;
-
+
return(prebuilt);
}
@@ -308,6 +341,19 @@ row_prebuilt_free(
{
ulint i;
+ if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) {
+ fprintf(stderr,
+ "InnoDB: Error: trying to free a corrupt\n"
+ "InnoDB: table handle. Magic n %lu, table name %s\n",
+ prebuilt->magic_n, prebuilt->table->name);
+
+ mem_analyze_corruption((byte*)prebuilt);
+
+ ut_a(0);
+ }
+
+ prebuilt->magic_n = ROW_PREBUILT_FREED;
+
btr_pcur_free_for_mysql(prebuilt->pcur);
btr_pcur_free_for_mysql(prebuilt->clust_pcur);
@@ -341,6 +387,8 @@ row_prebuilt_free(
}
}
+ dict_table_decrement_handle_count(prebuilt->table);
+
mem_heap_free(prebuilt->heap);
}
@@ -356,6 +404,28 @@ row_update_prebuilt_trx(
handle */
trx_t* trx) /* in: transaction handle */
{
+ if (trx->magic_n != TRX_MAGIC_N) {
+ fprintf(stderr,
+ "InnoDB: Error: trying to use a corrupt\n"
+ "InnoDB: trx handle. Magic n %lu\n",
+ trx->magic_n);
+
+ mem_analyze_corruption((byte*)trx);
+
+ ut_a(0);
+ }
+
+ if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) {
+ fprintf(stderr,
+ "InnoDB: Error: trying to use a corrupt\n"
+ "InnoDB: table handle. Magic n %lu, table name %s\n",
+ prebuilt->magic_n, prebuilt->table->name);
+
+ mem_analyze_corruption((byte*)prebuilt);
+
+ ut_a(0);
+ }
+
prebuilt->trx = trx;
if (prebuilt->ins_graph) {
@@ -563,6 +633,17 @@ row_insert_for_mysql(
ut_ad(trx);
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
+ if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) {
+ fprintf(stderr,
+ "InnoDB: Error: trying to free a corrupt\n"
+ "InnoDB: table handle. Magic n %lu, table name %s\n",
+ prebuilt->magic_n, prebuilt->table->name);
+
+ mem_analyze_corruption((byte*)prebuilt);
+
+ ut_a(0);
+ }
+
if (srv_created_new_raw || srv_force_recovery) {
fprintf(stderr,
"InnoDB: A new raw disk partition was initialized or\n"
@@ -748,6 +829,17 @@ row_update_for_mysql(
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
UT_NOT_USED(mysql_rec);
+ if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) {
+ fprintf(stderr,
+ "InnoDB: Error: trying to free a corrupt\n"
+ "InnoDB: table handle. Magic n %lu, table name %s\n",
+ prebuilt->magic_n, prebuilt->table->name);
+
+ mem_analyze_corruption((byte*)prebuilt);
+
+ ut_a(0);
+ }
+
if (srv_created_new_raw || srv_force_recovery) {
fprintf(stderr,
"InnoDB: A new raw disk partition was initialized or\n"
@@ -782,38 +874,6 @@ row_update_for_mysql(
generated for the table: MySQL does not know anything about
the row id used as the clustered index key */
-#ifdef notdefined
- /* We have to search for the correct cursor position */
-
- ref_len = dict_index_get_n_unique(clust_index);
-
- heap = mem_heap_create(450);
-
- row_tuple = dtuple_create(heap, dict_table_get_n_cols(table));
- dict_table_copy_types(row_tuple, table);
-
- if (prebuilt->ins_upd_rec_buff == NULL) {
- prebuilt->ins_upd_rec_buff = mem_heap_alloc(prebuilt->heap,
- prebuilt->mysql_row_len);
- }
-
- row_mysql_convert_row_to_innobase(row_tuple, prebuilt, mysql_rec);
-
- search_tuple = dtuple_create(heap, ref_len);
-
- row_build_row_ref_from_row(search_tuple, table, row_tuple);
-
- mtr_start(&mtr);
-
- btr_pcur_open_with_no_init(clust_index, search_tuple, PAGE_CUR_LE,
- BTR_SEARCH_LEAF, node->pcur, 0, &mtr);
-
- btr_pcur_store_position(node->pcur, &mtr);
-
- mtr_commit(&mtr);
-
- mem_heap_free(heap);
-#endif
savept = trx_savept_take(trx);
thr = que_fork_get_first_thr(prebuilt->upd_graph);
@@ -923,6 +983,50 @@ row_get_mysql_key_number_for_index(
}
/*************************************************************************
+Recovers an orphaned tmp table inside InnoDB by renaming it. In the table
+name #sql becomes rsql, and "_recover_innodb_tmp_table" is catenated to
+the end of name. table->name should be of the form
+"dbname/rsql..._recover_innodb_tmp_table". This renames a table whose
+name is "#sql..." */
+static
+int
+row_mysql_recover_tmp_table(
+/*========================*/
+ /* out: error code or DB_SUCCESS */
+ dict_table_t* table, /* in: table definition */
+ trx_t* trx) /* in: transaction handle */
+{
+ char* ptr;
+ char old_name[1000];
+
+ ut_memcpy(old_name, table->name, ut_strlen(table->name) + 1);
+
+ ptr = old_name;
+
+ for (;;) {
+ if (ptr >= old_name + ut_strlen(table->name) - 6) {
+ trx_commit_for_mysql(trx);
+
+ return(DB_ERROR);
+ }
+
+ if (0 == ut_memcmp(ptr, "/rsql", 5)) {
+ ptr++;
+ *ptr = '#';
+
+ break;
+ }
+
+ ptr++;
+ }
+
+ old_name[ut_strlen(table->name)
+ - ut_strlen("_recover_innodb_tmp_table")] = '\0';
+
+ return(row_rename_table_for_mysql(old_name, table->name, trx));
+}
+
+/*************************************************************************
Does a table creation operation for MySQL. If the name of the created
table ends to characters INNODB_MONITOR, then this also starts
printing of monitor output by the master thread. */
@@ -976,6 +1080,24 @@ row_create_table_for_mysql(
namelen = ut_strlen(table->name);
+ keywordlen = ut_strlen("_recover_innodb_tmp_table");
+
+ if (namelen >= keywordlen
+ && 0 == ut_memcmp(table->name + namelen - keywordlen,
+ "_recover_innodb_tmp_table", keywordlen)) {
+
+ /* MySQL prevents accessing of tables whose name begins
+ with #sql, that is temporary tables. If mysqld crashes in
+ the middle of an ALTER TABLE, we may get an orphaned
+ #sql-table in the tablespace. We have here a special
+ mechanism to recover such tables by renaming them to
+ rsql... */
+
+ return(row_mysql_recover_tmp_table(table, trx));
+ }
+
+ namelen = ut_strlen(table->name);
+
keywordlen = ut_strlen((char *) "innodb_monitor");
if (namelen >= keywordlen
@@ -1079,8 +1201,11 @@ row_create_table_for_mysql(
row_drop_table_for_mysql(table->name, trx, TRUE);
} else {
ut_a(err == DB_DUPLICATE_KEY);
+
+ ut_print_timestamp(stderr);
+
fprintf(stderr,
- "InnoDB: Error: table %s already exists in InnoDB internal\n"
+ " InnoDB: Error: table %s already exists in InnoDB internal\n"
"InnoDB: data dictionary. Have you deleted the .frm file\n"
"InnoDB: and not used DROP TABLE? Have you used DROP DATABASE\n"
"InnoDB: for InnoDB tables in MySQL version <= 3.23.43?\n"
@@ -1120,6 +1245,8 @@ row_create_index_for_mysql(
ind_node_t* node;
mem_heap_t* heap;
que_thr_t* thr;
+ ulint namelen;
+ ulint keywordlen;
ulint err;
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
@@ -1128,6 +1255,18 @@ row_create_index_for_mysql(
trx_start_if_not_started(trx);
+ namelen = ut_strlen(index->table_name);
+
+ keywordlen = ut_strlen("_recover_innodb_tmp_table");
+
+ if (namelen >= keywordlen
+ && 0 == ut_memcmp(
+ index->table_name + namelen - keywordlen,
+ "_recover_innodb_tmp_table", keywordlen)) {
+
+ return(DB_SUCCESS);
+ }
+
/* Serialize data dictionary operations with dictionary mutex:
no deadlocks can occur then in these operations */
@@ -1191,6 +1330,8 @@ row_table_add_foreign_constraints(
char* name) /* in: table full name in the normalized form
database_name/table_name */
{
+ ulint namelen;
+ ulint keywordlen;
ulint err;
ut_a(sql_string);
@@ -1199,6 +1340,18 @@ row_table_add_foreign_constraints(
trx_start_if_not_started(trx);
+ namelen = ut_strlen(name);
+
+ keywordlen = ut_strlen("_recover_innodb_tmp_table");
+
+ if (namelen >= keywordlen
+ && 0 == ut_memcmp(
+ name + namelen - keywordlen,
+ "_recover_innodb_tmp_table", keywordlen)) {
+
+ return(DB_SUCCESS);
+ }
+
/* Serialize data dictionary operations with dictionary mutex:
no deadlocks can occur then in these operations */
@@ -1231,6 +1384,177 @@ row_table_add_foreign_constraints(
}
/*************************************************************************
+Drops a table for MySQL as a background operation. MySQL relies on Unix
+in ALTER TABLE to the fact that the table handler does not remove the
+table before all handles to it has been removed. Furhermore, the MySQL's
+call to drop table must be non-blocking. Therefore we do the drop table
+as a background operation, which is taken care of by the master thread
+in srv0srv.c. */
+static
+int
+row_drop_table_for_mysql_in_background(
+/*===================================*/
+ /* out: error code or DB_SUCCESS */
+ char* name) /* in: table name */
+{
+ ulint error;
+ trx_t* trx;
+
+ trx = trx_allocate_for_background();
+
+/* fprintf(stderr, "InnoDB: Dropping table %s in background drop list\n",
+ name); */
+ /* Drop the table in InnoDB */
+
+ error = row_drop_table_for_mysql(name, trx, FALSE);
+
+ if (error != DB_SUCCESS) {
+ fprintf(stderr,
+ "InnoDB: Error: Dropping table %s in background drop list failed\n",
+ name);
+ }
+
+ /* Flush the log to reduce probability that the .frm files and
+ the InnoDB data dictionary get out-of-sync if the user runs
+ with innodb_flush_log_at_trx_commit = 0 */
+
+ log_flush_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP);
+
+ trx_commit_for_mysql(trx);
+
+ trx_free_for_background(trx);
+
+ return(DB_SUCCESS);
+}
+
+/*************************************************************************
+The master thread in srv0srv.c calls this regularly to drop tables which
+we must drop in background after queries to them have ended. Such lazy
+dropping of tables is needed in ALTER TABLE on Unix. */
+
+ulint
+row_drop_tables_for_mysql_in_background(void)
+/*=========================================*/
+ /* out: how many tables dropped
+ + remaining tables in list */
+{
+ row_mysql_drop_t* drop;
+ dict_table_t* table;
+ ulint n_tables;
+ ulint n_tables_dropped = 0;
+loop:
+ mutex_enter(&kernel_mutex);
+
+ if (!row_mysql_drop_list_inited) {
+
+ UT_LIST_INIT(row_mysql_drop_list);
+ row_mysql_drop_list_inited = TRUE;
+ }
+
+ drop = UT_LIST_GET_FIRST(row_mysql_drop_list);
+
+ n_tables = UT_LIST_GET_LEN(row_mysql_drop_list);
+
+ mutex_exit(&kernel_mutex);
+
+ if (drop == NULL) {
+
+ return(n_tables + n_tables_dropped);
+ }
+
+ mutex_enter(&(dict_sys->mutex));
+ table = dict_table_get_low(drop->table_name);
+ mutex_exit(&(dict_sys->mutex));
+
+ if (table == NULL) {
+ /* If for some reason the table has already been dropped
+ through some other mechanism, do not try to drop it */
+
+ goto already_dropped;
+ }
+
+ if (table->n_mysql_handles_opened > 0) {
+
+ return(n_tables + n_tables_dropped);
+ }
+
+ n_tables_dropped++;
+
+ row_drop_table_for_mysql_in_background(drop->table_name);
+
+already_dropped:
+ mutex_enter(&kernel_mutex);
+
+ UT_LIST_REMOVE(row_mysql_drop_list, row_mysql_drop_list, drop);
+
+ ut_print_timestamp(stderr);
+ fprintf(stderr,
+ " InnoDB: Dropped table %s in background drop queue.\n",
+ drop->table_name);
+
+ mem_free(drop->table_name);
+
+ mem_free(drop);
+
+ mutex_exit(&kernel_mutex);
+
+ goto loop;
+}
+
+/*************************************************************************
+Get the background drop list length. NOTE: the caller must own the kernel
+mutex! */
+
+ulint
+row_get_background_drop_list_len_low(void)
+/*======================================*/
+ /* out: how many tables in list */
+{
+ ut_ad(mutex_own(&kernel_mutex));
+
+ if (!row_mysql_drop_list_inited) {
+
+ UT_LIST_INIT(row_mysql_drop_list);
+ row_mysql_drop_list_inited = TRUE;
+ }
+
+ return(UT_LIST_GET_LEN(row_mysql_drop_list));
+}
+
+/*************************************************************************
+Adds a table to the list of tables which the master thread drops in
+background. We need this on Unix because in ALTER TABLE MySQL may call
+drop table even if the table has running queries on it. */
+static
+void
+row_add_table_to_background_drop_list(
+/*==================================*/
+ dict_table_t* table) /* in: table */
+{
+ row_mysql_drop_t* drop;
+
+ drop = mem_alloc(sizeof(row_mysql_drop_t));
+
+ drop->table_name = mem_alloc(1 + ut_strlen(table->name));
+
+ ut_memcpy(drop->table_name, table->name, 1 + ut_strlen(table->name));
+
+ mutex_enter(&kernel_mutex);
+
+ if (!row_mysql_drop_list_inited) {
+
+ UT_LIST_INIT(row_mysql_drop_list);
+ row_mysql_drop_list_inited = TRUE;
+ }
+
+ UT_LIST_ADD_LAST(row_mysql_drop_list, row_mysql_drop_list, drop);
+
+/* fprintf(stderr, "InnoDB: Adding table %s to background drop list\n",
+ drop->table_name); */
+ mutex_exit(&kernel_mutex);
+}
+
+/*************************************************************************
Drops a table for MySQL. If the name of the dropped table ends to
characters INNODB_MONITOR, then this also stops printing of monitor
output by the master thread. */
@@ -1253,6 +1577,7 @@ row_drop_table_for_mysql(
ulint len;
ulint namelen;
ulint keywordlen;
+ ulint rounds = 0;
char buf[10000];
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
@@ -1419,9 +1744,10 @@ row_drop_table_for_mysql(
if (!table) {
err = DB_TABLE_NOT_FOUND;
+ ut_print_timestamp(stderr);
fprintf(stderr,
- "InnoDB: Error: table %s does not exist in the InnoDB internal\n"
+ " InnoDB: Error: table %s does not exist in the InnoDB internal\n"
"InnoDB: data dictionary though MySQL is trying to drop it.\n"
"InnoDB: Have you copied the .frm file of the table to the\n"
"InnoDB: MySQL database directory from another database?\n",
@@ -1429,15 +1755,26 @@ row_drop_table_for_mysql(
goto funct_exit;
}
+ if (table->n_mysql_handles_opened > 0) {
+
+ ut_print_timestamp(stderr);
+ fprintf(stderr,
+ " InnoDB: Warning: MySQL is trying to drop table %s\n"
+ "InnoDB: though there are still open handles to it.\n"
+ "InnoDB: Adding the table to the background drop queue.\n",
+ table->name);
+
+ row_add_table_to_background_drop_list(table);
+
+ err = DB_SUCCESS;
+
+ goto funct_exit;
+ }
+
/* Remove any locks there are on the table or its records */
lock_reset_all_on_table(table);
- /* TODO: check that MySQL prevents users from accessing the table
- after this function row_drop_table_for_mysql has been called:
- otherwise anyone with an open handle to the table could, for example,
- come to read the table! Monty said that it prevents. */
-
trx->dict_operation = TRUE;
trx->table_id = table->id;
@@ -1473,6 +1810,8 @@ funct_exit:
trx->op_info = (char *) "";
+ srv_wake_master_thread();
+
return((int) err);
}
@@ -1486,6 +1825,7 @@ row_drop_database_for_mysql(
char* name, /* in: database name which ends to '/' */
trx_t* trx) /* in: transaction handle */
{
+ dict_table_t* table;
char* table_name;
int err = DB_SUCCESS;
@@ -1496,12 +1836,35 @@ row_drop_database_for_mysql(
trx->op_info = (char *) "dropping database";
trx_start_if_not_started(trx);
-
+loop:
mutex_enter(&(dict_sys->mutex));
while ((table_name = dict_get_first_table_name_in_db(name))) {
ut_a(memcmp(table_name, name, strlen(name)) == 0);
+ table = dict_table_get_low(table_name);
+
+ ut_a(table);
+
+ /* Wait until MySQL does not have any queries running on
+ the table */
+
+ if (table->n_mysql_handles_opened > 0) {
+ mutex_exit(&(dict_sys->mutex));
+
+ ut_print_timestamp(stderr);
+ fprintf(stderr,
+ " InnoDB: Warning: MySQL is trying to drop database %s\n"
+ "InnoDB: though there are still open handles to table %s.\n",
+ name, table_name);
+
+ os_thread_sleep(1000000);
+
+ mem_free(table_name);
+
+ goto loop;
+ }
+
err = row_drop_table_for_mysql(table_name, trx, TRUE);
mem_free(table_name);
@@ -1644,7 +2007,31 @@ row_rename_table_for_mysql(
err = trx->error_state;
if (err != DB_SUCCESS) {
- row_mysql_handle_errors(&err, trx, thr, NULL);
+ if (err == DB_DUPLICATE_KEY) {
+ ut_print_timestamp(stderr);
+
+ fprintf(stderr,
+ " InnoDB: Error: table %s exists in the InnoDB internal data\n"
+ "InnoDB: dictionary though MySQL is trying rename table %s to it.\n"
+ "InnoDB: Have you deleted the .frm file and not used DROP TABLE?\n",
+ new_name, old_name);
+
+ fprintf(stderr,
+ "InnoDB: If table %s is a temporary table #sql..., then it can be that\n"
+ "InnoDB: there are still queries running on the table, and it will be\n"
+ "InnoDB: dropped automatically when the queries end.\n", new_name);
+
+ fprintf(stderr,
+ "InnoDB: You can drop the orphaned table inside InnoDB by\n"
+ "InnoDB: creating an InnoDB table with the same name in another\n"
+ "InnoDB: database and moving the .frm file to the current database.\n"
+ "InnoDB: Then MySQL thinks the table exists, and DROP TABLE will\n"
+ "InnoDB: succeed.\n");
+ }
+
+ trx->error_state = DB_SUCCESS;
+ trx_general_rollback_for_mysql(trx, FALSE, NULL);
+ trx->error_state = DB_SUCCESS;
} else {
ut_a(dict_table_rename_in_cache(table, new_name));
}
@@ -1801,7 +2188,7 @@ row_check_table_for_mysql(
ulint ret = DB_SUCCESS;
prebuilt->trx->op_info = (char *) "checking table";
-
+
index = dict_table_get_first_index(table);
while (index != NULL) {
diff --git a/innobase/row/row0sel.c b/innobase/row/row0sel.c
index 44cba9e3af4..abae7f373bf 100644
--- a/innobase/row/row0sel.c
+++ b/innobase/row/row0sel.c
@@ -2493,6 +2493,17 @@ row_search_for_mysql(
ut_ad(sync_thread_levels_empty_gen(FALSE));
+ if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) {
+ fprintf(stderr,
+ "InnoDB: Error: trying to free a corrupt\n"
+ "InnoDB: table handle. Magic n %lu, table name %s\n",
+ prebuilt->magic_n, prebuilt->table->name);
+
+ mem_analyze_corruption((byte*)prebuilt);
+
+ ut_a(0);
+ }
+
/* printf("Match mode %lu\n search tuple ", match_mode);
dtuple_print(search_tuple);
diff --git a/innobase/srv/srv0srv.c b/innobase/srv/srv0srv.c
index 3f8763f91d6..c3d3ed53058 100644
--- a/innobase/srv/srv0srv.c
+++ b/innobase/srv/srv0srv.c
@@ -20,7 +20,7 @@ Windows 2000 will have something called thread pooling
Another possibility could be to use some very fast user space
thread library. This might confuse NT though.
-(c) 1995 InnoDB Oy
+(c) 1995 Innobase Oy
Created 10/8/1995 Heikki Tuuri
*******************************************************/
@@ -49,6 +49,10 @@ Created 10/8/1995 Heikki Tuuri
#include "btr0sea.h"
#include "dict0load.h"
#include "srv0start.h"
+#include "row0mysql.h"
+
+/* Buffer which can be used in printing fatal error messages */
+char srv_fatal_errbuf[5000];
/* The following counter is incremented whenever there is some user activity
in the server */
@@ -88,8 +92,43 @@ ibool srv_log_archive_on = TRUE;
ulint srv_log_buffer_size = ULINT_MAX; /* size in database pages */
ibool srv_flush_log_at_trx_commit = TRUE;
-byte srv_latin1_ordering[256]; /* The sort order table of the latin1
- character set */
+byte srv_latin1_ordering[256] /* The sort order table of the latin1
+ character set. The following table is
+ the MySQL order as of Feb 10th, 2002 */
+= {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
+, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
+, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17
+, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
+, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27
+, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F
+, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37
+, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F
+, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47
+, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F
+, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57
+, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F
+, 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47
+, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F
+, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57
+, 0x58, 0x59, 0x5A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F
+, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87
+, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F
+, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97
+, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F
+, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7
+, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF
+, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7
+, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF
+, 0x41, 0x41, 0x41, 0x41, 0x5C, 0x5B, 0x5C, 0x43
+, 0x45, 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49
+, 0x44, 0x4E, 0x4F, 0x4F, 0x4F, 0x4F, 0x5D, 0xD7
+, 0xD8, 0x55, 0x55, 0x55, 0x59, 0x59, 0xDE, 0xDF
+, 0x41, 0x41, 0x41, 0x41, 0x5C, 0x5B, 0x5C, 0x43
+, 0x45, 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49
+, 0x44, 0x4E, 0x4F, 0x4F, 0x4F, 0x4F, 0x5D, 0xF7
+, 0xD8, 0x55, 0x55, 0x55, 0x59, 0x59, 0xDE, 0xFF
+};
ibool srv_use_native_aio = FALSE;
@@ -132,6 +171,9 @@ lint srv_conc_n_threads = 0; /* number of OS threads currently
thread increments this, but a thread
waiting for a lock decrements this
temporarily */
+ulint srv_conc_n_waiting_threads = 0; /* number of OS threads waiting in the
+ FIFO for a permission to enter InnoDB
+ */
typedef struct srv_conc_slot_struct srv_conc_slot_t;
struct srv_conc_slot_struct{
@@ -152,6 +194,11 @@ UT_LIST_BASE_NODE_T(srv_conc_slot_t) srv_conc_queue; /* queue of threads
waiting to get in */
srv_conc_slot_t srv_conc_slots[OS_THREAD_MAX_N]; /* array of wait
slots */
+
+/* Number of times a thread is allowed to enter InnoDB within the same
+SQL query after it has once got the ticket at srv_conc_enter_innodb */
+#define SRV_FREE_TICKETS_TO_ENTER 500
+
/*-----------------------*/
/* If the following is set TRUE then we do not run purge and insert buffer
merge to completion before shutdown */
@@ -1636,6 +1683,8 @@ srv_general_init(void)
thr_local_init();
}
+/*======================= InnoDB Server FIFO queue =======================*/
+
/*************************************************************************
Puts an OS thread to wait if there are too many concurrent threads
(>= srv_thread_concurrency) inside InnoDB. The threads wait in a FIFO queue. */
@@ -1649,11 +1698,29 @@ srv_conc_enter_innodb(
srv_conc_slot_t* slot;
ulint i;
+ if (srv_thread_concurrency >= 500) {
+ /* Disable the concurrency check */
+
+ return;
+ }
+
+ /* If trx has 'free tickets' to enter the engine left, then use one
+ such ticket */
+
+ if (trx->n_tickets_to_enter_innodb > 0) {
+ trx->n_tickets_to_enter_innodb--;
+
+ return;
+ }
+
os_fast_mutex_lock(&srv_conc_mutex);
if (srv_conc_n_threads < (lint)srv_thread_concurrency) {
- srv_conc_n_threads++;
+ srv_conc_n_threads++;
+ trx->declared_to_be_inside_innodb = TRUE;
+ trx->n_tickets_to_enter_innodb = SRV_FREE_TICKETS_TO_ENTER;
+
os_fast_mutex_unlock(&srv_conc_mutex);
return;
@@ -1674,6 +1741,8 @@ srv_conc_enter_innodb(
thread enter */
srv_conc_n_threads++;
+ trx->declared_to_be_inside_innodb = TRUE;
+ trx->n_tickets_to_enter_innodb = 0;
os_fast_mutex_unlock(&srv_conc_mutex);
@@ -1693,6 +1762,8 @@ srv_conc_enter_innodb(
os_event_reset(slot->event);
+ srv_conc_n_waiting_threads++;
+
os_fast_mutex_unlock(&srv_conc_mutex);
/* Go to wait for the event; when a thread leaves InnoDB it will
@@ -1702,6 +1773,8 @@ srv_conc_enter_innodb(
os_fast_mutex_lock(&srv_conc_mutex);
+ srv_conc_n_waiting_threads--;
+
/* NOTE that the thread which released this thread already
incremented the thread counter on behalf of this thread */
@@ -1709,6 +1782,9 @@ srv_conc_enter_innodb(
UT_LIST_REMOVE(srv_conc_queue, srv_conc_queue, slot);
+ trx->declared_to_be_inside_innodb = TRUE;
+ trx->n_tickets_to_enter_innodb = SRV_FREE_TICKETS_TO_ENTER;
+
os_fast_mutex_unlock(&srv_conc_mutex);
}
@@ -1717,29 +1793,52 @@ This lets a thread enter InnoDB regardless of the number of threads inside
InnoDB. This must be called when a thread ends a lock wait. */
void
-srv_conc_force_enter_innodb(void)
-/*=============================*/
+srv_conc_force_enter_innodb(
+/*========================*/
+ trx_t* trx) /* in: transaction object associated with the
+ thread */
{
+ if (srv_thread_concurrency >= 500) {
+
+ return;
+ }
+
os_fast_mutex_lock(&srv_conc_mutex);
srv_conc_n_threads++;
+ trx->declared_to_be_inside_innodb = TRUE;
+ trx->n_tickets_to_enter_innodb = 0;
os_fast_mutex_unlock(&srv_conc_mutex);
}
/*************************************************************************
-This must be called when a thread exits InnoDB. This must also be called
-when a thread goes to wait for a lock. */
+This must be called when a thread exits InnoDB in a lock wait or at the
+end of an SQL statement. */
void
-srv_conc_exit_innodb(void)
-/*======================*/
+srv_conc_force_exit_innodb(
+/*=======================*/
+ trx_t* trx) /* in: transaction object associated with the
+ thread */
{
srv_conc_slot_t* slot = NULL;
+ if (srv_thread_concurrency >= 500) {
+
+ return;
+ }
+
+ if (trx->declared_to_be_inside_innodb == FALSE) {
+
+ return;
+ }
+
os_fast_mutex_lock(&srv_conc_mutex);
srv_conc_n_threads--;
+ trx->declared_to_be_inside_innodb = FALSE;
+ trx->n_tickets_to_enter_innodb = 0;
if (srv_conc_n_threads < (lint)srv_thread_concurrency) {
/* Look for a slot where a thread is waiting and no other
@@ -1769,6 +1868,38 @@ srv_conc_exit_innodb(void)
}
/*************************************************************************
+This must be called when a thread exits InnoDB. */
+
+void
+srv_conc_exit_innodb(
+/*=================*/
+ trx_t* trx) /* in: transaction object associated with the
+ thread */
+{
+ srv_conc_slot_t* slot = NULL;
+
+ if (srv_thread_concurrency >= 500) {
+
+ return;
+ }
+
+ if (trx->n_tickets_to_enter_innodb > 0) {
+ /* We will pretend the thread is still inside InnoDB though it
+ now leaves the InnoDB engine. In this way we save
+ a lot of semaphore operations. srv_conc_force_exit_innodb is
+ used to declare the thread definitely outside InnoDB. It
+ should be called when there is a lock wait or an SQL statement
+ ends. */
+
+ return;
+ }
+
+ srv_conc_force_exit_innodb(trx);
+}
+
+/*========================================================================*/
+
+/*************************************************************************
Normalizes init parameter values to use units we use inside InnoDB. */
static
ulint
@@ -1825,17 +1956,12 @@ srv_boot(void)
srv_init();
- /* Reserve the first slot for the current thread, i.e., the master
- thread */
-
- srv_table_reserve_slot(SRV_MASTER);
-
return(DB_SUCCESS);
}
/*************************************************************************
Reserves a slot in the thread table for the current MySQL OS thread.
-NOTE! The server mutex has to be reserved by the caller! */
+NOTE! The kernel mutex has to be reserved by the caller! */
static
srv_slot_t*
srv_table_reserve_slot_for_mysql(void)
@@ -1845,6 +1971,8 @@ srv_table_reserve_slot_for_mysql(void)
srv_slot_t* slot;
ulint i;
+ ut_ad(mutex_own(&kernel_mutex));
+
i = 0;
slot = srv_mysql_table + i;
@@ -1914,7 +2042,7 @@ srv_suspend_mysql_thread(
other thread holding a lock which this thread waits for must be
allowed to enter, sooner or later */
- srv_conc_exit_innodb();
+ srv_conc_force_exit_innodb(thr_get_trx(thr));
/* Wait for the release */
@@ -1922,7 +2050,7 @@ srv_suspend_mysql_thread(
/* Return back inside InnoDB */
- srv_conc_force_enter_innodb();
+ srv_conc_force_enter_innodb(thr_get_trx(thr));
mutex_enter(&kernel_mutex);
@@ -2061,8 +2189,9 @@ loop:
"ROW OPERATIONS\n"
"--------------\n");
printf(
- "%ld queries inside InnoDB; main thread: %s\n",
- srv_conc_n_threads, srv_main_thread_op_info);
+ "%ld queries inside InnoDB, %ld queries in queue; main thread: %s\n",
+ srv_conc_n_threads, srv_conc_n_waiting_threads,
+ srv_main_thread_op_info);
printf(
"Number of rows inserted %lu, updated %lu, deleted %lu, read %lu\n",
srv_n_rows_inserted,
@@ -2265,6 +2394,22 @@ srv_active_wake_master_thread(void)
}
}
+/***********************************************************************
+Wakes up the master thread if it is suspended or being suspended. */
+
+void
+srv_wake_master_thread(void)
+/*========================*/
+{
+ srv_activity_count++;
+
+ mutex_enter(&kernel_mutex);
+
+ srv_release_threads(SRV_MASTER, 1);
+
+ mutex_exit(&kernel_mutex);
+}
+
/*************************************************************************
The master thread controlling the server. */
@@ -2287,6 +2432,7 @@ srv_master_thread(
ulint n_bytes_merged;
ulint n_pages_flushed;
ulint n_bytes_archived;
+ ulint n_tables_to_drop;
ulint n_ios;
ulint n_ios_old;
ulint n_ios_very_old;
@@ -2324,6 +2470,16 @@ loop:
srv_main_thread_op_info = (char *) "sleeping";
os_thread_sleep(1000000);
+ /* ALTER TABLE in MySQL requires on Unix that the table handler
+ can drop tables lazily after there no longer are SELECT
+ queries to them. */
+
+ srv_main_thread_op_info = "doing background drop tables";
+
+ row_drop_tables_for_mysql_in_background();
+
+ srv_main_thread_op_info = "";
+
if (srv_force_recovery >= SRV_FORCE_NO_BACKGROUND) {
goto suspend_thread;
@@ -2373,6 +2529,11 @@ loop:
printf("Master thread wakes up!\n");
}
+#ifdef MEM_PERIODIC_CHECK
+ /* Check magic numbers of every allocated mem block once in 10
+ seconds */
+ mem_validate_all_blocks();
+#endif
/* If there were less than 200 i/os during the 10 second period,
we assume that there is free disk i/o capacity available, and it
makes sense to do a buffer pool flush. */
@@ -2429,6 +2590,12 @@ background_loop:
/* In this loop we run background operations when the server
is quiet and we also come here about once in 10 seconds */
+ srv_main_thread_op_info = "doing background drop tables";
+
+ n_tables_to_drop = row_drop_tables_for_mysql_in_background();
+
+ srv_main_thread_op_info = "";
+
srv_main_thread_op_info = "flushing buffer pool pages";
/* Flush a few oldest pages to make the checkpoint younger */
@@ -2514,11 +2681,13 @@ background_loop:
log_archive_do(FALSE, &n_bytes_archived);
if (srv_fast_shutdown && srv_shutdown_state > 0) {
- if (n_pages_flushed + n_bytes_archived != 0) {
+ if (n_tables_to_drop + n_pages_flushed
+ + n_bytes_archived != 0) {
goto background_loop;
}
- } else if (n_pages_purged + n_bytes_merged + n_pages_flushed
+ } else if (n_tables_to_drop +
+ n_pages_purged + n_bytes_merged + n_pages_flushed
+ n_bytes_archived != 0) {
goto background_loop;
}
@@ -2537,6 +2706,12 @@ suspend_thread:
mutex_enter(&kernel_mutex);
+ if (row_get_background_drop_list_len_low() > 0) {
+ mutex_exit(&kernel_mutex);
+
+ goto loop;
+ }
+
event = srv_suspend_thread();
mutex_exit(&kernel_mutex);
diff --git a/innobase/srv/srv0start.c b/innobase/srv/srv0start.c
index 702386a1a99..bbb48331811 100644
--- a/innobase/srv/srv0start.c
+++ b/innobase/srv/srv0start.c
@@ -271,13 +271,18 @@ open_or_create_log_file(
} else {
*log_file_created = TRUE;
+ ut_print_timestamp(stderr);
+
fprintf(stderr,
- "InnoDB: Log file %s did not exist: new to be created\n",
+ " InnoDB: Log file %s did not exist: new to be created\n",
name);
fprintf(stderr, "InnoDB: Setting log file %s size to %lu MB\n",
name, srv_log_file_size
>> (20 - UNIV_PAGE_SIZE_SHIFT));
+ fprintf(stderr,
+ "InnoDB: Database physically writes the file full: wait...\n");
+
ret = os_file_set_size(name, files[i],
srv_calc_low32(srv_log_file_size),
srv_calc_high32(srv_log_file_size));
@@ -456,8 +461,9 @@ open_or_create_data_files(
one_created = TRUE;
if (i > 0) {
+ ut_print_timestamp(stderr);
fprintf(stderr,
- "InnoDB: Data file %s did not exist: new to be created\n",
+ " InnoDB: Data file %s did not exist: new to be created\n",
name);
} else {
fprintf(stderr,
@@ -466,8 +472,9 @@ open_or_create_data_files(
*create_new_db = TRUE;
}
+ ut_print_timestamp(stderr);
fprintf(stderr,
- "InnoDB: Setting file %s size to %lu MB\n",
+ " InnoDB: Setting file %s size to %lu MB\n",
name, (srv_data_file_sizes[i]
>> (20 - UNIV_PAGE_SIZE_SHIFT)));
@@ -815,7 +822,7 @@ innobase_start_or_create_for_mysql(void)
fprintf(stderr,
"InnoDB: Cannot initialize created log files because\n"
"InnoDB: data files were not in sync with each other\n"
- "InnoDB: or the data files are corrupt./n");
+ "InnoDB: or the data files are corrupt.\n");
return(DB_ERROR);
}
@@ -911,6 +918,12 @@ innobase_start_or_create_for_mysql(void)
mtr_commit(&mtr);
}
+ if (recv_needed_recovery) {
+ ut_print_timestamp(stderr);
+ fprintf(stderr,
+ " InnoDB: Flushing modified pages from the buffer pool...\n");
+ }
+
log_make_checkpoint_at(ut_dulint_max, TRUE);
if (!srv_log_archive_on) {
@@ -991,9 +1004,8 @@ innobase_shutdown_for_mysql(void)
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: Warning: shutting down a not properly started\n");
- ut_print_timestamp(stderr);
fprintf(stderr,
- " InnoDB: or created database!\n");
+ " InnoDB: or created database!\n");
}
return(DB_SUCCESS);
diff --git a/innobase/sync/sync0arr.c b/innobase/sync/sync0arr.c
index 573829fcef8..4487fa72995 100644
--- a/innobase/sync/sync0arr.c
+++ b/innobase/sync/sync0arr.c
@@ -913,6 +913,17 @@ sync_array_print_long_waits(void)
noticed = TRUE;
}
+
+ if (cell->wait_object != NULL
+ && difftime(time(NULL), cell->reservation_time) > 420) {
+
+ fprintf(stderr,
+"InnoDB: Error: semaphore wait has lasted > 420 seconds\n"
+"InnoDB: We intentionally crash the server, because it appears to be hung.\n"
+ );
+
+ ut_a(0);
+ }
}
if (noticed) {
diff --git a/innobase/trx/trx0purge.c b/innobase/trx/trx0purge.c
index 31a0c20ad14..97362d00b4b 100644
--- a/innobase/trx/trx0purge.c
+++ b/innobase/trx/trx0purge.c
@@ -226,9 +226,9 @@ trx_purge_sys_create(void)
value */
purge_sys->sess = sess_open(com_endpoint, (byte*)"purge_system", 13);
- purge_sys->trx = (purge_sys->sess)->trx;
+ purge_sys->trx = purge_sys->sess->trx;
- (purge_sys->trx)->type = TRX_PURGE;
+ purge_sys->trx->type = TRX_PURGE;
ut_a(trx_start_low(purge_sys->trx, ULINT_UNDEFINED));
diff --git a/innobase/trx/trx0sys.c b/innobase/trx/trx0sys.c
index e79e4594637..b29ffb4b3bf 100644
--- a/innobase/trx/trx0sys.c
+++ b/innobase/trx/trx0sys.c
@@ -389,6 +389,115 @@ trx_sys_flush_max_trx_id(void)
mtr_commit(&mtr);
}
+/*********************************************************************
+Updates the offset information about the end of the MySQL binlog entry
+which corresponds to the transaction just being committed. */
+
+void
+trx_sys_update_mysql_binlog_offset(
+/*===============================*/
+ trx_t* trx, /* in: transaction being committed */
+ mtr_t* mtr) /* in: mtr */
+{
+ trx_sysf_t* sys_header;
+ char namebuf[TRX_SYS_MYSQL_LOG_NAME_LEN];
+
+ ut_ad(mutex_own(&kernel_mutex));
+ ut_ad(trx->mysql_log_file_name);
+
+ memset(namebuf, ' ', TRX_SYS_MYSQL_LOG_NAME_LEN - 1);
+ namebuf[TRX_SYS_MYSQL_LOG_NAME_LEN - 1] = '\0';
+
+ /* Copy the whole MySQL log file name to the buffer, or only the
+ last characters, if it does not fit */
+
+ if (ut_strlen(trx->mysql_log_file_name)
+ > TRX_SYS_MYSQL_LOG_NAME_LEN - 1) {
+ ut_memcpy(namebuf, trx->mysql_log_file_name
+ + ut_strlen(trx->mysql_log_file_name)
+ - (TRX_SYS_MYSQL_LOG_NAME_LEN - 1),
+ TRX_SYS_MYSQL_LOG_NAME_LEN - 1);
+ } else {
+ ut_memcpy(namebuf, trx->mysql_log_file_name,
+ 1 + ut_strlen(trx->mysql_log_file_name));
+ }
+
+ namebuf[TRX_SYS_MYSQL_LOG_NAME_LEN - 1] = '\0';
+
+ sys_header = trx_sysf_get(mtr);
+
+ if (mach_read_from_4(sys_header + TRX_SYS_MYSQL_LOG_INFO
+ + TRX_SYS_MYSQL_LOG_MAGIC_N_FLD)
+ != TRX_SYS_MYSQL_LOG_MAGIC_N) {
+
+ mlog_write_ulint(sys_header + TRX_SYS_MYSQL_LOG_INFO
+ + TRX_SYS_MYSQL_LOG_MAGIC_N_FLD,
+ TRX_SYS_MYSQL_LOG_MAGIC_N,
+ MLOG_4BYTES, mtr);
+ }
+
+ if (0 != ut_memcmp(sys_header + TRX_SYS_MYSQL_LOG_INFO
+ + TRX_SYS_MYSQL_LOG_NAME,
+ namebuf, TRX_SYS_MYSQL_LOG_NAME_LEN)) {
+
+ mlog_write_string(sys_header + TRX_SYS_MYSQL_LOG_INFO
+ + TRX_SYS_MYSQL_LOG_NAME,
+ namebuf, TRX_SYS_MYSQL_LOG_NAME_LEN, mtr);
+ }
+
+ if (mach_read_from_4(sys_header + TRX_SYS_MYSQL_LOG_INFO
+ + TRX_SYS_MYSQL_LOG_OFFSET_HIGH) > 0
+ || (trx->mysql_log_offset >> 32) > 0) {
+
+ mlog_write_ulint(sys_header + TRX_SYS_MYSQL_LOG_INFO
+ + TRX_SYS_MYSQL_LOG_OFFSET_HIGH,
+ (ulint)(trx->mysql_log_offset >> 32),
+ MLOG_4BYTES, mtr);
+ }
+
+ mlog_write_ulint(sys_header + TRX_SYS_MYSQL_LOG_INFO
+ + TRX_SYS_MYSQL_LOG_OFFSET_LOW,
+ (ulint)(trx->mysql_log_offset & 0xFFFFFFFF),
+ MLOG_4BYTES, mtr);
+
+ trx->mysql_log_file_name = NULL;
+}
+
+/*********************************************************************
+Prints to stderr the MySQL binlog offset info in the trx system header if
+the magic number shows it valid. */
+
+void
+trx_sys_print_mysql_binlog_offset(void)
+/*===================================*/
+{
+ trx_sysf_t* sys_header;
+ mtr_t mtr;
+
+ mtr_start(&mtr);
+
+ sys_header = trx_sysf_get(&mtr);
+
+ if (mach_read_from_4(sys_header + TRX_SYS_MYSQL_LOG_INFO
+ + TRX_SYS_MYSQL_LOG_MAGIC_N_FLD)
+ != TRX_SYS_MYSQL_LOG_MAGIC_N) {
+
+ mtr_commit(&mtr);
+
+ return;
+ }
+
+ fprintf(stderr,
+ "InnoDB: Last MySQL binlog file offset %lu %lu, file name %s\n",
+ mach_read_from_4(sys_header + TRX_SYS_MYSQL_LOG_INFO
+ + TRX_SYS_MYSQL_LOG_OFFSET_HIGH),
+ mach_read_from_4(sys_header + TRX_SYS_MYSQL_LOG_INFO
+ + TRX_SYS_MYSQL_LOG_OFFSET_LOW),
+ sys_header + TRX_SYS_MYSQL_LOG_INFO + TRX_SYS_MYSQL_LOG_NAME);
+
+ mtr_commit(&mtr);
+}
+
/********************************************************************
Looks for a free slot for a rollback segment in the trx system file copy. */
@@ -519,7 +628,7 @@ trx_sys_init_at_db_start(void)
"InnoDB: %lu uncommitted transaction(s) which must be rolled back\n",
UT_LIST_GET_LEN(trx_sys->trx_list));
- fprintf(stderr, "Trx id counter is %lu %lu\n",
+ fprintf(stderr, "InnoDB: Trx id counter is %lu %lu\n",
ut_dulint_get_high(trx_sys->max_trx_id),
ut_dulint_get_low(trx_sys->max_trx_id));
}
diff --git a/innobase/trx/trx0trx.c b/innobase/trx/trx0trx.c
index 5f696d9c01a..607c80edd09 100644
--- a/innobase/trx/trx0trx.c
+++ b/innobase/trx/trx0trx.c
@@ -26,9 +26,9 @@ Created 3/26/1996 Heikki Tuuri
/* Copy of the prototype for innobase_mysql_print_thd: this
- copy must be equal to the one in mysql/sql/ha_innobase.cc ! */
-void innobase_mysql_print_thd(void* thd);
+copy must be equal to the one in mysql/sql/ha_innobase.cc ! */
+void innobase_mysql_print_thd(void* thd);
/* Dummy session used currently in MySQL interface */
sess_t* trx_dummy_sess = NULL;
@@ -64,6 +64,8 @@ trx_create(
trx = mem_alloc(sizeof(trx_t));
+ trx->magic_n = TRX_MAGIC_N;
+
trx->op_info = (char *) "";
trx->type = TRX_USER;
@@ -76,6 +78,9 @@ trx_create(
trx->n_mysql_tables_in_use = 0;
trx->mysql_n_tables_locked = 0;
+ trx->mysql_log_file_name = NULL;
+ trx->mysql_log_offset = 0;
+
trx->ignore_duplicates_in_insert = FALSE;
mutex_create(&(trx->undo_mutex));
@@ -111,6 +116,9 @@ trx_create(
trx->has_search_latch = FALSE;
trx->search_latch_timeout = BTR_SEA_TIMEOUT;
+ trx->declared_to_be_inside_innodb = FALSE;
+ trx->n_tickets_to_enter_innodb = 0;
+
trx->auto_inc_lock = NULL;
trx->read_view_heap = mem_heap_create(256);
@@ -152,6 +160,32 @@ trx_allocate_for_mysql(void)
}
/************************************************************************
+Creates a transaction object for background operations by the master thread. */
+
+trx_t*
+trx_allocate_for_background(void)
+/*=============================*/
+ /* out, own: transaction object */
+{
+ trx_t* trx;
+
+ mutex_enter(&kernel_mutex);
+
+ /* Open a dummy session */
+
+ if (!trx_dummy_sess) {
+ trx_dummy_sess = sess_open(NULL, (byte*)"Dummy sess",
+ ut_strlen("Dummy sess"));
+ }
+
+ trx = trx_create(trx_dummy_sess);
+
+ mutex_exit(&kernel_mutex);
+
+ return(trx);
+}
+
+/************************************************************************
Releases the search latch if trx has reserved it. */
void
@@ -175,6 +209,11 @@ trx_free(
trx_t* trx) /* in, own: trx object */
{
ut_ad(mutex_own(&kernel_mutex));
+
+ ut_a(trx->magic_n == TRX_MAGIC_N);
+
+ trx->magic_n = 11112222;
+
ut_a(trx->conc_state == TRX_NOT_STARTED);
mutex_free(&(trx->undo_mutex));
@@ -236,6 +275,21 @@ trx_free_for_mysql(
mutex_exit(&kernel_mutex);
}
+/************************************************************************
+Frees a transaction object of a background operation of the master thread. */
+
+void
+trx_free_for_background(
+/*====================*/
+ trx_t* trx) /* in, own: trx object */
+{
+ mutex_enter(&kernel_mutex);
+
+ trx_free(trx);
+
+ mutex_exit(&kernel_mutex);
+}
+
/********************************************************************
Inserts the trx handle in the trx system trx list in the right position.
The list is sorted on the trx id so that the biggest id is at the list
@@ -568,6 +622,13 @@ trx_commit_off_kernel(
mutex_exit(&(rseg->mutex));
+ /* Update the latest MySQL binlog name and offset info
+ in trx sys header if MySQL binlogging is on */
+
+ if (trx->mysql_log_file_name) {
+ trx_sys_update_mysql_binlog_offset(trx, &mtr);
+ }
+
/* If we did not take the shortcut, the following call
commits the mini-transaction, making the whole transaction
committed in the file-based world at this log sequence number;
diff --git a/innobase/trx/trx0undo.c b/innobase/trx/trx0undo.c
index d53f8bd73de..aae31f3726b 100644
--- a/innobase/trx/trx0undo.c
+++ b/innobase/trx/trx0undo.c
@@ -1310,9 +1310,10 @@ trx_undo_mem_init_for_reuse(
{
ut_ad(mutex_own(&((undo->rseg)->mutex)));
- if (undo->id >= TRX_RSEG_N_SLOTS) {
- fprintf(stderr,
- "InnoDB: Error: undo->id is %lu\n", undo->id);
+ if (undo->id >= TRX_RSEG_N_SLOTS) {
+ fprintf(stderr, "InnoDB: Error: undo->id is %lu\n", undo->id);
+
+ mem_analyze_corruption((byte*)undo);
ut_a(0);
}
@@ -1399,7 +1400,7 @@ trx_undo_create(
/************************************************************************
Reuses a cached undo log. */
-UNIV_INLINE
+static
trx_undo_t*
trx_undo_reuse_cached(
/*==================*/
@@ -1442,6 +1443,12 @@ trx_undo_reuse_cached(
ut_ad(undo->size == 1);
ut_ad(undo->hdr_page_no == undo->top_page_no);
+ if (undo->id >= TRX_RSEG_N_SLOTS) {
+ fprintf(stderr, "InnoDB: Error: undo->id is %lu\n", undo->id);
+ mem_analyze_corruption((byte*)undo);
+ ut_a(0);
+ }
+
undo_page = trx_undo_page_get(undo->space, undo->hdr_page_no, mtr);
if (type == TRX_UNDO_INSERT) {
@@ -1572,8 +1579,8 @@ trx_undo_set_state_at_finish(
ut_ad(trx && undo && mtr);
if (undo->id >= TRX_RSEG_N_SLOTS) {
- fprintf(stderr,
- "InnoDB: Error: undo->id is %lu\n", undo->id);
+ fprintf(stderr, "InnoDB: Error: undo->id is %lu\n", undo->id);
+ mem_analyze_corruption((byte*)undo);
ut_a(0);
}
diff --git a/innobase/ut/ut0mem.c b/innobase/ut/ut0mem.c
index 84a79d39556..79351ff120f 100644
--- a/innobase/ut/ut0mem.c
+++ b/innobase/ut/ut0mem.c
@@ -81,8 +81,10 @@ ut_malloc_low(
"InnoDB: by InnoDB %lu bytes. Operating system errno: %d\n"
"InnoDB: Cannot continue operation!\n"
"InnoDB: Check if you should increase the swap file or\n"
- "InnoDB: ulimits of your operating system.\n",
- n, ut_total_allocated_memory, errno);
+ "InnoDB: ulimits of your operating system.\n"
+ "InnoDB: On FreeBSD check you have compiled the OS with\n"
+ "InnoDB: a big enough maximum process size.\n",
+ n, ut_total_allocated_memory, errno);
os_fast_mutex_unlock(&ut_list_mutex);