summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorheikki@hundin.mysql.fi <>2004-02-09 23:57:29 +0200
committerheikki@hundin.mysql.fi <>2004-02-09 23:57:29 +0200
commitd9790a406c69a46eb197cea725c1e7c7e480ac41 (patch)
tree4b1a405fb8e787dcba8a5a3f77ac537aa6b874fe
parentd2d1e6f7261846c34a93fe17c1c10a3a0be2e3ca (diff)
downloadmariadb-git-d9790a406c69a46eb197cea725c1e7c7e480ac41.tar.gz
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
-rw-r--r--innobase/dict/dict0dict.c18
-rw-r--r--innobase/include/dict0dict.h9
-rw-r--r--innobase/row/row0mysql.c7
-rw-r--r--sql/ha_innodb.cc23
-rw-r--r--sql/ha_innodb.h1
-rw-r--r--sql/handler.h2
-rw-r--r--sql/sql_insert.cc10
7 files changed, 68 insertions, 2 deletions
diff --git a/innobase/dict/dict0dict.c b/innobase/dict/dict0dict.c
index e0597c66dba..5b2a16ce9f3 100644
--- a/innobase/dict/dict0dict.c
+++ b/innobase/dict/dict0dict.c
@@ -1846,6 +1846,24 @@ dict_index_build_internal_non_clust(
/*====================== 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. */
static
void
diff --git a/innobase/include/dict0dict.h b/innobase/include/dict0dict.h
index 6f56c54224b..7d55521f228 100644
--- a/innobase/include/dict0dict.h
+++ b/innobase/include/dict0dict.h
@@ -206,6 +206,15 @@ dict_foreign_add_to_cache(
/* out: DB_SUCCESS or error code */
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
the foreign key constraints declared in the string. This function
should be called after the indexes for a table have been created.
diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c
index ce88bcc5933..a95d0fc6b95 100644
--- a/innobase/row/row0mysql.c
+++ b/innobase/row/row0mysql.c
@@ -1997,8 +1997,15 @@ row_drop_table_for_mysql(
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);
+ while (foreign && foreign->foreign_table == table) {
+ foreign = UT_LIST_GET_NEXT(referenced_list, foreign);
+ }
+
if (foreign && trx->check_foreigns) {
char* buf = dict_foreign_err_buf;
diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc
index 01af07f34ec..73f517285a0 100644
--- a/sql/ha_innodb.cc
+++ b/sql/ha_innodb.cc
@@ -4289,7 +4289,28 @@ ha_innobase::get_foreign_key_create_info(void)
prebuilt->trx->op_info = (char*)"";
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
diff --git a/sql/ha_innodb.h b/sql/ha_innodb.h
index 8a51019b18e..384b3dec949 100644
--- a/sql/ha_innodb.h
+++ b/sql/ha_innodb.h
@@ -179,6 +179,7 @@ class ha_innobase: public handler
int check(THD* thd, HA_CHECK_OPT* check_opt);
char* update_table_comment(const char* comment);
char* get_foreign_key_create_info();
+ uint referenced_by_foreign_key();
void free_foreign_key_create_info(char* str);
THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
enum thr_lock_type lock_type);
diff --git a/sql/handler.h b/sql/handler.h
index 60edf539e2c..750db45759e 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -319,6 +319,8 @@ public:
virtual void append_create_info(String *packet) {}
virtual char* get_foreign_key_create_info()
{ 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()
{ return; } /* prepare InnoDB for HANDLER */
virtual void free_foreign_key_create_info(char* str) {}
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 97b7c1db03d..4d7a9f7e508 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -442,7 +442,15 @@ int write_record(TABLE *table,COPY_INFO *info)
HA_READ_KEY_EXACT))))
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])))
goto err;