Commit 899babbd authored by unknown's avatar unknown

Many files:

  Merge InnoDB-4.0.5b: minor improvements to foreign keys, more logical data dictionary lock


sql/ha_innodb.cc:
  Merge InnoDB-4.0.5b: minor improvements to foreign keys, more logical data dictionary lock
innobase/dict/dict0crea.c:
  Merge InnoDB-4.0.5b: minor improvements to foreign keys, more logical data dictionary lock
innobase/dict/dict0dict.c:
  Merge InnoDB-4.0.5b: minor improvements to foreign keys, more logical data dictionary lock
innobase/include/log0recv.h:
  Merge InnoDB-4.0.5b: minor improvements to foreign keys, more logical data dictionary lock
innobase/include/row0mysql.h:
  Merge InnoDB-4.0.5b: minor improvements to foreign keys, more logical data dictionary lock
innobase/include/srv0srv.h:
  Merge InnoDB-4.0.5b: minor improvements to foreign keys, more logical data dictionary lock
innobase/include/trx0trx.h:
  Merge InnoDB-4.0.5b: minor improvements to foreign keys, more logical data dictionary lock
innobase/log/log0recv.c:
  Merge InnoDB-4.0.5b: minor improvements to foreign keys, more logical data dictionary lock
innobase/os/os0sync.c:
  Merge InnoDB-4.0.5b: minor improvements to foreign keys, more logical data dictionary lock
innobase/os/os0thread.c:
  Merge InnoDB-4.0.5b: minor improvements to foreign keys, more logical data dictionary lock
innobase/row/row0ins.c:
  Merge InnoDB-4.0.5b: minor improvements to foreign keys, more logical data dictionary lock
innobase/row/row0mysql.c:
  Merge InnoDB-4.0.5b: minor improvements to foreign keys, more logical data dictionary lock
innobase/row/row0purge.c:
  Merge InnoDB-4.0.5b: minor improvements to foreign keys, more logical data dictionary lock
innobase/row/row0undo.c:
  Merge InnoDB-4.0.5b: minor improvements to foreign keys, more logical data dictionary lock
innobase/row/row0upd.c:
  Merge InnoDB-4.0.5b: minor improvements to foreign keys, more logical data dictionary lock
innobase/srv/srv0srv.c:
  Merge InnoDB-4.0.5b: minor improvements to foreign keys, more logical data dictionary lock
innobase/srv/srv0start.c:
  Merge InnoDB-4.0.5b: minor improvements to foreign keys, more logical data dictionary lock
innobase/trx/trx0roll.c:
  Merge InnoDB-4.0.5b: minor improvements to foreign keys, more logical data dictionary lock
innobase/trx/trx0trx.c:
  Merge InnoDB-4.0.5b: minor improvements to foreign keys, more logical data dictionary lock
parent 58cc5007
......@@ -1041,7 +1041,7 @@ dict_create_or_check_foreign_constraint_tables(void)
que_t* graph;
ulint error;
trx_t* trx;
char* str;
char* str;
mutex_enter(&(dict_sys->mutex));
......@@ -1060,20 +1060,24 @@ dict_create_or_check_foreign_constraint_tables(void)
return(DB_SUCCESS);
}
mutex_exit(&(dict_sys->mutex));
trx = trx_allocate_for_mysql();
trx->op_info = (char *) "creating foreign key sys tables";
row_mysql_lock_data_dictionary(trx);
if (table1) {
fprintf(stderr,
"InnoDB: dropping incompletely created SYS_FOREIGN table\n");
row_drop_table_for_mysql((char *) "SYS_FOREIGN", trx, TRUE);
row_drop_table_for_mysql((char *) "SYS_FOREIGN", trx);
}
if (table2) {
fprintf(stderr,
"InnoDB: dropping incompletely created SYS_FOREIGN_COLS table\n");
row_drop_table_for_mysql((char *) "SYS_FOREIGN_COLS",trx,TRUE);
row_drop_table_for_mysql((char *) "SYS_FOREIGN_COLS", trx);
}
fprintf(stderr,
......@@ -1122,8 +1126,8 @@ dict_create_or_check_foreign_constraint_tables(void)
fprintf(stderr,
"InnoDB: dropping incompletely created SYS_FOREIGN tables\n");
row_drop_table_for_mysql((char *) "SYS_FOREIGN", trx, TRUE);
row_drop_table_for_mysql((char *) "SYS_FOREIGN_COLS",trx,TRUE);
row_drop_table_for_mysql((char *) "SYS_FOREIGN", trx);
row_drop_table_for_mysql((char *) "SYS_FOREIGN_COLS", trx);
error = DB_MUST_GET_MORE_FILE_SPACE;
}
......@@ -1132,6 +1136,8 @@ dict_create_or_check_foreign_constraint_tables(void)
trx->op_info = (char *) "";
row_mysql_unlock_data_dictionary(trx);
trx_free_for_mysql(trx);
if (error == DB_SUCCESS) {
......@@ -1139,8 +1145,6 @@ dict_create_or_check_foreign_constraint_tables(void)
"InnoDB: Foreign key constraint system tables created\n");
}
mutex_exit(&(dict_sys->mutex));
return(error);
}
......
......@@ -30,13 +30,16 @@ Created 1/8/1996 Heikki Tuuri
dict_sys_t* dict_sys = NULL; /* the dictionary system */
rw_lock_t dict_operation_lock; /* table create, drop, etc. reserve
this in X-mode, implicit or backround
this in X-mode; implicit or backround
operations purge, rollback, foreign
key checks reserve this in S-mode; we
cannot trust that MySQL protects
implicit or background operations
from dropping a table: this is our
mechanism */
a table drop since MySQL does not
know of them; therefore we need this;
NOTE: a transaction which reserves
this must keep book on the mode in
trx->dict_operation_lock_mode */
#define DICT_HEAP_SIZE 100 /* initial memory heap size when
creating a table or index object */
......@@ -182,6 +185,58 @@ dict_foreign_free(
/*==============*/
dict_foreign_t* foreign); /* in, own: foreign key struct */
/************************************************************************
Checks if the database name in two table names is the same. */
static
ibool
dict_tables_have_same_db(
/*=====================*/
/* out: TRUE if same db name */
char* name1, /* in: table name in the form dbname '/' tablename */
char* name2) /* in: table name in the form dbname '/' tablename */
{
ulint i;
for (i = 0; i < 100000; i++) {
if (name1[i] == '/' && name2[i] == '/') {
return(TRUE);
}
if (name1[i] != name2[i]) {
return(FALSE);
}
}
ut_a(0);
return(FALSE);
}
/************************************************************************
Return the end of table name where we have removed dbname and '/'. */
static
char*
dict_remove_db_name(
/*================*/
/* out: table name */
char* name) /* in: table name in the form dbname '/' tablename */
{
ulint i;
for (i = 0; i < 100000 ; i++) {
if (name[i] == '/') {
return(name + i + 1);
}
}
ut_a(0);
return(NULL);
}
/************************************************************************
Reserves the dictionary system mutex for MySQL. */
......@@ -1926,7 +1981,8 @@ dict_scan_col(
old_ptr = ptr;
while (!isspace(*ptr) && *ptr != ',' && *ptr != ')' && *ptr != '`') {
while (!isspace(*ptr) && *ptr != ',' && *ptr != ')' && *ptr != '`'
&& *ptr != '\0') {
ptr++;
}
......@@ -2000,7 +2056,7 @@ dict_scan_table_name(
old_ptr = ptr;
while (!isspace(*ptr) && *ptr != '(' && *ptr != '`') {
while (!isspace(*ptr) && *ptr != '(' && *ptr != '`' && *ptr != '\0') {
if (*ptr == '.') {
dot_ptr = ptr;
}
......@@ -2023,17 +2079,28 @@ dict_scan_table_name(
}
#ifdef __WIN__
ut_cpy_in_lower_case(second_table_name + i, old_ptr,
ptr - old_ptr);
ptr - old_ptr);
#else
ut_memcpy(second_table_name + i, old_ptr, ptr - old_ptr);
if (srv_lower_case_table_names) {
ut_cpy_in_lower_case(second_table_name + i, old_ptr,
ptr - old_ptr);
} else {
ut_memcpy(second_table_name + i, old_ptr,
ptr - old_ptr);
}
#endif
second_table_name[i + (ptr - old_ptr)] = '\0';
} else {
#ifdef __WIN__
ut_cpy_in_lower_case(second_table_name, old_ptr,
ptr - old_ptr);
ptr - old_ptr);
#else
ut_memcpy(second_table_name, old_ptr, ptr - old_ptr);
if (srv_lower_case_table_names) {
ut_cpy_in_lower_case(second_table_name, old_ptr,
ptr - old_ptr);
} else {
ut_memcpy(second_table_name, old_ptr, ptr - old_ptr);
}
#endif
second_table_name[dot_ptr - old_ptr] = '/';
second_table_name[ptr - old_ptr] = '\0';
......@@ -2050,6 +2117,44 @@ dict_scan_table_name(
return(ptr);
}
/*************************************************************************
Skips one 'word', like an id. For the lexical definition of 'word', see the
code below. */
static
char*
dict_skip_word(
/*===========*/
/* out: scanned to */
char* ptr, /* in: scanned to */
ibool* success)/* out: TRUE if success, FALSE if just spaces left in
string */
{
*success = FALSE;
while (isspace(*ptr)) {
ptr++;
}
if (*ptr == '\0') {
return(ptr);
}
if (*ptr == '`') {
ptr++;
}
while (!isspace(*ptr) && *ptr != ',' && *ptr != '(' && *ptr != '`'
&& *ptr != '\0') {
ptr++;
}
*success = TRUE;
return(ptr);
}
/*************************************************************************
Returns the number of opening brackets '(' subtracted by the number
of closing brackets ')' between string and ptr. */
......@@ -2119,7 +2224,6 @@ dict_create_foreign_constraints(
if (table == NULL) {
return(DB_ERROR);
}
loop:
ptr = dict_scan_to(ptr, (char *) "FOREIGN");
......@@ -2148,7 +2252,19 @@ dict_create_foreign_constraints(
ptr = dict_accept(ptr, (char *) "(", &success);
if (!success) {
goto loop;
/* MySQL allows also an index id before the '('; we
skip it */
ptr = dict_skip_word(ptr, &success);
if (!success) {
return(DB_CANNOT_ADD_CONSTRAINT);
}
ptr = dict_accept(ptr, (char *) "(", &success);
if (!success) {
return(DB_CANNOT_ADD_CONSTRAINT);
}
}
i = 0;
......@@ -2223,6 +2339,7 @@ dict_create_foreign_constraints(
if (!success) {
dict_foreign_free(foreign);
return(DB_CANNOT_ADD_CONSTRAINT);
}
......@@ -2236,6 +2353,7 @@ dict_create_foreign_constraints(
if (!success) {
dict_foreign_free(foreign);
return(DB_CANNOT_ADD_CONSTRAINT);
}
......@@ -2263,14 +2381,20 @@ dict_create_foreign_constraints(
ptr = dict_accept(ptr, "DELETE", &success);
if (!success) {
dict_foreign_free(foreign);
return(DB_CANNOT_ADD_CONSTRAINT);
}
ptr = dict_accept(ptr, "RESTRICT", &success);
if (success) {
goto try_find_index;
}
ptr = dict_accept(ptr, "CASCADE", &success);
if (success) {
foreign->type = DICT_FOREIGN_ON_DELETE_CASCADE;
goto try_find_index;
......@@ -2279,32 +2403,47 @@ dict_create_foreign_constraints(
ptr = dict_accept(ptr, "SET", &success);
if (!success) {
goto try_find_index;
dict_foreign_free(foreign);
return(DB_CANNOT_ADD_CONSTRAINT);
}
ptr = dict_accept(ptr, "NULL", &success);
if (success) {
for (j = 0; j < foreign->n_fields; j++) {
if ((dict_index_get_nth_type(
if (!success) {
dict_foreign_free(foreign);
return(DB_CANNOT_ADD_CONSTRAINT);
}
for (j = 0; j < foreign->n_fields; j++) {
if ((dict_index_get_nth_type(
foreign->foreign_index, j)->prtype)
& DATA_NOT_NULL) {
/* It is not sensible to define SET NULL
if the column is not allowed to be NULL! */
/* It is not sensible to define SET NULL
if the column is not allowed to be NULL! */
dict_foreign_free(foreign);
return(DB_CANNOT_ADD_CONSTRAINT);
}
dict_foreign_free(foreign);
return(DB_CANNOT_ADD_CONSTRAINT);
}
}
foreign->type = DICT_FOREIGN_ON_DELETE_SET_NULL;
foreign->type = DICT_FOREIGN_ON_DELETE_SET_NULL;
try_find_index:
/* We check that there are no superfluous words like 'ON UPDATE ...'
which we do not support yet. */
goto try_find_index;
ptr = dict_accept(ptr, (char *) "ON", &success);
if (success) {
dict_foreign_free(foreign);
return(DB_CANNOT_ADD_CONSTRAINT);
}
try_find_index:
/* Try to find an index which contains the columns as the first fields
and in the right order, and the types are the same as in
foreign->foreign_index */
......@@ -2351,6 +2490,7 @@ dict_create_foreign_constraints(
referenced_table->referenced_list,
foreign);
}
goto loop;
}
......@@ -2849,6 +2989,14 @@ dict_update_statistics_low(
ulint size;
ulint sum_of_index_sizes = 0;
/* If we have set a high innodb_force_recovery level, do not calculate
statistics, as a badly corrupted index can cause a crash in it. */
if (srv_force_recovery >= SRV_FORCE_NO_IBUF_MERGE) {
return;
}
/* Find out the sizes of the indexes and how many different values
for the key they approximately have */
......@@ -3152,16 +3300,25 @@ dict_print_info_on_foreign_keys_in_create_format(
}
}
buf2 += sprintf(buf2, ") REFERENCES `%s` (",
if (dict_tables_have_same_db(table->name,
foreign->referenced_table_name)) {
/* Do not print the database name of the referenced
table */
buf2 += sprintf(buf2, ") REFERENCES `%s` (",
dict_remove_db_name(
foreign->referenced_table_name));
} else {
buf2 += sprintf(buf2, ") REFERENCES `%s` (",
foreign->referenced_table_name);
/* Change the '/' in the table name to '.' */
/* Change the '/' in the table name to '.' */
for (i = ut_strlen(buf); i > 0; i--) {
if (buf[i] == '/') {
for (i = ut_strlen(buf); i > 0; i--) {
if (buf[i] == '/') {
buf[i] = '.';
buf[i] = '.';
break;
break;
}
}
}
......
......@@ -334,6 +334,7 @@ extern ibool recv_no_ibuf_operations;
extern ibool recv_needed_recovery;
extern ibool recv_is_making_a_backup;
extern ulint recv_max_parsed_page_no;
/* Size of the parsing buffer; it must accommodate RECV_SCAN_SIZE many
times! */
......
......@@ -230,18 +230,35 @@ row_update_cascade_for_mysql(
or set null operation */
dict_table_t* table); /* in: table where we do the operation */
/*************************************************************************
Locks the data dictionary exclusively for performing a table create
operation. */
Locks the data dictionary exclusively for performing a table create or other
data dictionary modification operation. */
void
row_mysql_lock_data_dictionary(void);
/*================================*/
row_mysql_lock_data_dictionary(
/*===========================*/
trx_t* trx); /* in: transaction */
/*************************************************************************
Unlocks the data dictionary exclusively lock. */
Unlocks the data dictionary exclusive lock. */
void
row_mysql_unlock_data_dictionary(void);
/*==================================*/
row_mysql_unlock_data_dictionary(
/*=============================*/
trx_t* trx); /* in: transaction */
/*************************************************************************
Locks the data dictionary in shared mode from modifications, for performing
foreign key check, rollback, or other operation invisible to MySQL. */
void
row_mysql_freeze_data_dictionary(
/*=============================*/
trx_t* trx); /* in: transaction */
/*************************************************************************
Unlocks the data dictionary shared lock. */
void
row_mysql_unfreeze_data_dictionary(
/*===============================*/
trx_t* trx); /* in: transaction */
/*************************************************************************
Does a table creation operation for MySQL. If the name of the created
table ends to characters INNODB_MONITOR, then this also starts
......@@ -310,11 +327,9 @@ output by the master thread. */
int
row_drop_table_for_mysql(
/*=====================*/
/* out: error code or DB_SUCCESS */
char* name, /* in: table name */
trx_t* trx, /* in: transaction handle */
ibool has_dict_mutex);/* in: TRUE if the caller already owns the
dictionary system mutex */
/* out: error code or DB_SUCCESS */
char* name, /* in: table name */
trx_t* trx); /* in: transaction handle */
/*************************************************************************
Drops a database for MySQL. */
......
......@@ -28,6 +28,9 @@ extern os_event_t srv_lock_timeout_thread_event;
at a time */
#define SRV_AUTO_EXTEND_INCREMENT (8 * ((1024 * 1024) / UNIV_PAGE_SIZE))
/* This is set to TRUE if the MySQL user has set it in MySQL */
extern ibool srv_lower_case_table_names;
/* Server parameters which are read from the initfile */
extern char* srv_data_home;
......
......@@ -386,9 +386,10 @@ struct trx_struct{
/* how many tables the current SQL
statement uses, except those
in consistent read */
ibool has_dict_operation_lock;
/* TRUE if the trx currently holds
an s-lock on dict_operation_lock */
ibool dict_operation_lock_mode;
/* 0, RW_S_LATCH, or RW_X_LATCH:
the latch mode trx currently holds
on dict_operation_lock */
ibool has_search_latch;
/* TRUE if this trx has latched the
search system latch in S-mode */
......@@ -427,34 +428,6 @@ struct trx_struct{
mysql_trx_list; /* list of transactions created for
MySQL */
/*------------------------------*/
mutex_t undo_mutex; /* mutex protecting the fields in this
section (down to undo_no_arr), EXCEPT
last_sql_stat_start, which can be
accessed only when we know that there
cannot be any activity in the undo
logs! */
dulint undo_no; /* next undo log record number to
assign */
trx_savept_t last_sql_stat_start;
/* undo_no when the last sql statement
was started: in case of an error, trx
is rolled back down to this undo
number; see note at undo_mutex! */
trx_rseg_t* rseg; /* rollback segment assigned to the
transaction, or NULL if not assigned
yet */
trx_undo_t* insert_undo; /* pointer to the insert undo log, or
NULL if no inserts performed yet */
trx_undo_t* update_undo; /* pointer to the update undo log, or
NULL if no update performed yet */
dulint roll_limit; /* least undo number to undo during
a rollback */
ulint pages_undone; /* number of undo log pages undone
since the last undo log truncation */
trx_undo_arr_t* undo_no_arr; /* array of undo numbers of undo log
records which are currently processed
by a rollback operation */
/*------------------------------*/
ulint error_state; /* 0 if no error, otherwise error
number */
void* error_info; /* if the error number indicates a
......@@ -508,6 +481,34 @@ struct trx_struct{
/*------------------------------*/
mem_heap_t* read_view_heap; /* memory heap for the read view */
read_view_t* read_view; /* consistent read view or NULL */
/*------------------------------*/
mutex_t undo_mutex; /* mutex protecting the fields in this
section (down to undo_no_arr), EXCEPT
last_sql_stat_start, which can be
accessed only when we know that there
cannot be any activity in the undo
logs! */
dulint undo_no; /* next undo log record number to
assign */
trx_savept_t last_sql_stat_start;
/* undo_no when the last sql statement
was started: in case of an error, trx
is rolled back down to this undo
number; see note at undo_mutex! */
trx_rseg_t* rseg; /* rollback segment assigned to the
transaction, or NULL if not assigned
yet */
trx_undo_t* insert_undo; /* pointer to the insert undo log, or
NULL if no inserts performed yet */
trx_undo_t* update_undo; /* pointer to the update undo log, or
NULL if no update performed yet */
dulint roll_limit; /* least undo number to undo during
a rollback */
ulint pages_undone; /* number of undo log pages undone
since the last undo log truncation */
trx_undo_arr_t* undo_no_arr; /* array of undo numbers of undo log
records which are currently processed
by a rollback operation */
};
#define TRX_MAX_N_THREADS 32 /* maximum number of concurrent
......
......@@ -69,6 +69,8 @@ ulint recv_previous_parsed_rec_type = 999999;
ulint recv_previous_parsed_rec_offset = 0;
ulint recv_previous_parsed_rec_is_multi = 0;
ulint recv_max_parsed_page_no = 0;
/************************************************************
Creates the recovery system. */
......@@ -141,7 +143,13 @@ recv_sys_empty_hash(void)
/*=====================*/
{
ut_ad(mutex_own(&(recv_sys->mutex)));
ut_a(recv_sys->n_addrs == 0);
if (recv_sys->n_addrs != 0) {
fprintf(stderr,
"InnoDB: Error: %lu pages with log records were left unprocessed!\n"
"InnoDB: Maximum page number with log records on it %lu\n",
recv_sys->n_addrs, recv_max_parsed_page_no);
ut_a(0);
}
hash_table_free(recv_sys->addr_hash);
mem_heap_empty(recv_sys->heap);
......@@ -1361,6 +1369,12 @@ recv_apply_log_recs_for_backup(
n_pages_total += file_sizes[i];
}
if (recv_max_parsed_page_no >= n_pages_total) {
printf(
"InnoDB: Error: tablespace size %lu pages, but a log record on page %lu!\n",
n_pages_total, recv_max_parsed_page_no);
}
printf(
"InnoDB: Starting an apply batch of log records to the database...\n"
"InnoDB: Progress in percents: ");
......@@ -1701,6 +1715,10 @@ recv_parse_log_rec(
return(0);
}
if (*page_no > recv_max_parsed_page_no) {
recv_max_parsed_page_no = *page_no;
}
return(new_ptr - ptr);
}
......
......@@ -66,8 +66,12 @@ os_event_create(
event = ut_malloc(sizeof(struct os_event_struct));
os_fast_mutex_init(&(event->os_mutex));
pthread_cond_init(&(event->cond_var), NULL);
#if defined(UNIV_HOTBACKUP) && defined(UNIV_HPUX10)
pthread_cond_init(&(event->cond_var), pthread_condattr_default);
#else
pthread_cond_init(&(event->cond_var), NULL);
#endif
event->is_set = FALSE;
return(event);
......@@ -440,9 +444,13 @@ os_fast_mutex_init(
ut_a(fast_mutex);
InitializeCriticalSection((LPCRITICAL_SECTION) fast_mutex);
#else
#if defined(UNIV_HOTBACKUP) && defined(UNIV_HPUX10)
pthread_mutex_init(fast_mutex, pthread_mutexattr_default);
#else
pthread_mutex_init(fast_mutex, MY_MUTEX_INIT_FAST);
#endif
#endif
}
/**************************************************************
......
......@@ -126,8 +126,10 @@ os_thread_create(
os_thread_t pthread;
pthread_attr_t attr;
#if !(defined(UNIV_HOTBACKUP) && defined(UNIV_HPUX10))
pthread_attr_init(&attr);
#endif
#ifdef UNIV_AIX
/* We must make sure a thread stack is at least 32 kB, otherwise
InnoDB might crash; we do not know if the default stack size on
......@@ -142,16 +144,21 @@ os_thread_create(
exit(1);
}
#endif
ret = pthread_create(&pthread, &attr, start_f, arg);
#if defined(UNIV_HOTBACKUP) && defined(UNIV_HPUX10)
ret = pthread_create(&pthread, pthread_attr_default, start_f, arg);
#else
ret = pthread_create(&pthread, &attr, start_f, arg);
#endif
if (ret) {
fprintf(stderr,
"InnoDB: Error: pthread_create returned %d\n", ret);
exit(1);
}
#if !(defined(UNIV_HOTBACKUP) && defined(UNIV_HPUX10))
pthread_attr_destroy(&attr);
#endif
if (srv_set_thread_priorities) {
my_pthread_setprio(pthread, srv_query_thread_priority);
......
......@@ -643,7 +643,7 @@ row_ins_check_foreign_constraint(
run_again:
ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_SHARED));
err = DB_SUCCESS;
if (thr_get_trx(thr)->check_foreigns == FALSE) {
......@@ -880,21 +880,16 @@ row_ins_check_foreign_constraints(
trx);
}
if (!trx->has_dict_operation_lock) {
if (0 == trx->dict_operation_lock_mode) {
got_s_lock = TRUE;
rw_lock_s_lock(&dict_operation_lock);
trx->has_dict_operation_lock = TRUE;
row_mysql_freeze_data_dictionary(trx);
}
err = row_ins_check_foreign_constraint(TRUE, foreign,
table, index, entry, thr);
if (got_s_lock) {
rw_lock_s_unlock(&dict_operation_lock);
trx->has_dict_operation_lock = FALSE;
row_mysql_unfreeze_data_dictionary(trx);
}
if (err != DB_SUCCESS) {
......
......@@ -1134,32 +1134,73 @@ row_mysql_recover_tmp_table(
}
/*************************************************************************
Locks the data dictionary exclusively for performing a table create
operation. */
Locks the data dictionary in shared mode from modifications, for performing
foreign key check, rollback, or other operation invisible to MySQL. */
void
row_mysql_lock_data_dictionary(void)
/*================================*/
row_mysql_freeze_data_dictionary(
/*=============================*/
trx_t* trx) /* in: transaction */
{
ut_a(trx->dict_operation_lock_mode == 0);
rw_lock_s_lock(&dict_operation_lock);
trx->dict_operation_lock_mode = RW_S_LATCH;
}
/*************************************************************************
Unlocks the data dictionary shared lock. */
void
row_mysql_unfreeze_data_dictionary(
/*===============================*/
trx_t* trx) /* in: transaction */
{
ut_a(trx->dict_operation_lock_mode == RW_S_LATCH);
rw_lock_s_unlock(&dict_operation_lock);
trx->dict_operation_lock_mode = 0;
}
/*************************************************************************
Locks the data dictionary exclusively for performing a table create or other
data dictionary modification operation. */
void
row_mysql_lock_data_dictionary(
/*===========================*/
trx_t* trx) /* in: transaction */
{
ut_a(trx->dict_operation_lock_mode == 0);
/* Serialize data dictionary operations with dictionary mutex:
no deadlocks or lock waits can occur then in these operations */
rw_lock_x_lock(&dict_operation_lock);
trx->dict_operation_lock_mode = RW_X_LATCH;
mutex_enter(&(dict_sys->mutex));
}
/*************************************************************************
Unlocks the data dictionary exclusively lock. */
Unlocks the data dictionary exclusive lock. */
void
row_mysql_unlock_data_dictionary(void)
/*==================================*/
row_mysql_unlock_data_dictionary(
/*=============================*/
trx_t* trx) /* in: transaction */
{
ut_a(trx->dict_operation_lock_mode == RW_X_LATCH);
/* Serialize data dictionary operations with dictionary mutex:
no deadlocks can occur then in these operations */
mutex_exit(&(dict_sys->mutex));
rw_lock_x_unlock(&dict_operation_lock);
trx->dict_operation_lock_mode = 0;
}
/*************************************************************************
......@@ -1183,6 +1224,7 @@ row_create_table_for_mysql(
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
ut_ad(trx->dict_operation_lock_mode == RW_X_LATCH);
ut_ad(mutex_own(&(dict_sys->mutex)));
if (srv_created_new_raw) {
......@@ -1331,7 +1373,7 @@ row_create_table_for_mysql(
fprintf(stderr,
"InnoDB: Warning: cannot create table %s because tablespace full\n",
table->name);
row_drop_table_for_mysql(table->name, trx, TRUE);
row_drop_table_for_mysql(table->name, trx);
} else {
ut_a(err == DB_DUPLICATE_KEY);
......@@ -1425,7 +1467,7 @@ row_create_index_for_mysql(
trx_general_rollback_for_mysql(trx, FALSE, NULL);
row_drop_table_for_mysql(index->table_name, trx, TRUE);
row_drop_table_for_mysql(index->table_name, trx);
trx->error_state = DB_SUCCESS;
}
......@@ -1499,7 +1541,7 @@ row_table_add_foreign_constraints(
trx_general_rollback_for_mysql(trx, FALSE, NULL);
row_drop_table_for_mysql(name, trx, TRUE);
row_drop_table_for_mysql(name, trx);
trx->error_state = DB_SUCCESS;
}
......@@ -1530,7 +1572,7 @@ row_drop_table_for_mysql_in_background(
name); */
/* Drop the table in InnoDB */
error = row_drop_table_for_mysql(name, trx, FALSE);
error = row_drop_table_for_mysql(name, trx);
if (error != DB_SUCCESS) {
fprintf(stderr,
......@@ -1689,9 +1731,7 @@ row_drop_table_for_mysql(
/*=====================*/
/* out: error code or DB_SUCCESS */
char* name, /* in: table name */
trx_t* trx, /* in: transaction handle */
ibool has_dict_mutex) /* in: TRUE if the caller already owns the
dictionary system mutex */
trx_t* trx) /* in: transaction handle */
{
dict_table_t* table;
que_thr_t* thr;
......@@ -1703,6 +1743,7 @@ row_drop_table_for_mysql(
ulint namelen;
ulint keywordlen;
ulint rounds = 0;
ibool locked_dictionary = FALSE;
char buf[10000];
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
......@@ -1846,12 +1887,13 @@ row_drop_table_for_mysql(
/* Serialize data dictionary operations with dictionary mutex:
no deadlocks can occur then in these operations */
if (!has_dict_mutex) {
if (trx->dict_operation_lock_mode != RW_X_LATCH) {
/* Prevent foreign key checks etc. while we are dropping the
table */
rw_lock_x_lock(&dict_operation_lock);
mutex_enter(&(dict_sys->mutex));
row_mysql_lock_data_dictionary(trx);
locked_dictionary = TRUE;
}
ut_ad(mutex_own(&(dict_sys->mutex)));
......@@ -1948,9 +1990,8 @@ row_drop_table_for_mysql(
}
funct_exit:
if (!has_dict_mutex) {
mutex_exit(&(dict_sys->mutex));
rw_lock_x_unlock(&dict_operation_lock);
if (locked_dictionary) {
row_mysql_unlock_data_dictionary(trx);
}
que_graph_free(graph);
......@@ -1986,8 +2027,7 @@ row_drop_database_for_mysql(
trx_start_if_not_started(trx);
loop:
rw_lock_x_lock(&dict_operation_lock);
mutex_enter(&(dict_sys->mutex));
row_mysql_lock_data_dictionary(trx);
while ((table_name = dict_get_first_table_name_in_db(name))) {
ut_a(memcmp(table_name, name, strlen(name)) == 0);
......@@ -2000,8 +2040,7 @@ row_drop_database_for_mysql(
the table */
if (table->n_mysql_handles_opened > 0) {
mutex_exit(&(dict_sys->mutex));
rw_lock_x_unlock(&dict_operation_lock);
row_mysql_unlock_data_dictionary(trx);
ut_print_timestamp(stderr);
fprintf(stderr,
......@@ -2016,7 +2055,7 @@ row_drop_database_for_mysql(
goto loop;
}
err = row_drop_table_for_mysql(table_name, trx, TRUE);
err = row_drop_table_for_mysql(table_name, trx);
mem_free(table_name);
......@@ -2028,8 +2067,7 @@ row_drop_database_for_mysql(
}
}
mutex_exit(&(dict_sys->mutex));
rw_lock_x_unlock(&dict_operation_lock);
row_mysql_unlock_data_dictionary(trx);
trx_commit_for_mysql(trx);
......@@ -2166,8 +2204,7 @@ row_rename_table_for_mysql(
/* Serialize data dictionary operations with dictionary mutex:
no deadlocks can occur then in these operations */
rw_lock_x_lock(&dict_operation_lock);
mutex_enter(&(dict_sys->mutex));
row_mysql_lock_data_dictionary(trx);
table = dict_table_get_low(old_name);
......@@ -2249,8 +2286,7 @@ row_rename_table_for_mysql(
}
}
funct_exit:
mutex_exit(&(dict_sys->mutex));
rw_lock_x_unlock(&dict_operation_lock);
row_mysql_unlock_data_dictionary(trx);
que_graph_free(graph);
......
......@@ -24,6 +24,7 @@ Created 3/14/1997 Heikki Tuuri
#include "row0row.h"
#include "row0upd.h"
#include "row0vers.h"
#include "row0mysql.h"
#include "log0log.h"
/************************************************************************
......@@ -454,8 +455,8 @@ ibool
row_purge_parse_undo_rec(
/*=====================*/
/* out: TRUE if purge operation required:
NOTE that then the CALLER must s-unlock
dict_operation_lock! */
NOTE that then the CALLER must unfreeze
data dictionary! */
purge_node_t* node, /* in: row undo node */
ibool* updated_extern,
/* out: TRUE if an externally stored field
......@@ -464,6 +465,7 @@ row_purge_parse_undo_rec(
{
dict_index_t* clust_index;
byte* ptr;
trx_t* trx;
dulint undo_no;
dulint table_id;
dulint trx_id;
......@@ -473,6 +475,8 @@ row_purge_parse_undo_rec(
ulint cmpl_info;
ut_ad(node && thr);
trx = thr_get_trx(thr);
ptr = trx_undo_rec_get_pars(node->undo_rec, &type, &cmpl_info,
updated_extern, &undo_no, &table_id);
......@@ -498,17 +502,18 @@ row_purge_parse_undo_rec(
/* Prevent DROP TABLE etc. from running when we are doing the purge
for this row */
rw_lock_s_lock(&dict_operation_lock);
mutex_enter(&(dict_sys->mutex));
row_mysql_freeze_data_dictionary(trx);
node->table = dict_table_get_on_id_low(table_id, thr_get_trx(thr));
mutex_enter(&(dict_sys->mutex));
mutex_exit(&(dict_sys->mutex));
node->table = dict_table_get_on_id_low(table_id, thr_get_trx(thr));
mutex_exit(&(dict_sys->mutex));
if (node->table == NULL) {
/* The table has been dropped: no need to do purge */
rw_lock_s_unlock(&dict_operation_lock);
row_mysql_unfreeze_data_dictionary(trx);
return(FALSE);
}
......@@ -518,7 +523,7 @@ row_purge_parse_undo_rec(
if (clust_index == NULL) {
/* The table was corrupt in the data dictionary */
rw_lock_s_unlock(&dict_operation_lock);
row_mysql_unfreeze_data_dictionary(trx);
return(FALSE);
}
......@@ -556,9 +561,12 @@ row_purge(
dulint roll_ptr;
ibool purge_needed;
ibool updated_extern;
trx_t* trx;
ut_ad(node && thr);
trx = thr_get_trx(thr);
node->undo_rec = trx_purge_fetch_next_rec(&roll_ptr,
&(node->reservation),
node->heap);
......@@ -577,8 +585,8 @@ row_purge(
} else {
purge_needed = row_purge_parse_undo_rec(node, &updated_extern,
thr);
/* If purge_needed == TRUE, we must also remember to unlock
dict_operation_lock! */
/* If purge_needed == TRUE, we must also remember to unfreeze
data dictionary! */
}
if (purge_needed) {
......@@ -600,7 +608,7 @@ row_purge(
btr_pcur_close(&(node->pcur));
}
rw_lock_s_unlock(&dict_operation_lock);
row_mysql_unfreeze_data_dictionary(trx);
}
/* Do some cleanup */
......
......@@ -24,6 +24,7 @@ Created 1/8/1997 Heikki Tuuri
#include "row0row.h"
#include "row0uins.h"
#include "row0umod.h"
#include "row0mysql.h"
#include "srv0srv.h"
/* How to undo row operations?
......@@ -204,6 +205,7 @@ row_undo(
ulint err;
trx_t* trx;
dulint roll_ptr;
ibool froze_data_dict = FALSE;
ut_ad(node && thr);
......@@ -256,13 +258,13 @@ row_undo(
/* Prevent DROP TABLE etc. while we are rolling back this row.
If we are doing a TABLE CREATE or some other dictionary operation,
then we already have dict_operation_lock locked in x-mode. Do not
try to lock again in s-mode, because that would cause a hang.
TODO: keep track when trx exactly has the latch locked!!!
TODO: trx->dict_operation tells it only in some cases!!! */
if (!trx->dict_operation) {
rw_lock_s_lock(&dict_operation_lock);
try to lock again in s-mode, because that would cause a hang. */
if (trx->dict_operation_lock_mode == 0) {
row_mysql_freeze_data_dictionary(trx);
froze_data_dict = TRUE;
}
if (node->state == UNDO_NODE_INSERT) {
......@@ -275,9 +277,9 @@ row_undo(
err = row_undo_mod(node, thr);
}
if (!trx->dict_operation) {
if (froze_data_dict) {
rw_lock_s_unlock(&dict_operation_lock);
row_mysql_unfreeze_data_dictionary(trx);
}
/* Do some cleanup */
......
......@@ -89,14 +89,16 @@ row_upd_index_is_referenced(
{
dict_table_t* table = index->table;
dict_foreign_t* foreign;
ibool froze_data_dict = FALSE;
if (!UT_LIST_GET_FIRST(table->referenced_list)) {
return(FALSE);
}
if (!trx->has_dict_operation_lock) {
rw_lock_s_lock(&dict_operation_lock);
if (trx->dict_operation_lock_mode == 0) {
row_mysql_freeze_data_dictionary(trx);
froze_data_dict = TRUE;
}
foreign = UT_LIST_GET_FIRST(table->referenced_list);
......@@ -104,8 +106,8 @@ row_upd_index_is_referenced(
while (foreign) {
if (foreign->referenced_index == index) {
if (!trx->has_dict_operation_lock) {
rw_lock_s_unlock(&dict_operation_lock);
if (froze_data_dict) {
row_mysql_unfreeze_data_dictionary(trx);
}
return(TRUE);
......@@ -114,8 +116,8 @@ row_upd_index_is_referenced(
foreign = UT_LIST_GET_NEXT(referenced_list, foreign);
}
if (!trx->has_dict_operation_lock) {
rw_lock_s_unlock(&dict_operation_lock);
if (froze_data_dict) {
row_mysql_unfreeze_data_dictionary(trx);
}
return(FALSE);
......@@ -162,12 +164,10 @@ row_upd_check_references_constraints(
mtr_start(mtr);
if (!trx->has_dict_operation_lock) {
if (trx->dict_operation_lock_mode == 0) {
got_s_lock = TRUE;
rw_lock_s_lock(&dict_operation_lock);
trx->has_dict_operation_lock = TRUE;
row_mysql_freeze_data_dictionary(trx);
}
foreign = UT_LIST_GET_FIRST(table->referenced_list);
......@@ -211,10 +211,7 @@ row_upd_check_references_constraints(
if (err != DB_SUCCESS) {
if (got_s_lock) {
rw_lock_s_unlock(
&dict_operation_lock);
trx->has_dict_operation_lock
= FALSE;
row_mysql_unfreeze_data_dictionary(trx);
}
mem_heap_free(heap);
......@@ -227,8 +224,7 @@ row_upd_check_references_constraints(
}
if (got_s_lock) {
rw_lock_s_unlock(&dict_operation_lock);
trx->has_dict_operation_lock = FALSE;
row_mysql_unfreeze_data_dictionary(trx);
}
mem_heap_free(heap);
......
......@@ -51,6 +51,10 @@ Created 10/8/1995 Heikki Tuuri
#include "srv0start.h"
#include "row0mysql.h"
/* This is set to TRUE if the MySQL user has set it in MySQL; currently
affects only FOREIGN KEY definition parsing */
ibool srv_lower_case_table_names = FALSE;
/* Buffer which can be used in printing fatal error messages */
char srv_fatal_errbuf[5000];
......@@ -2064,6 +2068,7 @@ srv_suspend_mysql_thread(
os_event_t event;
double wait_time;
trx_t* trx;
ibool had_dict_lock = FALSE;
ut_ad(!mutex_own(&kernel_mutex));
......@@ -2107,18 +2112,22 @@ srv_suspend_mysql_thread(
srv_conc_force_exit_innodb(thr_get_trx(thr));
/* Release possible foreign key check latch */
if (trx->has_dict_operation_lock) {
if (trx->dict_operation_lock_mode == RW_S_LATCH) {
had_dict_lock = TRUE;
rw_lock_s_unlock(&dict_operation_lock);
row_mysql_unfreeze_data_dictionary(trx);
}
ut_a(trx->dict_operation_lock_mode == 0);
/* Wait for the release */
os_event_wait(event);
if (trx->has_dict_operation_lock) {
if (had_dict_lock) {
rw_lock_s_lock(&dict_operation_lock);
row_mysql_freeze_data_dictionary(trx);
}
/* Return back inside InnoDB */
......
......@@ -1380,7 +1380,7 @@ innobase_start_or_create_for_mysql(void)
if (0 != os_fast_mutex_trylock(&srv_os_test_mutex)) {
fprintf(stderr,
"InnoDB: Error: pthread_mutex_trylock returns an unexpected value on\n"
"InnoDB: success! Cannot continue.\n");
"InnoDB: success! Cannot continue.\n");
exit(1);
}
......@@ -1390,11 +1390,17 @@ innobase_start_or_create_for_mysql(void)
os_fast_mutex_unlock(&srv_os_test_mutex);
if (srv_print_verbose_log)
{
ut_print_timestamp(stderr);
fprintf(stderr, " InnoDB: Started\n");
if (srv_print_verbose_log) {
ut_print_timestamp(stderr);
fprintf(stderr, " InnoDB: Started\n");
}
if (srv_force_recovery > 0) {
fprintf(stderr,
"InnoDB: !!! innodb_force_recovery is set to %lu !!!\n",
srv_force_recovery);
}
return((int) DB_SUCCESS);
}
......
......@@ -254,7 +254,7 @@ trx_rollback_or_clean_all_without_sess(void)
mutex_exit(&kernel_mutex);
if (trx->dict_operation) {
mutex_enter(&(dict_sys->mutex));
row_mysql_lock_data_dictionary(trx);
}
que_run_threads(thr);
......@@ -290,14 +290,14 @@ trx_rollback_or_clean_all_without_sess(void)
fprintf(stderr,
"InnoDB: Table found: dropping table %s in recovery\n", table->name);
err = row_drop_table_for_mysql(table->name, trx,
TRUE);
err = row_drop_table_for_mysql(table->name, trx);
ut_a(err == (int) DB_SUCCESS);
}
}
if (trx->dict_operation) {
mutex_exit(&(dict_sys->mutex));
row_mysql_unlock_data_dictionary(trx);
}
fprintf(stderr, "InnoDB: Rolling back of trx id %lu %lu completed\n",
......
......@@ -134,7 +134,7 @@ trx_create(
trx->lock_heap = mem_heap_create_in_buffer(256);
UT_LIST_INIT(trx->trx_locks);
trx->has_dict_operation_lock = FALSE;
trx->dict_operation_lock_mode = 0;
trx->has_search_latch = FALSE;
trx->search_latch_timeout = BTR_SEA_TIMEOUT;
......@@ -261,6 +261,8 @@ trx_free(
ut_a(!trx->has_search_latch);
ut_a(!trx->auto_inc_lock);
ut_a(trx->dict_operation_lock_mode == 0);
if (trx->lock_heap) {
mem_heap_free(trx->lock_heap);
}
......
......@@ -1153,15 +1153,15 @@ ha_innobase::open(
ib_table = dict_table_get_and_increment_handle_count(
norm_name, NULL);
if (NULL == ib_table) {
sql_print_error("InnoDB error:\n\
Cannot find table %s from the internal data dictionary\n\
of InnoDB though the .frm file for the table exists. Maybe you\n\
have deleted and recreated InnoDB data files but have forgotten\n\
to delete the corresponding .frm files of InnoDB tables, or you\n\
have moved .frm files to another database?\n\
Look from section 15.1 of http://www.innodb.com/ibman.html\n\
how you can resolve the problem.\n",
ut_print_timestamp(stderr);
fprintf(stderr, " InnoDB error:\n"
"Cannot find table %s from the internal data dictionary\n"
"of InnoDB though the .frm file for the table exists. Maybe you\n"
"have deleted and recreated InnoDB data files but have forgotten\n"
"to delete the corresponding .frm files of InnoDB tables, or you\n"
"have moved .frm files to another database?\n"
"Look from section 15.1 of http://www.innodb.com/ibman.html\n"
"how you can resolve the problem.\n",
norm_name);
free_share(share);
......@@ -2961,16 +2961,21 @@ ha_innobase::create(
trx->check_unique_secondary = FALSE;
}
if (lower_case_table_names) {
srv_lower_case_table_names = TRUE;
} else {
srv_lower_case_table_names = FALSE;
}
fn_format(name2, name, "", "",2); // Remove the .frm extension
normalize_table_name(norm_name, name2);
/* Latch the InnoDB data dictionary exclusive so that no deadlocks
/* Latch the InnoDB data dictionary exclusively so that no deadlocks
or lock waits can happen in it during a table create operation.
(Drop table etc. do this latching in row0mysql.c.) */
Drop table etc. do this latching in row0mysql.c. */
row_mysql_lock_data_dictionary();
row_mysql_lock_data_dictionary(trx);
/* Create the table definition in InnoDB */
......@@ -2979,7 +2984,7 @@ ha_innobase::create(
if (error) {
innobase_commit_low(trx);
row_mysql_unlock_data_dictionary();
row_mysql_unlock_data_dictionary(trx);
trx_free_for_mysql(trx);
......@@ -3009,7 +3014,7 @@ ha_innobase::create(
if (error) {
innobase_commit_low(trx);
row_mysql_unlock_data_dictionary();
row_mysql_unlock_data_dictionary(trx);
trx_free_for_mysql(trx);
......@@ -3024,7 +3029,7 @@ ha_innobase::create(
(uint) primary_key_no))) {
innobase_commit_low(trx);
row_mysql_unlock_data_dictionary();
row_mysql_unlock_data_dictionary(trx);
trx_free_for_mysql(trx);
......@@ -3040,7 +3045,7 @@ ha_innobase::create(
innobase_commit_low(trx);
row_mysql_unlock_data_dictionary();
row_mysql_unlock_data_dictionary(trx);
trx_free_for_mysql(trx);
......@@ -3057,7 +3062,7 @@ ha_innobase::create(
if (error) {
innobase_commit_low(trx);
row_mysql_unlock_data_dictionary();
row_mysql_unlock_data_dictionary(trx);
trx_free_for_mysql(trx);
......@@ -3066,7 +3071,7 @@ ha_innobase::create(
innobase_commit_low(trx);
row_mysql_unlock_data_dictionary();
row_mysql_unlock_data_dictionary(trx);
/* Flush the log to reduce probability that the .frm files and
the InnoDB data dictionary get out-of-sync if the user runs
......@@ -3108,6 +3113,12 @@ ha_innobase::delete_table(
DBUG_ENTER("ha_innobase::delete_table");
if (lower_case_table_names) {
srv_lower_case_table_names = TRUE;
} else {
srv_lower_case_table_names = FALSE;
}
trx = trx_allocate_for_mysql();
name_len = strlen(name);
......@@ -3121,7 +3132,7 @@ ha_innobase::delete_table(
/* Drop the table in InnoDB */
error = row_drop_table_for_mysql(norm_name, trx, FALSE);
error = row_drop_table_for_mysql(norm_name, trx);
/* Flush the log to reduce probability that the .frm files and
the InnoDB data dictionary get out-of-sync if the user runs
......@@ -3218,6 +3229,12 @@ ha_innobase::rename_table(
DBUG_ENTER("ha_innobase::rename_table");
if (lower_case_table_names) {
srv_lower_case_table_names = TRUE;
} else {
srv_lower_case_table_names = FALSE;
}
trx = trx_allocate_for_mysql();
name_len1 = strlen(from);
......@@ -3406,6 +3423,15 @@ ha_innobase::info(
DBUG_ENTER("info");
/* If we are forcing recovery at a high level, we will suppress
statistics calculation on tables, because that may crash the
server if an index is badly corrupted. */
if (srv_force_recovery >= SRV_FORCE_NO_IBUF_MERGE) {
return;
}
/* Warning: since it is not sure that MySQL calls external_lock
before calling this function, the trx field in prebuilt can be
obsolete! */
......
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