Commit e0fbc5d2 authored by Tatiana A. Nurnberg's avatar Tatiana A. Nurnberg

Bug#48525: trigger changes "Column 'id' cannot be null" behaviour

CHECK_FIELD_IGNORE was treated as CHECK_FIELD_ERROR_FOR_NULL;
UPDATE...SET...NULL on NOT NULL fields behaved differently after
a trigger.

Now distinguishes between IGNORE and ERROR_FOR_NULL and save/restores
check-field options.

mysql-test/r/trigger.result:
  Show that UPDATE...SET...NULL on NOT NULL columns doesn't behave differently
  when run after a trigger.
mysql-test/t/trigger.test:
  Show that UPDATE...SET...NULL on NOT NULL columns doesn't behave differently
  when run after a trigger.
sql/field_conv.cc:
  CHECK_FIELD_IGNORE was treated as CHECK_FIELD_ERROR_FOR_NULL.
  Distinguish between the two.
sql/sp_head.cc:
  raise error as needed
sql/sql_class.cc:
  Save and restore check-fields options.
sql/sql_class.h:
  Make room so we can save check-fields options.
sql/sql_insert.cc:
  raise error as needed
parent 141bb7d1
...@@ -2087,4 +2087,21 @@ ERROR 42S02: Table 'test.a_nonextisting_table' doesn't exist ...@@ -2087,4 +2087,21 @@ ERROR 42S02: Table 'test.a_nonextisting_table' doesn't exist
SELECT * FROM t2; SELECT * FROM t2;
a b a b
DROP TABLE t1, t2; DROP TABLE t1, t2;
CREATE TABLE t1 (id INT NOT NULL);
CREATE TABLE t2 (id INT NOT NULL);
INSERT t1 VALUES (1),(2),(3);
UPDATE t1 SET id=NULL;
Warnings:
Warning 1048 Column 'id' cannot be null
Warning 1048 Column 'id' cannot be null
Warning 1048 Column 'id' cannot be null
CREATE TRIGGER t1_bu BEFORE UPDATE ON t1 FOR EACH ROW
INSERT INTO t2 VALUES (3);
UPDATE t1 SET id=NULL;
Warnings:
Warning 1048 Column 'id' cannot be null
Warning 1048 Column 'id' cannot be null
Warning 1048 Column 'id' cannot be null
DROP TRIGGER t1_bu;
DROP TABLE t1,t2;
End of 5.1 tests. End of 5.1 tests.
...@@ -2396,4 +2396,17 @@ SELECT * FROM t2; ...@@ -2396,4 +2396,17 @@ SELECT * FROM t2;
DROP TABLE t1, t2; DROP TABLE t1, t2;
#
# Bug #48525: trigger changes "Column 'id' cannot be null" behaviour
#
CREATE TABLE t1 (id INT NOT NULL);
CREATE TABLE t2 (id INT NOT NULL);
INSERT t1 VALUES (1),(2),(3);
UPDATE t1 SET id=NULL;
CREATE TRIGGER t1_bu BEFORE UPDATE ON t1 FOR EACH ROW
INSERT INTO t2 VALUES (3);
UPDATE t1 SET id=NULL;
DROP TRIGGER t1_bu;
DROP TABLE t1,t2;
--echo End of 5.1 tests. --echo End of 5.1 tests.
...@@ -122,13 +122,18 @@ set_field_to_null(Field *field) ...@@ -122,13 +122,18 @@ set_field_to_null(Field *field)
return 0; return 0;
} }
field->reset(); field->reset();
if (field->table->in_use->count_cuted_fields == CHECK_FIELD_WARN) switch (field->table->in_use->count_cuted_fields) {
{ case CHECK_FIELD_WARN:
field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
/* fall through */
case CHECK_FIELD_IGNORE:
return 0; return 0;
case CHECK_FIELD_ERROR_FOR_NULL:
if (!field->table->in_use->no_errors)
my_error(ER_BAD_NULL_ERROR, MYF(0), field->field_name);
return -1;
} }
if (!field->table->in_use->no_errors) DBUG_ASSERT(0); // impossible
my_error(ER_BAD_NULL_ERROR, MYF(0), field->field_name);
return -1; return -1;
} }
...@@ -178,13 +183,18 @@ set_field_to_null_with_conversions(Field *field, bool no_conversions) ...@@ -178,13 +183,18 @@ set_field_to_null_with_conversions(Field *field, bool no_conversions)
field->table->auto_increment_field_not_null= FALSE; field->table->auto_increment_field_not_null= FALSE;
return 0; // field is set in fill_record() return 0; // field is set in fill_record()
} }
if (field->table->in_use->count_cuted_fields == CHECK_FIELD_WARN) switch (field->table->in_use->count_cuted_fields) {
{ case CHECK_FIELD_WARN:
field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_BAD_NULL_ERROR, 1); field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_BAD_NULL_ERROR, 1);
/* fall through */
case CHECK_FIELD_IGNORE:
return 0; return 0;
case CHECK_FIELD_ERROR_FOR_NULL:
if (!field->table->in_use->no_errors)
my_error(ER_BAD_NULL_ERROR, MYF(0), field->field_name);
return -1;
} }
if (!field->table->in_use->no_errors) DBUG_ASSERT(0); // impossible
my_error(ER_BAD_NULL_ERROR, MYF(0), field->field_name);
return -1; return -1;
} }
......
...@@ -3009,6 +3009,7 @@ int ...@@ -3009,6 +3009,7 @@ int
sp_instr_set_trigger_field::execute(THD *thd, uint *nextp) sp_instr_set_trigger_field::execute(THD *thd, uint *nextp)
{ {
DBUG_ENTER("sp_instr_set_trigger_field::execute"); DBUG_ENTER("sp_instr_set_trigger_field::execute");
thd->count_cuted_fields= CHECK_FIELD_ERROR_FOR_NULL;
DBUG_RETURN(m_lex_keeper.reset_lex_and_exec_core(thd, nextp, TRUE, this)); DBUG_RETURN(m_lex_keeper.reset_lex_and_exec_core(thd, nextp, TRUE, this));
} }
......
...@@ -3100,6 +3100,7 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup, ...@@ -3100,6 +3100,7 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup,
} }
#endif #endif
backup->count_cuted_fields= count_cuted_fields;
backup->options= options; backup->options= options;
backup->in_sub_stmt= in_sub_stmt; backup->in_sub_stmt= in_sub_stmt;
backup->enable_slow_log= enable_slow_log; backup->enable_slow_log= enable_slow_log;
...@@ -3137,6 +3138,7 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup, ...@@ -3137,6 +3138,7 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup,
void THD::restore_sub_statement_state(Sub_statement_state *backup) void THD::restore_sub_statement_state(Sub_statement_state *backup)
{ {
DBUG_ENTER("THD::restore_sub_statement_state");
#ifndef EMBEDDED_LIBRARY #ifndef EMBEDDED_LIBRARY
/* BUG#33029, if we are replicating from a buggy master, restore /* BUG#33029, if we are replicating from a buggy master, restore
auto_inc_intervals_forced so that the top statement can use the auto_inc_intervals_forced so that the top statement can use the
...@@ -3163,6 +3165,7 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup) ...@@ -3163,6 +3165,7 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup)
/* ha_release_savepoint() never returns error. */ /* ha_release_savepoint() never returns error. */
(void)ha_release_savepoint(this, sv); (void)ha_release_savepoint(this, sv);
} }
count_cuted_fields= backup->count_cuted_fields;
transaction.savepoints= backup->savepoints; transaction.savepoints= backup->savepoints;
options= backup->options; options= backup->options;
in_sub_stmt= backup->in_sub_stmt; in_sub_stmt= backup->in_sub_stmt;
...@@ -3192,6 +3195,7 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup) ...@@ -3192,6 +3195,7 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup)
*/ */
examined_row_count+= backup->examined_row_count; examined_row_count+= backup->examined_row_count;
cuted_fields+= backup->cuted_fields; cuted_fields+= backup->cuted_fields;
DBUG_VOID_RETURN;
} }
......
...@@ -995,6 +995,7 @@ public: ...@@ -995,6 +995,7 @@ public:
bool enable_slow_log; bool enable_slow_log;
bool last_insert_id_used; bool last_insert_id_used;
SAVEPOINT *savepoints; SAVEPOINT *savepoints;
enum enum_check_fields count_cuted_fields;
}; };
......
...@@ -3158,7 +3158,7 @@ bool select_insert::send_data(List<Item> &values) ...@@ -3158,7 +3158,7 @@ bool select_insert::send_data(List<Item> &values)
thd->count_cuted_fields= CHECK_FIELD_WARN; // Calculate cuted fields thd->count_cuted_fields= CHECK_FIELD_WARN; // Calculate cuted fields
store_values(values); store_values(values);
thd->count_cuted_fields= CHECK_FIELD_IGNORE; thd->count_cuted_fields= CHECK_FIELD_ERROR_FOR_NULL;
if (thd->is_error()) if (thd->is_error())
{ {
table->auto_increment_field_not_null= FALSE; table->auto_increment_field_not_null= FALSE;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment