diff options
author | unknown <heikki@donna.mysql.fi> | 2002-01-28 22:18:49 +0200 |
---|---|---|
committer | unknown <heikki@donna.mysql.fi> | 2002-01-28 22:18:49 +0200 |
commit | ac540e96a915c30034a4fc03ef65b60b8392f664 (patch) | |
tree | aa7f771761883b28925048ff5e37e2eb85d77c00 /innobase | |
parent | 2aa572433b9ce47a511958ce263c8400c2e81ac3 (diff) | |
download | mariadb-git-ac540e96a915c30034a4fc03ef65b60b8392f664.tar.gz |
Many files:
Merge InnoDB-.48
sql/ha_innobase.cc:
Merge InnoDB-.48
innobase/include/dict0dict.h:
Merge InnoDB-.48
innobase/include/dict0mem.h:
Merge InnoDB-.48
innobase/include/mem0dbg.h:
Merge InnoDB-.48
innobase/include/mem0mem.h:
Merge InnoDB-.48
innobase/include/que0que.h:
Merge InnoDB-.48
innobase/include/row0mysql.h:
Merge InnoDB-.48
innobase/include/srv0srv.h:
Merge InnoDB-.48
innobase/include/trx0sys.h:
Merge InnoDB-.48
innobase/include/trx0trx.h:
Merge InnoDB-.48
innobase/include/mem0mem.ic:
Merge InnoDB-.48
innobase/dict/dict0dict.c:
Merge InnoDB-.48
innobase/dict/dict0mem.c:
Merge InnoDB-.48
innobase/log/log0recv.c:
Merge InnoDB-.48
innobase/mem/mem0dbg.c:
Merge InnoDB-.48
innobase/mem/mem0mem.c:
Merge InnoDB-.48
innobase/pars/lexyy.c:
Merge InnoDB-.48
innobase/que/que0que.c:
Merge InnoDB-.48
innobase/rem/rem0rec.c:
Merge InnoDB-.48
innobase/row/row0mysql.c:
Merge InnoDB-.48
innobase/row/row0sel.c:
Merge InnoDB-.48
innobase/srv/srv0srv.c:
Merge InnoDB-.48
innobase/sync/sync0arr.c:
Merge InnoDB-.48
innobase/trx/trx0sys.c:
Merge InnoDB-.48
innobase/trx/trx0trx.c:
Merge InnoDB-.48
innobase/trx/trx0undo.c:
Merge InnoDB-.48
Diffstat (limited to 'innobase')
-rw-r--r-- | innobase/dict/dict0dict.c | 32 | ||||
-rw-r--r-- | innobase/dict/dict0mem.c | 3 | ||||
-rw-r--r-- | innobase/include/dict0dict.h | 14 | ||||
-rw-r--r-- | innobase/include/dict0mem.h | 6 | ||||
-rw-r--r-- | innobase/include/mem0dbg.h | 14 | ||||
-rw-r--r-- | innobase/include/mem0mem.h | 97 | ||||
-rw-r--r-- | innobase/include/mem0mem.ic | 57 | ||||
-rw-r--r-- | innobase/include/que0que.h | 5 | ||||
-rw-r--r-- | innobase/include/row0mysql.h | 7 | ||||
-rw-r--r-- | innobase/include/srv0srv.h | 26 | ||||
-rw-r--r-- | innobase/include/trx0sys.h | 35 | ||||
-rw-r--r-- | innobase/include/trx0trx.h | 22 | ||||
-rw-r--r-- | innobase/log/log0recv.c | 20 | ||||
-rw-r--r-- | innobase/mem/mem0dbg.c | 94 | ||||
-rw-r--r-- | innobase/mem/mem0mem.c | 32 | ||||
-rw-r--r-- | innobase/pars/lexyy.c | 2 | ||||
-rw-r--r-- | innobase/que/que0que.c | 40 | ||||
-rw-r--r-- | innobase/rem/rem0rec.c | 11 | ||||
-rw-r--r-- | innobase/row/row0mysql.c | 223 | ||||
-rw-r--r-- | innobase/row/row0sel.c | 11 | ||||
-rw-r--r-- | innobase/srv/srv0srv.c | 124 | ||||
-rw-r--r-- | innobase/sync/sync0arr.c | 11 | ||||
-rw-r--r-- | innobase/trx/trx0sys.c | 111 | ||||
-rw-r--r-- | innobase/trx/trx0trx.c | 13 | ||||
-rw-r--r-- | innobase/trx/trx0undo.c | 19 |
25 files changed, 846 insertions, 183 deletions
diff --git a/innobase/dict/dict0dict.c b/innobase/dict/dict0dict.c index 9e32be9e02d..95f9b54c914 100644 --- a/innobase/dict/dict0dict.c +++ b/innobase/dict/dict0dict.c @@ -196,6 +196,38 @@ dict_mutex_exit_for_mysql(void) } /************************************************************************ +Increments the count of open MySQL handles to a table. */ + +void +dict_table_increment_handle_count( +/*==============================*/ + dict_table_t* table) /* in: table */ +{ + mutex_enter(&(dict_sys->mutex)); + + table->n_mysql_handles_opened++; + + mutex_exit(&(dict_sys->mutex)); +} + +/************************************************************************ +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* 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/include/dict0dict.h b/innobase/include/dict0dict.h index ae313398c99..79d67ecae15 100644 --- a/innobase/include/dict0dict.h +++ b/innobase/include/dict0dict.h @@ -26,6 +26,20 @@ Created 1/8/1996 Heikki Tuuri #include "ut0byte.h" #include "trx0types.h" +/************************************************************************ +Increments the count of open MySQL handles to a table. */ + +void +dict_table_increment_handle_count( +/*==============================*/ + dict_table_t* table); /* in: table */ +/************************************************************************ +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. */ 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/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..57fac93d3ac 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,7 +266,9 @@ 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 */ /*#######################################################################*/ @@ -336,8 +300,13 @@ struct mem_block_info_struct { free block to the heap, if we need more space; otherwise, this is NULL */ 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 */ }; +#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/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..0346299bb10 100644 --- a/innobase/include/row0mysql.h +++ b/innobase/include/row0mysql.h @@ -323,11 +323,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 diff --git a/innobase/include/srv0srv.h b/innobase/include/srv0srv.h index 1a37460569b..05989c6410e 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 */ @@ -261,15 +263,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. 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 */ +/************************************************************************* +This must be called when a thread exits 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..d67628b8bad 100644 --- a/innobase/include/trx0trx.h +++ b/innobase/include/trx0trx.h @@ -290,10 +290,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 +324,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..7be172685ae 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 @@ -1020,7 +1022,7 @@ loop: if (!has_printed) { fprintf(stderr, "InnoDB: Starting an apply batch of log records to the database...\n" -"InnoDB: Progress in percents:"); +"InnoDB: Progress in percents: "); has_printed = TRUE; } @@ -2032,12 +2034,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 +2205,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 0d71708b906..f8f62dffa8b 100644 --- a/innobase/mem/mem0dbg.c +++ b/innobase/mem/mem0dbg.c @@ -808,7 +808,7 @@ mem_validate_no_assert(void) } mutex_exit(&mem_hash_mutex); - + return(error); #else @@ -832,3 +832,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..58fb618e2db 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 @@ -85,18 +86,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 +108,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; @@ -164,7 +161,11 @@ 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; + mem_block_set_len(block, len); mem_block_set_type(block, type); mem_block_set_free(block, MEM_BLOCK_HEADER_SIZE); @@ -223,8 +224,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); @@ -255,7 +256,8 @@ mem_heap_block_free( 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. */ 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 96e505f8b80..97843311d21 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/rem0rec.c b/innobase/rem/rem0rec.c index 749e19575bc..3889f62afa2 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 9622a7cee32..c0f527017e0 100644 --- a/innobase/row/row0mysql.c +++ b/innobase/row/row0mysql.c @@ -242,10 +242,14 @@ row_create_prebuilt( ulint ref_len; ulint i; + dict_table_increment_handle_count(table); + heap = mem_heap_create(128); prebuilt = mem_heap_alloc(heap, sizeof(row_prebuilt_t)); + prebuilt->magic_n = ROW_PREBUILT_ALLOCATED; + prebuilt->table = table; prebuilt->trx = NULL; @@ -294,7 +298,7 @@ row_create_prebuilt( prebuilt->blob_heap = NULL; prebuilt->old_vers_heap = NULL; - + return(prebuilt); } @@ -308,6 +312,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 +358,8 @@ row_prebuilt_free( } } + dict_table_decrement_handle_count(prebuilt->table); + mem_heap_free(prebuilt->heap); } @@ -356,6 +375,28 @@ row_update_prebuilt_trx( handle */ trx_t* trx) /* in: transaction handle */ { + 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 (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 +604,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 +800,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 +845,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 +954,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 +1051,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("innodb_monitor"); if (namelen >= keywordlen @@ -1118,6 +1211,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()); @@ -1126,6 +1221,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 */ @@ -1189,6 +1296,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); @@ -1197,6 +1306,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 */ @@ -1251,6 +1372,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()); @@ -1427,11 +1549,38 @@ row_drop_table_for_mysql( /* Remove any locks there are on the table or its records */ lock_reset_all_on_table(table); +loop: + if (table->n_mysql_handles_opened > 0) { + rw_lock_s_unlock(&(purge_sys->purge_is_running)); + + rw_lock_x_unlock(&(dict_foreign_key_check_lock)); + + mutex_exit(&(dict_sys->mutex)); + + if (rounds > 60) { + fprintf(stderr, + "InnoDB: waiting for queries to table %s to end before dropping it\n", + name); + } + + os_thread_sleep(1000000); + + mutex_enter(&(dict_sys->mutex)); + + rw_lock_x_lock(&(dict_foreign_key_check_lock)); + + rw_lock_s_lock(&(purge_sys->purge_is_running)); - /* 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. */ + rounds++; + + if (rounds > 120) { + fprintf(stderr, +"InnoDB: Warning: queries to table %s have not ended but we continue anyway\n", + name); + } else { + goto loop; + } + } trx->dict_operation = TRUE; trx->table_id = table->id; diff --git a/innobase/row/row0sel.c b/innobase/row/row0sel.c index 2cccc217621..663a544faac 100644 --- a/innobase/row/row0sel.c +++ b/innobase/row/row0sel.c @@ -2492,6 +2492,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 d77db335366..d75783fe425 100644 --- a/innobase/srv/srv0srv.c +++ b/innobase/srv/srv0srv.c @@ -50,6 +50,9 @@ Created 10/8/1995 Heikki Tuuri #include "dict0load.h" #include "srv0start.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 */ ulint srv_activity_count = 0; @@ -132,6 +135,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 +158,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 */ @@ -1627,6 +1638,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. */ @@ -1640,11 +1653,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; @@ -1665,6 +1696,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); @@ -1684,6 +1717,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 @@ -1693,6 +1728,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 */ @@ -1700,6 +1737,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); } @@ -1708,29 +1748,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 @@ -1760,6 +1823,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 @@ -1905,7 +2000,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 */ @@ -1913,7 +2008,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); @@ -2052,8 +2147,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, @@ -2315,6 +2411,12 @@ loop: srv_main_thread_op_info = "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. */ + +/* row_drop_tables_for_mysql_in_background(); */ + if (srv_force_recovery >= SRV_FORCE_NO_BACKGROUND) { goto suspend_thread; diff --git a/innobase/sync/sync0arr.c b/innobase/sync/sync0arr.c index c41754316d0..7788b104120 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/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 18c80819245..43cca5b62b3 100644 --- a/innobase/trx/trx0trx.c +++ b/innobase/trx/trx0trx.c @@ -76,6 +76,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 +114,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); @@ -568,6 +574,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 8b83163bfc2..b5341871228 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); } |