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(
}
/*************************************************************************
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
char*
dict_scan_col(
/*==========*/
dict_scan_id(
/*=========*/
/* 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 */
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 */
{
dict_col_t* col;
char* old_ptr;
ulint i;
*success = FALSE;
char quote = '\0';
*start = NULL;
while (isspace(*ptr)) {
ptr++;
......@@ -2139,51 +2138,76 @@ dict_scan_col(
}
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. */
quote = *ptr++;
}
*start = ptr;
while (*ptr && *ptr != quote) {
ptr++;
if (quote) {
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++;
} else {
return(ptr); /* Syntax error */
ptr++;
}
} else {
old_ptr = ptr;
}
*len = (ulint) (ptr - *start);
while (!isspace(*ptr) && *ptr != ',' && *ptr != ')'
&& *ptr != '\0') {
if (quote) {
if (*ptr == quote) {
ptr++;
}
*column_name_len = (ulint)(ptr - old_ptr);
} else {
/* 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) {
*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;
ptr = dict_scan_id(ptr, &scanned_id, &scanned_id_len, FALSE);
while (isspace(*ptr)) {
ptr++;
if (scanned_id == NULL) {
return(ptr); /* Syntax error */
}
if (*ptr == '\0') {
return(ptr);
}
if (*ptr == '.') {
/* We scanned the database name; scan also the table name */
if (*ptr == '`' || *ptr == '"') {
quote = *ptr;
ptr++;
}
old_ptr = ptr;
database_name = scanned_id;
database_name_len = scanned_id_len;
if (quote) {
while (*ptr != quote && *ptr != '\0') {
ptr++;
}
ptr = dict_scan_id(ptr, &table_name, &table_name_len, FALSE);
if (*ptr == '\0') {
if (table_name == NULL) {
return(old_ptr); /* Syntax error */
return(ptr); /* Syntax error */
}
} else {
while (!isspace(*ptr) && *ptr != '(' && *ptr != '\0') {
table_name = scanned_id;
table_name_len = scanned_id_len;
}
if (*ptr == '.') {
dot_ptr = ptr;
}
ptr++;
if (database_name == NULL) {
/* Use the database name of the foreign key table */
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__
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);
} else {
ut_memcpy(second_table_name + i, old_ptr,
ptr - old_ptr);
}
#endif
second_table_name[i + (ptr - old_ptr)] = '\0';
if (srv_lower_case_table_names) {
ut_cpy_in_lower_case(second_table_name, database_name,
database_name_len);
} else {
ut_memcpy(second_table_name, database_name,
database_name_len);
}
#endif
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);
} else {
ut_memcpy(second_table_name, old_ptr, ptr - old_ptr);
}
#endif
second_table_name[dot_ptr - old_ptr] = '/';
second_table_name[ptr - old_ptr] = '\0';
if (srv_lower_case_table_names) {
ut_cpy_in_lower_case(second_table_name + database_name_len + 1,
table_name, table_name_len);
} else {
ut_memcpy(second_table_name + database_name_len + 1,
table_name, table_name_len);
}
#endif
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 @@ dict_foreign_parse_drop_constraints(
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())
{
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;
}
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