Commit ac540e96 authored by unknown's avatar unknown

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
parent 2aa57243
...@@ -195,6 +195,38 @@ dict_mutex_exit_for_mysql(void) ...@@ -195,6 +195,38 @@ dict_mutex_exit_for_mysql(void)
mutex_exit(&(dict_sys->mutex)); mutex_exit(&(dict_sys->mutex));
} }
/************************************************************************
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. */ Gets the nth column of a table. */
......
...@@ -59,6 +59,9 @@ dict_mem_table_create( ...@@ -59,6 +59,9 @@ dict_mem_table_create(
table->n_def = 0; table->n_def = 0;
table->n_cols = n_cols + DATA_N_SYS_COLS; table->n_cols = n_cols + DATA_N_SYS_COLS;
table->mem_fix = 0; table->mem_fix = 0;
table->n_mysql_handles_opened = 0;
table->cached = FALSE; table->cached = FALSE;
table->cols = mem_heap_alloc(heap, (n_cols + DATA_N_SYS_COLS) table->cols = mem_heap_alloc(heap, (n_cols + DATA_N_SYS_COLS)
......
...@@ -26,6 +26,20 @@ Created 1/8/1996 Heikki Tuuri ...@@ -26,6 +26,20 @@ Created 1/8/1996 Heikki Tuuri
#include "ut0byte.h" #include "ut0byte.h"
#include "trx0types.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. */ Inits the data dictionary module. */
......
...@@ -307,6 +307,12 @@ struct dict_table_struct{ ...@@ -307,6 +307,12 @@ struct dict_table_struct{
ulint mem_fix;/* count of how many times the table ulint mem_fix;/* count of how many times the table
and its indexes has been fixed in memory; and its indexes has been fixed in memory;
currently NOT used */ 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 ibool cached; /* TRUE if the table object has been added
to the dictionary cache */ to the dictionary cache */
lock_t* auto_inc_lock;/* a buffer for an auto-inc lock lock_t* auto_inc_lock;/* a buffer for an auto-inc lock
......
...@@ -10,11 +10,14 @@ Created 6/9/1994 Heikki Tuuri ...@@ -10,11 +10,14 @@ Created 6/9/1994 Heikki Tuuri
/* In the debug version each allocated field is surrounded with /* In the debug version each allocated field is surrounded with
check fields whose sizes are given below */ check fields whose sizes are given below */
#ifdef UNIV_MEM_DEBUG
#define MEM_FIELD_HEADER_SIZE ut_calc_align(2 * sizeof(ulint),\ #define MEM_FIELD_HEADER_SIZE ut_calc_align(2 * sizeof(ulint),\
UNIV_MEM_ALIGNMENT) UNIV_MEM_ALIGNMENT)
#define MEM_FIELD_TRAILER_SIZE sizeof(ulint) #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 /* Space needed when allocating for a user a field of
length N. The space is allocated only in multiples of length N. The space is allocated only in multiples of
...@@ -115,3 +118,12 @@ ibool ...@@ -115,3 +118,12 @@ ibool
mem_validate(void); mem_validate(void);
/*===============*/ /*===============*/
/* out: TRUE if ok */ /* 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 */
...@@ -61,58 +61,41 @@ mem_init( ...@@ -61,58 +61,41 @@ mem_init(
/****************************************************************** /******************************************************************
Use this macro instead of the corresponding function! Macro for memory Use this macro instead of the corresponding function! Macro for memory
heap creation. */ heap creation. */
#ifdef UNIV_MEM_DEBUG
#define mem_heap_create(N) mem_heap_create_func(\ #define mem_heap_create(N) mem_heap_create_func(\
(N), NULL, MEM_HEAP_DYNAMIC,\ (N), NULL, MEM_HEAP_DYNAMIC,\
IB__FILE__, __LINE__) 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 Use this macro instead of the corresponding function! Macro for memory
heap creation. */ heap creation. */
#ifdef UNIV_MEM_DEBUG
#define mem_heap_create_in_buffer(N) mem_heap_create_func(\ #define mem_heap_create_in_buffer(N) mem_heap_create_func(\
(N), NULL, MEM_HEAP_BUFFER,\ (N), NULL, MEM_HEAP_BUFFER,\
IB__FILE__, __LINE__) 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 Use this macro instead of the corresponding function! Macro for memory
heap creation. */ heap creation. */
#ifdef UNIV_MEM_DEBUG
#define mem_heap_create_in_btr_search(N) mem_heap_create_func(\ #define mem_heap_create_in_btr_search(N) mem_heap_create_func(\
(N), NULL, MEM_HEAP_BTR_SEARCH |\ (N), NULL, MEM_HEAP_BTR_SEARCH |\
MEM_HEAP_BUFFER,\ MEM_HEAP_BUFFER,\
IB__FILE__, __LINE__) 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 Use this macro instead of the corresponding function! Macro for fast
memory heap creation. An initial block of memory B is given by the 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 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. */ 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(\ #define mem_heap_fast_create(N, B) mem_heap_create_func(\
(N), (B), MEM_HEAP_DYNAMIC,\ (N), (B), MEM_HEAP_DYNAMIC,\
IB__FILE__, __LINE__) 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 Use this macro instead of the corresponding function! Macro for memory
heap freeing. */ heap freeing. */
#ifdef UNIV_MEM_DEBUG
#define mem_heap_free(heap) mem_heap_free_func(\ #define mem_heap_free(heap) mem_heap_free_func(\
(heap), IB__FILE__, __LINE__) (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 NOTE: Use the corresponding macros instead of this function. Creates a
memory heap which allocates memory from dynamic space. For debugging memory heap which allocates memory from dynamic space. For debugging
...@@ -139,11 +122,9 @@ mem_heap_create_func( ...@@ -139,11 +122,9 @@ mem_heap_create_func(
block is not unintentionally erased block is not unintentionally erased
(if allocated in the stack), before (if allocated in the stack), before
the memory heap is explicitly freed. */ 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 */
#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 */ ulint line /* in: line where created */
#endif
); );
/********************************************************************* /*********************************************************************
NOTE: Use the corresponding macro instead of this function. NOTE: Use the corresponding macro instead of this function.
...@@ -152,11 +133,9 @@ UNIV_INLINE ...@@ -152,11 +133,9 @@ UNIV_INLINE
void void
mem_heap_free_func( mem_heap_free_func(
/*===============*/ /*===============*/
mem_heap_t* heap /* in, own: heap to be freed */ mem_heap_t* heap, /* in, own: heap to be freed */
#ifdef UNIV_MEM_DEBUG char* file_name, /* in: file name where freed */
,char* file_name, /* in: file name where freed */ ulint line /* in: line where freed */
ulint line /* in: line where freed */
#endif
); );
/******************************************************************* /*******************************************************************
Allocates n bytes of memory from a memory heap. */ Allocates n bytes of memory from a memory heap. */
...@@ -220,25 +199,18 @@ UNIV_INLINE ...@@ -220,25 +199,18 @@ UNIV_INLINE
ulint ulint
mem_heap_get_size( mem_heap_get_size(
/*==============*/ /*==============*/
mem_heap_t* heap); /* in: heap */ mem_heap_t* heap); /* in: heap */
/****************************************************************** /******************************************************************
Use this macro instead of the corresponding function! Use this macro instead of the corresponding function!
Macro for memory buffer allocation */ Macro for memory buffer allocation */
#ifdef UNIV_MEM_DEBUG
#define mem_alloc(N) mem_alloc_func(\ #define mem_alloc(N) mem_alloc_func((N), IB__FILE__, __LINE__)
(N), IB__FILE__, __LINE__)
#else
#define mem_alloc(N) mem_alloc_func(N)
#endif
/****************************************************************** /******************************************************************
Use this macro instead of the corresponding function! Use this macro instead of the corresponding function!
Macro for memory buffer allocation */ Macro for memory buffer allocation */
#ifdef UNIV_MEM_DEBUG
#define mem_alloc_noninline(N) mem_alloc_func_noninline(\ #define mem_alloc_noninline(N) mem_alloc_func_noninline(\
(N), IB__FILE__, __LINE__) (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. NOTE: Use the corresponding macro instead of this function.
Allocates a single buffer of memory from the dynamic memory of Allocates a single buffer of memory from the dynamic memory of
...@@ -250,11 +222,9 @@ mem_alloc_func( ...@@ -250,11 +222,9 @@ mem_alloc_func(
/*===========*/ /*===========*/
/* out, own: free storage, NULL /* out, own: free storage, NULL
if did not succeed */ if did not succeed */
ulint n /* in: desired number of bytes */ ulint n, /* in: desired number of bytes */
#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 */
ulint line /* in: line where created */
#endif
); );
/******************************************************************* /*******************************************************************
NOTE: Use the corresponding macro instead of this function. NOTE: Use the corresponding macro instead of this function.
...@@ -267,21 +237,15 @@ mem_alloc_func_noninline( ...@@ -267,21 +237,15 @@ mem_alloc_func_noninline(
/*=====================*/ /*=====================*/
/* out, own: free storage, NULL if did not /* out, own: free storage, NULL if did not
succeed */ succeed */
ulint n /* in: desired number of bytes */ ulint n, /* in: desired number of bytes */
#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 */ ulint line /* in: line where created */
#endif
); );
/****************************************************************** /******************************************************************
Use this macro instead of the corresponding function! Use this macro instead of the corresponding function!
Macro for memory buffer freeing */ Macro for memory buffer freeing */
#ifdef UNIV_MEM_DEBUG
#define mem_free(PTR) mem_free_func(\ #define mem_free(PTR) mem_free_func((PTR), IB__FILE__, __LINE__)
(PTR), IB__FILE__, __LINE__)
#else
#define mem_free(PTR) mem_free_func(PTR)
#endif
/******************************************************************* /*******************************************************************
NOTE: Use the corresponding macro instead of this function. NOTE: Use the corresponding macro instead of this function.
Frees a single buffer of storage from Frees a single buffer of storage from
...@@ -290,11 +254,9 @@ UNIV_INLINE ...@@ -290,11 +254,9 @@ UNIV_INLINE
void void
mem_free_func( mem_free_func(
/*==========*/ /*==========*/
void* ptr /* in, own: buffer to be freed */ void* ptr, /* in, own: buffer to be freed */
#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 */
ulint line /* in: line where created */
#endif
); );
/******************************************************************* /*******************************************************************
Implements realloc. */ Implements realloc. */
...@@ -304,7 +266,9 @@ mem_realloc( ...@@ -304,7 +266,9 @@ mem_realloc(
/*========*/ /*========*/
/* out, own: free storage, NULL if did not succeed */ /* out, own: free storage, NULL if did not succeed */
void* buf, /* in: pointer to an old buffer */ 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 { ...@@ -336,8 +300,13 @@ struct mem_block_info_struct {
free block to the heap, if we need more space; free block to the heap, if we need more space;
otherwise, this is NULL */ otherwise, this is NULL */
ulint magic_n;/* magic number for debugging */ 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 */ /* Header size for a memory heap block */
#define MEM_BLOCK_HEADER_SIZE ut_calc_align(sizeof(mem_block_info_t),\ #define MEM_BLOCK_HEADER_SIZE ut_calc_align(sizeof(mem_block_info_t),\
UNIV_MEM_ALIGNMENT) UNIV_MEM_ALIGNMENT)
......
...@@ -24,8 +24,10 @@ mem_heap_create_block( ...@@ -24,8 +24,10 @@ mem_heap_create_block(
if init_block is not NULL, its size in bytes */ if init_block is not NULL, its size in bytes */
void* init_block, /* in: init block in fast create, type must be void* init_block, /* in: init block in fast create, type must be
MEM_HEAP_DYNAMIC */ 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 */ MEM_HEAP_BUFFER */
char* file_name,/* in: file name where created */
ulint line); /* in: line where created */
/********************************************************************** /**********************************************************************
Frees a block from a memory heap. */ Frees a block from a memory heap. */
...@@ -392,21 +394,20 @@ mem_heap_create_func( ...@@ -392,21 +394,20 @@ mem_heap_create_func(
block is not unintentionally erased block is not unintentionally erased
(if allocated in the stack), before (if allocated in the stack), before
the memory heap is explicitly freed. */ 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 */ 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 */ ulint line /* in: line where created */
#endif
) )
{ {
mem_block_t* block; mem_block_t* block;
if (n > 0) { 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 { } else {
block = mem_heap_create_block(NULL, MEM_BLOCK_START_SIZE, block = mem_heap_create_block(NULL, MEM_BLOCK_START_SIZE,
init_block, type); init_block, type, file_name, line);
} }
ut_ad(block); ut_ad(block);
...@@ -438,11 +439,9 @@ UNIV_INLINE ...@@ -438,11 +439,9 @@ UNIV_INLINE
void void
mem_heap_free_func( mem_heap_free_func(
/*===============*/ /*===============*/
mem_heap_t* heap /* in, own: heap to be freed */ mem_heap_t* heap, /* in, own: heap to be freed */
#ifdef UNIV_MEM_DEBUG char* file_name, /* in: file name where freed */
,char* file_name, /* in: file name where freed */
ulint line /* in: line where freed */ ulint line /* in: line where freed */
#endif
) )
{ {
mem_block_t* block; mem_block_t* block;
...@@ -488,14 +487,12 @@ mem_alloc_func( ...@@ -488,14 +487,12 @@ mem_alloc_func(
/*===========*/ /*===========*/
/* out, own: free storage, NULL if did not /* out, own: free storage, NULL if did not
succeed */ succeed */
ulint n /* in: desired number of bytes */ ulint n, /* in: desired number of bytes */
#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 */ ulint line /* in: line where created */
#endif
) )
{ {
#ifndef UNIV_MEM_DEBUG #ifdef notdefined
void* buf; void* buf;
buf = mem_area_alloc(n, mem_comm_pool); buf = mem_area_alloc(n, mem_comm_pool);
...@@ -505,7 +502,7 @@ mem_alloc_func( ...@@ -505,7 +502,7 @@ mem_alloc_func(
#endif #endif
return(buf); return(buf);
#else #else
mem_heap_t* heap; mem_heap_t* heap;
void* buf; void* buf;
...@@ -524,11 +521,11 @@ mem_alloc_func( ...@@ -524,11 +521,11 @@ mem_alloc_func(
buf = mem_heap_alloc(heap, n); 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); - MEM_FIELD_HEADER_SIZE);
return(buf); return(buf);
#endif #endif
} }
/******************************************************************* /*******************************************************************
...@@ -539,26 +536,22 @@ UNIV_INLINE ...@@ -539,26 +536,22 @@ UNIV_INLINE
void void
mem_free_func( mem_free_func(
/*==========*/ /*==========*/
void* ptr /* in, own: buffer to be freed */ void* ptr, /* in, own: buffer to be freed */
#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 */ ulint line /* in: line where created */
#endif
) )
{ {
#ifndef UNIV_MEM_DEBUG #ifdef notdefined
mem_area_free(ptr, mem_comm_pool); mem_area_free(ptr, mem_comm_pool);
#else #else
mem_heap_t* heap; mem_heap_t* heap;
heap = (mem_heap_t*)((byte*)ptr - MEM_BLOCK_HEADER_SIZE heap = (mem_heap_t*)((byte*)ptr - MEM_BLOCK_HEADER_SIZE
- MEM_FIELD_HEADER_SIZE); - MEM_FIELD_HEADER_SIZE);
mem_heap_free_func(heap, file_name, line); mem_heap_free_func(heap, file_name, line);
#endif
#endif
} }
/********************************************************************* /*********************************************************************
...@@ -567,7 +560,7 @@ UNIV_INLINE ...@@ -567,7 +560,7 @@ UNIV_INLINE
ulint ulint
mem_heap_get_size( mem_heap_get_size(
/*==============*/ /*==============*/
mem_heap_t* heap) /* in: heap */ mem_heap_t* heap) /* in: heap */
{ {
mem_block_t* block; mem_block_t* block;
ulint size = 0; ulint size = 0;
...@@ -597,9 +590,11 @@ mem_realloc( ...@@ -597,9 +590,11 @@ mem_realloc(
/*========*/ /*========*/
/* out, own: free storage, NULL if did not succeed */ /* out, own: free storage, NULL if did not succeed */
void* buf, /* in: pointer to an old buffer */ 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); mem_free(buf);
return(mem_alloc(n)); return(mem_alloc_func(n, file_name, line));
} }
...@@ -327,6 +327,8 @@ mutex with the exceptions named below */ ...@@ -327,6 +327,8 @@ mutex with the exceptions named below */
struct que_thr_struct{ struct que_thr_struct{
que_common_t common; /* type: QUE_NODE_THR */ 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_node_t* child; /* graph child node */
que_t* graph; /* graph where this node belongs */ que_t* graph; /* graph where this node belongs */
ibool is_active; /* TRUE if the thread has been set ibool is_active; /* TRUE if the thread has been set
...@@ -357,6 +359,9 @@ struct que_thr_struct{ ...@@ -357,6 +359,9 @@ struct que_thr_struct{
thus far */ 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 */ /* Query graph fork node: its fields are protected by the kernel mutex */
struct que_fork_struct{ struct que_fork_struct{
que_common_t common; /* type: QUE_NODE_FORK */ que_common_t common; /* type: QUE_NODE_FORK */
......
...@@ -323,11 +323,18 @@ struct mysql_row_templ_struct { ...@@ -323,11 +323,18 @@ struct mysql_row_templ_struct {
/* After fetching this many rows, we start caching them in fetch_cache */ /* After fetching this many rows, we start caching them in fetch_cache */
#define MYSQL_FETCH_CACHE_THRESHOLD 4 #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 /* A struct for (sometimes lazily) prebuilt structures in an Innobase table
handle used within MySQL; these are used to save CPU time. */ handle used within MySQL; these are used to save CPU time. */
struct row_prebuilt_struct { 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 */ dict_table_t* table; /* Innobase table handle */
trx_t* trx; /* current transaction handle */ trx_t* trx; /* current transaction handle */
ibool sql_stat_start; /* TRUE when we start processing of ibool sql_stat_start; /* TRUE when we start processing of
......
...@@ -17,6 +17,8 @@ Created 10/10/1995 Heikki Tuuri ...@@ -17,6 +17,8 @@ Created 10/10/1995 Heikki Tuuri
#include "que0types.h" #include "que0types.h"
#include "trx0types.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 /* When this event is set the lock timeout and InnoDB monitor
thread starts running */ thread starts running */
...@@ -261,15 +263,27 @@ This lets a thread enter InnoDB regardless of the number of threads inside ...@@ -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. */ InnoDB. This must be called when a thread ends a lock wait. */
void 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 This must be called when a thread exits InnoDB in a lock wait or at the
when a thread goes to wait for a lock. */ end of an SQL statement. */
void 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. */ Puts a MySQL OS thread to wait for a lock to be released. */
......
...@@ -218,6 +218,22 @@ trx_in_trx_list( ...@@ -218,6 +218,22 @@ trx_in_trx_list(
/*============*/ /*============*/
/* out: TRUE if is in */ /* out: TRUE if is in */
trx_t* in_trx);/* in: trx */ 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 */ /* The automatically created system rollback segment has this id */
#define TRX_SYS_SYSTEM_RSEG_ID 0 #define TRX_SYS_SYSTEM_RSEG_ID 0
...@@ -236,7 +252,7 @@ therefore 256 */ ...@@ -236,7 +252,7 @@ therefore 256 */
/* Transaction system header; protected by trx_sys->mutex */ /* 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 modulo TRX_SYS_TRX_ID_UPDATE_MARGIN
written to a file page by any written to a file page by any
transaction; the assignment of transaction; the assignment of
...@@ -252,6 +268,23 @@ therefore 256 */ ...@@ -252,6 +268,23 @@ therefore 256 */
segment specification slots */ 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 */ /* The offset of the doublewrite buffer header on the trx system header page */
#define TRX_SYS_DOUBLEWRITE (UNIV_PAGE_SIZE - 200) #define TRX_SYS_DOUBLEWRITE (UNIV_PAGE_SIZE - 200)
/*-------------------------------------------------------------*/ /*-------------------------------------------------------------*/
......
...@@ -290,10 +290,20 @@ struct trx_struct{ ...@@ -290,10 +290,20 @@ struct trx_struct{
table */ table */
dulint table_id; /* table id if the preceding field is dulint table_id; /* table id if the preceding field is
TRUE */ TRUE */
/*------------------------------*/
void* mysql_thd; /* MySQL thread handle corresponding void* mysql_thd; /* MySQL thread handle corresponding
to this trx, or NULL */ 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 os_thread_id_t mysql_thread_id;/* id of the MySQL thread associated
with this transaction object */ with this transaction object */
/*------------------------------*/
ulint n_mysql_tables_in_use; /* number of Innobase tables ulint n_mysql_tables_in_use; /* number of Innobase tables
used in the processing of the current used in the processing of the current
SQL statement in MySQL */ SQL statement in MySQL */
...@@ -314,6 +324,18 @@ struct trx_struct{ ...@@ -314,6 +324,18 @@ struct trx_struct{
calls from MySQL; this is intended calls from MySQL; this is intended
to reduce contention on the search to reduce contention on the search
latch */ 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 lock_t* auto_inc_lock; /* possible auto-inc lock reserved by
the transaction; note that it is also the transaction; note that it is also
in the lock list trx_locks */ in the lock list trx_locks */
......
...@@ -51,6 +51,8 @@ recv_sys_t* recv_sys = NULL; ...@@ -51,6 +51,8 @@ recv_sys_t* recv_sys = NULL;
ibool recv_recovery_on = FALSE; ibool recv_recovery_on = FALSE;
ibool recv_recovery_from_backup_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 /* 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 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 the log record hash table becomes too full, and log records must be merged
...@@ -1020,7 +1022,7 @@ loop: ...@@ -1020,7 +1022,7 @@ loop:
if (!has_printed) { if (!has_printed) {
fprintf(stderr, fprintf(stderr,
"InnoDB: Starting an apply batch of log records to the database...\n" "InnoDB: Starting an apply batch of log records to the database...\n"
"InnoDB: Progress in percents:"); "InnoDB: Progress in percents: ");
has_printed = TRUE; has_printed = TRUE;
} }
...@@ -2032,12 +2034,16 @@ recv_recovery_from_checkpoint_start( ...@@ -2032,12 +2034,16 @@ recv_recovery_from_checkpoint_start(
if (ut_dulint_cmp(checkpoint_lsn, max_flushed_lsn) != 0 if (ut_dulint_cmp(checkpoint_lsn, max_flushed_lsn) != 0
|| ut_dulint_cmp(checkpoint_lsn, min_flushed_lsn) != 0) { || ut_dulint_cmp(checkpoint_lsn, min_flushed_lsn) != 0) {
recv_needed_recovery = TRUE;
ut_print_timestamp(stderr);
fprintf(stderr, fprintf(stderr,
"InnoDB: Database was not shut down normally.\n" " InnoDB: Database was not shut down normally.\n"
"InnoDB: Starting recovery from log files...\n"); "InnoDB: Starting recovery from log files...\n");
fprintf(stderr, fprintf(stderr,
"InnoDB: Starting log scan based on checkpoint at\n" "InnoDB: Starting log scan based on checkpoint at\n"
"InnoDB: log sequence number %lu %lu\n", "InnoDB: log sequence number %lu %lu\n",
ut_dulint_get_high(checkpoint_lsn), ut_dulint_get_high(checkpoint_lsn),
ut_dulint_get_low(checkpoint_lsn)); ut_dulint_get_low(checkpoint_lsn));
} }
...@@ -2199,6 +2205,10 @@ recv_recovery_from_checkpoint_finish(void) ...@@ -2199,6 +2205,10 @@ recv_recovery_from_checkpoint_finish(void)
"InnoDB: Log records applied to the database\n"); "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 */ /* Free the resources of the recovery system */
recv_recovery_on = FALSE; recv_recovery_on = FALSE;
......
...@@ -808,7 +808,7 @@ mem_validate_no_assert(void) ...@@ -808,7 +808,7 @@ mem_validate_no_assert(void)
} }
mutex_exit(&mem_hash_mutex); mutex_exit(&mem_hash_mutex);
return(error); return(error);
#else #else
...@@ -832,3 +832,95 @@ mem_validate(void) ...@@ -832,3 +832,95 @@ mem_validate(void)
return(TRUE); 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++;
}
}
...@@ -14,8 +14,9 @@ Created 6/9/1994 Heikki Tuuri ...@@ -14,8 +14,9 @@ Created 6/9/1994 Heikki Tuuri
#include "mach0data.h" #include "mach0data.h"
#include "buf0buf.h" #include "buf0buf.h"
#include "mem0dbg.c"
#include "btr0sea.h" #include "btr0sea.h"
#include "srv0srv.h"
#include "mem0dbg.c"
/* /*
THE MEMORY MANAGEMENT THE MEMORY MANAGEMENT
...@@ -85,18 +86,12 @@ mem_alloc_func_noninline( ...@@ -85,18 +86,12 @@ mem_alloc_func_noninline(
/*=====================*/ /*=====================*/
/* out, own: free storage, NULL if did not /* out, own: free storage, NULL if did not
succeed */ succeed */
ulint n /* in: desired number of bytes */ ulint n, /* in: desired number of bytes */
#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 */ ulint line /* in: line where created */
#endif
) )
{ {
return(mem_alloc_func(n return(mem_alloc_func(n, file_name, line));
#ifdef UNIV_MEM_DEBUG
, file_name, line
#endif
));
} }
/******************************************************************* /*******************************************************************
...@@ -113,8 +108,10 @@ mem_heap_create_block( ...@@ -113,8 +108,10 @@ mem_heap_create_block(
if init_block is not NULL, its size in bytes */ if init_block is not NULL, its size in bytes */
void* init_block, /* in: init block in fast create, type must be void* init_block, /* in: init block in fast create, type must be
MEM_HEAP_DYNAMIC */ 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 */ 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; mem_block_t* block;
ulint len; ulint len;
...@@ -164,7 +161,11 @@ mem_heap_create_block( ...@@ -164,7 +161,11 @@ mem_heap_create_block(
} }
block->magic_n = MEM_BLOCK_MAGIC_N; 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_len(block, len);
mem_block_set_type(block, type); mem_block_set_type(block, type);
mem_block_set_free(block, MEM_BLOCK_HEADER_SIZE); mem_block_set_free(block, MEM_BLOCK_HEADER_SIZE);
...@@ -223,8 +224,8 @@ mem_heap_add_block( ...@@ -223,8 +224,8 @@ mem_heap_add_block(
new_size = n; 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) { if (new_block == NULL) {
return(NULL); return(NULL);
...@@ -255,7 +256,8 @@ mem_heap_block_free( ...@@ -255,7 +256,8 @@ mem_heap_block_free(
type = heap->type; type = heap->type;
len = block->len; len = block->len;
init_block = block->init_block; init_block = block->init_block;
block->magic_n = MEM_FREED_BLOCK_MAGIC_N;
#ifdef UNIV_MEM_DEBUG #ifdef UNIV_MEM_DEBUG
/* In the debug version we set the memory to a random combination /* In the debug version we set the memory to a random combination
of hex 0xDE and 0xAD. */ of hex 0xDE and 0xAD. */
......
...@@ -7373,7 +7373,7 @@ void *ptr; ...@@ -7373,7 +7373,7 @@ void *ptr;
unsigned int size; unsigned int size;
#endif #endif
{ {
return (void *) mem_realloc( ptr, size ); return (void *) mem_realloc( ptr, size, __FILE__, __LINE__ );
} }
#ifdef YY_USE_PROTOS #ifdef YY_USE_PROTOS
......
...@@ -183,6 +183,8 @@ que_thr_create( ...@@ -183,6 +183,8 @@ que_thr_create(
thr->common.type = QUE_NODE_THR; thr->common.type = QUE_NODE_THR;
thr->common.parent = parent; thr->common.parent = parent;
thr->magic_n = QUE_THR_MAGIC_N;
thr->graph = parent->graph; thr->graph = parent->graph;
thr->state = QUE_THR_COMMAND_WAIT; thr->state = QUE_THR_COMMAND_WAIT;
...@@ -485,7 +487,6 @@ que_graph_free_recursive( ...@@ -485,7 +487,6 @@ que_graph_free_recursive(
tab_node_t* cre_tab; tab_node_t* cre_tab;
ind_node_t* cre_ind; ind_node_t* cre_ind;
if (node == NULL) { if (node == NULL) {
return; return;
...@@ -509,6 +510,16 @@ que_graph_free_recursive( ...@@ -509,6 +510,16 @@ que_graph_free_recursive(
thr = node; 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); que_graph_free_recursive(thr->child);
break; break;
...@@ -606,6 +617,10 @@ que_graph_free_recursive( ...@@ -606,6 +617,10 @@ que_graph_free_recursive(
break; break;
default: default:
fprintf(stderr,
"que_node struct appears corrupt; type %lu\n",
que_node_get_type(node));
mem_analyze_corruption((byte*)node);
ut_a(0); ut_a(0);
} }
} }
...@@ -1068,20 +1083,29 @@ que_thr_stop_for_mysql( ...@@ -1068,20 +1083,29 @@ que_thr_stop_for_mysql(
mutex_exit(&kernel_mutex); mutex_exit(&kernel_mutex);
} }
/************************************************************************** /**************************************************************************
Moves a thread from another state to the QUE_THR_RUNNING state. Increments 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 the n_active_thrs counters of the query graph and transaction if thr was
not active. */ not active. */
void void
que_thr_move_to_run_state_for_mysql( que_thr_move_to_run_state_for_mysql(
/*================================*/ /*================================*/
que_thr_t* thr, /* in: an query thread */ que_thr_t* thr, /* in: an query thread */
trx_t* trx) /* in: transaction */ 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) { if (!thr->is_active) {
(thr->graph)->n_active_thrs++; thr->graph->n_active_thrs++;
trx->n_active_thrs++; trx->n_active_thrs++;
...@@ -1097,6 +1121,7 @@ que_thr_move_to_run_state_for_mysql( ...@@ -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 A patch for MySQL used to 'stop' a dummy query thread used in MySQL
select, when there is no error or lock wait. */ select, when there is no error or lock wait. */
void void
que_thr_stop_for_mysql_no_error( que_thr_stop_for_mysql_no_error(
/*============================*/ /*============================*/
...@@ -1105,6 +1130,15 @@ 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); 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->state = QUE_THR_COMPLETED;
thr->is_active = FALSE; thr->is_active = FALSE;
......
...@@ -105,6 +105,17 @@ rec_get_nth_field( ...@@ -105,6 +105,17 @@ rec_get_nth_field(
ut_ad(rec && len); ut_ad(rec && len);
ut_ad(n < rec_get_n_fields(rec)); 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)) { if (rec_get_1byte_offs_flag(rec)) {
os = rec_1_get_field_start_offs(rec, n); os = rec_1_get_field_start_offs(rec, n);
......
...@@ -242,10 +242,14 @@ row_create_prebuilt( ...@@ -242,10 +242,14 @@ row_create_prebuilt(
ulint ref_len; ulint ref_len;
ulint i; ulint i;
dict_table_increment_handle_count(table);
heap = mem_heap_create(128); heap = mem_heap_create(128);
prebuilt = mem_heap_alloc(heap, sizeof(row_prebuilt_t)); prebuilt = mem_heap_alloc(heap, sizeof(row_prebuilt_t));
prebuilt->magic_n = ROW_PREBUILT_ALLOCATED;
prebuilt->table = table; prebuilt->table = table;
prebuilt->trx = NULL; prebuilt->trx = NULL;
...@@ -294,7 +298,7 @@ row_create_prebuilt( ...@@ -294,7 +298,7 @@ row_create_prebuilt(
prebuilt->blob_heap = NULL; prebuilt->blob_heap = NULL;
prebuilt->old_vers_heap = NULL; prebuilt->old_vers_heap = NULL;
return(prebuilt); return(prebuilt);
} }
...@@ -308,6 +312,19 @@ row_prebuilt_free( ...@@ -308,6 +312,19 @@ row_prebuilt_free(
{ {
ulint i; 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->pcur);
btr_pcur_free_for_mysql(prebuilt->clust_pcur); btr_pcur_free_for_mysql(prebuilt->clust_pcur);
...@@ -341,6 +358,8 @@ row_prebuilt_free( ...@@ -341,6 +358,8 @@ row_prebuilt_free(
} }
} }
dict_table_decrement_handle_count(prebuilt->table);
mem_heap_free(prebuilt->heap); mem_heap_free(prebuilt->heap);
} }
...@@ -356,6 +375,28 @@ row_update_prebuilt_trx( ...@@ -356,6 +375,28 @@ row_update_prebuilt_trx(
handle */ handle */
trx_t* trx) /* in: transaction 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; prebuilt->trx = trx;
if (prebuilt->ins_graph) { if (prebuilt->ins_graph) {
...@@ -563,6 +604,17 @@ row_insert_for_mysql( ...@@ -563,6 +604,17 @@ row_insert_for_mysql(
ut_ad(trx); ut_ad(trx);
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); 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) { if (srv_created_new_raw || srv_force_recovery) {
fprintf(stderr, fprintf(stderr,
"InnoDB: A new raw disk partition was initialized or\n" "InnoDB: A new raw disk partition was initialized or\n"
...@@ -748,6 +800,17 @@ row_update_for_mysql( ...@@ -748,6 +800,17 @@ row_update_for_mysql(
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
UT_NOT_USED(mysql_rec); 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) { if (srv_created_new_raw || srv_force_recovery) {
fprintf(stderr, fprintf(stderr,
"InnoDB: A new raw disk partition was initialized or\n" "InnoDB: A new raw disk partition was initialized or\n"
...@@ -782,38 +845,6 @@ row_update_for_mysql( ...@@ -782,38 +845,6 @@ row_update_for_mysql(
generated for the table: MySQL does not know anything about generated for the table: MySQL does not know anything about
the row id used as the clustered index key */ 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); savept = trx_savept_take(trx);
thr = que_fork_get_first_thr(prebuilt->upd_graph); thr = que_fork_get_first_thr(prebuilt->upd_graph);
...@@ -922,6 +953,50 @@ row_get_mysql_key_number_for_index( ...@@ -922,6 +953,50 @@ row_get_mysql_key_number_for_index(
return(i); return(i);
} }
/*************************************************************************
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 Does a table creation operation for MySQL. If the name of the created
table ends to characters INNODB_MONITOR, then this also starts table ends to characters INNODB_MONITOR, then this also starts
...@@ -976,6 +1051,24 @@ row_create_table_for_mysql( ...@@ -976,6 +1051,24 @@ row_create_table_for_mysql(
namelen = ut_strlen(table->name); 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"); keywordlen = ut_strlen("innodb_monitor");
if (namelen >= keywordlen if (namelen >= keywordlen
...@@ -1118,6 +1211,8 @@ row_create_index_for_mysql( ...@@ -1118,6 +1211,8 @@ row_create_index_for_mysql(
ind_node_t* node; ind_node_t* node;
mem_heap_t* heap; mem_heap_t* heap;
que_thr_t* thr; que_thr_t* thr;
ulint namelen;
ulint keywordlen;
ulint err; ulint err;
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
...@@ -1126,6 +1221,18 @@ row_create_index_for_mysql( ...@@ -1126,6 +1221,18 @@ row_create_index_for_mysql(
trx_start_if_not_started(trx); 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: /* Serialize data dictionary operations with dictionary mutex:
no deadlocks can occur then in these operations */ no deadlocks can occur then in these operations */
...@@ -1189,6 +1296,8 @@ row_table_add_foreign_constraints( ...@@ -1189,6 +1296,8 @@ row_table_add_foreign_constraints(
char* name) /* in: table full name in the normalized form char* name) /* in: table full name in the normalized form
database_name/table_name */ database_name/table_name */
{ {
ulint namelen;
ulint keywordlen;
ulint err; ulint err;
ut_a(sql_string); ut_a(sql_string);
...@@ -1197,6 +1306,18 @@ row_table_add_foreign_constraints( ...@@ -1197,6 +1306,18 @@ row_table_add_foreign_constraints(
trx_start_if_not_started(trx); 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: /* Serialize data dictionary operations with dictionary mutex:
no deadlocks can occur then in these operations */ no deadlocks can occur then in these operations */
...@@ -1251,6 +1372,7 @@ row_drop_table_for_mysql( ...@@ -1251,6 +1372,7 @@ row_drop_table_for_mysql(
ulint len; ulint len;
ulint namelen; ulint namelen;
ulint keywordlen; ulint keywordlen;
ulint rounds = 0;
char buf[10000]; char buf[10000];
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
...@@ -1427,11 +1549,38 @@ row_drop_table_for_mysql( ...@@ -1427,11 +1549,38 @@ row_drop_table_for_mysql(
/* Remove any locks there are on the table or its records */ /* Remove any locks there are on the table or its records */
lock_reset_all_on_table(table); 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 rounds++;
after this function row_drop_table_for_mysql has been called:
otherwise anyone with an open handle to the table could, for example, if (rounds > 120) {
come to read the table! Monty said that it prevents. */ 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->dict_operation = TRUE;
trx->table_id = table->id; trx->table_id = table->id;
......
...@@ -2492,6 +2492,17 @@ row_search_for_mysql( ...@@ -2492,6 +2492,17 @@ row_search_for_mysql(
ut_ad(sync_thread_levels_empty_gen(FALSE)); 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); /* printf("Match mode %lu\n search tuple ", match_mode);
dtuple_print(search_tuple); dtuple_print(search_tuple);
......
...@@ -50,6 +50,9 @@ Created 10/8/1995 Heikki Tuuri ...@@ -50,6 +50,9 @@ Created 10/8/1995 Heikki Tuuri
#include "dict0load.h" #include "dict0load.h"
#include "srv0start.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 /* The following counter is incremented whenever there is some user activity
in the server */ in the server */
ulint srv_activity_count = 0; ulint srv_activity_count = 0;
...@@ -132,6 +135,9 @@ lint srv_conc_n_threads = 0; /* number of OS threads currently ...@@ -132,6 +135,9 @@ lint srv_conc_n_threads = 0; /* number of OS threads currently
thread increments this, but a thread thread increments this, but a thread
waiting for a lock decrements this waiting for a lock decrements this
temporarily */ 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; typedef struct srv_conc_slot_struct srv_conc_slot_t;
struct srv_conc_slot_struct{ struct srv_conc_slot_struct{
...@@ -152,6 +158,11 @@ UT_LIST_BASE_NODE_T(srv_conc_slot_t) srv_conc_queue; /* queue of threads ...@@ -152,6 +158,11 @@ UT_LIST_BASE_NODE_T(srv_conc_slot_t) srv_conc_queue; /* queue of threads
waiting to get in */ waiting to get in */
srv_conc_slot_t srv_conc_slots[OS_THREAD_MAX_N]; /* array of wait srv_conc_slot_t srv_conc_slots[OS_THREAD_MAX_N]; /* array of wait
slots */ 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 /* If the following is set TRUE then we do not run purge and insert buffer
merge to completion before shutdown */ merge to completion before shutdown */
...@@ -1627,6 +1638,8 @@ srv_general_init(void) ...@@ -1627,6 +1638,8 @@ srv_general_init(void)
thr_local_init(); thr_local_init();
} }
/*======================= InnoDB Server FIFO queue =======================*/
/************************************************************************* /*************************************************************************
Puts an OS thread to wait if there are too many concurrent threads 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. */ (>= srv_thread_concurrency) inside InnoDB. The threads wait in a FIFO queue. */
...@@ -1640,11 +1653,29 @@ srv_conc_enter_innodb( ...@@ -1640,11 +1653,29 @@ srv_conc_enter_innodb(
srv_conc_slot_t* slot; srv_conc_slot_t* slot;
ulint i; 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); os_fast_mutex_lock(&srv_conc_mutex);
if (srv_conc_n_threads < (lint)srv_thread_concurrency) { 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); os_fast_mutex_unlock(&srv_conc_mutex);
return; return;
...@@ -1665,6 +1696,8 @@ srv_conc_enter_innodb( ...@@ -1665,6 +1696,8 @@ srv_conc_enter_innodb(
thread enter */ thread enter */
srv_conc_n_threads++; 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); os_fast_mutex_unlock(&srv_conc_mutex);
...@@ -1684,6 +1717,8 @@ srv_conc_enter_innodb( ...@@ -1684,6 +1717,8 @@ srv_conc_enter_innodb(
os_event_reset(slot->event); os_event_reset(slot->event);
srv_conc_n_waiting_threads++;
os_fast_mutex_unlock(&srv_conc_mutex); os_fast_mutex_unlock(&srv_conc_mutex);
/* Go to wait for the event; when a thread leaves InnoDB it will /* Go to wait for the event; when a thread leaves InnoDB it will
...@@ -1693,6 +1728,8 @@ srv_conc_enter_innodb( ...@@ -1693,6 +1728,8 @@ srv_conc_enter_innodb(
os_fast_mutex_lock(&srv_conc_mutex); os_fast_mutex_lock(&srv_conc_mutex);
srv_conc_n_waiting_threads--;
/* NOTE that the thread which released this thread already /* NOTE that the thread which released this thread already
incremented the thread counter on behalf of this thread */ incremented the thread counter on behalf of this thread */
...@@ -1700,6 +1737,9 @@ srv_conc_enter_innodb( ...@@ -1700,6 +1737,9 @@ srv_conc_enter_innodb(
UT_LIST_REMOVE(srv_conc_queue, srv_conc_queue, slot); 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); os_fast_mutex_unlock(&srv_conc_mutex);
} }
...@@ -1708,29 +1748,52 @@ This lets a thread enter InnoDB regardless of the number of threads inside ...@@ -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. */ InnoDB. This must be called when a thread ends a lock wait. */
void 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); os_fast_mutex_lock(&srv_conc_mutex);
srv_conc_n_threads++; 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); os_fast_mutex_unlock(&srv_conc_mutex);
} }
/************************************************************************* /*************************************************************************
This must be called when a thread exits InnoDB. This must also be called This must be called when a thread exits InnoDB in a lock wait or at the
when a thread goes to wait for a lock. */ end of an SQL statement. */
void 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; 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); os_fast_mutex_lock(&srv_conc_mutex);
srv_conc_n_threads--; 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) { if (srv_conc_n_threads < (lint)srv_thread_concurrency) {
/* Look for a slot where a thread is waiting and no other /* Look for a slot where a thread is waiting and no other
...@@ -1759,6 +1822,38 @@ srv_conc_exit_innodb(void) ...@@ -1759,6 +1822,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. */ Normalizes init parameter values to use units we use inside InnoDB. */
static static
...@@ -1905,7 +2000,7 @@ srv_suspend_mysql_thread( ...@@ -1905,7 +2000,7 @@ srv_suspend_mysql_thread(
other thread holding a lock which this thread waits for must be other thread holding a lock which this thread waits for must be
allowed to enter, sooner or later */ allowed to enter, sooner or later */
srv_conc_exit_innodb(); srv_conc_force_exit_innodb(thr_get_trx(thr));
/* Wait for the release */ /* Wait for the release */
...@@ -1913,7 +2008,7 @@ srv_suspend_mysql_thread( ...@@ -1913,7 +2008,7 @@ srv_suspend_mysql_thread(
/* Return back inside InnoDB */ /* Return back inside InnoDB */
srv_conc_force_enter_innodb(); srv_conc_force_enter_innodb(thr_get_trx(thr));
mutex_enter(&kernel_mutex); mutex_enter(&kernel_mutex);
...@@ -2052,8 +2147,9 @@ loop: ...@@ -2052,8 +2147,9 @@ loop:
"ROW OPERATIONS\n" "ROW OPERATIONS\n"
"--------------\n"); "--------------\n");
printf( printf(
"%ld queries inside InnoDB; main thread: %s\n", "%ld queries inside InnoDB, %ld queries in queue; main thread: %s\n",
srv_conc_n_threads, srv_main_thread_op_info); srv_conc_n_threads, srv_conc_n_waiting_threads,
srv_main_thread_op_info);
printf( printf(
"Number of rows inserted %lu, updated %lu, deleted %lu, read %lu\n", "Number of rows inserted %lu, updated %lu, deleted %lu, read %lu\n",
srv_n_rows_inserted, srv_n_rows_inserted,
...@@ -2315,6 +2411,12 @@ loop: ...@@ -2315,6 +2411,12 @@ loop:
srv_main_thread_op_info = "sleeping"; srv_main_thread_op_info = "sleeping";
os_thread_sleep(1000000); 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) { if (srv_force_recovery >= SRV_FORCE_NO_BACKGROUND) {
goto suspend_thread; goto suspend_thread;
......
...@@ -913,6 +913,17 @@ sync_array_print_long_waits(void) ...@@ -913,6 +913,17 @@ sync_array_print_long_waits(void)
noticed = TRUE; 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) { if (noticed) {
......
...@@ -389,6 +389,115 @@ trx_sys_flush_max_trx_id(void) ...@@ -389,6 +389,115 @@ trx_sys_flush_max_trx_id(void)
mtr_commit(&mtr); 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. */ 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) ...@@ -519,7 +628,7 @@ trx_sys_init_at_db_start(void)
"InnoDB: %lu uncommitted transaction(s) which must be rolled back\n", "InnoDB: %lu uncommitted transaction(s) which must be rolled back\n",
UT_LIST_GET_LEN(trx_sys->trx_list)); 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_high(trx_sys->max_trx_id),
ut_dulint_get_low(trx_sys->max_trx_id)); ut_dulint_get_low(trx_sys->max_trx_id));
} }
......
...@@ -76,6 +76,9 @@ trx_create( ...@@ -76,6 +76,9 @@ trx_create(
trx->n_mysql_tables_in_use = 0; trx->n_mysql_tables_in_use = 0;
trx->mysql_n_tables_locked = 0; trx->mysql_n_tables_locked = 0;
trx->mysql_log_file_name = NULL;
trx->mysql_log_offset = 0;
trx->ignore_duplicates_in_insert = FALSE; trx->ignore_duplicates_in_insert = FALSE;
mutex_create(&(trx->undo_mutex)); mutex_create(&(trx->undo_mutex));
...@@ -111,6 +114,9 @@ trx_create( ...@@ -111,6 +114,9 @@ trx_create(
trx->has_search_latch = FALSE; trx->has_search_latch = FALSE;
trx->search_latch_timeout = BTR_SEA_TIMEOUT; 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->auto_inc_lock = NULL;
trx->read_view_heap = mem_heap_create(256); trx->read_view_heap = mem_heap_create(256);
...@@ -568,6 +574,13 @@ trx_commit_off_kernel( ...@@ -568,6 +574,13 @@ trx_commit_off_kernel(
mutex_exit(&(rseg->mutex)); 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 /* If we did not take the shortcut, the following call
commits the mini-transaction, making the whole transaction commits the mini-transaction, making the whole transaction
committed in the file-based world at this log sequence number; committed in the file-based world at this log sequence number;
......
...@@ -1310,9 +1310,10 @@ trx_undo_mem_init_for_reuse( ...@@ -1310,9 +1310,10 @@ trx_undo_mem_init_for_reuse(
{ {
ut_ad(mutex_own(&((undo->rseg)->mutex))); ut_ad(mutex_own(&((undo->rseg)->mutex)));
if (undo->id >= TRX_RSEG_N_SLOTS) { if (undo->id >= TRX_RSEG_N_SLOTS) {
fprintf(stderr, fprintf(stderr, "InnoDB: Error: undo->id is %lu\n", undo->id);
"InnoDB: Error: undo->id is %lu\n", undo->id);
mem_analyze_corruption((byte*)undo);
ut_a(0); ut_a(0);
} }
...@@ -1399,7 +1400,7 @@ trx_undo_create( ...@@ -1399,7 +1400,7 @@ trx_undo_create(
/************************************************************************ /************************************************************************
Reuses a cached undo log. */ Reuses a cached undo log. */
UNIV_INLINE static
trx_undo_t* trx_undo_t*
trx_undo_reuse_cached( trx_undo_reuse_cached(
/*==================*/ /*==================*/
...@@ -1442,6 +1443,12 @@ trx_undo_reuse_cached( ...@@ -1442,6 +1443,12 @@ trx_undo_reuse_cached(
ut_ad(undo->size == 1); ut_ad(undo->size == 1);
ut_ad(undo->hdr_page_no == undo->top_page_no); 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); undo_page = trx_undo_page_get(undo->space, undo->hdr_page_no, mtr);
if (type == TRX_UNDO_INSERT) { if (type == TRX_UNDO_INSERT) {
...@@ -1572,8 +1579,8 @@ trx_undo_set_state_at_finish( ...@@ -1572,8 +1579,8 @@ trx_undo_set_state_at_finish(
ut_ad(trx && undo && mtr); ut_ad(trx && undo && mtr);
if (undo->id >= TRX_RSEG_N_SLOTS) { if (undo->id >= TRX_RSEG_N_SLOTS) {
fprintf(stderr, fprintf(stderr, "InnoDB: Error: undo->id is %lu\n", undo->id);
"InnoDB: Error: undo->id is %lu\n", undo->id); mem_analyze_corruption((byte*)undo);
ut_a(0); ut_a(0);
} }
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment