Commit 674d512d authored by unknown's avatar unknown

Merge sinisa@bk-internal.mysql.com:/home/bk/mysql-4.0

into sinisa.nasamreza.org:/mnt/work/mysql-4.0


sql/sql_show.cc:
  Auto merged
parents a3274c22 33ac4727
...@@ -19,6 +19,11 @@ ...@@ -19,6 +19,11 @@
#ifndef _global_h #ifndef _global_h
#define _global_h #define _global_h
#ifndef EMBEDDED_LIBRARY
#define HAVE_REPLICATION
#define HAVE_EXTERNAL_CLIENT
#endif
#if defined( __EMX__) && !defined( MYSQL_SERVER) #if defined( __EMX__) && !defined( MYSQL_SERVER)
/* moved here to use below VOID macro redefinition */ /* moved here to use below VOID macro redefinition */
#define INCL_BASE #define INCL_BASE
......
...@@ -106,7 +106,7 @@ buf_flush_ready_for_replace( ...@@ -106,7 +106,7 @@ buf_flush_ready_for_replace(
BUF_BLOCK_FILE_PAGE and in the LRU list*/ BUF_BLOCK_FILE_PAGE and in the LRU list*/
{ {
ut_ad(mutex_own(&(buf_pool->mutex))); ut_ad(mutex_own(&(buf_pool->mutex)));
ut_ad(block->state == BUF_BLOCK_FILE_PAGE); ut_a(block->state == BUF_BLOCK_FILE_PAGE);
if ((ut_dulint_cmp(block->oldest_modification, ut_dulint_zero) > 0) if ((ut_dulint_cmp(block->oldest_modification, ut_dulint_zero) > 0)
|| (block->buf_fix_count != 0) || (block->buf_fix_count != 0)
...@@ -227,7 +227,9 @@ buf_flush_buffered_writes(void) ...@@ -227,7 +227,9 @@ buf_flush_buffered_writes(void)
} }
for (i = 0; i < trx_doublewrite->first_free; i++) { for (i = 0; i < trx_doublewrite->first_free; i++) {
block = trx_doublewrite->buf_block_arr[i]; block = trx_doublewrite->buf_block_arr[i];
ut_a(block->state == BUF_BLOCK_FILE_PAGE);
if (block->check_index_page_at_flush if (block->check_index_page_at_flush
&& !page_simple_validate(block->frame)) { && !page_simple_validate(block->frame)) {
...@@ -236,10 +238,12 @@ buf_flush_buffered_writes(void) ...@@ -236,10 +238,12 @@ buf_flush_buffered_writes(void)
ut_print_timestamp(stderr); ut_print_timestamp(stderr);
fprintf(stderr, fprintf(stderr,
" InnoDB: Apparent corruption of an index page\n" " InnoDB: Apparent corruption of an index page n:o %lu in space %lu\n"
"InnoDB: to be written to data file. We intentionally crash server\n" "InnoDB: to be written to data file. We intentionally crash server\n"
"InnoDB: to prevent corrupt data from ending up in data\n" "InnoDB: to prevent corrupt data from ending up in data\n"
"InnoDB: files.\n"); "InnoDB: files.\n",
block->offset, block->space);
ut_a(0); ut_a(0);
} }
} }
...@@ -432,6 +436,8 @@ buf_flush_try_page( ...@@ -432,6 +436,8 @@ buf_flush_try_page(
block = buf_page_hash_get(space, offset); block = buf_page_hash_get(space, offset);
ut_a(block->state == BUF_BLOCK_FILE_PAGE);
if (flush_type == BUF_FLUSH_LIST if (flush_type == BUF_FLUSH_LIST
&& block && buf_flush_ready_for_flush(block, flush_type)) { && block && buf_flush_ready_for_flush(block, flush_type)) {
......
...@@ -1173,6 +1173,7 @@ dict_create_add_foreigns_to_dictionary( ...@@ -1173,6 +1173,7 @@ dict_create_add_foreigns_to_dictionary(
if (NULL == dict_table_get_low((char *) "SYS_FOREIGN")) { if (NULL == dict_table_get_low((char *) "SYS_FOREIGN")) {
fprintf(stderr, fprintf(stderr,
"InnoDB: table SYS_FOREIGN not found from internal data dictionary\n"); "InnoDB: table SYS_FOREIGN not found from internal data dictionary\n");
return(DB_ERROR); return(DB_ERROR);
} }
...@@ -1259,6 +1260,13 @@ loop: ...@@ -1259,6 +1260,13 @@ loop:
"InnoDB: at http://www.innodb.com/ibman.html\n"); "InnoDB: at http://www.innodb.com/ibman.html\n");
} }
mutex_enter(&dict_foreign_err_mutex);
ut_sprintf_timestamp(buf);
sprintf(buf + strlen(buf),
" Internal error in foreign key constraint creation for table %.500s.\n"
"See the MySQL .err log in the datadir for more information.\n", table->name);
mutex_exit(&dict_foreign_err_mutex);
return(error); return(error);
} }
......
...@@ -185,6 +185,12 @@ dict_foreign_free( ...@@ -185,6 +185,12 @@ dict_foreign_free(
/*==============*/ /*==============*/
dict_foreign_t* foreign); /* in, own: foreign key struct */ dict_foreign_t* foreign); /* in, own: foreign key struct */
/* Buffer for storing detailed information about the latest foreig key
error */
char* dict_foreign_err_buf = NULL;
mutex_t dict_foreign_err_mutex; /* mutex protecting the buffer */
/************************************************************************ /************************************************************************
Checks if the database name in two table names is the same. */ Checks if the database name in two table names is the same. */
static static
...@@ -573,6 +579,11 @@ dict_init(void) ...@@ -573,6 +579,11 @@ dict_init(void)
rw_lock_create(&dict_operation_lock); rw_lock_create(&dict_operation_lock);
rw_lock_set_level(&dict_operation_lock, SYNC_DICT_OPERATION); rw_lock_set_level(&dict_operation_lock, SYNC_DICT_OPERATION);
dict_foreign_err_buf = mem_alloc(DICT_FOREIGN_ERR_BUF_LEN);
dict_foreign_err_buf[0] = '\0';
mutex_create(&dict_foreign_err_mutex);
mutex_set_level(&dict_foreign_err_mutex, SYNC_ANY_LATCH);
} }
/************************************************************************** /**************************************************************************
...@@ -1818,6 +1829,7 @@ dict_foreign_add_to_cache( ...@@ -1818,6 +1829,7 @@ dict_foreign_add_to_cache(
dict_foreign_t* for_in_cache = NULL; dict_foreign_t* for_in_cache = NULL;
dict_index_t* index; dict_index_t* index;
ibool added_to_referenced_list = FALSE; ibool added_to_referenced_list = FALSE;
char* buf = dict_foreign_err_buf;
ut_ad(mutex_own(&(dict_sys->mutex))); ut_ad(mutex_own(&(dict_sys->mutex)));
...@@ -1850,9 +1862,29 @@ dict_foreign_add_to_cache( ...@@ -1850,9 +1862,29 @@ dict_foreign_add_to_cache(
for_in_cache->foreign_index); for_in_cache->foreign_index);
if (index == NULL) { if (index == NULL) {
mutex_enter(&dict_foreign_err_mutex);
ut_sprintf_timestamp(buf);
sprintf(buf + strlen(buf),
" Error in foreign key constraint of table %.500s:\n"
"there is no index in referenced table which would contain\n"
"the columns as the first columns, or the data types in the\n"
"referenced table do not match to the ones in table. Constraint:\n",
for_in_cache->foreign_table_name);
dict_print_info_on_foreign_key_in_create_format(
for_in_cache, buf + strlen(buf));
if (for_in_cache->foreign_index) {
sprintf(buf + strlen(buf),
"\nThe index in the foreign key in table is %.500s\n"
"See http://www.innodb.com/ibman.html about correct foreign key definition.\n",
for_in_cache->foreign_index->name);
}
ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
mutex_exit(&dict_foreign_err_mutex);
if (for_in_cache == foreign) { if (for_in_cache == foreign) {
mem_heap_free(foreign->heap); mem_heap_free(foreign->heap);
} }
return(DB_CANNOT_ADD_CONSTRAINT); return(DB_CANNOT_ADD_CONSTRAINT);
} }
...@@ -1871,6 +1903,25 @@ dict_foreign_add_to_cache( ...@@ -1871,6 +1903,25 @@ dict_foreign_add_to_cache(
for_in_cache->referenced_index); for_in_cache->referenced_index);
if (index == NULL) { if (index == NULL) {
mutex_enter(&dict_foreign_err_mutex);
ut_sprintf_timestamp(buf);
sprintf(buf + strlen(buf),
" Error in foreign key constraint of table %.500s:\n"
"there is no index in the table which would contain\n"
"the columns as the first columns, or the data types in the\n"
"table do not match to the ones in the referenced table. Constraint:\n",
for_in_cache->foreign_table_name);
dict_print_info_on_foreign_key_in_create_format(
for_in_cache, buf + strlen(buf));
if (for_in_cache->foreign_index) {
sprintf(buf + strlen(buf),
"\nIndex of the foreign key in the referenced table is %.500s\n"
"See http://www.innodb.com/ibman.html about correct foreign key definition.\n",
for_in_cache->referenced_index->name);
}
ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
mutex_exit(&dict_foreign_err_mutex);
if (for_in_cache == foreign) { if (for_in_cache == foreign) {
if (added_to_referenced_list) { if (added_to_referenced_list) {
UT_LIST_REMOVE(referenced_list, UT_LIST_REMOVE(referenced_list,
...@@ -2141,18 +2192,21 @@ dict_scan_table_name( ...@@ -2141,18 +2192,21 @@ dict_scan_table_name(
} }
/************************************************************************* /*************************************************************************
Skips one 'word', like an id. For the lexical definition of 'word', see the Scans an id. For the lexical definition of an 'id', see the code below.
code below. */ Strips backquotes from around the id. */
static static
char* char*
dict_skip_word( dict_scan_id(
/*===========*/ /*=========*/
/* out: scanned to */ /* out: scanned to */
char* ptr, /* in: scanned to */ char* ptr, /* in: scanned to */
ibool* success)/* out: TRUE if success, FALSE if just spaces left in char** start, /* out: start of the id; NULL if no id was
string */ scannable */
ulint* len) /* out: length of the id */
{ {
*success = FALSE; ibool scanned_backquote = FALSE;
*start = NULL;
while (isspace(*ptr)) { while (isspace(*ptr)) {
ptr++; ptr++;
...@@ -2164,20 +2218,58 @@ dict_skip_word( ...@@ -2164,20 +2218,58 @@ dict_skip_word(
} }
if (*ptr == '`') { if (*ptr == '`') {
scanned_backquote = TRUE;
ptr++; ptr++;
} }
while (!isspace(*ptr) && *ptr != ',' && *ptr != '(' && *ptr != '`' *start = ptr;
&& *ptr != '\0') {
while (!isspace(*ptr) && *ptr != ',' && *ptr != '(' && *ptr != ')'
&& *ptr != '\0' && *ptr != '`') {
ptr++; ptr++;
} }
*len = (ulint) (ptr - *start);
if (scanned_backquote) {
if (*ptr == '`') {
ptr++;
} else {
/* Syntax error */
*start = NULL;
}
}
return(ptr);
}
/*************************************************************************
Skips one id. */
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 or a syntax error */
{
char* start;
ulint len;
*success = FALSE;
ptr = dict_scan_id(ptr, &start, &len);
if (start) {
*success = TRUE; *success = TRUE;
}
return(ptr); return(ptr);
} }
#ifdef currentlynotused
/************************************************************************* /*************************************************************************
Returns the number of opening brackets '(' subtracted by the number Returns the number of opening brackets '(' subtracted by the number
of closing brackets ')' between string and ptr. */ of closing brackets ')' between string and ptr. */
...@@ -2204,6 +2296,106 @@ dict_bracket_count( ...@@ -2204,6 +2296,106 @@ dict_bracket_count(
return(count); return(count);
} }
#endif
/*************************************************************************
Removes MySQL comments from an SQL string. A comment is either
(a) '#' to the end of the line,
(b) '--<space>' to the end of the line, or
(c) '<slash><asterisk>' till the next '<asterisk><slash>' (like the familiar
C comment syntax). */
static
char*
dict_strip_comments(
/*================*/
/* out, own: SQL string stripped from
comments; the caller must free this
with mem_free()! */
char* sql_string) /* in: SQL string */
{
char* str;
char* sptr;
char* ptr;
str = mem_alloc(strlen(sql_string) + 1);
sptr = sql_string;
ptr = str;
for (;;) {
if (*sptr == '\0') {
*ptr = '\0';
return(str);
}
if (*sptr == '#'
|| (strlen(sptr) >= 3 && 0 == memcmp("-- ", sptr, 3))) {
for (;;) {
/* In Unix a newline is 0x0D while in Windows
it is 0x0A followed by 0x0D */
if (*sptr == (char)0x0A
|| *sptr == (char)0x0D
|| *sptr == '\0') {
break;
}
sptr++;
}
}
if (strlen(sptr) >= 2 && *sptr == '/' && *(sptr + 1) == '*') {
for (;;) {
if (strlen(sptr) >= 2
&& *sptr == '*' && *(sptr + 1) == '/') {
sptr += 2;
break;
}
if (*sptr == '\0') {
break;
}
sptr++;
}
}
*ptr = *sptr;
ptr++;
sptr++;
}
}
/*************************************************************************
Reports a simple foreign key create clause syntax error. */
static
void
dict_foreign_report_syntax_err(
/*===========================*/
char* name, /* in: table name */
char* start_of_latest_foreign,/* in: start of the foreign key clause
in the SQL string */
char* ptr) /* in: place of the syntax error */
{
char* buf = dict_foreign_err_buf;
mutex_enter(&dict_foreign_err_mutex);
ut_sprintf_timestamp(buf);
sprintf(buf + strlen(buf),
" Error in foreign key constraint of table %.500s,\n%.500s.\n"
"Syntax error close to:\n%.500s\n", name, start_of_latest_foreign, ptr);
ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
mutex_exit(&dict_foreign_err_mutex);
}
/************************************************************************* /*************************************************************************
Scans a table create SQL string and adds to the data dictionary the foreign Scans a table create SQL string and adds to the data dictionary the foreign
...@@ -2211,10 +2403,10 @@ key constraints declared in the string. This function should be called after ...@@ -2211,10 +2403,10 @@ key constraints declared in the string. This function should be called after
the indexes for a table have been created. Each foreign key constraint must the indexes for a table have been created. Each foreign key constraint must
be accompanied with indexes in both participating tables. The indexes are be accompanied with indexes in both participating tables. The indexes are
allowed to contain more fields than mentioned in the constraint. */ allowed to contain more fields than mentioned in the constraint. */
static
ulint ulint
dict_create_foreign_constraints( dict_create_foreign_constraints_low(
/*============================*/ /*================================*/
/* out: error code or DB_SUCCESS */ /* out: error code or DB_SUCCESS */
trx_t* trx, /* in: transaction */ trx_t* trx, /* in: transaction */
char* sql_string, /* in: table create or ALTER TABLE char* sql_string, /* in: table create or ALTER TABLE
...@@ -2231,6 +2423,8 @@ dict_create_foreign_constraints( ...@@ -2231,6 +2423,8 @@ dict_create_foreign_constraints(
dict_index_t* index; dict_index_t* index;
dict_foreign_t* foreign; dict_foreign_t* foreign;
char* ptr = sql_string; char* ptr = sql_string;
char* start_of_latest_foreign = sql_string;
char* buf = dict_foreign_err_buf;
ibool success; ibool success;
ulint error; ulint error;
ulint i; ulint i;
...@@ -2248,6 +2442,15 @@ dict_create_foreign_constraints( ...@@ -2248,6 +2442,15 @@ dict_create_foreign_constraints(
table = dict_table_get_low(name); table = dict_table_get_low(name);
if (table == NULL) { if (table == NULL) {
mutex_enter(&dict_foreign_err_mutex);
ut_sprintf_timestamp(buf);
sprintf(buf + strlen(buf),
" Error in foreign key constraint of table %.500s.\n"
"Cannot find the table from the internal data dictionary of InnoDB.\n"
"Create table statement:\n%.2000\n", name, sql_string);
ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
mutex_exit(&dict_foreign_err_mutex);
return(DB_ERROR); return(DB_ERROR);
} }
loop: loop:
...@@ -2263,6 +2466,8 @@ loop: ...@@ -2263,6 +2466,8 @@ loop:
return(error); return(error);
} }
start_of_latest_foreign = ptr;
ptr = dict_accept(ptr, (char *) "FOREIGN", &success); ptr = dict_accept(ptr, (char *) "FOREIGN", &success);
if (!isspace(*ptr)) { if (!isspace(*ptr)) {
...@@ -2283,13 +2488,19 @@ loop: ...@@ -2283,13 +2488,19 @@ loop:
ptr = dict_skip_word(ptr, &success); ptr = dict_skip_word(ptr, &success);
if (!success) { if (!success) {
dict_foreign_report_syntax_err(name,
start_of_latest_foreign, ptr);
return(DB_CANNOT_ADD_CONSTRAINT); return(DB_CANNOT_ADD_CONSTRAINT);
} }
ptr = dict_accept(ptr, (char *) "(", &success); ptr = dict_accept(ptr, (char *) "(", &success);
if (!success) { if (!success) {
return(DB_CANNOT_ADD_CONSTRAINT); /* We do not flag a syntax error here because in an
ALTER TABLE we may also have DROP FOREIGN KEY abc */
goto loop;
} }
} }
...@@ -2300,6 +2511,15 @@ col_loop1: ...@@ -2300,6 +2511,15 @@ col_loop1:
ptr = dict_scan_col(ptr, &success, table, columns + i, ptr = dict_scan_col(ptr, &success, table, columns + i,
column_names + i, column_name_lens + i); column_names + i, column_name_lens + i);
if (!success) { if (!success) {
mutex_enter(&dict_foreign_err_mutex);
ut_sprintf_timestamp(buf);
sprintf(buf + strlen(buf),
" Error in foreign key constraint of table %.500s,\n%.500s.\n"
"Cannot resolve column name close to:\n%.500s\n", name,
start_of_latest_foreign, ptr);
ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
mutex_exit(&dict_foreign_err_mutex);
return(DB_CANNOT_ADD_CONSTRAINT); return(DB_CANNOT_ADD_CONSTRAINT);
} }
...@@ -2314,6 +2534,8 @@ col_loop1: ...@@ -2314,6 +2534,8 @@ col_loop1:
ptr = dict_accept(ptr, (char *) ")", &success); ptr = dict_accept(ptr, (char *) ")", &success);
if (!success) { if (!success) {
dict_foreign_report_syntax_err(name, start_of_latest_foreign,
ptr);
return(DB_CANNOT_ADD_CONSTRAINT); return(DB_CANNOT_ADD_CONSTRAINT);
} }
...@@ -2323,12 +2545,24 @@ col_loop1: ...@@ -2323,12 +2545,24 @@ col_loop1:
index = dict_foreign_find_index(table, column_names, i, NULL); index = dict_foreign_find_index(table, column_names, i, NULL);
if (!index) { if (!index) {
mutex_enter(&dict_foreign_err_mutex);
ut_sprintf_timestamp(buf);
sprintf(buf + strlen(buf),
" Error in foreign key constraint of table %.500s:\n"
"There is no index in the table %.500s where the columns appear\n"
"as the first columns. Constraint:\n%.500s\n"
"See http://www.innodb.com/ibman.html for correct foreign key definition.\n",
name, name, start_of_latest_foreign);
ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
mutex_exit(&dict_foreign_err_mutex);
return(DB_CANNOT_ADD_CONSTRAINT); return(DB_CANNOT_ADD_CONSTRAINT);
} }
ptr = dict_accept(ptr, (char *) "REFERENCES", &success); ptr = dict_accept(ptr, (char *) "REFERENCES", &success);
if (!success || !isspace(*ptr)) { if (!success || !isspace(*ptr)) {
dict_foreign_report_syntax_err(name, start_of_latest_foreign,
ptr);
return(DB_CANNOT_ADD_CONSTRAINT); return(DB_CANNOT_ADD_CONSTRAINT);
} }
...@@ -2358,6 +2592,15 @@ col_loop1: ...@@ -2358,6 +2592,15 @@ col_loop1:
if (!success || (!referenced_table && trx->check_foreigns)) { if (!success || (!referenced_table && trx->check_foreigns)) {
dict_foreign_free(foreign); dict_foreign_free(foreign);
mutex_enter(&dict_foreign_err_mutex);
ut_sprintf_timestamp(buf);
sprintf(buf + strlen(buf),
" Error in foreign key constraint of table %.500s,\n%.500s.\n"
"Cannot resolve table name close to:\n"
"%.500s\n", name, start_of_latest_foreign, ptr);
ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
mutex_exit(&dict_foreign_err_mutex);
return(DB_CANNOT_ADD_CONSTRAINT); return(DB_CANNOT_ADD_CONSTRAINT);
} }
...@@ -2365,7 +2608,8 @@ col_loop1: ...@@ -2365,7 +2608,8 @@ col_loop1:
if (!success) { if (!success) {
dict_foreign_free(foreign); dict_foreign_free(foreign);
dict_foreign_report_syntax_err(name, start_of_latest_foreign,
ptr);
return(DB_CANNOT_ADD_CONSTRAINT); return(DB_CANNOT_ADD_CONSTRAINT);
} }
...@@ -2380,6 +2624,15 @@ col_loop2: ...@@ -2380,6 +2624,15 @@ col_loop2:
if (!success) { if (!success) {
dict_foreign_free(foreign); dict_foreign_free(foreign);
mutex_enter(&dict_foreign_err_mutex);
ut_sprintf_timestamp(buf);
sprintf(buf + strlen(buf),
" Error in foreign key constraint of table %.500s,\n%.500s\n"
"Cannot resolve column name close to:\n"
"%.500s\n", name, start_of_latest_foreign, ptr);
ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
mutex_exit(&dict_foreign_err_mutex);
return(DB_CANNOT_ADD_CONSTRAINT); return(DB_CANNOT_ADD_CONSTRAINT);
} }
...@@ -2394,6 +2647,8 @@ col_loop2: ...@@ -2394,6 +2647,8 @@ col_loop2:
if (!success || foreign->n_fields != i) { if (!success || foreign->n_fields != i) {
dict_foreign_free(foreign); dict_foreign_free(foreign);
dict_foreign_report_syntax_err(name, start_of_latest_foreign,
ptr);
return(DB_CANNOT_ADD_CONSTRAINT); return(DB_CANNOT_ADD_CONSTRAINT);
} }
...@@ -2416,9 +2671,10 @@ scan_on_conditions: ...@@ -2416,9 +2671,10 @@ scan_on_conditions:
ptr = dict_accept(ptr, "UPDATE", &success); ptr = dict_accept(ptr, "UPDATE", &success);
if (!success) { if (!success) {
dict_foreign_free(foreign); dict_foreign_free(foreign);
dict_foreign_report_syntax_err(name,
start_of_latest_foreign, ptr);
return(DB_CANNOT_ADD_CONSTRAINT); return(DB_CANNOT_ADD_CONSTRAINT);
} }
...@@ -2454,6 +2710,8 @@ scan_on_conditions: ...@@ -2454,6 +2710,8 @@ scan_on_conditions:
if (!success) { if (!success) {
dict_foreign_free(foreign); dict_foreign_free(foreign);
dict_foreign_report_syntax_err(name,
start_of_latest_foreign, ptr);
return(DB_CANNOT_ADD_CONSTRAINT); return(DB_CANNOT_ADD_CONSTRAINT);
} }
...@@ -2471,7 +2729,8 @@ scan_on_conditions: ...@@ -2471,7 +2729,8 @@ scan_on_conditions:
if (!success) { if (!success) {
dict_foreign_free(foreign); dict_foreign_free(foreign);
dict_foreign_report_syntax_err(name, start_of_latest_foreign,
ptr);
return(DB_CANNOT_ADD_CONSTRAINT); return(DB_CANNOT_ADD_CONSTRAINT);
} }
...@@ -2479,7 +2738,8 @@ scan_on_conditions: ...@@ -2479,7 +2738,8 @@ scan_on_conditions:
if (!success) { if (!success) {
dict_foreign_free(foreign); dict_foreign_free(foreign);
dict_foreign_report_syntax_err(name, start_of_latest_foreign,
ptr);
return(DB_CANNOT_ADD_CONSTRAINT); return(DB_CANNOT_ADD_CONSTRAINT);
} }
...@@ -2493,6 +2753,15 @@ scan_on_conditions: ...@@ -2493,6 +2753,15 @@ scan_on_conditions:
dict_foreign_free(foreign); dict_foreign_free(foreign);
mutex_enter(&dict_foreign_err_mutex);
ut_sprintf_timestamp(buf);
sprintf(buf + strlen(buf),
" Error in foreign key constraint of table %.500s,\n%.500s.\n"
"You have defined a SET NULL condition though some of the\n"
"columns is defined as NOT NULL.\n", name, start_of_latest_foreign);
ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
mutex_exit(&dict_foreign_err_mutex);
return(DB_CANNOT_ADD_CONSTRAINT); return(DB_CANNOT_ADD_CONSTRAINT);
} }
} }
...@@ -2511,6 +2780,15 @@ try_find_index: ...@@ -2511,6 +2780,15 @@ try_find_index:
dict_foreign_free(foreign); dict_foreign_free(foreign);
mutex_enter(&dict_foreign_err_mutex);
ut_sprintf_timestamp(buf);
sprintf(buf + strlen(buf),
" Error in foreign key constraint of table %.500s,\n%.500s.\n"
"You have twice an ON DELETE clause or twice an ON UPDATE clause.\n",
name, start_of_latest_foreign);
ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
mutex_exit(&dict_foreign_err_mutex);
return(DB_CANNOT_ADD_CONSTRAINT); return(DB_CANNOT_ADD_CONSTRAINT);
} }
...@@ -2524,6 +2802,18 @@ try_find_index: ...@@ -2524,6 +2802,18 @@ try_find_index:
foreign->foreign_index); foreign->foreign_index);
if (!index) { if (!index) {
dict_foreign_free(foreign); dict_foreign_free(foreign);
mutex_enter(&dict_foreign_err_mutex);
ut_sprintf_timestamp(buf);
sprintf(buf + strlen(buf),
" Error in foreign key constraint of table %.500s:\n"
"Cannot find an index in the referenced table where the\n"
"referenced columns appear as the first columns, or column types\n"
"in the table and the referenced table do not match for constraint:\n%.500s\n"
"See http://www.innodb.com/ibman.html for correct foreign key definition.\n",
name, start_of_latest_foreign);
ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
mutex_exit(&dict_foreign_err_mutex);
return(DB_CANNOT_ADD_CONSTRAINT); return(DB_CANNOT_ADD_CONSTRAINT);
} }
} else { } else {
...@@ -2564,6 +2854,165 @@ try_find_index: ...@@ -2564,6 +2854,165 @@ try_find_index:
goto loop; goto loop;
} }
/*************************************************************************
Scans a table create SQL string and adds to the data dictionary the foreign
key constraints declared in the string. This function should be called after
the indexes for a table have been created. Each foreign key constraint must
be accompanied with indexes in both participating tables. The indexes are
allowed to contain more fields than mentioned in the constraint. */
ulint
dict_create_foreign_constraints(
/*============================*/
/* out: error code or DB_SUCCESS */
trx_t* trx, /* in: transaction */
char* sql_string, /* in: table create or ALTER TABLE
statement where foreign keys are declared like:
FOREIGN KEY (a, b) REFERENCES table2(c, d),
table2 can be written also with the database
name before it: test.table2; the default
database id the database of parameter name */
char* name) /* in: table full name in the normalized form
database_name/table_name */
{
char* str;
ulint err;
str = dict_strip_comments(sql_string);
err = dict_create_foreign_constraints_low(trx, str, name);
mem_free(str);
return(err);
}
/**************************************************************************
Parses the CONSTRAINT id's to be dropped in an ALTER TABLE statement. */
ulint
dict_foreign_parse_drop_constraints(
/*================================*/
/* out: DB_SUCCESS or
DB_CANNOT_DROP_CONSTRAINT if
syntax error or the constraint
id does not match */
mem_heap_t* heap, /* in: heap from which we can
allocate memory */
trx_t* trx, /* in: transaction */
dict_table_t* table, /* in: table */
ulint* n, /* out: number of constraints
to drop */
char*** constraints_to_drop) /* out: id's of the
constraints to drop */
{
dict_foreign_t* foreign;
ibool success;
char* str;
char* ptr;
char* buf = dict_foreign_err_buf;
char* start;
char* id;
ulint len;
*n = 0;
*constraints_to_drop = mem_heap_alloc(heap, 1000 * sizeof(char*));
str = dict_strip_comments(*(trx->mysql_query_str));
ptr = str;
ut_ad(mutex_own(&(dict_sys->mutex)));
loop:
ptr = dict_scan_to(ptr, (char *) "DROP");
if (*ptr == '\0') {
ut_a(*n < 1000);
mem_free(str);
return(DB_SUCCESS);
}
ptr = dict_accept(ptr, (char *) "DROP", &success);
if (!isspace(*ptr)) {
goto loop;
}
ptr = dict_accept(ptr, (char *) "FOREIGN", &success);
if (!success) {
goto loop;
}
ptr = dict_accept(ptr, (char *) "KEY", &success);
if (!success) {
goto syntax_error;
}
ptr = dict_scan_id(ptr, &start, &len);
if (start == NULL) {
goto syntax_error;
}
id = mem_heap_alloc(heap, len + 1);
ut_memcpy(id, start, len);
id[len] = '\0';
(*constraints_to_drop)[*n] = id;
(*n)++;
/* Look for the given constraint id */
foreign = UT_LIST_GET_FIRST(table->foreign_list);
while (foreign != NULL) {
if (0 == ut_strcmp(foreign->id, id)) {
/* Found */
break;
}
foreign = UT_LIST_GET_NEXT(foreign_list, foreign);
}
if (foreign == NULL) {
mutex_enter(&dict_foreign_err_mutex);
ut_sprintf_timestamp(buf);
sprintf(buf + strlen(buf),
" Error in dropping of a foreign key constraint of table %.500s,\n"
"just before:\n%s\n in SQL command\n%s\nCannot find a constraint with the\n"
"given id %s.\n", table->name, ptr, str, id);
ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
mutex_exit(&dict_foreign_err_mutex);
mem_free(str);
return(DB_CANNOT_DROP_CONSTRAINT);
}
goto loop;
syntax_error:
mutex_enter(&dict_foreign_err_mutex);
ut_sprintf_timestamp(buf);
sprintf(buf + strlen(buf),
" Syntax error in dropping of a foreign key constraint of table %.500s,\n"
"close to:\n%s\n in SQL command\n%s\n", table->name, ptr, str);
ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
mutex_exit(&dict_foreign_err_mutex);
mem_free(str);
return(DB_CANNOT_DROP_CONSTRAINT);
}
/*==================== END OF FOREIGN KEY PROCESSING ====================*/ /*==================== END OF FOREIGN KEY PROCESSING ====================*/
/************************************************************************** /**************************************************************************
...@@ -3285,7 +3734,6 @@ dict_index_print_low( ...@@ -3285,7 +3734,6 @@ dict_index_print_low(
n_vals = index->stat_n_diff_key_vals[1]; n_vals = index->stat_n_diff_key_vals[1];
} }
printf( printf(
" INDEX: name %s, table name %s, id %lu %lu, fields %lu/%lu, type %lu\n", " INDEX: name %s, table name %s, id %lu %lu, fields %lu/%lu, type %lu\n",
index->name, index->table_name, index->name, index->table_name,
...@@ -3327,43 +3775,27 @@ dict_field_print_low( ...@@ -3327,43 +3775,27 @@ dict_field_print_low(
} }
/************************************************************************** /**************************************************************************
Sprintfs to a string info on foreign keys of a table in a format suitable Sprintfs to a string info on a foreign key of a table in a format suitable
for CREATE TABLE. */ for CREATE TABLE. */
static
void
dict_print_info_on_foreign_keys_in_create_format(
/*=============================================*/
char* buf, /* in: auxiliary buffer */
char* str, /* in/out: pointer to a string */
ulint len, /* in: str has to be a buffer at least
len + 5000 bytes */
dict_table_t* table) /* in: table */
{
dict_foreign_t* foreign; char*
dict_print_info_on_foreign_key_in_create_format(
/*============================================*/
/* out: how far in buf we printed */
dict_foreign_t* foreign,/* in: foreign key constraint */
char* buf) /* in: buffer of at least 5000 bytes */
{
char* buf2 = buf;
ulint i; ulint i;
char* buf2;
buf2 = buf;
mutex_enter(&(dict_sys->mutex));
foreign = UT_LIST_GET_FIRST(table->foreign_list);
if (foreign == NULL) {
mutex_exit(&(dict_sys->mutex));
return;
}
while (foreign != NULL) {
buf2 += sprintf(buf2, ",\n FOREIGN KEY (");
buf2 += sprintf(buf2, ",\n CONSTRAINT `%s` FOREIGN KEY (",
foreign->id);
for (i = 0; i < foreign->n_fields; i++) { for (i = 0; i < foreign->n_fields; i++) {
if ((ulint)(buf2 - buf) >= len) { if ((ulint)(buf2 - buf) >= 4000) {
goto no_space; goto no_space;
} }
buf2 += sprintf(buf2, "`%s`", buf2 += sprintf(buf2, "`%.250s`",
foreign->foreign_col_names[i]); foreign->foreign_col_names[i]);
if (i + 1 < foreign->n_fields) { if (i + 1 < foreign->n_fields) {
...@@ -3371,15 +3803,15 @@ dict_print_info_on_foreign_keys_in_create_format( ...@@ -3371,15 +3803,15 @@ dict_print_info_on_foreign_keys_in_create_format(
} }
} }
if (dict_tables_have_same_db(table->name, if (dict_tables_have_same_db(foreign->foreign_table_name,
foreign->referenced_table_name)) { foreign->referenced_table_name)) {
/* Do not print the database name of the referenced /* Do not print the database name of the referenced
table */ table */
buf2 += sprintf(buf2, ") REFERENCES `%s` (", buf2 += sprintf(buf2, ") REFERENCES `%.500s` (",
dict_remove_db_name( dict_remove_db_name(
foreign->referenced_table_name)); foreign->referenced_table_name));
} else { } else {
buf2 += sprintf(buf2, ") REFERENCES `%s` (", buf2 += sprintf(buf2, ") REFERENCES `%.500s` (",
foreign->referenced_table_name); foreign->referenced_table_name);
/* Change the '/' in the table name to '.' */ /* Change the '/' in the table name to '.' */
...@@ -3394,10 +3826,11 @@ dict_print_info_on_foreign_keys_in_create_format( ...@@ -3394,10 +3826,11 @@ dict_print_info_on_foreign_keys_in_create_format(
} }
for (i = 0; i < foreign->n_fields; i++) { for (i = 0; i < foreign->n_fields; i++) {
if ((ulint)(buf2 - buf) >= len) { if ((ulint)(buf2 - buf) >= 4000) {
goto no_space; goto no_space;
} }
buf2 += sprintf(buf2, "`%s`", buf2 += sprintf(buf2, "`%.250s`",
foreign->referenced_col_names[i]); foreign->referenced_col_names[i]);
if (i + 1 < foreign->n_fields) { if (i + 1 < foreign->n_fields) {
buf2 += sprintf(buf2, ", "); buf2 += sprintf(buf2, ", ");
...@@ -3430,6 +3863,47 @@ dict_print_info_on_foreign_keys_in_create_format( ...@@ -3430,6 +3863,47 @@ dict_print_info_on_foreign_keys_in_create_format(
buf2 += sprintf(buf2, " ON UPDATE NO ACTION"); buf2 += sprintf(buf2, " ON UPDATE NO ACTION");
} }
no_space:
return(buf2);
}
/**************************************************************************
Sprintfs to a string info on foreign keys of a table in a format suitable
for CREATE TABLE. */
static
void
dict_print_info_on_foreign_keys_in_create_format(
/*=============================================*/
char* buf, /* in: auxiliary buffer */
char* str, /* in/out: pointer to a string */
ulint len, /* in: buf has to be a buffer of at least
len + 5000 bytes; str must have at least
len + 1 bytes */
dict_table_t* table) /* in: table */
{
dict_foreign_t* foreign;
char* buf2;
buf2 = buf;
mutex_enter(&(dict_sys->mutex));
foreign = UT_LIST_GET_FIRST(table->foreign_list);
if (foreign == NULL) {
mutex_exit(&(dict_sys->mutex));
return;
}
while (foreign != NULL) {
if ((ulint)(buf2 - buf) >= len) {
goto no_space;
}
buf2 = dict_print_info_on_foreign_key_in_create_format(
foreign, buf2);
foreign = UT_LIST_GET_NEXT(foreign_list, foreign); foreign = UT_LIST_GET_NEXT(foreign_list, foreign);
} }
no_space: no_space:
...@@ -3489,7 +3963,7 @@ dict_print_info_on_foreign_keys( ...@@ -3489,7 +3963,7 @@ dict_print_info_on_foreign_keys(
goto no_space; goto no_space;
} }
buf2 += sprintf(buf2, "%s", buf2 += sprintf(buf2, "%.500s",
foreign->foreign_col_names[i]); foreign->foreign_col_names[i]);
if (i + 1 < foreign->n_fields) { if (i + 1 < foreign->n_fields) {
...@@ -3497,14 +3971,14 @@ dict_print_info_on_foreign_keys( ...@@ -3497,14 +3971,14 @@ dict_print_info_on_foreign_keys(
} }
} }
buf2 += sprintf(buf2, ") REFER %s(", buf2 += sprintf(buf2, ") REFER %.500s(",
foreign->referenced_table_name); foreign->referenced_table_name);
for (i = 0; i < foreign->n_fields; i++) { for (i = 0; i < foreign->n_fields; i++) {
if ((ulint)(buf2 - buf) >= len) { if ((ulint)(buf2 - buf) >= len) {
goto no_space; goto no_space;
} }
buf2 += sprintf(buf2, "%s", buf2 += sprintf(buf2, "%.500s",
foreign->referenced_col_names[i]); foreign->referenced_col_names[i]);
if (i + 1 < foreign->n_fields) { if (i + 1 < foreign->n_fields) {
buf2 += sprintf(buf2, " "); buf2 += sprintf(buf2, " ");
......
...@@ -456,7 +456,7 @@ dict_load_indexes( ...@@ -456,7 +456,7 @@ dict_load_indexes(
ut_ad(len == 8); ut_ad(len == 8);
id = mach_read_from_8(field); id = mach_read_from_8(field);
ut_a(0 == ut_strcmp("NAME", ut_a(0 == ut_strcmp((char*)"NAME",
dict_field_get_col( dict_field_get_col(
dict_index_get_nth_field( dict_index_get_nth_field(
dict_table_get_first_index(sys_indexes), 4))->name)); dict_table_get_first_index(sys_indexes), 4))->name));
...@@ -515,7 +515,7 @@ dict_load_indexes( ...@@ -515,7 +515,7 @@ dict_load_indexes(
&& ((type & DICT_CLUSTERED) && ((type & DICT_CLUSTERED)
|| ((table == dict_sys->sys_tables) || ((table == dict_sys->sys_tables)
&& (name_len == ut_strlen("ID_IND")) && (name_len == ut_strlen("ID_IND"))
&& (0 == ut_memcmp(name_buf, "ID_IND", && (0 == ut_memcmp(name_buf, (char*)"ID_IND",
name_len))))) { name_len))))) {
/* The index was created in memory already in /* The index was created in memory already in
...@@ -566,6 +566,7 @@ dict_load_table( ...@@ -566,6 +566,7 @@ dict_load_table(
char* buf; char* buf;
ulint space; ulint space;
ulint n_cols; ulint n_cols;
ulint err;
mtr_t mtr; mtr_t mtr;
ut_ad(mutex_own(&(dict_sys->mutex))); ut_ad(mutex_own(&(dict_sys->mutex)));
...@@ -674,8 +675,25 @@ dict_load_table( ...@@ -674,8 +675,25 @@ dict_load_table(
dict_load_indexes(table, heap); dict_load_indexes(table, heap);
ut_a(DB_SUCCESS == dict_load_foreigns(table->name)); err = dict_load_foreigns(table->name);
/*
if (err != DB_SUCCESS) {
mutex_enter(&dict_foreign_err_mutex);
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: Error: could not make a foreign key definition to match\n"
"InnoDB: the foreign key table or the referenced table!\n"
"InnoDB: The data dictionary of InnoDB is corrupt. You may need to drop\n"
"InnoDB: and recreate the foreign key table or the referenced table.\n"
"InnoDB: Send a detailed bug report to mysql@lists.mysql.com\n"
"InnoDB: Latest foreign key error printout:\n%s\n", dict_foreign_err_buf);
mutex_exit(&dict_foreign_err_mutex);
}
*/
mem_heap_free(heap); mem_heap_free(heap);
return(table); return(table);
...@@ -978,8 +996,8 @@ dict_load_foreign( ...@@ -978,8 +996,8 @@ dict_load_foreign(
field = rec_get_nth_field(rec, 4, &len); field = rec_get_nth_field(rec, 4, &len);
foreign->referenced_table_name = mem_heap_alloc(foreign->heap, 1 + len); foreign->referenced_table_name = mem_heap_alloc(foreign->heap,
1 + len);
ut_memcpy(foreign->referenced_table_name, field, len); ut_memcpy(foreign->referenced_table_name, field, len);
foreign->referenced_table_name[len] = '\0'; foreign->referenced_table_name[len] = '\0';
...@@ -988,10 +1006,19 @@ dict_load_foreign( ...@@ -988,10 +1006,19 @@ dict_load_foreign(
dict_load_foreign_cols(id, foreign); dict_load_foreign_cols(id, foreign);
/* If the foreign table is not yet in the dictionary cache, we
have to load it so that we are able to make type comparisons
in the next function call. */
dict_table_get_low(foreign->foreign_table_name);
/* Note that there may already be a foreign constraint object in /* Note that there may already be a foreign constraint object in
the dictionary cache for this constraint: then the following the dictionary cache for this constraint: then the following
call only sets the pointers in it to point to the appropriate table call only sets the pointers in it to point to the appropriate table
and index objects and frees the newly created object foreign. */ and index objects and frees the newly created object foreign.
Adding to the cache should always succeed since we are not creating
a new foreign key constraint but loading one from the data
dictionary. */
err = dict_foreign_add_to_cache(foreign); err = dict_foreign_add_to_cache(foreign);
......
...@@ -2420,17 +2420,22 @@ ibuf_delete_rec( ...@@ -2420,17 +2420,22 @@ ibuf_delete_rec(
fprintf(stderr, "InnoDB: ibuf cursor restoration fails!\n"); fprintf(stderr, "InnoDB: ibuf cursor restoration fails!\n");
fprintf(stderr, "InnoDB: ibuf record inserted to page %lu\n", fprintf(stderr, "InnoDB: ibuf record inserted to page %lu\n",
page_no); page_no);
fflush(stderr);
rec_print(btr_pcur_get_rec(pcur)); rec_print(btr_pcur_get_rec(pcur));
rec_print(pcur->old_rec); rec_print(pcur->old_rec);
dtuple_print(search_tuple); dtuple_print(search_tuple);
rec_print(page_rec_get_next(btr_pcur_get_rec(pcur))); rec_print(page_rec_get_next(btr_pcur_get_rec(pcur)));
fflush(stdout);
mtr_commit(mtr); mtr_commit(mtr);
fprintf(stderr, "InnoDB: Validating insert buffer tree:\n"); fprintf(stderr, "InnoDB: Validating insert buffer tree:\n");
ut_a(btr_validate_tree(ibuf_data->index->tree)); ut_a(btr_validate_tree(ibuf_data->index->tree));
fprintf(stderr, "InnoDB: Ibuf tree ok\n");
fprintf(stderr, "InnoDB: ibuf tree ok\n");
fflush(stderr);
} }
ut_a(success); ut_a(success);
......
...@@ -44,6 +44,8 @@ Created 5/24/1996 Heikki Tuuri ...@@ -44,6 +44,8 @@ Created 5/24/1996 Heikki Tuuri
#define DB_CORRUPTION 39 /* data structure corruption noticed */ #define DB_CORRUPTION 39 /* data structure corruption noticed */
#define DB_COL_APPEARS_TWICE_IN_INDEX 40 /* InnoDB cannot handle an index #define DB_COL_APPEARS_TWICE_IN_INDEX 40 /* InnoDB cannot handle an index
where same column appears twice */ where same column appears twice */
#define DB_CANNOT_DROP_CONSTRAINT 40 /* dropping a foreign key constraint
from a table failed */
/* The following are partial failure codes */ /* The following are partial failure codes */
#define DB_FAIL 1000 #define DB_FAIL 1000
......
...@@ -219,6 +219,24 @@ dict_create_foreign_constraints( ...@@ -219,6 +219,24 @@ dict_create_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 */
/************************************************************************** /**************************************************************************
Parses the CONSTRAINT id's to be dropped in an ALTER TABLE statement. */
ulint
dict_foreign_parse_drop_constraints(
/*================================*/
/* out: DB_SUCCESS or
DB_CANNOT_DROP_CONSTRAINT if
syntax error or the constraint
id does not match */
mem_heap_t* heap, /* in: heap from which we can
allocate memory */
trx_t* trx, /* in: transaction */
dict_table_t* table, /* in: table */
ulint* n, /* out: number of constraints
to drop */
char*** constraints_to_drop); /* out: id's of the
constraints to drop */
/**************************************************************************
Returns a table object and memoryfixes it. NOTE! This is a high-level Returns a table object and memoryfixes it. NOTE! This is a high-level
function to be used mainly from outside the 'dict' directory. Inside this function to be used mainly from outside the 'dict' directory. Inside this
directory dict_table_get_low is usually the appropriate function. */ directory dict_table_get_low is usually the appropriate function. */
...@@ -333,6 +351,16 @@ dict_print_info_on_foreign_keys( ...@@ -333,6 +351,16 @@ dict_print_info_on_foreign_keys(
char* str, /* in/out: pointer to a string */ char* str, /* in/out: pointer to a string */
ulint len, /* in: space in str available for info */ ulint len, /* in: space in str available for info */
dict_table_t* table); /* in: table */ dict_table_t* table); /* in: table */
/**************************************************************************
Sprintfs to a string info on a foreign key of a table in a format suitable
for CREATE TABLE. */
char*
dict_print_info_on_foreign_key_in_create_format(
/*============================================*/
/* out: how far in buf we printed */
dict_foreign_t* foreign,/* in: foreign key constraint */
char* buf); /* in: buffer of at least 5000 bytes */
/************************************************************************ /************************************************************************
Gets the first index on the table (the clustered index). */ Gets the first index on the table (the clustered index). */
UNIV_INLINE UNIV_INLINE
...@@ -808,6 +836,13 @@ void ...@@ -808,6 +836,13 @@ void
dict_mutex_exit_for_mysql(void); dict_mutex_exit_for_mysql(void);
/*===========================*/ /*===========================*/
/* The following len must be at least 10000 bytes! */
#define DICT_FOREIGN_ERR_BUF_LEN 10000
/* Buffer for storing detailed information about the latest foreig key
error */
extern char* dict_foreign_err_buf;
extern mutex_t dict_foreign_err_mutex; /* mutex protecting the buffer */
extern dict_sys_t* dict_sys; /* the dictionary system */ extern dict_sys_t* dict_sys; /* the dictionary system */
extern rw_lock_t dict_operation_lock; extern rw_lock_t dict_operation_lock;
......
...@@ -35,7 +35,6 @@ row_ins_check_foreign_constraint( ...@@ -35,7 +35,6 @@ row_ins_check_foreign_constraint(
dictionary cache if they exist at all */ dictionary cache if they exist at all */
dict_table_t* table, /* in: if check_ref is TRUE, then the foreign dict_table_t* table, /* in: if check_ref is TRUE, then the foreign
table, else the referenced table */ table, else the referenced table */
dict_index_t* index, /* in: index in table */
dtuple_t* entry, /* in: index entry for index */ dtuple_t* entry, /* in: index entry for index */
que_thr_t* thr); /* in: query thread */ que_thr_t* thr); /* in: query thread */
/************************************************************************* /*************************************************************************
......
...@@ -3092,8 +3092,7 @@ lock_deadlock_recursive( ...@@ -3092,8 +3092,7 @@ lock_deadlock_recursive(
err_buf += strlen(err_buf); err_buf += strlen(err_buf);
err_buf += sprintf(err_buf, err_buf += sprintf(err_buf,
" LATEST DETECTED DEADLOCK:\n" "\n*** (1) TRANSACTION:\n");
"*** (1) TRANSACTION:\n");
trx_print(err_buf, wait_lock->trx); trx_print(err_buf, wait_lock->trx);
err_buf += strlen(err_buf); err_buf += strlen(err_buf);
...@@ -3935,24 +3934,15 @@ lock_print_info( ...@@ -3935,24 +3934,15 @@ lock_print_info(
return; return;
} }
buf += sprintf(buf, "Trx id counter %lu %lu\n",
ut_dulint_get_high(trx_sys->max_trx_id),
ut_dulint_get_low(trx_sys->max_trx_id));
buf += sprintf(buf,
"Purge done for trx's n:o < %lu %lu undo n:o < %lu %lu\n",
ut_dulint_get_high(purge_sys->purge_trx_no),
ut_dulint_get_low(purge_sys->purge_trx_no),
ut_dulint_get_high(purge_sys->purge_undo_no),
ut_dulint_get_low(purge_sys->purge_undo_no));
lock_mutex_enter_kernel(); lock_mutex_enter_kernel();
buf += sprintf(buf,
"Total number of lock structs in row lock hash table %lu\n",
lock_get_n_rec_locks());
if (lock_deadlock_found) { if (lock_deadlock_found) {
buf += sprintf(buf,
"------------------------\n"
"LATEST DETECTED DEADLOCK\n"
"------------------------\n");
if ((ulint)(buf_end - buf) if ((ulint)(buf_end - buf)
< 100 + strlen(lock_latest_err_buf)) { < 100 + strlen(lock_latest_err_buf)) {
...@@ -3972,6 +3962,26 @@ lock_print_info( ...@@ -3972,6 +3962,26 @@ lock_print_info(
return; return;
} }
buf += sprintf(buf,
"------------\n"
"TRANSACTIONS\n"
"------------\n");
buf += sprintf(buf, "Trx id counter %lu %lu\n",
ut_dulint_get_high(trx_sys->max_trx_id),
ut_dulint_get_low(trx_sys->max_trx_id));
buf += sprintf(buf,
"Purge done for trx's n:o < %lu %lu undo n:o < %lu %lu\n",
ut_dulint_get_high(purge_sys->purge_trx_no),
ut_dulint_get_low(purge_sys->purge_trx_no),
ut_dulint_get_high(purge_sys->purge_undo_no),
ut_dulint_get_low(purge_sys->purge_undo_no));
buf += sprintf(buf,
"Total number of lock structs in row lock hash table %lu\n",
lock_get_n_rec_locks());
buf += sprintf(buf, "LIST OF TRANSACTIONS FOR EACH SESSION:\n"); buf += sprintf(buf, "LIST OF TRANSACTIONS FOR EACH SESSION:\n");
/* First print info on non-active transactions */ /* First print info on non-active transactions */
......
...@@ -214,9 +214,14 @@ os_file_get_last_error(void) ...@@ -214,9 +214,14 @@ os_file_get_last_error(void)
"InnoDB: the directory. It may also be you have created a subdirectory\n" "InnoDB: the directory. It may also be you have created a subdirectory\n"
"InnoDB: of the same name as a data file.\n"); "InnoDB: of the same name as a data file.\n");
} else { } else {
if (strerror((int)err) != NULL) {
fprintf(stderr, fprintf(stderr,
"InnoDB: Look from section 13.2 at http://www.innodb.com/ibman.html\n" "InnoDB: Error number %lu means '%s'.\n", err, strerror((int)err));
"InnoDB: what the error number means.\n"); }
fprintf(stderr,
"InnoDB: See also section 13.2 at http://www.innodb.com/ibman.html\n"
"InnoDB: about operating system error numbers.\n");
} }
} }
...@@ -252,9 +257,14 @@ os_file_get_last_error(void) ...@@ -252,9 +257,14 @@ os_file_get_last_error(void)
"InnoDB: The error means mysqld does not have the access rights to\n" "InnoDB: The error means mysqld does not have the access rights to\n"
"InnoDB: the directory.\n"); "InnoDB: the directory.\n");
} else { } else {
if (strerror((int)err) != NULL) {
fprintf(stderr,
"InnoDB: Error number %lu means '%s'.\n", err, strerror((int)err));
}
fprintf(stderr, fprintf(stderr,
"InnoDB: Look from section 13.2 at http://www.innodb.com/ibman.html\n" "InnoDB: See also section 13.2 at http://www.innodb.com/ibman.html\n"
"InnoDB: what the error number means or use the perror program of MySQL.\n"); "InnoDB: about operating system error numbers.\n");
} }
} }
......
...@@ -437,6 +437,111 @@ row_ins_cascade_calc_update_vec( ...@@ -437,6 +437,111 @@ row_ins_cascade_calc_update_vec(
return(n_fields_updated); return(n_fields_updated);
} }
/*************************************************************************
Reports a foreign key error associated with an update or a delete of a
parent table index entry. */
static
void
row_ins_foreign_report_err(
/*=======================*/
char* errstr, /* in: error string from the viewpoint
of the parent table */
que_thr_t* thr, /* in: query thread whose run_node
is an update node */
dict_foreign_t* foreign, /* in: foreign key constraint */
rec_t* rec, /* in: a matching index record in the
child table */
dtuple_t* entry) /* in: index entry in the parent
table */
{
char* buf = dict_foreign_err_buf;
mutex_enter(&dict_foreign_err_mutex);
ut_sprintf_timestamp(buf);
sprintf(buf + strlen(buf), " Transaction:\n");
trx_print(buf + strlen(buf), thr_get_trx(thr));
sprintf(buf + strlen(buf),
"Foreign key constraint fails for table %.500s:\n",
foreign->foreign_table_name);
dict_print_info_on_foreign_key_in_create_format(
foreign, buf + strlen(buf));
sprintf(buf + strlen(buf), "\n%s", errstr);
sprintf(buf + strlen(buf),
" in parent table, in index %.500s tuple:\n",
foreign->referenced_index->name);
if (entry) {
dtuple_sprintf(buf + strlen(buf), 1000, entry);
}
sprintf(buf + strlen(buf),
"\nBut in child table %.500s, in index %.500s, there is a record:\n",
foreign->foreign_table_name, foreign->foreign_index->name);
if (rec) {
rec_sprintf(buf + strlen(buf), 1000, rec);
}
sprintf(buf + strlen(buf), "\n");
ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
mutex_exit(&dict_foreign_err_mutex);
}
/*************************************************************************
Reports a foreign key error to dict_foreign_err_buf when we are trying
to add an index entry to a child table. Note that the adding may be the result
of an update, too. */
static
void
row_ins_foreign_report_add_err(
/*===========================*/
que_thr_t* thr, /* in: query thread whose run_node
is an insert node */
dict_foreign_t* foreign, /* in: foreign key constraint */
rec_t* rec, /* in: a record in the parent table:
it does not match entry because we
have an error! */
dtuple_t* entry) /* in: index entry to insert in the
child table */
{
char* buf = dict_foreign_err_buf;
mutex_enter(&dict_foreign_err_mutex);
ut_sprintf_timestamp(buf);
sprintf(buf + strlen(buf), " Transaction:\n");
trx_print(buf + strlen(buf), thr_get_trx(thr));
sprintf(buf + strlen(buf),
"Foreign key constraint fails for table %.500s:\n",
foreign->foreign_table_name);
dict_print_info_on_foreign_key_in_create_format(
foreign, buf + strlen(buf));
sprintf(buf + strlen(buf),
"\nTrying to add in child table, in index %.500s tuple:\n",
foreign->foreign_index->name);
if (entry) {
dtuple_sprintf(buf + strlen(buf), 1000, entry);
}
sprintf(buf + strlen(buf),
"\nBut in parent table %.500s, in index %.500s,\n"
"the closest match we can find is record:\n",
foreign->referenced_table_name,
foreign->referenced_index->name);
if (rec && page_rec_is_supremum(rec)) {
/* If the cursor ended on a supremum record, it is better
to report the previous record in the error message, so that
the user gets a more descriptive error message. */
rec = page_rec_get_prev(rec);
}
if (rec) {
rec_sprintf(buf + strlen(buf), 1000, rec);
}
sprintf(buf + strlen(buf), "\n");
ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
mutex_exit(&dict_foreign_err_mutex);
}
/************************************************************************* /*************************************************************************
Perform referential actions or checks when a parent row is deleted or updated Perform referential actions or checks when a parent row is deleted or updated
and the constraint had an ON DELETE or ON UPDATE condition which was not and the constraint had an ON DELETE or ON UPDATE condition which was not
...@@ -453,6 +558,8 @@ row_ins_foreign_check_on_constraint( ...@@ -453,6 +558,8 @@ row_ins_foreign_check_on_constraint(
type is != 0 */ type is != 0 */
btr_pcur_t* pcur, /* in: cursor placed on a matching btr_pcur_t* pcur, /* in: cursor placed on a matching
index record in the child table */ index record in the child table */
dtuple_t* entry, /* in: index entry in the parent
table */
mtr_t* mtr) /* in: mtr holding the latch of pcur mtr_t* mtr) /* in: mtr holding the latch of pcur
page */ page */
{ {
...@@ -506,6 +613,10 @@ row_ins_foreign_check_on_constraint( ...@@ -506,6 +613,10 @@ row_ins_foreign_check_on_constraint(
return(DB_SUCCESS); return(DB_SUCCESS);
} }
row_ins_foreign_report_err((char*)"Trying to delete",
thr, foreign,
btr_pcur_get_rec(pcur), entry);
return(DB_ROW_IS_REFERENCED); return(DB_ROW_IS_REFERENCED);
} }
...@@ -523,6 +634,10 @@ row_ins_foreign_check_on_constraint( ...@@ -523,6 +634,10 @@ row_ins_foreign_check_on_constraint(
return(DB_SUCCESS); return(DB_SUCCESS);
} }
row_ins_foreign_report_err((char*)"Trying to update",
thr, foreign,
btr_pcur_get_rec(pcur), entry);
return(DB_ROW_IS_REFERENCED); return(DB_ROW_IS_REFERENCED);
} }
...@@ -580,6 +695,10 @@ row_ins_foreign_check_on_constraint( ...@@ -580,6 +695,10 @@ row_ins_foreign_check_on_constraint(
err = DB_ROW_IS_REFERENCED; err = DB_ROW_IS_REFERENCED;
row_ins_foreign_report_err(
(char*)"Trying an update, possibly causing a cyclic cascaded update\n"
"in the child table,", thr, foreign, btr_pcur_get_rec(pcur), entry);
goto nonstandard_exit_func; goto nonstandard_exit_func;
} }
...@@ -809,7 +928,6 @@ row_ins_check_foreign_constraint( ...@@ -809,7 +928,6 @@ row_ins_check_foreign_constraint(
dictionary cache if they exist at all */ dictionary cache if they exist at all */
dict_table_t* table, /* in: if check_ref is TRUE, then the foreign dict_table_t* table, /* in: if check_ref is TRUE, then the foreign
table, else the referenced table */ table, else the referenced table */
dict_index_t* index __attribute__((unused)),/* in: index in table */
dtuple_t* entry, /* in: index entry for index */ dtuple_t* entry, /* in: index entry for index */
que_thr_t* thr) /* in: query thread */ que_thr_t* thr) /* in: query thread */
{ {
...@@ -824,6 +942,7 @@ row_ins_check_foreign_constraint( ...@@ -824,6 +942,7 @@ row_ins_check_foreign_constraint(
int cmp; int cmp;
ulint err; ulint err;
ulint i; ulint i;
char* buf = dict_foreign_err_buf;
mtr_t mtr; mtr_t mtr;
run_again: run_again:
...@@ -884,6 +1003,25 @@ run_again: ...@@ -884,6 +1003,25 @@ run_again:
if (check_table == NULL) { if (check_table == NULL) {
if (check_ref) { if (check_ref) {
mutex_enter(&dict_foreign_err_mutex);
ut_sprintf_timestamp(buf);
sprintf(buf + strlen(buf), " Transaction:\n");
trx_print(buf + strlen(buf), thr_get_trx(thr));
sprintf(buf + strlen(buf),
"Foreign key constraint fails for table %.500s:\n",
foreign->foreign_table_name);
dict_print_info_on_foreign_key_in_create_format(
foreign, buf + strlen(buf));
sprintf(buf + strlen(buf),
"\nTrying to add to index %.500s tuple:\n", foreign->foreign_index->name);
dtuple_sprintf(buf + strlen(buf), 1000, entry);
sprintf(buf + strlen(buf),
"\nBut the parent table %.500s does not currently exist!\n",
foreign->referenced_table_name);
ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
mutex_exit(&dict_foreign_err_mutex);
return(DB_NO_REFERENCED_ROW); return(DB_NO_REFERENCED_ROW);
} }
...@@ -949,7 +1087,8 @@ run_again: ...@@ -949,7 +1087,8 @@ run_again:
if (cmp == 0) { if (cmp == 0) {
if (rec_get_deleted_flag(rec)) { if (rec_get_deleted_flag(rec)) {
err = row_ins_set_shared_rec_lock(LOCK_ORDINARY, err = row_ins_set_shared_rec_lock(
LOCK_ORDINARY,
rec, check_index, thr); rec, check_index, thr);
if (err != DB_SUCCESS) { if (err != DB_SUCCESS) {
...@@ -989,13 +1128,17 @@ run_again: ...@@ -989,13 +1128,17 @@ run_again:
err = err =
row_ins_foreign_check_on_constraint( row_ins_foreign_check_on_constraint(
thr, foreign, &pcur, &mtr); thr, foreign, &pcur, entry,
&mtr);
if (err != DB_SUCCESS) { if (err != DB_SUCCESS) {
break; break;
} }
} else { } else {
row_ins_foreign_report_err(
(char*)"Trying to delete or update",
thr, foreign, rec, entry);
err = DB_ROW_IS_REFERENCED; err = DB_ROW_IS_REFERENCED;
break; break;
} }
...@@ -1012,6 +1155,8 @@ run_again: ...@@ -1012,6 +1155,8 @@ run_again:
if (check_ref) { if (check_ref) {
err = DB_NO_REFERENCED_ROW; err = DB_NO_REFERENCED_ROW;
row_ins_foreign_report_add_err(
thr, foreign, rec, entry);
} else { } else {
err = DB_SUCCESS; err = DB_SUCCESS;
} }
...@@ -1025,6 +1170,9 @@ next_rec: ...@@ -1025,6 +1170,9 @@ next_rec:
if (!moved) { if (!moved) {
if (check_ref) { if (check_ref) {
rec = btr_pcur_get_rec(&pcur);
row_ins_foreign_report_add_err(
thr, foreign, rec, entry);
err = DB_NO_REFERENCED_ROW; err = DB_NO_REFERENCED_ROW;
} else { } else {
err = DB_SUCCESS; err = DB_SUCCESS;
...@@ -1100,7 +1248,7 @@ row_ins_check_foreign_constraints( ...@@ -1100,7 +1248,7 @@ row_ins_check_foreign_constraints(
} }
err = row_ins_check_foreign_constraint(TRUE, foreign, err = row_ins_check_foreign_constraint(TRUE, foreign,
table, index, entry, thr); table, entry, thr);
if (got_s_lock) { if (got_s_lock) {
row_mysql_unfreeze_data_dictionary(trx); row_mysql_unfreeze_data_dictionary(trx);
} }
......
...@@ -1156,7 +1156,7 @@ row_mysql_recover_tmp_table( ...@@ -1156,7 +1156,7 @@ row_mysql_recover_tmp_table(
return(DB_ERROR); return(DB_ERROR);
} }
if (0 == ut_memcmp(ptr, "/rsql", 5)) { if (0 == ut_memcmp(ptr, (char*)"/rsql", 5)) {
ptr++; ptr++;
*ptr = '#'; *ptr = '#';
...@@ -1281,9 +1281,9 @@ row_create_table_for_mysql( ...@@ -1281,9 +1281,9 @@ row_create_table_for_mysql(
trx->op_info = (char *) "creating table"; trx->op_info = (char *) "creating table";
if (0 == ut_strcmp(table->name, "mysql/host") if (0 == ut_strcmp(table->name, (char*)"mysql/host")
|| 0 == ut_strcmp(table->name, "mysql/user") || 0 == ut_strcmp(table->name, (char*)"mysql/user")
|| 0 == ut_strcmp(table->name, "mysql/db")) { || 0 == ut_strcmp(table->name, (char*)"mysql/db")) {
fprintf(stderr, fprintf(stderr,
"InnoDB: Error: trying to create a MySQL system table %s of type InnoDB.\n" "InnoDB: Error: trying to create a MySQL system table %s of type InnoDB.\n"
...@@ -1303,7 +1303,7 @@ row_create_table_for_mysql( ...@@ -1303,7 +1303,7 @@ row_create_table_for_mysql(
if (namelen >= keywordlen if (namelen >= keywordlen
&& 0 == ut_memcmp(table->name + namelen - keywordlen, && 0 == ut_memcmp(table->name + namelen - keywordlen,
"_recover_innodb_tmp_table", keywordlen)) { (char*)"_recover_innodb_tmp_table", keywordlen)) {
/* MySQL prevents accessing of tables whose name begins /* MySQL prevents accessing of tables whose name begins
with #sql, that is temporary tables. If mysqld crashes in with #sql, that is temporary tables. If mysqld crashes in
...@@ -1371,7 +1371,7 @@ row_create_table_for_mysql( ...@@ -1371,7 +1371,7 @@ row_create_table_for_mysql(
if (namelen >= keywordlen if (namelen >= keywordlen
&& 0 == ut_memcmp(table->name + namelen - keywordlen, && 0 == ut_memcmp(table->name + namelen - keywordlen,
"innodb_mem_validate", keywordlen)) { (char*)"innodb_mem_validate", keywordlen)) {
/* We define here a debugging feature intended for /* We define here a debugging feature intended for
developers */ developers */
...@@ -1481,7 +1481,7 @@ row_create_index_for_mysql( ...@@ -1481,7 +1481,7 @@ row_create_index_for_mysql(
if (namelen >= keywordlen if (namelen >= keywordlen
&& 0 == ut_memcmp( && 0 == ut_memcmp(
index->table_name + namelen - keywordlen, index->table_name + namelen - keywordlen,
"_recover_innodb_tmp_table", keywordlen)) { (char*)"_recover_innodb_tmp_table", keywordlen)) {
return(DB_SUCCESS); return(DB_SUCCESS);
} }
...@@ -1586,7 +1586,7 @@ row_table_add_foreign_constraints( ...@@ -1586,7 +1586,7 @@ row_table_add_foreign_constraints(
if (namelen >= keywordlen if (namelen >= keywordlen
&& 0 == ut_memcmp( && 0 == ut_memcmp(
name + namelen - keywordlen, name + namelen - keywordlen,
"_recover_innodb_tmp_table", keywordlen)) { (char*)"_recover_innodb_tmp_table", keywordlen)) {
return(DB_SUCCESS); return(DB_SUCCESS);
} }
...@@ -1808,7 +1808,6 @@ row_drop_table_for_mysql( ...@@ -1808,7 +1808,6 @@ row_drop_table_for_mysql(
ulint len; ulint len;
ulint namelen; ulint namelen;
ulint keywordlen; ulint keywordlen;
ulint rounds = 0;
ibool locked_dictionary = FALSE; ibool locked_dictionary = FALSE;
char buf[10000]; char buf[10000];
...@@ -2155,7 +2154,7 @@ row_is_mysql_tmp_table_name( ...@@ -2155,7 +2154,7 @@ row_is_mysql_tmp_table_name(
ulint i; ulint i;
for (i = 0; i <= ut_strlen(name) - 5; i++) { for (i = 0; i <= ut_strlen(name) - 5; i++) {
if (ut_memcmp(name + i, "/#sql", 5) == 0) { if (ut_memcmp(name + i, (char*)"/#sql", 5) == 0) {
return(TRUE); return(TRUE);
} }
...@@ -2177,12 +2176,16 @@ row_rename_table_for_mysql( ...@@ -2177,12 +2176,16 @@ row_rename_table_for_mysql(
{ {
dict_table_t* table; dict_table_t* table;
que_thr_t* thr; que_thr_t* thr;
que_t* graph; que_t* graph = NULL;
ulint err; ulint err;
char* str1; char* str1;
char* str2; char* str2;
char* str3; char* str3;
mem_heap_t* heap = NULL;
char** constraints_to_drop = NULL;
ulint n_constraints_to_drop = 0;
ulint len; ulint len;
ulint i;
char buf[10000]; char buf[10000];
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
...@@ -2201,9 +2204,9 @@ row_rename_table_for_mysql( ...@@ -2201,9 +2204,9 @@ row_rename_table_for_mysql(
return(DB_ERROR); return(DB_ERROR);
} }
if (0 == ut_strcmp(new_name, "mysql/host") if (0 == ut_strcmp(new_name, (char*)"mysql/host")
|| 0 == ut_strcmp(new_name, "mysql/user") || 0 == ut_strcmp(new_name, (char*)"mysql/user")
|| 0 == ut_strcmp(new_name, "mysql/db")) { || 0 == ut_strcmp(new_name, (char*)"mysql/db")) {
fprintf(stderr, fprintf(stderr,
"InnoDB: Error: trying to create a MySQL system table %s of type InnoDB.\n" "InnoDB: Error: trying to create a MySQL system table %s of type InnoDB.\n"
...@@ -2217,6 +2220,19 @@ row_rename_table_for_mysql( ...@@ -2217,6 +2220,19 @@ row_rename_table_for_mysql(
trx->op_info = (char *) "renaming table"; trx->op_info = (char *) "renaming table";
trx_start_if_not_started(trx); trx_start_if_not_started(trx);
/* Serialize data dictionary operations with dictionary mutex:
no deadlocks can occur then in these operations */
row_mysql_lock_data_dictionary(trx);
table = dict_table_get_low(old_name);
if (!table) {
err = DB_TABLE_NOT_FOUND;
goto funct_exit;
}
str1 = (char *) str1 = (char *)
"PROCEDURE RENAME_TABLE_PROC () IS\n" "PROCEDURE RENAME_TABLE_PROC () IS\n"
"new_table_name CHAR;\n" "new_table_name CHAR;\n"
...@@ -2229,14 +2245,43 @@ row_rename_table_for_mysql( ...@@ -2229,14 +2245,43 @@ row_rename_table_for_mysql(
if (row_is_mysql_tmp_table_name(new_name)) { if (row_is_mysql_tmp_table_name(new_name)) {
/* We want to preserve the original foreign key /* MySQL is doing an ALTER TABLE command and it renames the
constraint definitions despite the name change */ original table to a temporary table name. We want to preserve
the original foreign key constraint definitions despite the
name change. An exception is those constraints for which
the ALTER TABLE contained DROP FOREIGN KEY <foreign key id>.*/
str3 = (char*) heap = mem_heap_create(100);
err = dict_foreign_parse_drop_constraints(heap, trx,
table,
&n_constraints_to_drop,
&constraints_to_drop);
if (err != DB_SUCCESS) {
goto funct_exit;
}
str3 = mem_heap_alloc(heap,
1000 + 500 * n_constraints_to_drop);
*str3 = '\0';
sprintf(str3,
"';\n" "';\n"
"UPDATE SYS_TABLES SET NAME = new_table_name\n" "UPDATE SYS_TABLES SET NAME = new_table_name\n"
"WHERE NAME = old_table_name;\n" "WHERE NAME = old_table_name;\n");
"END;\n";
for (i = 0; i < n_constraints_to_drop; i++) {
sprintf(str3 + strlen(str3),
"DELETE FROM SYS_FOREIGN_COLS WHERE ID = '%s';\n"
"DELETE FROM SYS_FOREIGN WHERE ID = '%s';\n",
constraints_to_drop[i],
constraints_to_drop[i]);
}
sprintf(str3 + strlen(str3),
"END;\n");
ut_a(strlen(str3) < 1000 + 500 * n_constraints_to_drop);
} else { } else {
str3 = (char*) str3 = (char*)
"';\n" "';\n"
...@@ -2267,13 +2312,6 @@ row_rename_table_for_mysql( ...@@ -2267,13 +2312,6 @@ row_rename_table_for_mysql(
ut_memcpy(buf + len, str3, ut_strlen(str3) + 1); ut_memcpy(buf + len, str3, ut_strlen(str3) + 1);
/* Serialize data dictionary operations with dictionary mutex:
no deadlocks can occur then in these operations */
row_mysql_lock_data_dictionary(trx);
table = dict_table_get_low(old_name);
graph = pars_sql(buf); graph = pars_sql(buf);
ut_a(graph); ut_a(graph);
...@@ -2283,12 +2321,6 @@ row_rename_table_for_mysql( ...@@ -2283,12 +2321,6 @@ row_rename_table_for_mysql(
graph->fork_type = QUE_FORK_MYSQL_INTERFACE; graph->fork_type = QUE_FORK_MYSQL_INTERFACE;
if (!table) {
err = DB_TABLE_NOT_FOUND;
goto funct_exit;
}
ut_a(thr = que_fork_start_command(graph, SESS_COMM_EXECUTE, 0)); ut_a(thr = que_fork_start_command(graph, SESS_COMM_EXECUTE, 0));
que_run_threads(thr); que_run_threads(thr);
...@@ -2329,6 +2361,13 @@ row_rename_table_for_mysql( ...@@ -2329,6 +2361,13 @@ row_rename_table_for_mysql(
if (row_is_mysql_tmp_table_name(old_name)) { if (row_is_mysql_tmp_table_name(old_name)) {
/* MySQL is doing an ALTER TABLE command and it
renames the created temporary table to the name
of the original table. In the ALTER TABLE we maybe
created some FOREIGN KEY constraints for the temporary
table. But we want to load also the foreign key
constraint definitions for the original table name. */
err = dict_load_foreigns(new_name); err = dict_load_foreigns(new_name);
if (err != DB_SUCCESS) { if (err != DB_SUCCESS) {
...@@ -2354,7 +2393,13 @@ row_rename_table_for_mysql( ...@@ -2354,7 +2393,13 @@ row_rename_table_for_mysql(
funct_exit: funct_exit:
row_mysql_unlock_data_dictionary(trx); row_mysql_unlock_data_dictionary(trx);
if (graph) {
que_graph_free(graph); que_graph_free(graph);
}
if (heap) {
mem_heap_free(heap);
}
trx_commit_for_mysql(trx); trx_commit_for_mysql(trx);
......
...@@ -2601,6 +2601,24 @@ row_search_for_mysql( ...@@ -2601,6 +2601,24 @@ row_search_for_mysql(
printf("N tables locked %lu\n", trx->mysql_n_tables_locked); printf("N tables locked %lu\n", trx->mysql_n_tables_locked);
*/ */
/*-------------------------------------------------------------*/
/* PHASE 0: Release a possible s-latch we are holding on the
adaptive hash index latch if there is someone waiting behind */
if (trx->has_search_latch
&& btr_search_latch.writer != RW_LOCK_NOT_LOCKED) {
/* There is an x-latch request on the adaptive hash index:
release the s-latch to reduce starvation and wait for
BTR_SEA_TIMEOUT rounds before trying to keep it again over
calls from MySQL */
rw_lock_s_unlock(&btr_search_latch);
trx->has_search_latch = FALSE;
trx->search_latch_timeout = BTR_SEA_TIMEOUT;
}
/*-------------------------------------------------------------*/ /*-------------------------------------------------------------*/
/* PHASE 1: Try to pop the row from the prefetch cache */ /* PHASE 1: Try to pop the row from the prefetch cache */
...@@ -2737,22 +2755,6 @@ row_search_for_mysql( ...@@ -2737,22 +2755,6 @@ row_search_for_mysql(
and if we try that, we can deadlock on the adaptive and if we try that, we can deadlock on the adaptive
hash index semaphore! */ hash index semaphore! */
if (btr_search_latch.writer != RW_LOCK_NOT_LOCKED) {
/* There is an x-latch request: release
a possible s-latch to reduce starvation
and wait for BTR_SEA_TIMEOUT rounds before
trying to keep it again over calls from
MySQL */
if (trx->has_search_latch) {
rw_lock_s_unlock(&btr_search_latch);
trx->has_search_latch = FALSE;
}
trx->search_latch_timeout = BTR_SEA_TIMEOUT;
goto no_shortcut;
}
#ifndef UNIV_SEARCH_DEBUG #ifndef UNIV_SEARCH_DEBUG
if (!trx->has_search_latch) { if (!trx->has_search_latch) {
rw_lock_s_lock(&btr_search_latch); rw_lock_s_lock(&btr_search_latch);
...@@ -2810,7 +2812,6 @@ row_search_for_mysql( ...@@ -2810,7 +2812,6 @@ row_search_for_mysql(
} }
} }
no_shortcut:
/*-------------------------------------------------------------*/ /*-------------------------------------------------------------*/
/* PHASE 3: Open or restore index cursor position */ /* PHASE 3: Open or restore index cursor position */
......
...@@ -218,7 +218,7 @@ row_upd_check_references_constraints( ...@@ -218,7 +218,7 @@ row_upd_check_references_constraints(
being dropped while the check is running. */ being dropped while the check is running. */
err = row_ins_check_foreign_constraint(FALSE, foreign, err = row_ins_check_foreign_constraint(FALSE, foreign,
table, index, entry, thr); table, entry, thr);
if (foreign->foreign_table) { if (foreign->foreign_table) {
mutex_enter(&(dict_sys->mutex)); mutex_enter(&(dict_sys->mutex));
......
...@@ -1869,11 +1869,11 @@ retry: ...@@ -1869,11 +1869,11 @@ retry:
/* Go to wait for the event; when a thread leaves InnoDB it will /* Go to wait for the event; when a thread leaves InnoDB it will
release this thread */ release this thread */
trx->op_info = "waiting in InnoDB queue"; trx->op_info = (char*)"waiting in InnoDB queue";
os_event_wait(slot->event); os_event_wait(slot->event);
trx->op_info = ""; trx->op_info = (char*)"";
os_fast_mutex_lock(&srv_conc_mutex); os_fast_mutex_lock(&srv_conc_mutex);
...@@ -2346,9 +2346,19 @@ srv_sprintf_innodb_monitor( ...@@ -2346,9 +2346,19 @@ srv_sprintf_innodb_monitor(
buf = buf + strlen(buf); buf = buf + strlen(buf);
ut_a(buf < buf_end + 1500); ut_a(buf < buf_end + 1500);
buf += sprintf(buf, "------------\n" if (*dict_foreign_err_buf != '\0') {
"TRANSACTIONS\n" buf += sprintf(buf,
"------------\n"); "------------------------\n"
"LATEST FOREIGN KEY ERROR\n"
"------------------------\n");
if (buf_end - buf > 6000) {
buf+= sprintf(buf, "%.4000s", dict_foreign_err_buf);
}
}
ut_a(buf < buf_end + 1500);
lock_print_info(buf, buf_end); lock_print_info(buf, buf_end);
buf = buf + strlen(buf); buf = buf + strlen(buf);
......
...@@ -204,7 +204,7 @@ ut_get_year_month_day( ...@@ -204,7 +204,7 @@ ut_get_year_month_day(
cal_tm_ptr = localtime(&tm); cal_tm_ptr = localtime(&tm);
*year = (ulint)cal_tm_ptr->tm_year; *year = (ulint)cal_tm_ptr->tm_year + 1900;
*month = (ulint)cal_tm_ptr->tm_mon + 1; *month = (ulint)cal_tm_ptr->tm_mon + 1;
*day = (ulint)cal_tm_ptr->tm_mday; *day = (ulint)cal_tm_ptr->tm_mday;
#endif #endif
......
...@@ -47,7 +47,7 @@ sum(all a) count(all a) avg(all a) std(all a) bit_or(all a) bit_and(all a) min(a ...@@ -47,7 +47,7 @@ sum(all a) count(all a) avg(all a) std(all a) bit_or(all a) bit_and(all a) min(a
21 6 3.5000 1.7078 7 0 1 6 E 21 6 3.5000 1.7078 7 0 1 6 E
select grp, sum(a),count(a),avg(a),std(a),bit_or(a),bit_and(a),min(a),max(a),min(c),max(c) from t1 group by grp; select grp, sum(a),count(a),avg(a),std(a),bit_or(a),bit_and(a),min(a),max(a),min(c),max(c) from t1 group by grp;
grp sum(a) count(a) avg(a) std(a) bit_or(a) bit_and(a) min(a) max(a) min(c) max(c) grp sum(a) count(a) avg(a) std(a) bit_or(a) bit_and(a) min(a) max(a) min(c) max(c)
NULL 0 0 NULL NULL 0 0 NULL NULL NULL NULL 0 NULL NULL 0 0 NULL NULL
1 1 1 1.0000 0.0000 1 1 1 1 a a 1 1 1 1.0000 0.0000 1 1 1 1 a a
2 5 2 2.5000 0.5000 3 2 2 3 b c 2 5 2 2.5000 0.5000 3 2 2 3 b c
3 15 3 5.0000 0.8165 7 4 4 6 C E 3 15 3 5.0000 0.8165 7 4 4 6 C E
...@@ -204,3 +204,44 @@ select max(t2.a1) from t1 left outer join t2 on t1.a2=t2.a1 and 1=0 where t2.a1= ...@@ -204,3 +204,44 @@ select max(t2.a1) from t1 left outer join t2 on t1.a2=t2.a1 and 1=0 where t2.a1=
max(t2.a1) max(t2.a1)
NULL NULL
drop table t1,t2; drop table t1,t2;
CREATE TABLE t1 (a int, b int);
select count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1;
count(b) sum(b) avg(b) std(b) min(b) max(b) bit_and(b) bit_or(b)
0 NULL NULL NULL NULL NULL -1 0
select a,count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1 group by a;
a count(b) sum(b) avg(b) std(b) min(b) max(b) bit_and(b) bit_or(b)
insert into t1 values (1,null);
select a,count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1 group by a;
a count(b) sum(b) avg(b) std(b) min(b) max(b) bit_and(b) bit_or(b)
1 0 NULL NULL NULL NULL NULL -1 0
insert into t1 values (1,null);
insert into t1 values (2,null);
select a,count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1 group by a;
a count(b) sum(b) avg(b) std(b) min(b) max(b) bit_and(b) bit_or(b)
1 0 NULL NULL NULL NULL NULL 0 0
2 0 NULL NULL NULL NULL NULL 0 0
select SQL_BIG_RESULT a,count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1 group by a;
a count(b) sum(b) avg(b) std(b) min(b) max(b) bit_and(b) bit_or(b)
1 0 NULL NULL NULL NULL NULL -1 0
2 0 NULL NULL NULL NULL NULL -1 0
insert into t1 values (2,1);
select a,count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1 group by a;
a count(b) sum(b) avg(b) std(b) min(b) max(b) bit_and(b) bit_or(b)
1 0 NULL NULL NULL NULL NULL 0 0
2 1 1 1.0000 0.0000 1 1 0 1
select SQL_BIG_RESULT a,count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1 group by a;
a count(b) sum(b) avg(b) std(b) min(b) max(b) bit_and(b) bit_or(b)
1 0 NULL NULL NULL NULL NULL -1 0
2 1 1 1.0000 0.0000 1 1 1 1
insert into t1 values (3,1);
select a,count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1 group by a;
a count(b) sum(b) avg(b) std(b) min(b) max(b) bit_and(b) bit_or(b)
1 0 NULL NULL NULL NULL NULL 0 0
2 1 1 1.0000 0.0000 1 1 0 1
3 1 1 1.0000 0.0000 1 1 1 1
select SQL_BIG_RESULT a,count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1 group by a;
a count(b) sum(b) avg(b) std(b) min(b) max(b) bit_and(b) bit_or(b)
1 0 NULL NULL NULL NULL NULL -1 0
2 1 1 1.0000 0.0000 1 1 1 1
3 1 1 1.0000 0.0000 1 1 1 1
drop table t1;
...@@ -126,3 +126,24 @@ select max(t1.a2) from t1 left outer join t2 on t1.a1=10 where t1.a1=20; ...@@ -126,3 +126,24 @@ select max(t1.a2) from t1 left outer join t2 on t1.a1=10 where t1.a1=20;
select max(t1.a2) from t1 left outer join t2 on t1.a1=10 where t1.a1=10; select max(t1.a2) from t1 left outer join t2 on t1.a1=10 where t1.a1=10;
select max(t2.a1) from t1 left outer join t2 on t1.a2=t2.a1 and 1=0 where t2.a1='AAA'; select max(t2.a1) from t1 left outer join t2 on t1.a2=t2.a1 and 1=0 where t2.a1='AAA';
drop table t1,t2; drop table t1,t2;
#
# Test of group function and NULL values
#
CREATE TABLE t1 (a int, b int);
select count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1;
select a,count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1 group by a;
insert into t1 values (1,null);
select a,count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1 group by a;
insert into t1 values (1,null);
insert into t1 values (2,null);
select a,count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1 group by a;
select SQL_BIG_RESULT a,count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1 group by a;
insert into t1 values (2,1);
select a,count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1 group by a;
select SQL_BIG_RESULT a,count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1 group by a;
insert into t1 values (3,1);
select a,count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1 group by a;
select SQL_BIG_RESULT a,count(b), sum(b), avg(b), std(b), min(b), max(b), bit_and(b), bit_or(b) from t1 group by a;
drop table t1;
...@@ -224,7 +224,7 @@ then ...@@ -224,7 +224,7 @@ then
if test -n "$open_files" if test -n "$open_files"
then then
ulimit -n $open_files ulimit -n $open_files
args="open-files-limit=$open_files $args" args="--open-files-limit=$open_files $args"
fi fi
if test -n "$core_file_size" if test -n "$core_file_size"
then then
......
...@@ -242,6 +242,10 @@ convert_error_code_to_mysql( ...@@ -242,6 +242,10 @@ convert_error_code_to_mysql(
return(HA_ERR_CANNOT_ADD_FOREIGN); return(HA_ERR_CANNOT_ADD_FOREIGN);
} else if (error == (int) DB_CANNOT_DROP_CONSTRAINT) {
return(HA_WRONG_CREATE_OPTION);
} else if (error == (int) DB_COL_APPEARS_TWICE_IN_INDEX) { } else if (error == (int) DB_COL_APPEARS_TWICE_IN_INDEX) {
return(HA_ERR_CRASHED); return(HA_ERR_CRASHED);
...@@ -3049,6 +3053,9 @@ ha_innobase::create( ...@@ -3049,6 +3053,9 @@ ha_innobase::create(
trx = trx_allocate_for_mysql(); trx = trx_allocate_for_mysql();
trx->mysql_thd = thd;
trx->mysql_query_str = &((*thd).query);
if (thd->options & OPTION_NO_FOREIGN_KEY_CHECKS) { if (thd->options & OPTION_NO_FOREIGN_KEY_CHECKS) {
trx->check_foreigns = FALSE; trx->check_foreigns = FALSE;
} }
...@@ -3231,6 +3238,9 @@ ha_innobase::delete_table( ...@@ -3231,6 +3238,9 @@ ha_innobase::delete_table(
trx = trx_allocate_for_mysql(); trx = trx_allocate_for_mysql();
trx->mysql_thd = current_thd;
trx->mysql_query_str = &((*current_thd).query);
name_len = strlen(name); name_len = strlen(name);
assert(name_len < 1000); assert(name_len < 1000);
...@@ -3309,6 +3319,8 @@ innobase_drop_database( ...@@ -3309,6 +3319,8 @@ innobase_drop_database(
casedn_str(namebuf); casedn_str(namebuf);
#endif #endif
trx = trx_allocate_for_mysql(); trx = trx_allocate_for_mysql();
trx->mysql_thd = current_thd;
trx->mysql_query_str = &((*current_thd).query);
error = row_drop_database_for_mysql(namebuf, trx); error = row_drop_database_for_mysql(namebuf, trx);
...@@ -3368,6 +3380,8 @@ ha_innobase::rename_table( ...@@ -3368,6 +3380,8 @@ ha_innobase::rename_table(
} }
trx = trx_allocate_for_mysql(); trx = trx_allocate_for_mysql();
trx->mysql_thd = current_thd;
trx->mysql_query_str = &((*current_thd).query);
name_len1 = strlen(from); name_len1 = strlen(from);
name_len2 = strlen(to); name_len2 = strlen(to);
......
...@@ -175,12 +175,14 @@ Item_sum_hybrid::fix_fields(THD *thd,TABLE_LIST *tables) ...@@ -175,12 +175,14 @@ Item_sum_hybrid::fix_fields(THD *thd,TABLE_LIST *tables)
void Item_sum_sum::reset() void Item_sum_sum::reset()
{ {
null_value=0; sum=0.0; Item_sum_sum::add(); null_value=1; sum=0.0; Item_sum_sum::add();
} }
bool Item_sum_sum::add() bool Item_sum_sum::add()
{ {
sum+=args[0]->val(); sum+=args[0]->val();
if (!args[0]->null_value)
null_value= 0;
return 0; return 0;
} }
...@@ -566,7 +568,9 @@ void Item_sum_sum::reset_field() ...@@ -566,7 +568,9 @@ void Item_sum_sum::reset_field()
{ {
double nr=args[0]->val(); // Nulls also return 0 double nr=args[0]->val(); // Nulls also return 0
float8store(result_field->ptr,nr); float8store(result_field->ptr,nr);
null_value=0; if (args[0]->null_value)
result_field->set_null();
else
result_field->set_notnull(); result_field->set_notnull();
} }
...@@ -623,7 +627,10 @@ void Item_sum_sum::update_field(int offset) ...@@ -623,7 +627,10 @@ void Item_sum_sum::update_field(int offset)
float8get(old_nr,res+offset); float8get(old_nr,res+offset);
nr=args[0]->val(); nr=args[0]->val();
if (!args[0]->null_value) if (!args[0]->null_value)
{
old_nr+=nr; old_nr+=nr;
result_field->set_notnull();
}
float8store(res,old_nr); float8store(res,old_nr);
} }
......
...@@ -92,9 +92,6 @@ public: ...@@ -92,9 +92,6 @@ public:
class Item_sum_int :public Item_sum_num class Item_sum_int :public Item_sum_num
{ {
void fix_length_and_dec()
{ decimals=0; max_length=21; maybe_null=null_value=0; }
public: public:
Item_sum_int(Item *item_par) :Item_sum_num(item_par) {} Item_sum_int(Item *item_par) :Item_sum_num(item_par) {}
Item_sum_int(List<Item> &list) :Item_sum_num(list) {} Item_sum_int(List<Item> &list) :Item_sum_num(list) {}
...@@ -102,6 +99,8 @@ public: ...@@ -102,6 +99,8 @@ public:
String *val_str(String*str); String *val_str(String*str);
enum Item_result result_type () const { return INT_RESULT; } enum Item_result result_type () const { return INT_RESULT; }
unsigned int size_of() { return sizeof(*this);} unsigned int size_of() { return sizeof(*this);}
void fix_length_and_dec()
{ decimals=0; max_length=21; maybe_null=null_value=0; }
}; };
...@@ -118,6 +117,7 @@ class Item_sum_sum :public Item_sum_num ...@@ -118,6 +117,7 @@ class Item_sum_sum :public Item_sum_num
double val(); double val();
void reset_field(); void reset_field();
void update_field(int offset); void update_field(int offset);
void no_rows_in_result() {}
const char *func_name() const { return "sum"; } const char *func_name() const { return "sum"; }
unsigned int size_of() { return sizeof(*this);} unsigned int size_of() { return sizeof(*this);}
}; };
...@@ -361,6 +361,8 @@ class Item_sum_bit :public Item_sum_int ...@@ -361,6 +361,8 @@ class Item_sum_bit :public Item_sum_int
longlong val_int(); longlong val_int();
void reset_field(); void reset_field();
unsigned int size_of() { return sizeof(*this);} unsigned int size_of() { return sizeof(*this);}
void fix_length_and_dec()
{ decimals=0; max_length=21; unsigned_flag=1; maybe_null=null_value=0; }
}; };
......
...@@ -921,6 +921,8 @@ void clean_up(bool print_message) ...@@ -921,6 +921,8 @@ void clean_up(bool print_message)
free_max_user_conn(); free_max_user_conn();
end_slave_list(); end_slave_list();
#ifdef HAVE_OPENSSL #ifdef HAVE_OPENSSL
if (ssl_acceptor_fd)
my_free((gptr) ssl_acceptor_fd, MYF(MY_ALLOW_ZERO_PTR));
free_des_key_file(); free_des_key_file();
#endif /* HAVE_OPENSSL */ #endif /* HAVE_OPENSSL */
#ifdef USE_REGEX #ifdef USE_REGEX
...@@ -3562,7 +3564,7 @@ relay logs", ...@@ -3562,7 +3564,7 @@ relay logs",
{"skip-stack-trace", OPT_SKIP_STACK_TRACE, {"skip-stack-trace", OPT_SKIP_STACK_TRACE,
"Don't print a stack trace on failure", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, "Don't print a stack trace on failure", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0,
0, 0, 0, 0}, 0, 0, 0, 0},
{"skip-symlink", OPT_SKIP_SYMLINKS, "Don't allow symlinking of tables", {"skip-symlink", OPT_SKIP_SYMLINKS, "Don't allow symlinking of tables. Depricated option. Use --skip-symbolic-links instead",
0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
{"skip-thread-priority", OPT_SKIP_PRIOR, {"skip-thread-priority", OPT_SKIP_PRIOR,
"Don't give threads different priorities.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, "Don't give threads different priorities.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0,
...@@ -3606,11 +3608,12 @@ replicating a LOAD DATA INFILE command", ...@@ -3606,11 +3608,12 @@ replicating a LOAD DATA INFILE command",
{"external-locking", OPT_USE_LOCKING, "Use system (external) locking. With this option enabled you can run myisamchk to test (not repair) tables while the MySQL server is running", {"external-locking", OPT_USE_LOCKING, "Use system (external) locking. With this option enabled you can run myisamchk to test (not repair) tables while the MySQL server is running",
(gptr*) &opt_external_locking, (gptr*) &opt_external_locking, (gptr*) &opt_external_locking, (gptr*) &opt_external_locking,
0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
#ifdef USE_SYMDIR {"use-symbolic-links", 's', "Enable symbolic link support. Depricated option; Use --symbolic-links instead",
{"use-symbolic-links", 's', "Enable symbolic link support", (gptr*) &my_use_symdir, (gptr*) &my_use_symdir, 0, GET_BOOL, NO_ARG,
IF_PURIFY(0,1), 0, 0, 0, 0, 0},
{"--symbolic-links", 's', "Enable symbolic link support",
(gptr*) &my_use_symdir, (gptr*) &my_use_symdir, 0, GET_BOOL, NO_ARG, (gptr*) &my_use_symdir, (gptr*) &my_use_symdir, 0, GET_BOOL, NO_ARG,
IF_PURIFY(0,1), 0, 0, 0, 0, 0}, IF_PURIFY(0,1), 0, 0, 0, 0, 0},
#endif
{"user", 'u', "Run mysqld daemon as user", 0, 0, 0, GET_STR, REQUIRED_ARG, {"user", 'u', "Run mysqld daemon as user", 0, 0, 0, GET_STR, REQUIRED_ARG,
0, 0, 0, 0, 0, 0}, 0, 0, 0, 0, 0, 0},
{"version", 'V', "Output version information and exit", 0, 0, 0, GET_NO_ARG, {"version", 'V', "Output version information and exit", 0, 0, 0, GET_NO_ARG,
...@@ -4424,9 +4427,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), ...@@ -4424,9 +4427,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
delay_key_write_options= (uint) DELAY_KEY_WRITE_NONE; delay_key_write_options= (uint) DELAY_KEY_WRITE_NONE;
myisam_concurrent_insert=0; myisam_concurrent_insert=0;
myisam_recover_options= HA_RECOVER_NONE; myisam_recover_options= HA_RECOVER_NONE;
my_disable_symlinks=1;
my_use_symdir=0; my_use_symdir=0;
have_symlink=SHOW_OPTION_DISABLED;
ha_open_options&= ~(HA_OPEN_ABORT_IF_CRASHED | HA_OPEN_DELAY_KEY_WRITE); ha_open_options&= ~(HA_OPEN_ABORT_IF_CRASHED | HA_OPEN_DELAY_KEY_WRITE);
#ifdef HAVE_QUERY_CACHE #ifdef HAVE_QUERY_CACHE
query_cache_size=0; query_cache_size=0;
...@@ -4473,9 +4474,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), ...@@ -4473,9 +4474,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
test_flags|=TEST_NO_STACKTRACE; test_flags|=TEST_NO_STACKTRACE;
break; break;
case (int) OPT_SKIP_SYMLINKS: case (int) OPT_SKIP_SYMLINKS:
my_disable_symlinks=1;
my_use_symdir=0; my_use_symdir=0;
have_symlink=SHOW_OPTION_DISABLED;
break; break;
case (int) OPT_BIND_ADDRESS: case (int) OPT_BIND_ADDRESS:
if (argument && isdigit(argument[0])) if (argument && isdigit(argument[0]))
......
...@@ -47,13 +47,6 @@ ...@@ -47,13 +47,6 @@
can't normally do this the client should have a bigger max_allowed_packet. can't normally do this the client should have a bigger max_allowed_packet.
*/ */
#ifdef MYSQL_SERVER
#define USE_QUERY_CACHE
extern uint test_flags;
extern void query_cache_insert(NET *net, const char *packet, ulong length);
#else
#endif
#if defined(__WIN__) || !defined(MYSQL_SERVER) #if defined(__WIN__) || !defined(MYSQL_SERVER)
/* The following is because alarms doesn't work on windows. */ /* The following is because alarms doesn't work on windows. */
#define NO_ALARM #define NO_ALARM
...@@ -62,16 +55,23 @@ extern void query_cache_insert(NET *net, const char *packet, ulong length); ...@@ -62,16 +55,23 @@ extern void query_cache_insert(NET *net, const char *packet, ulong length);
#ifndef NO_ALARM #ifndef NO_ALARM
#include "my_pthread.h" #include "my_pthread.h"
void sql_print_error(const char *format,...); void sql_print_error(const char *format,...);
extern ulong bytes_sent, bytes_received;
extern pthread_mutex_t LOCK_bytes_sent , LOCK_bytes_received;
#else #else
#undef statistic_add
#define statistic_add(A,B,C)
#define DONT_USE_THR_ALARM #define DONT_USE_THR_ALARM
#endif /* NO_ALARM */ #endif /* NO_ALARM */
#include "thr_alarm.h" #include "thr_alarm.h"
#ifdef MYSQL_SERVER
#define USE_QUERY_CACHE
extern uint test_flags;
extern void query_cache_insert(NET *net, const char *packet, ulong length);
extern ulong bytes_sent, bytes_received;
extern pthread_mutex_t LOCK_bytes_sent , LOCK_bytes_received;
#else
#undef statistic_add
#define statistic_add(A,B,C)
#endif
#define TEST_BLOCKING 8 #define TEST_BLOCKING 8
#define MAX_PACKET_LENGTH (256L*256L*256L-1) #define MAX_PACKET_LENGTH (256L*256L*256L-1)
......
...@@ -542,19 +542,19 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user, ...@@ -542,19 +542,19 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
break; break;
case SSL_TYPE_X509: /* Client should have any valid certificate. */ case SSL_TYPE_X509: /* Client should have any valid certificate. */
/* /*
Connections with non-valid certificates are dropped already We need to check for absence of SSL because without SSL
in sslaccept() anyway, so we do not check validity here. we should reject connection.
*/ */
if (SSL_get_peer_certificate(vio->ssl_)) if (vio_type(vio) == VIO_TYPE_SSL && SSL_get_peer_certificate(vio->ssl_))
user_access=acl_user->access; user_access=acl_user->access;
break; break;
case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */ case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */
/* /*
We do not check for absence of SSL because without SSL it does We need to check for absence of SSL because without SSL
not pass all checks here anyway. we should reject connection.
If cipher name is specified, we compare it to actual cipher in
use.
*/ */
if (vio_type(vio) == VIO_TYPE_SSL)
{
if (acl_user->ssl_cipher) if (acl_user->ssl_cipher)
{ {
DBUG_PRINT("info",("comparing ciphers: '%s' and '%s'", DBUG_PRINT("info",("comparing ciphers: '%s' and '%s'",
...@@ -564,6 +564,10 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user, ...@@ -564,6 +564,10 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
user_access=acl_user->access; user_access=acl_user->access;
else else
{ {
if (global_system_variables.log_warnings)
sql_print_error("X509 ciphers mismatch: should be '%s' but is '%s'",
acl_user->ssl_cipher,
SSL_get_cipher(vio->ssl_));
user_access=NO_ACCESS; user_access=NO_ACCESS;
break; break;
} }
...@@ -581,6 +585,9 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user, ...@@ -581,6 +585,9 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
acl_user->x509_issuer, ptr)); acl_user->x509_issuer, ptr));
if (strcmp(acl_user->x509_issuer, ptr)) if (strcmp(acl_user->x509_issuer, ptr))
{ {
if (global_system_variables.log_warnings)
sql_print_error("X509 issuer mismatch: should be '%s' but is '%s'",
acl_user->x509_issuer, ptr);
user_access=NO_ACCESS; user_access=NO_ACCESS;
free(ptr); free(ptr);
break; break;
...@@ -596,13 +603,19 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user, ...@@ -596,13 +603,19 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
DBUG_PRINT("info",("comparing subjects: '%s' and '%s'", DBUG_PRINT("info",("comparing subjects: '%s' and '%s'",
acl_user->x509_subject, ptr)); acl_user->x509_subject, ptr));
if (strcmp(acl_user->x509_subject,ptr)) if (strcmp(acl_user->x509_subject,ptr))
{
if (global_system_variables.log_warnings)
sql_print_error("X509 subject mismatch: '%s' vs '%s'",
acl_user->x509_subject, ptr);
user_access=NO_ACCESS; user_access=NO_ACCESS;
}
else else
user_access=acl_user->access; user_access=acl_user->access;
free(ptr); free(ptr);
} }
break; break;
} }
}
#else /* HAVE_OPENSSL */ #else /* HAVE_OPENSSL */
user_access=acl_user->access; user_access=acl_user->access;
#endif /* HAVE_OPENSSL */ #endif /* HAVE_OPENSSL */
......
...@@ -1228,7 +1228,7 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables, ...@@ -1228,7 +1228,7 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables,
case SHOW_RPL_STATUS: case SHOW_RPL_STATUS:
net_store_data(&packet2, rpl_status_type[(int)rpl_status]); net_store_data(&packet2, rpl_status_type[(int)rpl_status]);
break; break;
#ifndef EMBEDDED_LIBRARY #ifdef HAVE_REPLICATION
case SHOW_SLAVE_RUNNING: case SHOW_SLAVE_RUNNING:
{ {
LOCK_ACTIVE_MI; LOCK_ACTIVE_MI;
......
...@@ -29,8 +29,8 @@ skip-locking ...@@ -29,8 +29,8 @@ skip-locking
set-variable = key_buffer=384M set-variable = key_buffer=384M
set-variable = max_allowed_packet=1M set-variable = max_allowed_packet=1M
set-variable = table_cache=512 set-variable = table_cache=512
set-variable = sort_buffer=2M set-variable = sort_buffer_size=2M
set-variable = record_buffer=2M set-variable = read_buffer_size=2M
set-variable = myisam_sort_buffer_size=64M set-variable = myisam_sort_buffer_size=64M
set-variable = thread_cache=8 set-variable = thread_cache=8
# Try number of CPU's*2 for thread_concurrency # Try number of CPU's*2 for thread_concurrency
...@@ -53,7 +53,36 @@ log-bin ...@@ -53,7 +53,36 @@ log-bin
# but will not function as a master if omitted # but will not function as a master if omitted
server-id = 1 server-id = 1
# Replication Slave Server (comment out master section to use this) # Replication Slave (comment out master section to use this)
#
# To configure this host as a replication slave, you can choose between
# two methods :
#
# 1) Use the CHANGE MASTER TO command (fully described in our manual) -
# the syntax is:
#
# CHANGE MASTER TO MASTER_HOST=<host>, MASTER_PORT=<port>,
# MASTER_USER=<user>, MASTER_PASSWORD=<password> ;
#
# where you replace <host>, <user>, <password> by quoted strings and
# <port> by the master's port number (3306 by default).
#
# Example:
#
# CHANGE MASTER TO MASTER_HOST='125.564.12.1', MASTER_PORT=3306,
# MASTER_USER='joe', MASTER_PASSWORD='secret';
#
# OR
#
# 2) Set the variables below. However, in case you choose this method, then
# start replication for the first time (even unsuccessfully, for example
# if you mistyped the password in master-password and the slave fails to
# connect), the slave will create a master.info file, and any later
# change in this file to the variables' values below will be ignored and
# overridden by the content of the master.info file, unless you shutdown
# the slave server, delete master.info and restart the slaver server.
# For that reason, you may want to leave the lines below untouched
# (commented) and instead use CHANGE MASTER TO (see above)
# #
# required unique id between 2 and 2^32 - 1 # required unique id between 2 and 2^32 - 1
# (and different from the master) # (and different from the master)
...@@ -113,13 +142,13 @@ no-auto-rehash ...@@ -113,13 +142,13 @@ no-auto-rehash
[isamchk] [isamchk]
set-variable = key_buffer=256M set-variable = key_buffer=256M
set-variable = sort_buffer=256M set-variable = sort_buffer_size=256M
set-variable = read_buffer=2M set-variable = read_buffer=2M
set-variable = write_buffer=2M set-variable = write_buffer=2M
[myisamchk] [myisamchk]
set-variable = key_buffer=256M set-variable = key_buffer=256M
set-variable = sort_buffer=256M set-variable = sort_buffer_size=256M
set-variable = read_buffer=2M set-variable = read_buffer=2M
set-variable = write_buffer=2M set-variable = write_buffer=2M
......
...@@ -29,8 +29,8 @@ skip-locking ...@@ -29,8 +29,8 @@ skip-locking
set-variable = key_buffer=256M set-variable = key_buffer=256M
set-variable = max_allowed_packet=1M set-variable = max_allowed_packet=1M
set-variable = table_cache=256 set-variable = table_cache=256
set-variable = sort_buffer=1M set-variable = sort_buffer_size=1M
set-variable = record_buffer=1M set-variable = read_buffer_size=1M
set-variable = myisam_sort_buffer_size=64M set-variable = myisam_sort_buffer_size=64M
set-variable = thread_cache=8 set-variable = thread_cache=8
# Try number of CPU's*2 for thread_concurrency # Try number of CPU's*2 for thread_concurrency
...@@ -53,7 +53,36 @@ log-bin ...@@ -53,7 +53,36 @@ log-bin
# but will not function as a master if omitted # but will not function as a master if omitted
server-id = 1 server-id = 1
# Replication Slave Server (comment out master section to use this) # Replication Slave (comment out master section to use this)
#
# To configure this host as a replication slave, you can choose between
# two methods :
#
# 1) Use the CHANGE MASTER TO command (fully described in our manual) -
# the syntax is:
#
# CHANGE MASTER TO MASTER_HOST=<host>, MASTER_PORT=<port>,
# MASTER_USER=<user>, MASTER_PASSWORD=<password> ;
#
# where you replace <host>, <user>, <password> by quoted strings and
# <port> by the master's port number (3306 by default).
#
# Example:
#
# CHANGE MASTER TO MASTER_HOST='125.564.12.1', MASTER_PORT=3306,
# MASTER_USER='joe', MASTER_PASSWORD='secret';
#
# OR
#
# 2) Set the variables below. However, in case you choose this method, then
# start replication for the first time (even unsuccessfully, for example
# if you mistyped the password in master-password and the slave fails to
# connect), the slave will create a master.info file, and any later
# change in this file to the variables' values below will be ignored and
# overridden by the content of the master.info file, unless you shutdown
# the slave server, delete master.info and restart the slaver server.
# For that reason, you may want to leave the lines below untouched
# (commented) and instead use CHANGE MASTER TO (see above)
# #
# required unique id between 2 and 2^32 - 1 # required unique id between 2 and 2^32 - 1
# (and different from the master) # (and different from the master)
...@@ -113,13 +142,13 @@ no-auto-rehash ...@@ -113,13 +142,13 @@ no-auto-rehash
[isamchk] [isamchk]
set-variable = key_buffer=128M set-variable = key_buffer=128M
set-variable = sort_buffer=128M set-variable = sort_buffer_size=128M
set-variable = read_buffer=2M set-variable = read_buffer=2M
set-variable = write_buffer=2M set-variable = write_buffer=2M
[myisamchk] [myisamchk]
set-variable = key_buffer=128M set-variable = key_buffer=128M
set-variable = sort_buffer=128M set-variable = sort_buffer_size=128M
set-variable = read_buffer=2M set-variable = read_buffer=2M
set-variable = write_buffer=2M set-variable = write_buffer=2M
......
...@@ -30,7 +30,7 @@ skip-locking ...@@ -30,7 +30,7 @@ skip-locking
set-variable = key_buffer=16M set-variable = key_buffer=16M
set-variable = max_allowed_packet=1M set-variable = max_allowed_packet=1M
set-variable = table_cache=64 set-variable = table_cache=64
set-variable = sort_buffer=512K set-variable = sort_buffer_size=512K
set-variable = net_buffer_length=8K set-variable = net_buffer_length=8K
set-variable = myisam_sort_buffer_size=8M set-variable = myisam_sort_buffer_size=8M
...@@ -51,7 +51,36 @@ log-bin ...@@ -51,7 +51,36 @@ log-bin
# but will not function as a master if omitted # but will not function as a master if omitted
server-id = 1 server-id = 1
# Replication Slave Server (comment out master section to use this) # Replication Slave (comment out master section to use this)
#
# To configure this host as a replication slave, you can choose between
# two methods :
#
# 1) Use the CHANGE MASTER TO command (fully described in our manual) -
# the syntax is:
#
# CHANGE MASTER TO MASTER_HOST=<host>, MASTER_PORT=<port>,
# MASTER_USER=<user>, MASTER_PASSWORD=<password> ;
#
# where you replace <host>, <user>, <password> by quoted strings and
# <port> by the master's port number (3306 by default).
#
# Example:
#
# CHANGE MASTER TO MASTER_HOST='125.564.12.1', MASTER_PORT=3306,
# MASTER_USER='joe', MASTER_PASSWORD='secret';
#
# OR
#
# 2) Set the variables below. However, in case you choose this method, then
# start replication for the first time (even unsuccessfully, for example
# if you mistyped the password in master-password and the slave fails to
# connect), the slave will create a master.info file, and any later
# change in this file to the variables' values below will be ignored and
# overridden by the content of the master.info file, unless you shutdown
# the slave server, delete master.info and restart the slaver server.
# For that reason, you may want to leave the lines below untouched
# (commented) and instead use CHANGE MASTER TO (see above)
# #
# required unique id between 2 and 2^32 - 1 # required unique id between 2 and 2^32 - 1
# (and different from the master) # (and different from the master)
...@@ -111,13 +140,13 @@ no-auto-rehash ...@@ -111,13 +140,13 @@ no-auto-rehash
[isamchk] [isamchk]
set-variable = key_buffer=20M set-variable = key_buffer=20M
set-variable = sort_buffer=20M set-variable = sort_buffer_size=20M
set-variable = read_buffer=2M set-variable = read_buffer=2M
set-variable = write_buffer=2M set-variable = write_buffer=2M
[myisamchk] [myisamchk]
set-variable = key_buffer=20M set-variable = key_buffer=20M
set-variable = sort_buffer=20M set-variable = sort_buffer_size=20M
set-variable = read_buffer=2M set-variable = read_buffer=2M
set-variable = write_buffer=2M set-variable = write_buffer=2M
......
...@@ -29,10 +29,18 @@ socket = @MYSQL_UNIX_ADDR@ ...@@ -29,10 +29,18 @@ socket = @MYSQL_UNIX_ADDR@
skip-locking skip-locking
set-variable = key_buffer=16K set-variable = key_buffer=16K
set-variable = max_allowed_packet=1M set-variable = max_allowed_packet=1M
set-variable = thread_stack=64K
set-variable = table_cache=4 set-variable = table_cache=4
set-variable = sort_buffer=64K set-variable = sort_buffer_size=64K
set-variable = net_buffer_length=2K set-variable = net_buffer_length=2K
set-variable = thread_stack=64K
# Don't listen on a TCP/IP port at all. This can be a security enhancement,
# if all processes that need to connect to mysqld run on the same host.
# All interaction with mysqld must be made via Unix sockets or named pipes.
# Note that using this option without enabling named pipes on Windows
# (via the "pipe" option) will render mysqld useless!
#
#skip-networking
server-id = 1 server-id = 1
# Uncomment the following if you want to log updates # Uncomment the following if you want to log updates
...@@ -67,11 +75,11 @@ no-auto-rehash ...@@ -67,11 +75,11 @@ no-auto-rehash
[isamchk] [isamchk]
set-variable = key_buffer=8M set-variable = key_buffer=8M
set-variable = sort_buffer=8M set-variable = sort_buffer_size=8M
[myisamchk] [myisamchk]
set-variable = key_buffer=8M set-variable = key_buffer=8M
set-variable = sort_buffer=8M set-variable = sort_buffer_size=8M
[mysqlhotcopy] [mysqlhotcopy]
interactive-timeout interactive-timeout
...@@ -281,7 +281,8 @@ int sslaccept(struct st_VioSSLAcceptorFd* ptr, Vio* vio, long timeout) ...@@ -281,7 +281,8 @@ int sslaccept(struct st_VioSSLAcceptorFd* ptr, Vio* vio, long timeout)
SSL_SESSION_set_timeout(SSL_get_session(vio->ssl_), timeout); SSL_SESSION_set_timeout(SSL_get_session(vio->ssl_), timeout);
SSL_set_fd(vio->ssl_,vio->sd); SSL_set_fd(vio->ssl_,vio->sd);
SSL_set_accept_state(vio->ssl_); SSL_set_accept_state(vio->ssl_);
if (SSL_do_handshake(vio->ssl_) < 1) if (SSL_do_handshake(vio->ssl_) < 1 ||
SSL_get_verify_result(vio->ssl_) != X509_V_OK)
{ {
DBUG_PRINT("error", ("SSL_do_handshake failure")); DBUG_PRINT("error", ("SSL_do_handshake failure"));
report_errors(); report_errors();
...@@ -354,7 +355,8 @@ int sslconnect(struct st_VioSSLConnectorFd* ptr, Vio* vio, long timeout) ...@@ -354,7 +355,8 @@ int sslconnect(struct st_VioSSLConnectorFd* ptr, Vio* vio, long timeout)
SSL_SESSION_set_timeout(SSL_get_session(vio->ssl_), timeout); SSL_SESSION_set_timeout(SSL_get_session(vio->ssl_), timeout);
SSL_set_fd (vio->ssl_, vio->sd); SSL_set_fd (vio->ssl_, vio->sd);
SSL_set_connect_state(vio->ssl_); SSL_set_connect_state(vio->ssl_);
if (SSL_do_handshake(vio->ssl_) < 1) if (SSL_do_handshake(vio->ssl_) < 1 ||
SSL_get_verify_result(vio->ssl_) != X509_V_OK)
{ {
DBUG_PRINT("error", ("SSL_do_handshake failure")); DBUG_PRINT("error", ("SSL_do_handshake failure"));
report_errors(); report_errors();
......
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