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)
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. */
......
......@@ -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)
......
......@@ -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. */
......
......@@ -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
......
......@@ -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 */
......@@ -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 */
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
);
/*******************************************************************
Allocates n bytes of memory from a memory heap. */
......@@ -224,21 +203,14 @@ mem_heap_get_size(
/******************************************************************
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 n, /* in: desired number of bytes */
char* file_name, /* in: file name where created */
ulint line /* in: line where created */
#endif
);
/*******************************************************************
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 */
void* ptr, /* in, own: buffer to be freed */
char* file_name, /* in: file name where created */
ulint line /* in: line where created */
#endif
);
/*******************************************************************
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)
......
......@@ -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
}
/*********************************************************************
......@@ -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));
}
......@@ -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 */
......
......@@ -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
......
......@@ -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. */
......
......@@ -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)
/*-------------------------------------------------------------*/
......
......@@ -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 */
......
......@@ -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,8 +2034,12 @@ 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: 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"
......@@ -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;
......
......@@ -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++;
}
}
......@@ -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,6 +161,10 @@ 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);
......@@ -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,6 +256,7 @@ 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
......
......@@ -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
......
......@@ -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;
......
......@@ -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);
......
......@@ -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;
......@@ -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);
......@@ -922,6 +953,50 @@ row_get_mysql_key_number_for_index(
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
table ends to characters INNODB_MONITOR, then this also starts
......@@ -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));
/* 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. */
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));
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;
......
......@@ -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);
......
......@@ -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,10 +1653,28 @@ 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++;
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);
......@@ -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
......@@ -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. */
static
......@@ -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;
......
......@@ -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) {
......
......@@ -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));
}
......
......@@ -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;
......
......@@ -1311,8 +1311,9 @@ 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);
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);
}
......
......@@ -112,6 +112,36 @@ static void innobase_print_error(const char* db_errpfx, char* buffer);
/* General functions */
/**********************************************************************
Releases possible search latch, auto inc lock, and InnoDB thread FIFO ticket.
These should be released at each SQL statement end. */
static
void
innobase_release_stat_resources(
/*============================*/
trx_t* trx) /* in: transaction object */
{
if (trx->has_search_latch) {
trx_search_latch_release_if_reserved(trx);
}
if (trx->auto_inc_lock) {
/* If we had reserved the auto-inc lock for
some table in this SQL statement, we release it now */
srv_conc_enter_innodb(trx);
row_unlock_table_autoinc_for_mysql(trx);
srv_conc_exit_innodb(trx);
}
if (trx->declared_to_be_inside_innodb) {
/* Release our possible ticket in the FIFO */
srv_conc_force_exit_innodb(trx);
}
}
/************************************************************************
Increments innobase_active_counter and every INNOBASE_WAKE_INTERVALth
time calls srv_active_wake_master_thread. This function should be used
......@@ -708,13 +738,13 @@ innobase_commit(
trx = check_trx_exists(thd);
if (trx_handle != (void*)&innodb_dummy_stmt_trx_handle) {
srv_conc_enter_innodb(trx);
trx_commit_for_mysql(trx);
srv_conc_exit_innodb();
}
/* Release possible statement level resources */
innobase_release_stat_resources(trx);
trx_mark_sql_stat_end(trx);
#ifndef DBUG_OFF
......@@ -746,9 +776,14 @@ innobase_report_binlog_offset_and_commit(
my_off_t end_offset) /* in: the offset in the binlog file
up to which we wrote */
{
/* Currently does nothing ! */
trx_t* trx;
return(0);
trx = (trx_t*)trx_handle;
trx->mysql_log_file_name = log_file_name;
trx->mysql_log_offset = (ib_longlong)end_offset;
return(innobase_commit(thd, trx_handle));
}
/*********************************************************************
......@@ -778,7 +813,10 @@ innobase_rollback(
error = trx_rollback_last_sql_stat_for_mysql(trx);
}
srv_conc_exit_innodb();
srv_conc_exit_innodb(trx);
/* Release possible statement level resources */
innobase_release_stat_resources(trx);
trx_mark_sql_stat_end(trx);
......@@ -1451,6 +1489,8 @@ ha_innobase::write_row(
if (last_query_id != user_thd->query_id) {
prebuilt->sql_stat_start = TRUE;
last_query_id = user_thd->query_id;
innobase_release_stat_resources(prebuilt->trx);
}
if (table->next_number_field && record == table->record[0]) {
......@@ -1501,7 +1541,7 @@ ha_innobase::write_row(
srv_conc_enter_innodb(prebuilt->trx);
error = row_lock_table_autoinc_for_mysql(prebuilt);
srv_conc_exit_innodb();
srv_conc_exit_innodb(prebuilt->trx);
if (error != DB_SUCCESS) {
......@@ -1518,7 +1558,7 @@ ha_innobase::write_row(
error = row_lock_table_autoinc_for_mysql(
prebuilt);
if (error != DB_SUCCESS) {
srv_conc_exit_innodb();
srv_conc_exit_innodb(prebuilt->trx);
error = convert_error_code_to_mysql(
error);
......@@ -1527,7 +1567,7 @@ ha_innobase::write_row(
}
auto_inc = dict_table_autoinc_get(prebuilt->table);
srv_conc_exit_innodb();
srv_conc_exit_innodb(prebuilt->trx);
/* If auto_inc is now != 0 the autoinc counter
was already initialized for the table: we can give
......@@ -1554,7 +1594,7 @@ ha_innobase::write_row(
srv_conc_enter_innodb(prebuilt->trx);
error = row_lock_table_autoinc_for_mysql(prebuilt);
srv_conc_exit_innodb();
srv_conc_exit_innodb(prebuilt->trx);
if (error != DB_SUCCESS) {
......@@ -1594,7 +1634,7 @@ ha_innobase::write_row(
error = row_insert_for_mysql((byte*) record, prebuilt);
srv_conc_exit_innodb();
srv_conc_exit_innodb(prebuilt->trx);
prebuilt->trx->ignore_duplicates_in_insert = FALSE;
......@@ -1789,6 +1829,8 @@ ha_innobase::update_row(
if (last_query_id != user_thd->query_id) {
prebuilt->sql_stat_start = TRUE;
last_query_id = user_thd->query_id;
innobase_release_stat_resources(prebuilt->trx);
}
if (prebuilt->upd_node) {
......@@ -1813,7 +1855,7 @@ ha_innobase::update_row(
error = row_update_for_mysql((byte*) old_row, prebuilt);
srv_conc_exit_innodb();
srv_conc_exit_innodb(prebuilt->trx);
error = convert_error_code_to_mysql(error);
......@@ -1842,6 +1884,8 @@ ha_innobase::delete_row(
if (last_query_id != user_thd->query_id) {
prebuilt->sql_stat_start = TRUE;
last_query_id = user_thd->query_id;
innobase_release_stat_resources(prebuilt->trx);
}
if (!prebuilt->upd_node) {
......@@ -1857,7 +1901,7 @@ ha_innobase::delete_row(
error = row_update_for_mysql((byte*) record, prebuilt);
srv_conc_exit_innodb();
srv_conc_exit_innodb(prebuilt->trx);
error = convert_error_code_to_mysql(error);
......@@ -1956,6 +2000,8 @@ ha_innobase::index_read(
if (last_query_id != user_thd->query_id) {
prebuilt->sql_stat_start = TRUE;
last_query_id = user_thd->query_id;
innobase_release_stat_resources(prebuilt->trx);
}
index = prebuilt->index;
......@@ -2001,7 +2047,7 @@ ha_innobase::index_read(
ret = row_search_for_mysql((byte*) buf, mode, prebuilt, match_mode, 0);
srv_conc_exit_innodb();
srv_conc_exit_innodb(prebuilt->trx);
if (ret == DB_SUCCESS) {
error = 0;
......@@ -2127,7 +2173,7 @@ ha_innobase::general_fetch(
ret = row_search_for_mysql((byte*)buf, 0, prebuilt, match_mode,
direction);
srv_conc_exit_innodb();
srv_conc_exit_innodb(prebuilt->trx);
if (ret == DB_SUCCESS) {
error = 0;
......@@ -2427,89 +2473,6 @@ int ha_innobase::reset(void)
return(0);
}
/**********************************************************************
As MySQL will execute an external lock for every new table it uses when it
starts to process an SQL statement, we can use this function to store the
pointer to the THD in the handle. We will also use this function to communicate
to InnoDB that a new SQL statement has started and that we must store a
savepoint to our transaction handle, so that we are able to roll back
the SQL statement in case of an error. */
int
ha_innobase::external_lock(
/*=======================*/
THD* thd, /* in: handle to the user thread */
int lock_type) /* in: lock type */
{
row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
int error = 0;
trx_t* trx;
DBUG_ENTER("ha_innobase::external_lock");
update_thd(thd);
trx = prebuilt->trx;
prebuilt->sql_stat_start = TRUE;
prebuilt->in_update_remember_pos = TRUE;
prebuilt->read_just_key = 0;
if (lock_type == F_WRLCK) {
/* If this is a SELECT, then it is in UPDATE TABLE ...
or SELECT ... FOR UPDATE */
prebuilt->select_lock_type = LOCK_X;
}
if (lock_type != F_UNLCK) {
if (trx->n_mysql_tables_in_use == 0) {
trx_mark_sql_stat_end(trx);
}
thd->transaction.all.innodb_active_trans = 1;
trx->n_mysql_tables_in_use++;
if (prebuilt->select_lock_type != LOCK_NONE) {
trx->mysql_n_tables_locked++;
}
} else {
trx->n_mysql_tables_in_use--;
auto_inc_counter_for_this_stat = 0;
if (trx->n_mysql_tables_in_use == 0) {
trx->mysql_n_tables_locked = 0;
if (trx->has_search_latch) {
trx_search_latch_release_if_reserved(trx);
}
if (trx->auto_inc_lock) {
/* If we had reserved the auto-inc lock for
some table in this SQL statement, we release
it now */
srv_conc_enter_innodb(trx);
row_unlock_table_autoinc_for_mysql(trx);
srv_conc_exit_innodb();
}
if (!(thd->options
& (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN))) {
innobase_commit(thd, trx);
}
}
}
DBUG_RETURN(error);
}
/*********************************************************************
Creates a table definition to an InnoDB database. */
static
......@@ -2685,7 +2648,7 @@ ha_innobase::create(
/* Create the table definition in InnoDB */
if ((error = create_table_def(trx, form, norm_name))) {
if (error = create_table_def(trx, form, norm_name)) {
trx_commit_for_mysql(trx);
......@@ -3265,6 +3228,78 @@ ha_innobase::update_table_comment(
return(str);
}
/**********************************************************************
As MySQL will execute an external lock for every new table it uses when it
starts to process an SQL statement, we can use this function to store the
pointer to the THD in the handle. We will also use this function to communicate
to InnoDB that a new SQL statement has started and that we must store a
savepoint to our transaction handle, so that we are able to roll back
the SQL statement in case of an error. */
int
ha_innobase::external_lock(
/*=======================*/
THD* thd, /* in: handle to the user thread */
int lock_type) /* in: lock type */
{
row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
int error = 0;
trx_t* trx;
DBUG_ENTER("ha_innobase::external_lock");
update_thd(thd);
trx = prebuilt->trx;
prebuilt->sql_stat_start = TRUE;
prebuilt->in_update_remember_pos = TRUE;
prebuilt->read_just_key = 0;
if (lock_type == F_WRLCK) {
/* If this is a SELECT, then it is in UPDATE TABLE ...
or SELECT ... FOR UPDATE */
prebuilt->select_lock_type = LOCK_X;
}
if (lock_type != F_UNLCK) {
if (trx->n_mysql_tables_in_use == 0) {
trx_mark_sql_stat_end(trx);
}
thd->transaction.all.innodb_active_trans = 1;
trx->n_mysql_tables_in_use++;
if (prebuilt->select_lock_type != LOCK_NONE) {
trx->mysql_n_tables_locked++;
}
} else {
trx->n_mysql_tables_in_use--;
auto_inc_counter_for_this_stat = 0;
if (trx->n_mysql_tables_in_use == 0) {
trx->mysql_n_tables_locked = 0;
/* Here we release the search latch, auto_inc_lock,
and InnoDB thread FIFO ticket if they were reserved. */
innobase_release_stat_resources(trx);
if (!(thd->options
& (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN))) {
innobase_commit(thd, trx);
}
}
}
DBUG_RETURN(error);
}
/****************************************************************************
Handling the shared INNOBASE_SHARE structure that is needed to provide table
locking.
......
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