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:
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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) {}
|
||||||
|
@ -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;
|
||||||
|
Reference in New Issue
Block a user