summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNisha Gopalakrishnan <nisha.gopalakrishnan@oracle.com>2016-02-10 19:57:17 +0530
committerNisha Gopalakrishnan <nisha.gopalakrishnan@oracle.com>2016-02-11 14:20:50 +0530
commitd9c541cb1be5b239787833d9d499067d44ea44d3 (patch)
tree18b421270670740fa3d0f3e3a82e1f82c8e89534
parent1fb6d4e6bf3dd79fffb034ca4b014930b0304e1f (diff)
downloadmariadb-git-d9c541cb1be5b239787833d9d499067d44ea44d3.tar.gz
BUG#22037930: INSERT IGNORE FAILS TO IGNORE FOREIGN KEY
CONSTRAINT. Analysis ======= INSERT and UPDATE operations using the IGNORE keyword which causes FOREIGN KEY constraint violations reports an error despite using the IGNORE keyword. Foreign key violation errors were not ignored and reported as errors instead of warnings even when IGNORE was set. Fix === Added code to ignore the foreign key violation errors and report them as warnings when the IGNORE keyword is used.
-rw-r--r--mysql-test/r/insert.result38
-rw-r--r--mysql-test/t/insert.test41
-rw-r--r--sql/handler.cc27
-rw-r--r--sql/handler.h10
-rw-r--r--sql/sql_insert.cc12
-rw-r--r--sql/sql_update.cc26
6 files changed, 141 insertions, 13 deletions
diff --git a/mysql-test/r/insert.result b/mysql-test/r/insert.result
index 655303be7f4..1aa22349593 100644
--- a/mysql-test/r/insert.result
+++ b/mysql-test/r/insert.result
@@ -686,3 +686,41 @@ ERROR 42000: Column 'a' specified twice
INSERT IGNORE t1 (a, a) SELECT 1,1 UNION SELECT 2,2;
ERROR 42000: Column 'a' specified twice
DROP TABLE t1;
+#
+# BUG#22037930: INSERT IGNORE FAILS TO IGNORE
+# FOREIGN KEY CONSTRAINT
+# Setup.
+CREATE TABLE t1 (fld1 INT PRIMARY KEY) ENGINE=INNODB;
+CREATE TABLE t2 (fld2 INT, FOREIGN KEY (fld2) REFERENCES t1 (fld1))
+ENGINE=INNODB;
+INSERT INTO t1 VALUES(0);
+INSERT INTO t2 VALUES(0);
+# Without fix, an error is reported.
+INSERT IGNORE INTO t2 VALUES(1);
+Warnings:
+Warning 1452 `test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`fld2`) REFERENCES `t1` (`fld1`)
+UPDATE IGNORE t2 SET fld2=20 WHERE fld2=0;
+Warnings:
+Warning 1452 `test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`fld2`) REFERENCES `t1` (`fld1`)
+UPDATE IGNORE t1 SET fld1=20 WHERE fld1=0;
+Warnings:
+Warning 1451 `test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`fld2`) REFERENCES `t1` (`fld1`)
+# Test for multi update.
+UPDATE IGNORE t1, t2 SET t2.fld2= t2.fld2 + 3;
+Warnings:
+Warning 1452 `test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`fld2`) REFERENCES `t1` (`fld1`)
+UPDATE IGNORE t1, t2 SET t1.fld1= t1.fld1 + 3;
+Warnings:
+Warning 1451 `test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`fld2`) REFERENCES `t1` (`fld1`)
+# Reports an error since IGNORE is not used.
+INSERT INTO t2 VALUES(1);
+ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`fld2`) REFERENCES `t1` (`fld1`))
+UPDATE t2 SET fld2=20 WHERE fld2=0;
+ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`fld2`) REFERENCES `t1` (`fld1`))
+UPDATE t1 SET fld1=20 WHERE fld1=0;
+ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`fld2`) REFERENCES `t1` (`fld1`))
+UPDATE t1, t2 SET t2.fld2= t2.fld2 + 3;
+ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`fld2`) REFERENCES `t1` (`fld1`))
+UPDATE t1, t2 SET t1.fld1= t1.fld1 + 3;
+ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`fld2`) REFERENCES `t1` (`fld1`))
+DROP TABLE t2, t1;
diff --git a/mysql-test/t/insert.test b/mysql-test/t/insert.test
index 2bf543511ac..ea89872200c 100644
--- a/mysql-test/t/insert.test
+++ b/mysql-test/t/insert.test
@@ -550,3 +550,44 @@ INSERT IGNORE t1 (a, a) SELECT 1,1;
INSERT IGNORE t1 (a, a) SELECT 1,1 UNION SELECT 2,2;
DROP TABLE t1;
+
+
+--echo #
+--echo # BUG#22037930: INSERT IGNORE FAILS TO IGNORE
+--echo # FOREIGN KEY CONSTRAINT
+
+--echo # Setup.
+CREATE TABLE t1 (fld1 INT PRIMARY KEY) ENGINE=INNODB;
+CREATE TABLE t2 (fld2 INT, FOREIGN KEY (fld2) REFERENCES t1 (fld1))
+ENGINE=INNODB;
+INSERT INTO t1 VALUES(0);
+INSERT INTO t2 VALUES(0);
+
+--echo # Without fix, an error is reported.
+--enable_warnings
+INSERT IGNORE INTO t2 VALUES(1);
+UPDATE IGNORE t2 SET fld2=20 WHERE fld2=0;
+UPDATE IGNORE t1 SET fld1=20 WHERE fld1=0;
+
+--echo # Test for multi update.
+UPDATE IGNORE t1, t2 SET t2.fld2= t2.fld2 + 3;
+UPDATE IGNORE t1, t2 SET t1.fld1= t1.fld1 + 3;
+--disable_warnings
+
+--echo # Reports an error since IGNORE is not used.
+--error ER_NO_REFERENCED_ROW_2
+INSERT INTO t2 VALUES(1);
+
+--error ER_NO_REFERENCED_ROW_2
+UPDATE t2 SET fld2=20 WHERE fld2=0;
+
+--error ER_ROW_IS_REFERENCED_2
+UPDATE t1 SET fld1=20 WHERE fld1=0;
+
+--error ER_NO_REFERENCED_ROW_2
+UPDATE t1, t2 SET t2.fld2= t2.fld2 + 3;
+
+--error ER_ROW_IS_REFERENCED_2
+UPDATE t1, t2 SET t1.fld1= t1.fld1 + 3;
+
+DROP TABLE t2, t1;
diff --git a/sql/handler.cc b/sql/handler.cc
index 6307e95a194..9d57cba73dc 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -5452,3 +5452,28 @@ fl_create_iterator(enum handler_iterator_type type,
}
}
#endif /*TRANS_LOG_MGM_EXAMPLE_CODE*/
+
+
+/**
+ Report a warning for FK constraint violation.
+
+ @param thd Thread handle.
+ @param table table on which the operation is performed.
+ @param error handler error number.
+*/
+void warn_fk_constraint_violation(THD *thd,TABLE *table, int error)
+{
+ String str;
+ switch(error) {
+ case HA_ERR_ROW_IS_REFERENCED:
+ table->file->get_error_message(error, &str);
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_ROW_IS_REFERENCED_2, str.c_ptr_safe());
+ break;
+ case HA_ERR_NO_REFERENCED_ROW:
+ table->file->get_error_message(error, &str);
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_NO_REFERENCED_ROW_2, str.c_ptr_safe());
+ break;
+ }
+}
diff --git a/sql/handler.h b/sql/handler.h
index 17306fe7dd4..29b6a86c030 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -2,7 +2,7 @@
#define HANDLER_INCLUDED
/*
- Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
@@ -303,6 +303,7 @@
/* Flags for method is_fatal_error */
#define HA_CHECK_DUP_KEY 1
#define HA_CHECK_DUP_UNIQUE 2
+#define HA_CHECK_FK_ERROR 4
#define HA_CHECK_DUP (HA_CHECK_DUP_KEY + HA_CHECK_DUP_UNIQUE)
enum legacy_db_type
@@ -1485,7 +1486,10 @@ public:
if (!error ||
((flags & HA_CHECK_DUP_KEY) &&
(error == HA_ERR_FOUND_DUPP_KEY ||
- error == HA_ERR_FOUND_DUPP_UNIQUE)))
+ error == HA_ERR_FOUND_DUPP_UNIQUE)) ||
+ ((flags & HA_CHECK_FK_ERROR) &&
+ (error == HA_ERR_ROW_IS_REFERENCED ||
+ error == HA_ERR_NO_REFERENCED_ROW)))
return FALSE;
return TRUE;
}
@@ -2362,4 +2366,6 @@ inline const char *table_case_name(HA_CREATE_INFO *info, const char *name)
return ((lower_case_table_names == 2 && info->alias) ? info->alias : name);
}
+void warn_fk_constraint_violation(THD *thd, TABLE *table, int error);
+
#endif /* HANDLER_INCLUDED */
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 7bfc7b083ac..a267108c847 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -1522,7 +1522,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
else
table->file->insert_id_for_cur_row= insert_id_for_cur_row;
bool is_duplicate_key_error;
- if (table->file->is_fatal_error(error, HA_CHECK_DUP))
+ if (table->file->is_fatal_error(error, HA_CHECK_DUP | HA_CHECK_FK_ERROR))
goto err;
is_duplicate_key_error= table->file->is_fatal_error(error, 0);
if (!is_duplicate_key_error)
@@ -1620,7 +1620,8 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
error != HA_ERR_RECORD_IS_THE_SAME)
{
if (info->ignore &&
- !table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
+ !table->file->is_fatal_error(error, HA_CHECK_DUP_KEY |
+ HA_CHECK_FK_ERROR))
{
goto ok_or_after_trg_err;
}
@@ -1733,7 +1734,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
{
DEBUG_SYNC(thd, "write_row_noreplace");
if (!info->ignore ||
- table->file->is_fatal_error(error, HA_CHECK_DUP))
+ table->file->is_fatal_error(error, HA_CHECK_DUP | HA_CHECK_FK_ERROR))
goto err;
table->file->restore_auto_increment(prev_insert_id);
goto ok_or_after_trg_err;
@@ -1751,6 +1752,9 @@ ok_or_after_trg_err:
my_safe_afree(key,table->s->max_unique_length,MAX_KEY_LENGTH);
if (!table->file->has_transactions())
thd->transaction.stmt.modified_non_trans_table= TRUE;
+ if (info->ignore &&
+ !table->file->is_fatal_error(error, HA_CHECK_FK_ERROR))
+ warn_fk_constraint_violation(thd, table, error);
DBUG_RETURN(trg_error);
err:
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index a29d474fbfc..64d1b3e49dc 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -735,7 +735,8 @@ int mysql_update(THD *thd,
error= 0;
}
else if (!ignore ||
- table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
+ table->file->is_fatal_error(error, HA_CHECK_DUP_KEY |
+ HA_CHECK_FK_ERROR))
{
/*
If (ignore && error is ignorable) we don't have to
@@ -743,7 +744,8 @@ int mysql_update(THD *thd,
*/
myf flags= 0;
- if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
+ if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY |
+ HA_CHECK_FK_ERROR))
flags|= ME_FATALERROR; /* Other handler errors are fatal */
prepare_record_for_error_message(error, table);
@@ -751,6 +753,9 @@ int mysql_update(THD *thd,
error= 1;
break;
}
+ else if (ignore && !table->file->is_fatal_error(error,
+ HA_CHECK_FK_ERROR))
+ warn_fk_constraint_violation(thd, table, error);
}
if (table->triggers &&
@@ -1883,7 +1888,8 @@ bool multi_update::send_data(List<Item> &not_used_values)
{
updated--;
if (!ignore ||
- table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
+ table->file->is_fatal_error(error, HA_CHECK_DUP_KEY |
+ HA_CHECK_FK_ERROR))
{
/*
If (ignore && error == is ignorable) we don't have to
@@ -1891,13 +1897,17 @@ bool multi_update::send_data(List<Item> &not_used_values)
*/
myf flags= 0;
- if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
+ if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY |
+ HA_CHECK_FK_ERROR))
flags|= ME_FATALERROR; /* Other handler errors are fatal */
prepare_record_for_error_message(error, table);
table->file->print_error(error,MYF(flags));
DBUG_RETURN(1);
}
+ else if (ignore && !table->file->is_fatal_error(error,
+ HA_CHECK_FK_ERROR))
+ warn_fk_constraint_violation(thd, table, error);
}
else
{
@@ -2138,8 +2148,12 @@ int multi_update::do_updates()
local_error != HA_ERR_RECORD_IS_THE_SAME)
{
if (!ignore ||
- table->file->is_fatal_error(local_error, HA_CHECK_DUP_KEY))
+ table->file->is_fatal_error(local_error, HA_CHECK_DUP_KEY |
+ HA_CHECK_FK_ERROR))
goto err;
+ else if (ignore && !table->file->is_fatal_error(local_error,
+ HA_CHECK_FK_ERROR))
+ warn_fk_constraint_violation(thd, table, local_error);
}
if (local_error != HA_ERR_RECORD_IS_THE_SAME)
updated++;