mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
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 sql/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 sql/ha_innodb.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 innobase/row/row0mysql.c: 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 innobase/dict/dict0dict.c: Remove redundant code; parse both the database name and the table name in a FOREIGN KEY constraint with quotes in mind
This commit is contained in:
@ -2108,6 +2108,68 @@ dict_accept(
|
||||
return(ptr + ut_strlen(string));
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
Scans an id. For the lexical definition of an 'id', see the code below.
|
||||
Strips backquotes or double quotes 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 */
|
||||
ibool accept_also_dot)/* in: TRUE if also a dot can appear in a
|
||||
non-quoted id; in a quoted id it can appear
|
||||
always */
|
||||
{
|
||||
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 != ')'
|
||||
&& (accept_also_dot || *ptr != '.')
|
||||
&& *ptr != ',' && *ptr != '\0') {
|
||||
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
|
||||
*len = (ulint) (ptr - *start);
|
||||
|
||||
if (quote) {
|
||||
if (*ptr == quote) {
|
||||
ptr++;
|
||||
} else {
|
||||
/* Syntax error */
|
||||
*start = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return(ptr);
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
Tries to scan a column name. */
|
||||
static
|
||||
@ -2124,66 +2186,28 @@ dict_scan_col(
|
||||
ulint* column_name_len)/* out: column name length */
|
||||
{
|
||||
dict_col_t* col;
|
||||
char* old_ptr;
|
||||
ulint i;
|
||||
|
||||
*success = FALSE;
|
||||
|
||||
while (isspace(*ptr)) {
|
||||
ptr++;
|
||||
}
|
||||
ptr = dict_scan_id(ptr, column_name, column_name_len, TRUE);
|
||||
|
||||
if (*ptr == '\0') {
|
||||
if (column_name == NULL) {
|
||||
|
||||
return(ptr);
|
||||
}
|
||||
|
||||
if (*ptr == '`' || *ptr == '"') {
|
||||
/* The identifier is quoted. Search for the end quote. We
|
||||
cannot use the general code here as the name may contain
|
||||
special characters like the space. */
|
||||
|
||||
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) {
|
||||
ptr++;
|
||||
}
|
||||
*column_name_len = (ulint)(ptr - old_ptr);
|
||||
|
||||
if (*ptr) { /* Skip end quote */
|
||||
ptr++;
|
||||
} else {
|
||||
return(ptr); /* Syntax error */
|
||||
}
|
||||
} else {
|
||||
old_ptr = ptr;
|
||||
|
||||
while (!isspace(*ptr) && *ptr != ',' && *ptr != ')'
|
||||
&& *ptr != '\0') {
|
||||
ptr++;
|
||||
}
|
||||
*column_name_len = (ulint)(ptr - old_ptr);
|
||||
}
|
||||
|
||||
if (table == NULL) {
|
||||
*success = TRUE;
|
||||
*column = NULL;
|
||||
*column_name = old_ptr;
|
||||
} else {
|
||||
for (i = 0; i < dict_table_get_n_cols(table); i++) {
|
||||
|
||||
col = dict_table_get_nth_col(table, i);
|
||||
|
||||
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)) {
|
||||
/* Found */
|
||||
|
||||
@ -2214,163 +2238,99 @@ dict_scan_table_name(
|
||||
the referenced table name; must be at least
|
||||
2500 bytes */
|
||||
{
|
||||
char* dot_ptr = NULL;
|
||||
char* old_ptr;
|
||||
char quote = '\0';
|
||||
char* database_name = NULL;
|
||||
ulint database_name_len = 999999999; /* init to a dummy value to
|
||||
suppress a compiler warning */
|
||||
char* table_name = NULL;
|
||||
ulint table_name_len;
|
||||
char* scanned_id;
|
||||
ulint scanned_id_len;
|
||||
ulint i;
|
||||
|
||||
*success = FALSE;
|
||||
*table = NULL;
|
||||
|
||||
while (isspace(*ptr)) {
|
||||
ptr++;
|
||||
ptr = dict_scan_id(ptr, &scanned_id, &scanned_id_len, FALSE);
|
||||
|
||||
if (scanned_id == NULL) {
|
||||
|
||||
return(ptr); /* Syntax error */
|
||||
}
|
||||
|
||||
if (*ptr == '\0') {
|
||||
|
||||
return(ptr);
|
||||
}
|
||||
|
||||
if (*ptr == '`' || *ptr == '"') {
|
||||
quote = *ptr;
|
||||
ptr++;
|
||||
}
|
||||
|
||||
old_ptr = ptr;
|
||||
|
||||
if (quote) {
|
||||
while (*ptr != quote && *ptr != '\0') {
|
||||
ptr++;
|
||||
}
|
||||
|
||||
if (*ptr == '\0') {
|
||||
|
||||
return(old_ptr); /* Syntax error */
|
||||
}
|
||||
} else {
|
||||
while (!isspace(*ptr) && *ptr != '(' && *ptr != '\0') {
|
||||
|
||||
if (*ptr == '.') {
|
||||
dot_ptr = ptr;
|
||||
}
|
||||
/* We scanned the database name; scan also the table name */
|
||||
|
||||
ptr++;
|
||||
|
||||
database_name = scanned_id;
|
||||
database_name_len = scanned_id_len;
|
||||
|
||||
ptr = dict_scan_id(ptr, &table_name, &table_name_len, FALSE);
|
||||
|
||||
if (table_name == NULL) {
|
||||
|
||||
return(ptr); /* Syntax error */
|
||||
}
|
||||
} else {
|
||||
table_name = scanned_id;
|
||||
table_name_len = scanned_id_len;
|
||||
}
|
||||
|
||||
if (ptr - old_ptr > 2000) {
|
||||
if (database_name == NULL) {
|
||||
/* Use the database name of the foreign key table */
|
||||
|
||||
return(old_ptr);
|
||||
}
|
||||
database_name = 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 = 0;
|
||||
while (name[i] != '/') {
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
|
||||
database_name_len = i;
|
||||
}
|
||||
|
||||
if (table_name_len + database_name_len > 2000) {
|
||||
|
||||
return(ptr); /* Too long name */
|
||||
}
|
||||
|
||||
#ifdef __WIN__
|
||||
ut_cpy_in_lower_case(second_table_name + i, old_ptr,
|
||||
ptr - old_ptr);
|
||||
ut_cpy_in_lower_case(second_table_name, database_name,
|
||||
database_name_len);
|
||||
#else
|
||||
if (srv_lower_case_table_names) {
|
||||
ut_cpy_in_lower_case(second_table_name + i, old_ptr,
|
||||
ptr - old_ptr);
|
||||
ut_cpy_in_lower_case(second_table_name, database_name,
|
||||
database_name_len);
|
||||
} else {
|
||||
ut_memcpy(second_table_name + i, old_ptr,
|
||||
ptr - old_ptr);
|
||||
ut_memcpy(second_table_name, database_name,
|
||||
database_name_len);
|
||||
}
|
||||
#endif
|
||||
second_table_name[i + (ptr - old_ptr)] = '\0';
|
||||
} else {
|
||||
second_table_name[database_name_len] = '/';
|
||||
|
||||
#ifdef __WIN__
|
||||
ut_cpy_in_lower_case(second_table_name, old_ptr,
|
||||
ptr - old_ptr);
|
||||
ut_cpy_in_lower_case(second_table_name + database_name_len + 1,
|
||||
table_name, table_name_len);
|
||||
#else
|
||||
if (srv_lower_case_table_names) {
|
||||
ut_cpy_in_lower_case(second_table_name, old_ptr,
|
||||
ptr - old_ptr);
|
||||
ut_cpy_in_lower_case(second_table_name + database_name_len + 1,
|
||||
table_name, table_name_len);
|
||||
} 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';
|
||||
}
|
||||
second_table_name[database_name_len + 1 + table_name_len] = '\0';
|
||||
|
||||
*success = TRUE;
|
||||
|
||||
*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);
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
Skips one id. */
|
||||
Skips one id. The id is allowed to contain also '.'. */
|
||||
static
|
||||
char*
|
||||
dict_skip_word(
|
||||
@ -2385,7 +2345,7 @@ dict_skip_word(
|
||||
|
||||
*success = FALSE;
|
||||
|
||||
ptr = dict_scan_id(ptr, &start, &len);
|
||||
ptr = dict_scan_id(ptr, &start, &len, TRUE);
|
||||
|
||||
if (start) {
|
||||
*success = TRUE;
|
||||
@ -3083,7 +3043,7 @@ loop:
|
||||
goto syntax_error;
|
||||
}
|
||||
|
||||
ptr = dict_scan_id(ptr, &start, &len);
|
||||
ptr = dict_scan_id(ptr, &start, &len, TRUE);
|
||||
|
||||
if (start == NULL) {
|
||||
|
||||
|
@ -1804,6 +1804,7 @@ row_drop_table_for_mysql(
|
||||
char* name, /* in: table name */
|
||||
trx_t* trx) /* in: transaction handle */
|
||||
{
|
||||
dict_foreign_t* foreign;
|
||||
dict_table_t* table;
|
||||
que_thr_t* thr;
|
||||
que_t* graph;
|
||||
@ -1996,6 +1997,31 @@ row_drop_table_for_mysql(
|
||||
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) {
|
||||
|
||||
ut_print_timestamp(stderr);
|
||||
|
@ -276,7 +276,7 @@ convert_error_code_to_mysql(
|
||||
|
||||
} 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) {
|
||||
|
||||
@ -3572,6 +3572,7 @@ ha_innobase::delete_table(
|
||||
int error;
|
||||
trx_t* parent_trx;
|
||||
trx_t* trx;
|
||||
THD *thd= current_thd;
|
||||
char norm_name[1000];
|
||||
|
||||
DBUG_ENTER("ha_innobase::delete_table");
|
||||
@ -3597,6 +3598,14 @@ ha_innobase::delete_table(
|
||||
trx->mysql_thd = current_thd;
|
||||
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);
|
||||
|
||||
assert(name_len < 1000);
|
||||
|
@ -167,7 +167,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
|
||||
String wrong_tables;
|
||||
db_type table_type;
|
||||
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");
|
||||
|
||||
if (lock_table_names(thd, tables))
|
||||
@ -212,6 +212,9 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
|
||||
error=ha_delete_table(table_type, path);
|
||||
if (error == ENOENT && if_exists)
|
||||
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)
|
||||
{
|
||||
/* Delete the table definition file */
|
||||
@ -247,7 +250,10 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
|
||||
error= 0;
|
||||
if (wrong_tables.length())
|
||||
{
|
||||
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;
|
||||
}
|
||||
DBUG_RETURN(error);
|
||||
|
Reference in New Issue
Block a user