Commit aea3c071 authored by heikki@donna.mysql.fi's avatar heikki@donna.mysql.fi

Many files:

  Small improvements
row0mysql.c:
  Small improvements + fix the ALTER TABLE problem by introducing a lazy drop table it can use
ha_innobase.cc:
  Some fine-tuning of optimization
parent 9c86441e
...@@ -242,7 +242,6 @@ loop: ...@@ -242,7 +242,6 @@ loop:
if (n_iterations > 30) { if (n_iterations > 30) {
ut_print_timestamp(stderr); ut_print_timestamp(stderr);
fprintf(stderr, fprintf(stderr,
" ***********************************************\n"
"InnoDB: Warning: difficult to find free blocks from\n" "InnoDB: Warning: difficult to find free blocks from\n"
"InnoDB: the buffer pool (%lu search iterations)! Consider\n" "InnoDB: the buffer pool (%lu search iterations)! Consider\n"
"InnoDB: increasing the buffer pool size.\n", "InnoDB: increasing the buffer pool size.\n",
......
...@@ -1088,7 +1088,15 @@ loop: ...@@ -1088,7 +1088,15 @@ loop:
node = UT_LIST_GET_FIRST(space->chain); node = UT_LIST_GET_FIRST(space->chain);
for (;;) { for (;;) {
ut_a(node); if (node == NULL) {
fprintf(stderr,
"InnoDB: Error: trying to access page number %lu in space %lu\n"
"InnoDB: which is outside the tablespace bounds.\n"
"InnoDB: Byte offset %lu, len %lu, i/o type %lu\n",
block_offset, space_id, byte_offset, len, type);
ut_a(0);
}
if (node->size > block_offset) { if (node->size > block_offset) {
/* Found! */ /* Found! */
......
...@@ -258,6 +258,7 @@ struct recv_sys_struct{ ...@@ -258,6 +258,7 @@ struct recv_sys_struct{
extern recv_sys_t* recv_sys; extern recv_sys_t* recv_sys;
extern ibool recv_recovery_on; extern ibool recv_recovery_on;
extern ibool recv_no_ibuf_operations; extern ibool recv_no_ibuf_operations;
extern ibool recv_needed_recovery;
/* States of recv_addr_struct */ /* States of recv_addr_struct */
#define RECV_NOT_PROCESSED 71 #define RECV_NOT_PROCESSED 71
......
...@@ -269,13 +269,24 @@ mem_realloc( ...@@ -269,13 +269,24 @@ mem_realloc(
ulint n, /* in: desired number of bytes */ ulint n, /* in: desired number of bytes */
char* file_name,/* in: file name where called */ char* file_name,/* in: file name where called */
ulint line); /* in: line where called */ ulint line); /* in: line where called */
#ifdef MEM_PERIODIC_CHECK
/**********************************************************************
Goes through the list of all allocated mem blocks, checks their magic
numbers, and reports possible corruption. */
void
mem_validate_all_blocks(void);
/*=========================*/
#endif
/*#######################################################################*/ /*#######################################################################*/
/* The info header of a block in a memory heap */ /* The info header of a block in a memory heap */
struct mem_block_info_struct { struct mem_block_info_struct {
ulint magic_n;/* magic number for debugging */
char file_name[8];/* file name where the mem heap was created */
ulint line; /* line number where the mem heap was created */
UT_LIST_BASE_NODE_T(mem_block_t) base; /* In the first block in the UT_LIST_BASE_NODE_T(mem_block_t) base; /* In the first block in the
the list this is the base node of the list of blocks; the list this is the base node of the list of blocks;
in subsequent blocks this is undefined */ in subsequent blocks this is undefined */
...@@ -299,9 +310,11 @@ struct mem_block_info_struct { ...@@ -299,9 +310,11 @@ struct mem_block_info_struct {
allocated buffer frame, which can be appended as a allocated buffer frame, which can be appended as a
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 */ #ifdef MEM_PERIODIC_CHECK
char file_name[8];/* file name where the mem heap was created */ UT_LIST_NODE_T(mem_block_t) mem_block_list;
ulint line; /* line number where the mem heap was created */ /* List of all mem blocks allocated; protected
by the mem_comm_pool mutex */
#endif
}; };
#define MEM_BLOCK_MAGIC_N 764741555 #define MEM_BLOCK_MAGIC_N 764741555
......
...@@ -72,6 +72,18 @@ mem_pool_get_reserved( ...@@ -72,6 +72,18 @@ mem_pool_get_reserved(
/* out: reserved mmeory in bytes */ /* out: reserved mmeory in bytes */
mem_pool_t* pool); /* in: memory pool */ mem_pool_t* pool); /* in: memory pool */
/************************************************************************ /************************************************************************
Reserves the mem pool mutex. */
void
mem_pool_mutex_enter(void);
/*======================*/
/************************************************************************
Releases the mem pool mutex. */
void
mem_pool_mutex_exit(void);
/*=====================*/
/************************************************************************
Validates a memory pool. */ Validates a memory pool. */
ibool ibool
......
...@@ -251,6 +251,24 @@ row_table_add_foreign_constraints( ...@@ -251,6 +251,24 @@ 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 */
/************************************************************************* /*************************************************************************
The master thread in srv0srv.c calls this regularly to drop tables which
we must drop in background after queries to them have ended. Such lazy
dropping of tables is needed in ALTER TABLE on Unix. */
ulint
row_drop_tables_for_mysql_in_background(void);
/*=========================================*/
/* out: how many tables dropped
+ remaining tables in list */
/*************************************************************************
Get the background drop list length. NOTE: the caller must own the kernel
mutex! */
ulint
row_get_background_drop_list_len_low(void);
/*======================================*/
/* out: how many tables in list */
/*************************************************************************
Drops a table for MySQL. If the name of the dropped table ends to Drops a table for MySQL. If the name of the dropped table ends to
characters INNODB_MONITOR, then this also stops printing of monitor characters INNODB_MONITOR, then this also stops printing of monitor
output by the master thread. */ output by the master thread. */
...@@ -426,7 +444,7 @@ struct row_prebuilt_struct { ...@@ -426,7 +444,7 @@ struct row_prebuilt_struct {
fetched row in fetch_cache */ fetched row in fetch_cache */
ulint n_fetch_cached; /* number of not yet fetched rows ulint n_fetch_cached; /* number of not yet fetched rows
in fetch_cache */ in fetch_cache */
mem_heap_t* blob_heap; /* in SELECTS BLOB fields are copied mem_heap_t* blob_heap; /* in SELECTS BLOB fie lds are copied
to this heap */ to this heap */
mem_heap_t* old_vers_heap; /* memory heap where a previous mem_heap_t* old_vers_heap; /* memory heap where a previous
version is built in consistent read */ version is built in consistent read */
......
...@@ -249,6 +249,12 @@ mutex, for performace reasons). */ ...@@ -249,6 +249,12 @@ mutex, for performace reasons). */
void void
srv_active_wake_master_thread(void); srv_active_wake_master_thread(void);
/*===============================*/ /*===============================*/
/***********************************************************************
Wakes up the master thread if it is suspended or being suspended. */
void
srv_wake_master_thread(void);
/*========================*/
/************************************************************************* /*************************************************************************
Puts an OS thread to wait if there are too many concurrent threads 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. */
......
...@@ -50,6 +50,13 @@ trx_allocate_for_mysql(void); ...@@ -50,6 +50,13 @@ trx_allocate_for_mysql(void);
/*========================*/ /*========================*/
/* out, own: transaction object */ /* out, own: transaction object */
/************************************************************************ /************************************************************************
Creates a transaction object for background operations by the master thread. */
trx_t*
trx_allocate_for_background(void);
/*=============================*/
/* out, own: transaction object */
/************************************************************************
Frees a transaction object. */ Frees a transaction object. */
void void
...@@ -63,6 +70,13 @@ void ...@@ -63,6 +70,13 @@ void
trx_free_for_mysql( trx_free_for_mysql(
/*===============*/ /*===============*/
trx_t* trx); /* in, own: trx object */ trx_t* trx); /* in, own: trx object */
/************************************************************************
Frees a transaction object of a background operation of the master thread. */
void
trx_free_for_background(
/*====================*/
trx_t* trx); /* in, own: trx object */
/******************************************************************** /********************************************************************
Creates trx objects for transactions and initializes the trx list of Creates trx objects for transactions and initializes the trx list of
trx_sys at database start. Rollback segment and undo log lists must trx_sys at database start. Rollback segment and undo log lists must
...@@ -266,11 +280,14 @@ struct trx_sig_struct{ ...@@ -266,11 +280,14 @@ struct trx_sig_struct{
transaction is waiting a reply */ transaction is waiting a reply */
}; };
#define TRX_MAGIC_N 91118598
/* The transaction handle; every session has a trx object which is freed only /* The transaction handle; every session has a trx object which is freed only
when the session is freed; in addition there may be session-less transactions when the session is freed; in addition there may be session-less transactions
rolling back after a database recovery */ rolling back after a database recovery */
struct trx_struct{ struct trx_struct{
ulint magic_n;
/* All the next fields are protected by the kernel mutex, except the /* All the next fields are protected by the kernel mutex, except the
undo logs which are protected by undo_mutex */ undo logs which are protected by undo_mutex */
char* op_info; /* English text describing the char* op_info; /* English text describing the
......
...@@ -1020,8 +1020,9 @@ loop: ...@@ -1020,8 +1020,9 @@ loop:
if (recv_addr->state == RECV_NOT_PROCESSED) { if (recv_addr->state == RECV_NOT_PROCESSED) {
if (!has_printed) { if (!has_printed) {
ut_print_timestamp(stderr);
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;
} }
......
...@@ -75,6 +75,14 @@ After freeing, all the blocks in the heap are set to random bytes ...@@ -75,6 +75,14 @@ After freeing, all the blocks in the heap are set to random bytes
to help us discover errors which result from the use of to help us discover errors which result from the use of
buffers in an already freed heap. */ buffers in an already freed heap. */
#ifdef MEM_PERIODIC_CHECK
ibool mem_block_list_inited;
/* List of all mem blocks allocated; protected by the mem_comm_pool mutex */
UT_LIST_BASE_NODE_T(mem_block_t) mem_block_list;
#endif
/******************************************************************* /*******************************************************************
NOTE: Use the corresponding macro instead of this function. 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
...@@ -169,7 +177,19 @@ mem_heap_create_block( ...@@ -169,7 +177,19 @@ mem_heap_create_block(
7); 7);
block->file_name[7]='\0'; block->file_name[7]='\0';
block->line = line; block->line = line;
#ifdef MEM_PERIODIC_CHECK
mem_pool_mutex_enter();
if (!mem_block_list_inited) {
mem_block_list_inited = TRUE;
UT_LIST_INIT(mem_block_list);
}
UT_LIST_ADD_LAST(mem_block_list, mem_block_list, block);
mem_pool_mutex_exit();
#endif
mem_block_set_len(block, len); mem_block_set_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);
...@@ -261,6 +281,13 @@ mem_heap_block_free( ...@@ -261,6 +281,13 @@ mem_heap_block_free(
UT_LIST_REMOVE(list, heap->base, block); UT_LIST_REMOVE(list, heap->base, block);
#ifdef MEM_PERIODIC_CHECK
mem_pool_mutex_enter();
UT_LIST_REMOVE(mem_block_list, mem_block_list, block);
mem_pool_mutex_exit();
#endif
type = heap->type; type = heap->type;
len = block->len; len = block->len;
init_block = block->init_block; init_block = block->init_block;
...@@ -306,3 +333,30 @@ mem_heap_free_block_free( ...@@ -306,3 +333,30 @@ mem_heap_free_block_free(
heap->free_block = NULL; heap->free_block = NULL;
} }
} }
#ifdef MEM_PERIODIC_CHECK
/**********************************************************************
Goes through the list of all allocated mem blocks, checks their magic
numbers, and reports possible corruption. */
void
mem_validate_all_blocks(void)
/*=========================*/
{
mem_block_t* block;
mem_pool_mutex_enter();
block = UT_LIST_GET_FIRST(mem_block_list);
while (block) {
if (block->magic_n != MEM_BLOCK_MAGIC_N) {
mem_analyze_corruption((byte*)block);
}
block = UT_LIST_GET_NEXT(mem_block_list, block);
}
mem_pool_mutex_exit();
}
#endif
...@@ -78,9 +78,9 @@ pool, and after that its locks will grow into the buffer pool. */ ...@@ -78,9 +78,9 @@ pool, and after that its locks will grow into the buffer pool. */
/* The smallest memory area total size */ /* The smallest memory area total size */
#define MEM_AREA_MIN_SIZE (2 * MEM_AREA_EXTRA_SIZE) #define MEM_AREA_MIN_SIZE (2 * MEM_AREA_EXTRA_SIZE)
/* Data structure for a memory pool. The space is allocated using the buddy /* Data structure for a memory pool. The space is allocated using the buddy
algorithm, where free list i contains areas of size 2 to power i. */ algorithm, where free list i contains areas of size 2 to power i. */
struct mem_pool_struct{ struct mem_pool_struct{
byte* buf; /* memory pool */ byte* buf; /* memory pool */
ulint size; /* memory common pool size */ ulint size; /* memory common pool size */
...@@ -98,6 +98,26 @@ mem_pool_t* mem_comm_pool = NULL; ...@@ -98,6 +98,26 @@ mem_pool_t* mem_comm_pool = NULL;
ulint mem_out_of_mem_err_msg_count = 0; ulint mem_out_of_mem_err_msg_count = 0;
/************************************************************************
Reserves the mem pool mutex. */
void
mem_pool_mutex_enter(void)
/*======================*/
{
mutex_enter(&(mem_comm_pool->mutex));
}
/************************************************************************
Releases the mem pool mutex. */
void
mem_pool_mutex_exit(void)
/*=====================*/
{
mutex_exit(&(mem_comm_pool->mutex));
}
/************************************************************************ /************************************************************************
Returns memory area size. */ Returns memory area size. */
UNIV_INLINE UNIV_INLINE
...@@ -240,15 +260,15 @@ mem_pool_fill_free_list( ...@@ -240,15 +260,15 @@ mem_pool_fill_free_list(
if (mem_out_of_mem_err_msg_count % 1000000000 == 0) { if (mem_out_of_mem_err_msg_count % 1000000000 == 0) {
/* We do not print the message every time: */ /* We do not print the message every time: */
ut_print_timestamp(stderr);
fprintf(stderr, fprintf(stderr,
"Innobase: Warning: out of memory in additional memory pool.\n"); " InnoDB: Out of memory in additional memory pool.\n"
fprintf(stderr, "InnoDB: InnoDB will start allocating memory from the OS.\n"
"Innobase: Innobase will start allocating memory from the OS.\n"); "InnoDB: You may get better performance if you configure a bigger\n"
fprintf(stderr, "InnoDB: value in the MySQL my.cnf file for\n"
"Innobase: You should restart the database with a bigger value in\n"); "InnoDB: innodb_additional_mem_pool_size.\n");
fprintf(stderr,
"Innobase: the MySQL .cnf file for innobase_additional_mem_pool_size.\n");
} }
mem_out_of_mem_err_msg_count++; mem_out_of_mem_err_msg_count++;
......
...@@ -26,6 +26,19 @@ Created 9/17/2000 Heikki Tuuri ...@@ -26,6 +26,19 @@ Created 9/17/2000 Heikki Tuuri
#include "trx0purge.h" #include "trx0purge.h"
#include "lock0lock.h" #include "lock0lock.h"
#include "rem0cmp.h" #include "rem0cmp.h"
#include "log0log.h"
/* List of tables we should drop in background. ALTER TABLE in MySQL requires
that the table handler can drop the table in background when there are no
queries to it any more. Protected by the kernel mutex. */
typedef struct row_mysql_drop_struct row_mysql_drop_t;
struct row_mysql_drop_struct{
char* table_name;
UT_LIST_NODE_T(row_mysql_drop_t) row_mysql_drop_list;
};
UT_LIST_BASE_NODE_T(row_mysql_drop_t) row_mysql_drop_list;
ibool row_mysql_drop_list_inited = FALSE;
/*********************************************************************** /***********************************************************************
Reads a MySQL format variable-length field (like VARCHAR) length and Reads a MySQL format variable-length field (like VARCHAR) length and
...@@ -172,10 +185,22 @@ handle_new_error: ...@@ -172,10 +185,22 @@ handle_new_error:
trx_general_rollback_for_mysql(trx, TRUE, savept); trx_general_rollback_for_mysql(trx, TRUE, savept);
} }
} else if (err == DB_TOO_BIG_RECORD) { } else if (err == DB_TOO_BIG_RECORD) {
if (savept) {
/* Roll back the latest, possibly incomplete
insertion or update */
trx_general_rollback_for_mysql(trx, TRUE, savept);
}
/* MySQL will roll back the latest SQL statement */ /* MySQL will roll back the latest SQL statement */
} else if (err == DB_ROW_IS_REFERENCED } else if (err == DB_ROW_IS_REFERENCED
|| err == DB_NO_REFERENCED_ROW || err == DB_NO_REFERENCED_ROW
|| err == DB_CANNOT_ADD_CONSTRAINT) { || err == DB_CANNOT_ADD_CONSTRAINT) {
if (savept) {
/* Roll back the latest, possibly incomplete
insertion or update */
trx_general_rollback_for_mysql(trx, TRUE, savept);
}
/* MySQL will roll back the latest SQL statement */ /* MySQL will roll back the latest SQL statement */
} else if (err == DB_LOCK_WAIT) { } else if (err == DB_LOCK_WAIT) {
...@@ -200,6 +225,12 @@ handle_new_error: ...@@ -200,6 +225,12 @@ handle_new_error:
trx_general_rollback_for_mysql(trx, FALSE, NULL); trx_general_rollback_for_mysql(trx, FALSE, NULL);
} else if (err == DB_OUT_OF_FILE_SPACE) { } else if (err == DB_OUT_OF_FILE_SPACE) {
if (savept) {
/* Roll back the latest, possibly incomplete
insertion or update */
trx_general_rollback_for_mysql(trx, TRUE, savept);
}
/* MySQL will roll back the latest SQL statement */ /* MySQL will roll back the latest SQL statement */
} else if (err == DB_MUST_GET_MORE_FILE_SPACE) { } else if (err == DB_MUST_GET_MORE_FILE_SPACE) {
...@@ -375,13 +406,13 @@ row_update_prebuilt_trx( ...@@ -375,13 +406,13 @@ 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) { if (trx->magic_n != TRX_MAGIC_N) {
fprintf(stderr, fprintf(stderr,
"InnoDB: Error: trying to free a corrupt\n" "InnoDB: Error: trying to use a corrupt\n"
"InnoDB: table handle. Magic n %lu, table name %s\n", "InnoDB: trx handle. Magic n %lu\n",
prebuilt->magic_n, prebuilt->table->name); trx->magic_n);
mem_analyze_corruption((byte*)prebuilt); mem_analyze_corruption((byte*)trx);
ut_a(0); ut_a(0);
} }
...@@ -1170,8 +1201,11 @@ row_create_table_for_mysql( ...@@ -1170,8 +1201,11 @@ row_create_table_for_mysql(
row_drop_table_for_mysql(table->name, trx, TRUE); row_drop_table_for_mysql(table->name, trx, TRUE);
} else { } else {
ut_a(err == DB_DUPLICATE_KEY); ut_a(err == DB_DUPLICATE_KEY);
ut_print_timestamp(stderr);
fprintf(stderr, fprintf(stderr,
"InnoDB: Error: table %s already exists in InnoDB internal\n" " InnoDB: Error: table %s already exists in InnoDB internal\n"
"InnoDB: data dictionary. Have you deleted the .frm file\n" "InnoDB: data dictionary. Have you deleted the .frm file\n"
"InnoDB: and not used DROP TABLE? Have you used DROP DATABASE\n" "InnoDB: and not used DROP TABLE? Have you used DROP DATABASE\n"
"InnoDB: for InnoDB tables in MySQL version <= 3.23.43?\n" "InnoDB: for InnoDB tables in MySQL version <= 3.23.43?\n"
...@@ -1349,6 +1383,164 @@ row_table_add_foreign_constraints( ...@@ -1349,6 +1383,164 @@ row_table_add_foreign_constraints(
return((int) err); return((int) err);
} }
/*************************************************************************
Drops a table for MySQL as a background operation. MySQL relies on Unix
in ALTER TABLE to the fact that the table handler does not remove the
table before all handles to it has been removed. Furhermore, the MySQL's
call to drop table must be non-blocking. Therefore we do the drop table
as a background operation, which is taken care of by the master thread
in srv0srv.c. */
static
int
row_drop_table_for_mysql_in_background(
/*===================================*/
/* out: error code or DB_SUCCESS */
char* name) /* in: table name */
{
ulint error;
trx_t* trx;
trx = trx_allocate_for_background();
/* fprintf(stderr, "InnoDB: Dropping table %s in background drop list\n",
name); */
/* Drop the table in InnoDB */
error = row_drop_table_for_mysql(name, trx, FALSE);
if (error != DB_SUCCESS) {
fprintf(stderr,
"InnoDB: Error: Dropping table %s in background drop list failed\n",
name);
}
/* Flush the log to reduce probability that the .frm files and
the InnoDB data dictionary get out-of-sync if the user runs
with innodb_flush_log_at_trx_commit = 0 */
log_flush_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP);
trx_commit_for_mysql(trx);
trx_free_for_background(trx);
return(DB_SUCCESS);
}
/*************************************************************************
The master thread in srv0srv.c calls this regularly to drop tables which
we must drop in background after queries to them have ended. Such lazy
dropping of tables is needed in ALTER TABLE on Unix. */
ulint
row_drop_tables_for_mysql_in_background(void)
/*=========================================*/
/* out: how many tables dropped
+ remaining tables in list */
{
row_mysql_drop_t* drop;
dict_table_t* table;
ulint n_tables;
ulint n_tables_dropped = 0;
loop:
mutex_enter(&kernel_mutex);
if (!row_mysql_drop_list_inited) {
UT_LIST_INIT(row_mysql_drop_list);
row_mysql_drop_list_inited = TRUE;
}
drop = UT_LIST_GET_FIRST(row_mysql_drop_list);
n_tables = UT_LIST_GET_LEN(row_mysql_drop_list);
mutex_exit(&kernel_mutex);
if (drop == NULL) {
return(n_tables + n_tables_dropped);
}
mutex_enter(&(dict_sys->mutex));
table = dict_table_get_low(drop->table_name);
mutex_exit(&(dict_sys->mutex));
if (table->n_mysql_handles_opened > 0) {
return(n_tables + n_tables_dropped);
}
n_tables_dropped++;
row_drop_table_for_mysql_in_background(drop->table_name);
mutex_enter(&kernel_mutex);
UT_LIST_REMOVE(row_mysql_drop_list, row_mysql_drop_list, drop);
mem_free(drop->table_name);
mem_free(drop);
mutex_exit(&kernel_mutex);
goto loop;
}
/*************************************************************************
Get the background drop list length. NOTE: the caller must own the kernel
mutex! */
ulint
row_get_background_drop_list_len_low(void)
/*======================================*/
/* out: how many tables in list */
{
ut_ad(mutex_own(&kernel_mutex));
if (!row_mysql_drop_list_inited) {
UT_LIST_INIT(row_mysql_drop_list);
row_mysql_drop_list_inited = TRUE;
}
return(UT_LIST_GET_LEN(row_mysql_drop_list));
}
/*************************************************************************
Adds a table to the list of tables which the master thread drops in
background. We need this on Unix because in ALTER TABLE MySQL may call
drop table even if the table has running queries on it. */
static
void
row_add_table_to_background_drop_list(
/*==================================*/
dict_table_t* table) /* in: table */
{
row_mysql_drop_t* drop;
drop = mem_alloc(sizeof(row_mysql_drop_t));
drop->table_name = mem_alloc(1 + ut_strlen(table->name));
ut_memcpy(drop->table_name, table->name, 1 + ut_strlen(table->name));
mutex_enter(&kernel_mutex);
if (!row_mysql_drop_list_inited) {
UT_LIST_INIT(row_mysql_drop_list);
row_mysql_drop_list_inited = TRUE;
}
UT_LIST_ADD_LAST(row_mysql_drop_list, row_mysql_drop_list, drop);
/* fprintf(stderr, "InnoDB: Adding table %s to background drop list\n",
drop->table_name); */
mutex_exit(&kernel_mutex);
}
/************************************************************************* /*************************************************************************
Drops a table for MySQL. If the name of the dropped table ends to Drops a table for MySQL. If the name of the dropped table ends to
characters INNODB_MONITOR, then this also stops printing of monitor characters INNODB_MONITOR, then this also stops printing of monitor
...@@ -1536,9 +1728,10 @@ row_drop_table_for_mysql( ...@@ -1536,9 +1728,10 @@ row_drop_table_for_mysql(
if (!table) { if (!table) {
err = DB_TABLE_NOT_FOUND; err = DB_TABLE_NOT_FOUND;
ut_print_timestamp(stderr);
fprintf(stderr, fprintf(stderr,
"InnoDB: Error: table %s does not exist in the InnoDB internal\n" " InnoDB: Error: table %s does not exist in the InnoDB internal\n"
"InnoDB: data dictionary though MySQL is trying to drop it.\n" "InnoDB: data dictionary though MySQL is trying to drop it.\n"
"InnoDB: Have you copied the .frm file of the table to the\n" "InnoDB: Have you copied the .frm file of the table to the\n"
"InnoDB: MySQL database directory from another database?\n", "InnoDB: MySQL database directory from another database?\n",
...@@ -1546,42 +1739,19 @@ row_drop_table_for_mysql( ...@@ -1546,42 +1739,19 @@ row_drop_table_for_mysql(
goto funct_exit; goto funct_exit;
} }
/* 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) { 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); row_add_table_to_background_drop_list(table);
mutex_enter(&(dict_sys->mutex)); err = DB_SUCCESS;
rw_lock_x_lock(&(dict_foreign_key_check_lock));
rw_lock_s_lock(&(purge_sys->purge_is_running));
rounds++; goto funct_exit;
if (rounds > 120) {
fprintf(stderr,
"InnoDB: Warning: queries to table %s have not ended but we continue anyway\n",
name);
} else {
goto loop;
}
} }
/* Remove any locks there are on the table or its records */
lock_reset_all_on_table(table);
trx->dict_operation = TRUE; trx->dict_operation = TRUE;
trx->table_id = table->id; trx->table_id = table->id;
...@@ -1617,6 +1787,8 @@ funct_exit: ...@@ -1617,6 +1787,8 @@ funct_exit:
trx->op_info = ""; trx->op_info = "";
srv_wake_master_thread();
return((int) err); return((int) err);
} }
...@@ -1788,7 +1960,31 @@ row_rename_table_for_mysql( ...@@ -1788,7 +1960,31 @@ row_rename_table_for_mysql(
err = trx->error_state; err = trx->error_state;
if (err != DB_SUCCESS) { if (err != DB_SUCCESS) {
row_mysql_handle_errors(&err, trx, thr, NULL); if (err == DB_DUPLICATE_KEY) {
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: Error: table %s exists in the InnoDB internal data\n"
"InnoDB: dictionary though MySQL is trying rename table %s to it.\n"
"InnoDB: Have you deleted the .frm file and not used DROP TABLE?\n",
new_name, old_name);
fprintf(stderr,
"InnoDB: If table %s is a temporary table #sql..., then it can be that\n"
"InnoDB: there are still queries running on the table, and it will be\n"
"InnoDB: dropped automatically when the queries end.\n", new_name);
fprintf(stderr,
"InnoDB: You can drop the orphaned table inside InnoDB by\n"
"InnoDB: creating an InnoDB table with the same name in another\n"
"InnoDB: database and moving the .frm file to the current database.\n"
"InnoDB: Then MySQL thinks the table exists, and DROP TABLE will\n"
"InnoDB: succeed.\n");
}
trx->error_state = DB_SUCCESS;
trx_general_rollback_for_mysql(trx, FALSE, NULL);
trx->error_state = DB_SUCCESS;
} else { } else {
ut_a(dict_table_rename_in_cache(table, new_name)); ut_a(dict_table_rename_in_cache(table, new_name));
} }
...@@ -1945,7 +2141,7 @@ row_check_table_for_mysql( ...@@ -1945,7 +2141,7 @@ row_check_table_for_mysql(
ulint ret = DB_SUCCESS; ulint ret = DB_SUCCESS;
prebuilt->trx->op_info = "checking table"; prebuilt->trx->op_info = "checking table";
index = dict_table_get_first_index(table); index = dict_table_get_first_index(table);
while (index != NULL) { while (index != NULL) {
......
...@@ -20,7 +20,7 @@ Windows 2000 will have something called thread pooling ...@@ -20,7 +20,7 @@ Windows 2000 will have something called thread pooling
Another possibility could be to use some very fast user space Another possibility could be to use some very fast user space
thread library. This might confuse NT though. thread library. This might confuse NT though.
(c) 1995 InnoDB Oy (c) 1995 Innobase Oy
Created 10/8/1995 Heikki Tuuri Created 10/8/1995 Heikki Tuuri
*******************************************************/ *******************************************************/
...@@ -49,6 +49,7 @@ Created 10/8/1995 Heikki Tuuri ...@@ -49,6 +49,7 @@ Created 10/8/1995 Heikki Tuuri
#include "btr0sea.h" #include "btr0sea.h"
#include "dict0load.h" #include "dict0load.h"
#include "srv0start.h" #include "srv0start.h"
#include "row0mysql.h"
/* Buffer which can be used in printing fatal error messages */ /* Buffer which can be used in printing fatal error messages */
char srv_fatal_errbuf[5000]; char srv_fatal_errbuf[5000];
...@@ -1911,17 +1912,12 @@ srv_boot(void) ...@@ -1911,17 +1912,12 @@ srv_boot(void)
srv_init(); srv_init();
/* Reserve the first slot for the current thread, i.e., the master
thread */
srv_table_reserve_slot(SRV_MASTER);
return(DB_SUCCESS); return(DB_SUCCESS);
} }
/************************************************************************* /*************************************************************************
Reserves a slot in the thread table for the current MySQL OS thread. Reserves a slot in the thread table for the current MySQL OS thread.
NOTE! The server mutex has to be reserved by the caller! */ NOTE! The kernel mutex has to be reserved by the caller! */
static static
srv_slot_t* srv_slot_t*
srv_table_reserve_slot_for_mysql(void) srv_table_reserve_slot_for_mysql(void)
...@@ -1931,6 +1927,8 @@ srv_table_reserve_slot_for_mysql(void) ...@@ -1931,6 +1927,8 @@ srv_table_reserve_slot_for_mysql(void)
srv_slot_t* slot; srv_slot_t* slot;
ulint i; ulint i;
ut_ad(mutex_own(&kernel_mutex));
i = 0; i = 0;
slot = srv_mysql_table + i; slot = srv_mysql_table + i;
...@@ -2352,6 +2350,22 @@ srv_active_wake_master_thread(void) ...@@ -2352,6 +2350,22 @@ srv_active_wake_master_thread(void)
} }
} }
/***********************************************************************
Wakes up the master thread if it is suspended or being suspended. */
void
srv_wake_master_thread(void)
/*========================*/
{
srv_activity_count++;
mutex_enter(&kernel_mutex);
srv_release_threads(SRV_MASTER, 1);
mutex_exit(&kernel_mutex);
}
/************************************************************************* /*************************************************************************
The master thread controlling the server. */ The master thread controlling the server. */
...@@ -2374,6 +2388,7 @@ srv_master_thread( ...@@ -2374,6 +2388,7 @@ srv_master_thread(
ulint n_bytes_merged; ulint n_bytes_merged;
ulint n_pages_flushed; ulint n_pages_flushed;
ulint n_bytes_archived; ulint n_bytes_archived;
ulint n_tables_to_drop;
ulint n_ios; ulint n_ios;
ulint n_ios_old; ulint n_ios_old;
ulint n_ios_very_old; ulint n_ios_very_old;
...@@ -2415,7 +2430,11 @@ loop: ...@@ -2415,7 +2430,11 @@ loop:
can drop tables lazily after there no longer are SELECT can drop tables lazily after there no longer are SELECT
queries to them. */ queries to them. */
/* row_drop_tables_for_mysql_in_background(); */ srv_main_thread_op_info = "doing background drop tables";
row_drop_tables_for_mysql_in_background();
srv_main_thread_op_info = "";
if (srv_force_recovery >= SRV_FORCE_NO_BACKGROUND) { if (srv_force_recovery >= SRV_FORCE_NO_BACKGROUND) {
...@@ -2465,6 +2484,11 @@ loop: ...@@ -2465,6 +2484,11 @@ loop:
printf("Master thread wakes up!\n"); printf("Master thread wakes up!\n");
} }
#ifdef MEM_PERIODIC_CHECK
/* Check magic numbers of every allocated mem block once in 10
seconds */
mem_validate_all_blocks();
#endif
/* If there were less than 200 i/os during the 10 second period, /* If there were less than 200 i/os during the 10 second period,
we assume that there is free disk i/o capacity available, and it we assume that there is free disk i/o capacity available, and it
makes sense to do a buffer pool flush. */ makes sense to do a buffer pool flush. */
...@@ -2521,6 +2545,12 @@ background_loop: ...@@ -2521,6 +2545,12 @@ background_loop:
/* In this loop we run background operations when the server /* In this loop we run background operations when the server
is quiet and we also come here about once in 10 seconds */ is quiet and we also come here about once in 10 seconds */
srv_main_thread_op_info = "doing background drop tables";
n_tables_to_drop = row_drop_tables_for_mysql_in_background();
srv_main_thread_op_info = "";
srv_main_thread_op_info = "flushing buffer pool pages"; srv_main_thread_op_info = "flushing buffer pool pages";
/* Flush a few oldest pages to make the checkpoint younger */ /* Flush a few oldest pages to make the checkpoint younger */
...@@ -2604,11 +2634,13 @@ background_loop: ...@@ -2604,11 +2634,13 @@ background_loop:
log_archive_do(FALSE, &n_bytes_archived); log_archive_do(FALSE, &n_bytes_archived);
if (srv_fast_shutdown && srv_shutdown_state > 0) { if (srv_fast_shutdown && srv_shutdown_state > 0) {
if (n_pages_flushed + n_bytes_archived != 0) { if (n_tables_to_drop + n_pages_flushed
+ n_bytes_archived != 0) {
goto background_loop; goto background_loop;
} }
} else if (n_pages_purged + n_bytes_merged + n_pages_flushed } else if (n_tables_to_drop +
n_pages_purged + n_bytes_merged + n_pages_flushed
+ n_bytes_archived != 0) { + n_bytes_archived != 0) {
goto background_loop; goto background_loop;
} }
...@@ -2627,6 +2659,12 @@ suspend_thread: ...@@ -2627,6 +2659,12 @@ suspend_thread:
mutex_enter(&kernel_mutex); mutex_enter(&kernel_mutex);
if (row_get_background_drop_list_len_low() > 0) {
mutex_exit(&kernel_mutex);
goto loop;
}
event = srv_suspend_thread(); event = srv_suspend_thread();
mutex_exit(&kernel_mutex); mutex_exit(&kernel_mutex);
......
...@@ -270,13 +270,18 @@ open_or_create_log_file( ...@@ -270,13 +270,18 @@ open_or_create_log_file(
} else { } else {
*log_file_created = TRUE; *log_file_created = TRUE;
ut_print_timestamp(stderr);
fprintf(stderr, fprintf(stderr,
"InnoDB: Log file %s did not exist: new to be created\n", " InnoDB: Log file %s did not exist: new to be created\n",
name); name);
fprintf(stderr, "InnoDB: Setting log file %s size to %lu MB\n", fprintf(stderr, "InnoDB: Setting log file %s size to %lu MB\n",
name, srv_log_file_size name, srv_log_file_size
>> (20 - UNIV_PAGE_SIZE_SHIFT)); >> (20 - UNIV_PAGE_SIZE_SHIFT));
fprintf(stderr,
"InnoDB: Database physically writes the file full: wait...\n");
ret = os_file_set_size(name, files[i], ret = os_file_set_size(name, files[i],
srv_calc_low32(srv_log_file_size), srv_calc_low32(srv_log_file_size),
srv_calc_high32(srv_log_file_size)); srv_calc_high32(srv_log_file_size));
...@@ -454,8 +459,9 @@ open_or_create_data_files( ...@@ -454,8 +459,9 @@ open_or_create_data_files(
one_created = TRUE; one_created = TRUE;
if (i > 0) { if (i > 0) {
ut_print_timestamp(stderr);
fprintf(stderr, fprintf(stderr,
"InnoDB: Data file %s did not exist: new to be created\n", " InnoDB: Data file %s did not exist: new to be created\n",
name); name);
} else { } else {
fprintf(stderr, fprintf(stderr,
...@@ -464,8 +470,9 @@ open_or_create_data_files( ...@@ -464,8 +470,9 @@ open_or_create_data_files(
*create_new_db = TRUE; *create_new_db = TRUE;
} }
ut_print_timestamp(stderr);
fprintf(stderr, fprintf(stderr,
"InnoDB: Setting file %s size to %lu MB\n", " InnoDB: Setting file %s size to %lu MB\n",
name, (srv_data_file_sizes[i] name, (srv_data_file_sizes[i]
>> (20 - UNIV_PAGE_SIZE_SHIFT))); >> (20 - UNIV_PAGE_SIZE_SHIFT)));
...@@ -905,6 +912,12 @@ innobase_start_or_create_for_mysql(void) ...@@ -905,6 +912,12 @@ innobase_start_or_create_for_mysql(void)
mtr_commit(&mtr); mtr_commit(&mtr);
} }
if (recv_needed_recovery) {
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: Flushing modified pages from the buffer pool...\n");
}
log_make_checkpoint_at(ut_dulint_max, TRUE); log_make_checkpoint_at(ut_dulint_max, TRUE);
if (!srv_log_archive_on) { if (!srv_log_archive_on) {
...@@ -983,9 +996,8 @@ innobase_shutdown_for_mysql(void) ...@@ -983,9 +996,8 @@ innobase_shutdown_for_mysql(void)
ut_print_timestamp(stderr); ut_print_timestamp(stderr);
fprintf(stderr, fprintf(stderr,
" InnoDB: Warning: shutting down a not properly started\n"); " InnoDB: Warning: shutting down a not properly started\n");
ut_print_timestamp(stderr);
fprintf(stderr, fprintf(stderr,
" InnoDB: or created database!\n"); " InnoDB: or created database!\n");
} }
return(DB_SUCCESS); return(DB_SUCCESS);
......
...@@ -226,9 +226,9 @@ trx_purge_sys_create(void) ...@@ -226,9 +226,9 @@ trx_purge_sys_create(void)
value */ value */
purge_sys->sess = sess_open(com_endpoint, (byte*)"purge_system", 13); purge_sys->sess = sess_open(com_endpoint, (byte*)"purge_system", 13);
purge_sys->trx = (purge_sys->sess)->trx; purge_sys->trx = purge_sys->sess->trx;
(purge_sys->trx)->type = TRX_PURGE; purge_sys->trx->type = TRX_PURGE;
ut_a(trx_start_low(purge_sys->trx, ULINT_UNDEFINED)); ut_a(trx_start_low(purge_sys->trx, ULINT_UNDEFINED));
......
...@@ -26,9 +26,9 @@ Created 3/26/1996 Heikki Tuuri ...@@ -26,9 +26,9 @@ Created 3/26/1996 Heikki Tuuri
/* Copy of the prototype for innobase_mysql_print_thd: this /* Copy of the prototype for innobase_mysql_print_thd: this
copy must be equal to the one in mysql/sql/ha_innobase.cc ! */ copy must be equal to the one in mysql/sql/ha_innobase.cc ! */
void innobase_mysql_print_thd(void* thd);
void innobase_mysql_print_thd(void* thd);
/* Dummy session used currently in MySQL interface */ /* Dummy session used currently in MySQL interface */
sess_t* trx_dummy_sess = NULL; sess_t* trx_dummy_sess = NULL;
...@@ -64,6 +64,8 @@ trx_create( ...@@ -64,6 +64,8 @@ trx_create(
trx = mem_alloc(sizeof(trx_t)); trx = mem_alloc(sizeof(trx_t));
trx->magic_n = TRX_MAGIC_N;
trx->op_info = ""; trx->op_info = "";
trx->type = TRX_USER; trx->type = TRX_USER;
...@@ -157,6 +159,32 @@ trx_allocate_for_mysql(void) ...@@ -157,6 +159,32 @@ trx_allocate_for_mysql(void)
return(trx); return(trx);
} }
/************************************************************************
Creates a transaction object for background operations by the master thread. */
trx_t*
trx_allocate_for_background(void)
/*=============================*/
/* out, own: transaction object */
{
trx_t* trx;
mutex_enter(&kernel_mutex);
/* Open a dummy session */
if (!trx_dummy_sess) {
trx_dummy_sess = sess_open(NULL, (byte*)"Dummy sess",
ut_strlen("Dummy sess"));
}
trx = trx_create(trx_dummy_sess);
mutex_exit(&kernel_mutex);
return(trx);
}
/************************************************************************ /************************************************************************
Releases the search latch if trx has reserved it. */ Releases the search latch if trx has reserved it. */
...@@ -181,6 +209,11 @@ trx_free( ...@@ -181,6 +209,11 @@ trx_free(
trx_t* trx) /* in, own: trx object */ trx_t* trx) /* in, own: trx object */
{ {
ut_ad(mutex_own(&kernel_mutex)); ut_ad(mutex_own(&kernel_mutex));
ut_a(trx->magic_n == TRX_MAGIC_N);
trx->magic_n = 11112222;
ut_a(trx->conc_state == TRX_NOT_STARTED); ut_a(trx->conc_state == TRX_NOT_STARTED);
mutex_free(&(trx->undo_mutex)); mutex_free(&(trx->undo_mutex));
...@@ -242,6 +275,21 @@ trx_free_for_mysql( ...@@ -242,6 +275,21 @@ trx_free_for_mysql(
mutex_exit(&kernel_mutex); mutex_exit(&kernel_mutex);
} }
/************************************************************************
Frees a transaction object of a background operation of the master thread. */
void
trx_free_for_background(
/*====================*/
trx_t* trx) /* in, own: trx object */
{
mutex_enter(&kernel_mutex);
trx_free(trx);
mutex_exit(&kernel_mutex);
}
/******************************************************************** /********************************************************************
Inserts the trx handle in the trx system trx list in the right position. Inserts the trx handle in the trx system trx list in the right position.
The list is sorted on the trx id so that the biggest id is at the list The list is sorted on the trx id so that the biggest id is at the list
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
/* This file defines the InnoDB handler: the interface between MySQL and /* This file defines the InnoDB handler: the interface between MySQL and
InnoDB */ InnoDB */
/* TODO list for the InnoDB handler: /* TODO list for the InnoDB handler:
- Ask Monty if strings of different languages can exist in the same - Ask Monty if strings of different languages can exist in the same
database. Answer: in near future yes, but not yet. database. Answer: in near future yes, but not yet.
...@@ -229,7 +229,7 @@ convert_error_code_to_mysql( ...@@ -229,7 +229,7 @@ convert_error_code_to_mysql(
extern "C" { extern "C" {
/***************************************************************** /*****************************************************************
Prints info of a THD object (== user session thread) to the Prints info of a THD object (== user session thread) to the
standatd output. NOTE that mysql/innobase/trx/trx0trx.c must contain standard output. NOTE that mysql/innobase/trx/trx0trx.c must contain
the prototype for this function! */ the prototype for this function! */
void void
...@@ -298,6 +298,8 @@ check_trx_exists( ...@@ -298,6 +298,8 @@ check_trx_exists(
thd->transaction.stmt.innobase_tid = thd->transaction.stmt.innobase_tid =
(void*)&innodb_dummy_stmt_trx_handle; (void*)&innodb_dummy_stmt_trx_handle;
} else {
ut_a(trx->magic_n == TRX_MAGIC_N);
} }
return(trx); return(trx);
...@@ -835,6 +837,7 @@ innobase_close_connection( ...@@ -835,6 +837,7 @@ innobase_close_connection(
whose transaction should be rolled back */ whose transaction should be rolled back */
{ {
if (NULL != thd->transaction.all.innobase_tid) { if (NULL != thd->transaction.all.innobase_tid) {
trx_rollback_for_mysql((trx_t*) trx_rollback_for_mysql((trx_t*)
(thd->transaction.all.innobase_tid)); (thd->transaction.all.innobase_tid));
trx_free_for_mysql((trx_t*) trx_free_for_mysql((trx_t*)
...@@ -2435,44 +2438,6 @@ ha_innobase::position( ...@@ -2435,44 +2438,6 @@ ha_innobase::position(
ref_stored_len = len; ref_stored_len = len;
} }
/***********************************************************************
Tells something additional to the handler about how to do things. */
int
ha_innobase::extra(
/*===============*/
/* out: 0 or error number */
enum ha_extra_function operation)
/* in: HA_EXTRA_DONT_USE_CURSOR_TO_UPDATE */
{
row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
switch (operation) {
case HA_EXTRA_RESET:
case HA_EXTRA_RESET_STATE:
prebuilt->read_just_key = 0;
break;
case HA_EXTRA_NO_KEYREAD:
prebuilt->read_just_key = 0;
break;
case HA_EXTRA_DONT_USE_CURSOR_TO_UPDATE:
prebuilt->in_update_remember_pos = FALSE;
break;
case HA_EXTRA_KEYREAD:
prebuilt->read_just_key = 1;
break;
default:/* Do nothing */
;
}
return(0);
}
int ha_innobase::reset(void)
{
return(0);
}
/********************************************************************* /*********************************************************************
Creates a table definition to an InnoDB database. */ Creates a table definition to an InnoDB database. */
static static
...@@ -2952,6 +2917,10 @@ ha_innobase::records_in_range( ...@@ -2952,6 +2917,10 @@ ha_innobase::records_in_range(
void* heap2; void* heap2;
DBUG_ENTER("records_in_range"); DBUG_ENTER("records_in_range");
/* Warning: since it is not sure that MySQL calls external_lock
before calling this function, the trx field in prebuilt can be
obsolete! */
active_index = keynr; active_index = keynr;
...@@ -2984,6 +2953,10 @@ ha_innobase::records_in_range( ...@@ -2984,6 +2953,10 @@ ha_innobase::records_in_range(
dtuple_free_for_mysql(heap2); dtuple_free_for_mysql(heap2);
my_free((char*) key_val_buff2, MYF(0)); my_free((char*) key_val_buff2, MYF(0));
if (prebuilt->trx) {
prebuilt->trx->op_info = "";
}
DBUG_RETURN((ha_rows) n_rows); DBUG_RETURN((ha_rows) n_rows);
} }
...@@ -3005,6 +2978,10 @@ ha_innobase::estimate_number_of_rows(void) ...@@ -3005,6 +2978,10 @@ ha_innobase::estimate_number_of_rows(void)
ulonglong estimate; ulonglong estimate;
ulonglong data_file_length; ulonglong data_file_length;
/* Warning: since it is not sure that MySQL calls external_lock
before calling this function, the trx field in prebuilt can be
obsolete! */
DBUG_ENTER("info"); DBUG_ENTER("info");
index = dict_table_get_first_index_noninline(prebuilt->table); index = dict_table_get_first_index_noninline(prebuilt->table);
...@@ -3035,10 +3012,12 @@ ha_innobase::scan_time() ...@@ -3035,10 +3012,12 @@ ha_innobase::scan_time()
{ {
row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
/* In the following formula we assume that scanning 10 pages /* Since MySQL seems to favor table scans too much over index
takes the same time as a disk seek: */ searches, we pretend that a sequential read takes the same time
as a random disk read, that is, we do not divide the following
return((double) (prebuilt->table->stat_clustered_index_size / 10)); by 10, which would be physically realistic. */
return((double) (prebuilt->table->stat_clustered_index_size));
} }
/************************************************************************* /*************************************************************************
...@@ -3058,7 +3037,11 @@ ha_innobase::info( ...@@ -3058,7 +3037,11 @@ ha_innobase::info(
ulong i; ulong i;
DBUG_ENTER("info"); DBUG_ENTER("info");
/* Warning: since it is not sure that MySQL calls external_lock
before calling this function, the trx field in prebuilt can be
obsolete! */
ib_table = prebuilt->table; ib_table = prebuilt->table;
if (flag & HA_STATUS_TIME) { if (flag & HA_STATUS_TIME) {
...@@ -3105,6 +3088,13 @@ ha_innobase::info( ...@@ -3105,6 +3088,13 @@ ha_innobase::info(
index->stat_n_diff_key_vals[j + 1]); index->stat_n_diff_key_vals[j + 1]);
} }
/* Since MySQL seems to favor table scans
too much over index searches, we pretend
index selectivity is 2 times better than
our estimate: */
rec_per_key = rec_per_key / 2;
if (rec_per_key == 0) { if (rec_per_key == 0) {
rec_per_key = 1; rec_per_key = 1;
} }
...@@ -3124,11 +3114,13 @@ ha_innobase::info( ...@@ -3124,11 +3114,13 @@ ha_innobase::info(
pointer and cause a seg fault. */ pointer and cause a seg fault. */
if (flag & HA_STATUS_ERRKEY) { if (flag & HA_STATUS_ERRKEY) {
ut_a(prebuilt->trx && prebuilt->trx->magic_n == TRX_MAGIC_N);
errkey = (unsigned int) row_get_mysql_key_number_for_index( errkey = (unsigned int) row_get_mysql_key_number_for_index(
(dict_index_t*) (dict_index_t*)
trx_get_error_info(prebuilt->trx)); trx_get_error_info(prebuilt->trx));
} }
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
...@@ -3148,7 +3140,9 @@ ha_innobase::check( ...@@ -3148,7 +3140,9 @@ ha_innobase::check(
{ {
row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
ulint ret; ulint ret;
ut_a(prebuilt->trx && prebuilt->trx->magic_n == TRX_MAGIC_N);
if (prebuilt->mysql_template == NULL) { if (prebuilt->mysql_template == NULL) {
/* Build the template; we will use a dummy template /* Build the template; we will use a dummy template
in index scans done in checking */ in index scans done in checking */
...@@ -3182,6 +3176,10 @@ ha_innobase::update_table_comment( ...@@ -3182,6 +3176,10 @@ ha_innobase::update_table_comment(
char* str = my_malloc(length + 550, MYF(0)); char* str = my_malloc(length + 550, MYF(0));
char* pos; char* pos;
/* Warning: since it is not sure that MySQL calls external_lock
before calling this function, the trx field in prebuilt can be
obsolete! */
if (!str) { if (!str) {
return((char*)comment); return((char*)comment);
} }
...@@ -3203,6 +3201,53 @@ ha_innobase::update_table_comment( ...@@ -3203,6 +3201,53 @@ ha_innobase::update_table_comment(
return(str); return(str);
} }
/***********************************************************************
Tells something additional to the handler about how to do things. */
int
ha_innobase::extra(
/*===============*/
/* out: 0 or error number */
enum ha_extra_function operation)
/* in: HA_EXTRA_DONT_USE_CURSOR_TO_UPDATE */
{
row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
/* Warning: since it is not sure that MySQL calls external_lock
before calling this function, the trx field in prebuilt can be
obsolete! */
switch (operation) {
case HA_EXTRA_RESET:
case HA_EXTRA_RESET_STATE:
prebuilt->read_just_key = 0;
break;
case HA_EXTRA_NO_KEYREAD:
prebuilt->read_just_key = 0;
break;
case HA_EXTRA_DONT_USE_CURSOR_TO_UPDATE:
prebuilt->in_update_remember_pos = FALSE;
break;
case HA_EXTRA_KEYREAD:
prebuilt->read_just_key = 1;
break;
default:/* Do nothing */
;
}
return(0);
}
/**********************************************************************
????????????? */
int
ha_innobase::reset(void)
/*====================*/
{
return(0);
}
/********************************************************************** /**********************************************************************
As MySQL will execute an external lock for every new table it uses when it 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 starts to process an SQL statement, we can use this function to store the
...@@ -3427,5 +3472,4 @@ ha_innobase::get_auto_increment() ...@@ -3427,5 +3472,4 @@ ha_innobase::get_auto_increment()
return(nr); return(nr);
} }
#endif /* HAVE_INNOBASE_DB */ #endif /* HAVE_INNOBASE_DB */
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