1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-30 16:24:05 +03:00

row0mysql.c:

Allow always DROPping of a table which is only referenced by FOREIGN KEY constraints from the same table
Many files:
  Do not let REPLACE to perform internally an UPDATE if the table is referenced by a FOREIGN KEY: the manual says that REPLACE must resolve a duplicate key error semantically with DELETE(s) + INSERT, and not by an UPDATE; the internal update caused foreign key checks and cascaded operations to behave in a semantically wrong way
This commit is contained in:
heikki@hundin.mysql.fi
2004-02-09 23:57:29 +02:00
parent d2d1e6f726
commit d9790a406c
7 changed files with 68 additions and 2 deletions

View File

@ -1845,6 +1845,24 @@ dict_index_build_internal_non_clust(
/*====================== FOREIGN KEY PROCESSING ========================*/ /*====================== FOREIGN KEY PROCESSING ========================*/
/*************************************************************************
Checks if a table is referenced by foreign keys. */
ibool
dict_table_referenced_by_foreign_key(
/*=================================*/
/* out: TRUE if table is referenced by a
foreign key */
dict_table_t* table) /* in: InnoDB table */
{
if (UT_LIST_GET_LEN(table->referenced_list) > 0) {
return(TRUE);
}
return(FALSE);
}
/************************************************************************* /*************************************************************************
Frees a foreign key struct. */ Frees a foreign key struct. */
static static

View File

@ -206,6 +206,15 @@ dict_foreign_add_to_cache(
/* out: DB_SUCCESS or error code */ /* out: DB_SUCCESS or error code */
dict_foreign_t* foreign); /* in, own: foreign key constraint */ dict_foreign_t* foreign); /* in, own: foreign key constraint */
/************************************************************************* /*************************************************************************
Checks if a table is referenced by foreign keys. */
ibool
dict_table_referenced_by_foreign_key(
/*=================================*/
/* out: TRUE if table is referenced by a
foreign key */
dict_table_t* table); /* in: InnoDB table */
/*************************************************************************
Scans a table create SQL string and adds to the data dictionary Scans a table create SQL string and adds to the data dictionary
the foreign key constraints declared in the string. This function the foreign key constraints declared in the string. This function
should be called after the indexes for a table have been created. should be called after the indexes for a table have been created.

View File

@ -1997,8 +1997,15 @@ row_drop_table_for_mysql(
goto funct_exit; goto funct_exit;
} }
/* Check if the table is referenced by foreign key constraints from
some other table (not the table itself) */
foreign = UT_LIST_GET_FIRST(table->referenced_list); foreign = UT_LIST_GET_FIRST(table->referenced_list);
while (foreign && foreign->foreign_table == table) {
foreign = UT_LIST_GET_NEXT(referenced_list, foreign);
}
if (foreign && trx->check_foreigns) { if (foreign && trx->check_foreigns) {
char* buf = dict_foreign_err_buf; char* buf = dict_foreign_err_buf;

View File

@ -4289,7 +4289,28 @@ ha_innobase::get_foreign_key_create_info(void)
prebuilt->trx->op_info = (char*)""; prebuilt->trx->op_info = (char*)"";
return(str); return(str);
} }
/***********************************************************************
Checks if a table is referenced by a foreign key. The MySQL manual states that
a REPLACE is either equivalent to an INSERT, or DELETE(s) + INSERT. Only a
delete is then allowed internally to resolve a duplicate key conflict in
REPLACE, not an update. */
uint
ha_innobase::referenced_by_foreign_key(void)
/*========================================*/
/* out: > 0 if referenced by a FOREIGN KEY */
{
row_prebuilt_t* prebuilt = (row_prebuilt_t*)innobase_prebuilt;
if (dict_table_referenced_by_foreign_key(prebuilt->table)) {
return(1);
}
return(0);
}
/*********************************************************************** /***********************************************************************
Frees the foreign key create info for a table stored in InnoDB, if it is Frees the foreign key create info for a table stored in InnoDB, if it is

View File

@ -179,6 +179,7 @@ class ha_innobase: public handler
int check(THD* thd, HA_CHECK_OPT* check_opt); int check(THD* thd, HA_CHECK_OPT* check_opt);
char* update_table_comment(const char* comment); char* update_table_comment(const char* comment);
char* get_foreign_key_create_info(); char* get_foreign_key_create_info();
uint referenced_by_foreign_key();
void free_foreign_key_create_info(char* str); void free_foreign_key_create_info(char* str);
THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
enum thr_lock_type lock_type); enum thr_lock_type lock_type);

View File

@ -319,6 +319,8 @@ public:
virtual void append_create_info(String *packet) {} virtual void append_create_info(String *packet) {}
virtual char* get_foreign_key_create_info() virtual char* get_foreign_key_create_info()
{ return(NULL);} /* gets foreign key create string from InnoDB */ { return(NULL);} /* gets foreign key create string from InnoDB */
virtual uint referenced_by_foreign_key() { return 0;} /* used in REPLACE;
is > 0 if table is referred by a FOREIGN KEY */
virtual void init_table_handle_for_HANDLER() virtual void init_table_handle_for_HANDLER()
{ return; } /* prepare InnoDB for HANDLER */ { return; } /* prepare InnoDB for HANDLER */
virtual void free_foreign_key_create_info(char* str) {} virtual void free_foreign_key_create_info(char* str) {}

View File

@ -442,7 +442,15 @@ int write_record(TABLE *table,COPY_INFO *info)
HA_READ_KEY_EXACT)))) HA_READ_KEY_EXACT))))
goto err; goto err;
} }
if (last_uniq_key(table,key_nr)) /*
The manual defines the REPLACE semantics that it is either an INSERT or
DELETE(s) + INSERT; FOREIGN KEY checks do not function in the defined
way if we allow MySQL to convert the latter operation internally to an
UPDATE.
*/
if (last_uniq_key(table,key_nr)
&& !table->file->referenced_by_foreign_key())
{ {
if ((error=table->file->update_row(table->record[1],table->record[0]))) if ((error=table->file->update_row(table->record[1],table->record[0])))
goto err; goto err;