diff options
author | heikki@hundin.mysql.fi <> | 2004-02-09 23:57:29 +0200 |
---|---|---|
committer | heikki@hundin.mysql.fi <> | 2004-02-09 23:57:29 +0200 |
commit | d9790a406c69a46eb197cea725c1e7c7e480ac41 (patch) | |
tree | 4b1a405fb8e787dcba8a5a3f77ac537aa6b874fe | |
parent | d2d1e6f7261846c34a93fe17c1c10a3a0be2e3ca (diff) | |
download | mariadb-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.c | 18 | ||||
-rw-r--r-- | innobase/include/dict0dict.h | 9 | ||||
-rw-r--r-- | innobase/row/row0mysql.c | 7 | ||||
-rw-r--r-- | sql/ha_innodb.cc | 23 | ||||
-rw-r--r-- | sql/ha_innodb.h | 1 | ||||
-rw-r--r-- | sql/handler.h | 2 | ||||
-rw-r--r-- | sql/sql_insert.cc | 10 |
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; |