dict0dict.c:

  Remove redundant code; parse both the database name and the table name in a FOREIGN KEY constraint with quotes in mind
row0mysql.c, ha_innodb.cc, sql_table.cc:
  Return error message Cannot delete or update a parent row... if we try to drop a table which is referenced by a FOREIGN KEY constraint, and the user has not set foreign_key_checks=0
parent 0216948c
...@@ -2109,25 +2109,24 @@ dict_accept( ...@@ -2109,25 +2109,24 @@ dict_accept(
} }
/************************************************************************* /*************************************************************************
Tries to scan a column name. */ Scans an id. For the lexical definition of an 'id', see the code below.
Strips backquotes or double quotes from around the id. */
static static
char* char*
dict_scan_col( 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 */ char** start, /* out: start of the id; NULL if no id was
dict_table_t* table, /* in: table in which the column is */ scannable */
dict_col_t** column, /* out: pointer to column if success */ ulint* len, /* out: length of the id */
char** column_name,/* out: pointer to column->name if ibool accept_also_dot)/* in: TRUE if also a dot can appear in a
success */ non-quoted id; in a quoted id it can appear
ulint* column_name_len)/* out: column name length */ always */
{ {
dict_col_t* col; char quote = '\0';
char* old_ptr;
ulint i; *start = NULL;
*success = FALSE;
while (isspace(*ptr)) { while (isspace(*ptr)) {
ptr++; ptr++;
...@@ -2139,51 +2138,76 @@ dict_scan_col( ...@@ -2139,51 +2138,76 @@ dict_scan_col(
} }
if (*ptr == '`' || *ptr == '"') { if (*ptr == '`' || *ptr == '"') {
/* The identifier is quoted. Search for the end quote. We quote = *ptr++;
cannot use the general code here as the name may contain }
special characters like the space. */
*start = ptr;
char quote = *ptr;
ptr++; /* Skip the quote */
old_ptr = ptr;
/* The column name should always end with 'quote' but we check
for an end zero just to be safe if this is called outside of
MySQL. */
while (*ptr && *ptr != quote) { if (quote) {
ptr++; while (*ptr != quote && *ptr != '\0') {
ptr++;
} }
*column_name_len = (ulint)(ptr - old_ptr); } else {
while (!isspace(*ptr) && *ptr != '(' && *ptr != ')'
&& (accept_also_dot || *ptr != '.')
&& *ptr != ',' && *ptr != '\0') {
if (*ptr) { /* Skip end quote */ ptr++;
ptr++;
} else {
return(ptr); /* Syntax error */
} }
} else { }
old_ptr = ptr;
*len = (ulint) (ptr - *start);
while (!isspace(*ptr) && *ptr != ',' && *ptr != ')' if (quote) {
&& *ptr != '\0') { if (*ptr == quote) {
ptr++; ptr++;
} } else {
*column_name_len = (ulint)(ptr - old_ptr); /* Syntax error */
*start = NULL;
}
} }
return(ptr);
}
/*************************************************************************
Tries to scan a column name. */
static
char*
dict_scan_col(
/*==========*/
/* out: scanned to */
char* ptr, /* in: scanned to */
ibool* success,/* out: TRUE if success */
dict_table_t* table, /* in: table in which the column is */
dict_col_t** column, /* out: pointer to column if success */
char** column_name,/* out: pointer to column->name if
success */
ulint* column_name_len)/* out: column name length */
{
dict_col_t* col;
ulint i;
*success = FALSE;
ptr = dict_scan_id(ptr, column_name, column_name_len, TRUE);
if (column_name == NULL) {
return(ptr); /* Syntax error */
}
if (table == NULL) { if (table == NULL) {
*success = TRUE; *success = TRUE;
*column = NULL; *column = NULL;
*column_name = old_ptr;
} else { } else {
for (i = 0; i < dict_table_get_n_cols(table); i++) { for (i = 0; i < dict_table_get_n_cols(table); i++) {
col = dict_table_get_nth_col(table, i); col = dict_table_get_nth_col(table, i);
if (ut_strlen(col->name) == *column_name_len if (ut_strlen(col->name) == *column_name_len
&& 0 == ut_cmp_in_lower_case(col->name, old_ptr, && 0 == ut_cmp_in_lower_case(col->name,
*column_name,
*column_name_len)) { *column_name_len)) {
/* Found */ /* Found */
...@@ -2214,163 +2238,99 @@ dict_scan_table_name( ...@@ -2214,163 +2238,99 @@ dict_scan_table_name(
the referenced table name; must be at least the referenced table name; must be at least
2500 bytes */ 2500 bytes */
{ {
char* dot_ptr = NULL; char* database_name = NULL;
char* old_ptr; ulint database_name_len = 999999999; /* init to a dummy value to
char quote = '\0'; suppress a compiler warning */
char* table_name = NULL;
ulint table_name_len;
char* scanned_id;
ulint scanned_id_len;
ulint i; ulint i;
*success = FALSE; *success = FALSE;
*table = NULL; *table = NULL;
ptr = dict_scan_id(ptr, &scanned_id, &scanned_id_len, FALSE);
while (isspace(*ptr)) { if (scanned_id == NULL) {
ptr++;
return(ptr); /* Syntax error */
} }
if (*ptr == '\0') { if (*ptr == '.') {
/* We scanned the database name; scan also the table name */
return(ptr);
}
if (*ptr == '`' || *ptr == '"') {
quote = *ptr;
ptr++; ptr++;
}
old_ptr = ptr; database_name = scanned_id;
database_name_len = scanned_id_len;
if (quote) { ptr = dict_scan_id(ptr, &table_name, &table_name_len, FALSE);
while (*ptr != quote && *ptr != '\0') {
ptr++;
}
if (*ptr == '\0') { if (table_name == NULL) {
return(old_ptr); /* Syntax error */ return(ptr); /* Syntax error */
} }
} else { } else {
while (!isspace(*ptr) && *ptr != '(' && *ptr != '\0') { table_name = scanned_id;
table_name_len = scanned_id_len;
}
if (*ptr == '.') { if (database_name == NULL) {
dot_ptr = ptr; /* Use the database name of the foreign key table */
}
ptr++; database_name = name;
i = 0;
while (name[i] != '/') {
i++;
} }
database_name_len = i;
} }
if (ptr - old_ptr > 2000) { if (table_name_len + database_name_len > 2000) {
return(old_ptr); return(ptr); /* Too long name */
} }
if (dot_ptr == NULL) {
/* Copy the database name from 'name' to the start */
for (i = 0;; i++) {
second_table_name[i] = name[i];
if (name[i] == '/') {
i++;
break;
}
}
#ifdef __WIN__ #ifdef __WIN__
ut_cpy_in_lower_case(second_table_name + i, old_ptr, ut_cpy_in_lower_case(second_table_name, database_name,
ptr - old_ptr); database_name_len);
#else #else
if (srv_lower_case_table_names) { if (srv_lower_case_table_names) {
ut_cpy_in_lower_case(second_table_name + i, old_ptr, ut_cpy_in_lower_case(second_table_name, database_name,
ptr - old_ptr); database_name_len);
} else {
ut_memcpy(second_table_name + i, old_ptr,
ptr - old_ptr);
}
#endif
second_table_name[i + (ptr - old_ptr)] = '\0';
} else { } else {
ut_memcpy(second_table_name, database_name,
database_name_len);
}
#endif
second_table_name[database_name_len] = '/';
#ifdef __WIN__ #ifdef __WIN__
ut_cpy_in_lower_case(second_table_name, old_ptr, ut_cpy_in_lower_case(second_table_name + database_name_len + 1,
ptr - old_ptr); table_name, table_name_len);
#else #else
if (srv_lower_case_table_names) { if (srv_lower_case_table_names) {
ut_cpy_in_lower_case(second_table_name, old_ptr, ut_cpy_in_lower_case(second_table_name + database_name_len + 1,
ptr - old_ptr); table_name, table_name_len);
} else { } else {
ut_memcpy(second_table_name, old_ptr, ptr - old_ptr); ut_memcpy(second_table_name + database_name_len + 1,
} table_name, table_name_len);
#endif
second_table_name[dot_ptr - old_ptr] = '/';
second_table_name[ptr - old_ptr] = '\0';
} }
#endif
second_table_name[database_name_len + 1 + table_name_len] = '\0';
*success = TRUE; *success = TRUE;
*table = dict_table_get_low(second_table_name); *table = dict_table_get_low(second_table_name);
if (quote && *ptr == quote) {
ptr++;
}
return(ptr);
}
/*************************************************************************
Scans an id. For the lexical definition of an 'id', see the code below.
Strips backquotes from around the id. */
static
char*
dict_scan_id(
/*=========*/
/* out: scanned to */
char* ptr, /* in: scanned to */
char** start, /* out: start of the id; NULL if no id was
scannable */
ulint* len) /* out: length of the id */
{
char quote = '\0';
*start = NULL;
while (isspace(*ptr)) {
ptr++;
}
if (*ptr == '\0') {
return(ptr);
}
if (*ptr == '`' || *ptr == '"') {
quote = *ptr++;
}
*start = ptr;
if (quote) {
while (*ptr != quote && *ptr != '\0') {
ptr++;
}
} else {
while (!isspace(*ptr) && *ptr != '(' && *ptr != ')'
&& *ptr != ',' && *ptr != '\0') {
ptr++;
}
}
*len = (ulint) (ptr - *start);
if (quote) {
if (*ptr == quote) {
ptr++;
} else {
/* Syntax error */
*start = NULL;
}
}
return(ptr); return(ptr);
} }
/************************************************************************* /*************************************************************************
Skips one id. */ Skips one id. The id is allowed to contain also '.'. */
static static
char* char*
dict_skip_word( dict_skip_word(
...@@ -2385,7 +2345,7 @@ dict_skip_word( ...@@ -2385,7 +2345,7 @@ dict_skip_word(
*success = FALSE; *success = FALSE;
ptr = dict_scan_id(ptr, &start, &len); ptr = dict_scan_id(ptr, &start, &len, TRUE);
if (start) { if (start) {
*success = TRUE; *success = TRUE;
...@@ -3083,7 +3043,7 @@ loop: ...@@ -3083,7 +3043,7 @@ loop:
goto syntax_error; goto syntax_error;
} }
ptr = dict_scan_id(ptr, &start, &len); ptr = dict_scan_id(ptr, &start, &len, TRUE);
if (start == NULL) { if (start == NULL) {
......
...@@ -1804,6 +1804,7 @@ row_drop_table_for_mysql( ...@@ -1804,6 +1804,7 @@ row_drop_table_for_mysql(
char* name, /* in: table name */ char* name, /* in: table name */
trx_t* trx) /* in: transaction handle */ trx_t* trx) /* in: transaction handle */
{ {
dict_foreign_t* foreign;
dict_table_t* table; dict_table_t* table;
que_thr_t* thr; que_thr_t* thr;
que_t* graph; que_t* graph;
...@@ -1996,6 +1997,31 @@ row_drop_table_for_mysql( ...@@ -1996,6 +1997,31 @@ row_drop_table_for_mysql(
goto funct_exit; goto funct_exit;
} }
foreign = UT_LIST_GET_FIRST(table->referenced_list);
if (foreign && trx->check_foreigns) {
char* buf = dict_foreign_err_buf;
/* We only allow dropping a referenced table if
FOREIGN_KEY_CHECKS is set to 0 */
err = DB_CANNOT_DROP_CONSTRAINT;
mutex_enter(&dict_foreign_err_mutex);
ut_sprintf_timestamp(buf);
sprintf(buf + strlen(buf),
" Cannot drop table %.500s\n", name);
sprintf(buf + strlen(buf),
"because it is referenced by %.500s\n", foreign->foreign_table_name);
ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
mutex_exit(&dict_foreign_err_mutex);
goto funct_exit;
}
if (table->n_mysql_handles_opened > 0) { if (table->n_mysql_handles_opened > 0) {
ut_print_timestamp(stderr); ut_print_timestamp(stderr);
......
...@@ -276,7 +276,7 @@ convert_error_code_to_mysql( ...@@ -276,7 +276,7 @@ convert_error_code_to_mysql(
} else if (error == (int) DB_CANNOT_DROP_CONSTRAINT) { } else if (error == (int) DB_CANNOT_DROP_CONSTRAINT) {
return(HA_WRONG_CREATE_OPTION); return(HA_ERR_ROW_IS_REFERENCED);
} else if (error == (int) DB_COL_APPEARS_TWICE_IN_INDEX) { } else if (error == (int) DB_COL_APPEARS_TWICE_IN_INDEX) {
...@@ -3572,6 +3572,7 @@ ha_innobase::delete_table( ...@@ -3572,6 +3572,7 @@ ha_innobase::delete_table(
int error; int error;
trx_t* parent_trx; trx_t* parent_trx;
trx_t* trx; trx_t* trx;
THD *thd= current_thd;
char norm_name[1000]; char norm_name[1000];
DBUG_ENTER("ha_innobase::delete_table"); DBUG_ENTER("ha_innobase::delete_table");
...@@ -3597,6 +3598,14 @@ ha_innobase::delete_table( ...@@ -3597,6 +3598,14 @@ ha_innobase::delete_table(
trx->mysql_thd = current_thd; trx->mysql_thd = current_thd;
trx->mysql_query_str = &((*current_thd).query); trx->mysql_query_str = &((*current_thd).query);
if (thd->options & OPTION_NO_FOREIGN_KEY_CHECKS) {
trx->check_foreigns = FALSE;
}
if (thd->options & OPTION_RELAXED_UNIQUE_CHECKS) {
trx->check_unique_secondary = FALSE;
}
name_len = strlen(name); name_len = strlen(name);
assert(name_len < 1000); assert(name_len < 1000);
......
...@@ -167,7 +167,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, ...@@ -167,7 +167,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
String wrong_tables; String wrong_tables;
db_type table_type; db_type table_type;
int error; int error;
bool some_tables_deleted=0, tmp_table_deleted=0; bool some_tables_deleted=0, tmp_table_deleted=0, foreign_key_error=0;
DBUG_ENTER("mysql_rm_table_part2"); DBUG_ENTER("mysql_rm_table_part2");
if (lock_table_names(thd, tables)) if (lock_table_names(thd, tables))
...@@ -212,6 +212,9 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, ...@@ -212,6 +212,9 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
error=ha_delete_table(table_type, path); error=ha_delete_table(table_type, path);
if (error == ENOENT && if_exists) if (error == ENOENT && if_exists)
error = 0; error = 0;
if (error == HA_ERR_ROW_IS_REFERENCED)
foreign_key_error=1; /* the table is referenced by a foreign key
constraint */
if (!error || error == ENOENT) if (!error || error == ENOENT)
{ {
/* Delete the table definition file */ /* Delete the table definition file */
...@@ -247,7 +250,10 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, ...@@ -247,7 +250,10 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
error= 0; error= 0;
if (wrong_tables.length()) if (wrong_tables.length())
{ {
my_error(ER_BAD_TABLE_ERROR,MYF(0),wrong_tables.c_ptr()); if (!foreign_key_error)
my_error(ER_BAD_TABLE_ERROR,MYF(0),wrong_tables.c_ptr());
else
my_error(ER_ROW_IS_REFERENCED,MYF(0));
error= 1; error= 1;
} }
DBUG_RETURN(error); DBUG_RETURN(error);
......
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