From 4ab180ad5e9c67427b561035e4b8e193d429fe5a Mon Sep 17 00:00:00 2001 From: Monty Date: Tue, 15 May 2018 17:02:08 +0300 Subject: [PATCH] MDEV-15461 Check Constraints with binary logging makes insert inconsistent Problem was that verify_constraints() didn't check if there was an error as part of evaluating constraints (can happen in strict mode). In one-row-insert the error was ignored when using binary logging as binary logging clear errors if insert succeeded. In multi-row-insert the error was noticed for the second row. After this fix one will get an error for both one and multi-row inserts if the constraints generates a warning in strict mode. --- mysql-test/r/check_constraint.result | 41 ++++++++++++++++++++++++++++ mysql-test/t/check_constraint.test | 24 ++++++++++++++++ sql/table.cc | 21 ++++++++++++-- 3 files changed, 83 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/check_constraint.result b/mysql-test/r/check_constraint.result index 70d64cd6ff7..9a32e6f12bc 100644 --- a/mysql-test/r/check_constraint.result +++ b/mysql-test/r/check_constraint.result @@ -156,3 +156,44 @@ create table t1 (id int auto_increment primary key, datecol datetime, check (dat insert into t1 (datecol) values (now()); insert into t1 (datecol) values (now()); drop table t1; +CREATE TABLE t1 ( +EmployeeID SMALLINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, +FirstName VARCHAR(30) NOT NULL CHECK (CHAR_LENGTH(FirstName > 2)) +); +INSERT INTO t1 VALUES (NULL, 'Ken'); +ERROR 22007: Truncated incorrect DOUBLE value: 'Ken' +SHOW WARNINGS; +Level Code Message +Error 1292 Truncated incorrect DOUBLE value: 'Ken' +Error 4025 CONSTRAINT `FirstName` failed for `test`.`t1` +INSERT INTO t1 VALUES (NULL, 'Ken'),(NULL, 'Brian'); +ERROR 22007: Truncated incorrect DOUBLE value: 'Ken' +SHOW WARNINGS; +Level Code Message +Error 1292 Truncated incorrect DOUBLE value: 'Ken' +Error 4025 CONSTRAINT `FirstName` failed for `test`.`t1` +INSERT IGNORE INTO t1 VALUES (NULL, 'Ken'); +Warnings: +Warning 1292 Truncated incorrect DOUBLE value: 'Ken' +INSERT IGNORE INTO t1 VALUES (NULL, 'Ken'),(NULL, 'Brian'); +Warnings: +Warning 1292 Truncated incorrect DOUBLE value: 'Ken' +Warning 1292 Truncated incorrect DOUBLE value: 'Brian' +set sql_mode=""; +INSERT INTO t1 VALUES (NULL, 'Ken'); +Warnings: +Warning 1292 Truncated incorrect DOUBLE value: 'Ken' +INSERT INTO t1 VALUES (NULL, 'Ken'),(NULL, 'Brian'); +Warnings: +Warning 1292 Truncated incorrect DOUBLE value: 'Ken' +Warning 1292 Truncated incorrect DOUBLE value: 'Brian' +set sql_mode=default; +select * from t1; +EmployeeID FirstName +1 Ken +2 Ken +3 Brian +4 Ken +5 Ken +6 Brian +drop table t1; diff --git a/mysql-test/t/check_constraint.test b/mysql-test/t/check_constraint.test index 9a77736acd7..02081071bd4 100644 --- a/mysql-test/t/check_constraint.test +++ b/mysql-test/t/check_constraint.test @@ -111,3 +111,27 @@ create table t1 (id int auto_increment primary key, datecol datetime, check (dat insert into t1 (datecol) values (now()); insert into t1 (datecol) values (now()); drop table t1; + +# +# MDEV-15461 Check Constraints with binary logging makes insert inconsistent +# + +CREATE TABLE t1 ( + EmployeeID SMALLINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + FirstName VARCHAR(30) NOT NULL CHECK (CHAR_LENGTH(FirstName > 2)) +); + +--error ER_TRUNCATED_WRONG_VALUE +INSERT INTO t1 VALUES (NULL, 'Ken'); +SHOW WARNINGS; +--error ER_TRUNCATED_WRONG_VALUE +INSERT INTO t1 VALUES (NULL, 'Ken'),(NULL, 'Brian'); +SHOW WARNINGS; +INSERT IGNORE INTO t1 VALUES (NULL, 'Ken'); +INSERT IGNORE INTO t1 VALUES (NULL, 'Ken'),(NULL, 'Brian'); +set sql_mode=""; +INSERT INTO t1 VALUES (NULL, 'Ken'); +INSERT INTO t1 VALUES (NULL, 'Ken'),(NULL, 'Brian'); +set sql_mode=default; +select * from t1; +drop table t1; diff --git a/sql/table.cc b/sql/table.cc index fff2be4f154..d7cbf555b72 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -5116,14 +5116,25 @@ int TABLE_LIST::view_check_option(THD *thd, bool ignore_failure) int TABLE::verify_constraints(bool ignore_failure) { + /* + We have to check is_error() first as we are checking it for each + constraint to catch fatal warnings. + */ + if (in_use->is_error()) + return (VIEW_CHECK_ERROR); + /* go trough check option clauses for fields and table */ if (check_constraints && !(in_use->variables.option_bits & OPTION_NO_CHECK_CONSTRAINT_CHECKS)) { for (Virtual_column_info **chk= check_constraints ; *chk ; chk++) { - /* yes! NULL is ok, see 4.23.3.4 Table check constraints, part 2, SQL:2016 */ - if ((*chk)->expr->val_int() == 0 && !(*chk)->expr->null_value) + /* + yes! NULL is ok. + see 4.23.3.4 Table check constraints, part 2, SQL:2016 + */ + if (((*chk)->expr->val_int() == 0 && !(*chk)->expr->null_value) || + in_use->is_error()) { my_error(ER_CONSTRAINT_FAILED, MYF(ignore_failure ? ME_JUST_WARNING : 0), (*chk)->name.str, @@ -5132,7 +5143,11 @@ int TABLE::verify_constraints(bool ignore_failure) } } } - return(VIEW_CHECK_OK); + /* + We have to check in_use() as checking constraints may have generated + warnings that should be treated as errors + */ + return(!in_use->is_error() ? VIEW_CHECK_OK : VIEW_CHECK_ERROR); }