Commit 094f4cf7 authored by Igor Babaev's avatar Igor Babaev

Fixed bug mdev-3845.

If triggers are used for an insert/update/delete statement than the values of
all virtual columns must be computed as any of them may be used by the triggers.
parent b0fec77d
......@@ -108,3 +108,44 @@ select * from t1;
drop table t1,t2;
drop procedure p1;
--echo #
--echo # Bug mdev-3845: values of virtual columns are not computed for triggers
--echo #
CREATE TABLE t1 (
a INTEGER UNSIGNED NULL DEFAULT NULL,
b INTEGER UNSIGNED GENERATED ALWAYS AS (a) VIRTUAL
);
CREATE TABLE t2 (c INTEGER UNSIGNED NOT NULL);
DELIMITER |;
CREATE TRIGGER t1_ins_aft
AFTER INSERT
ON t1
FOR EACH ROW
BEGIN
INSERT INTO t2 (c) VALUES (NEW.b);
END |
CREATE TRIGGER t1_del_bef
BEFORE DELETE
ON t1
FOR EACH ROW
BEGIN
INSERT INTO t2 (c) VALUES (OLD.b);
END |
DELIMITER ;|
INSERT INTO t1 (a) VALUES (1), (2), (3);
SELECT * FROM t2;
DELETE FROM t1;
SELECT * FROM t2;
DROP TRIGGER t1_ins_aft;
DROP TRIGGER t1_del_bef;
DROP TABLE t1,t2;
......@@ -85,3 +85,43 @@ a b c
300 30 30
drop table t1,t2;
drop procedure p1;
#
# Bug mdev-3845: values of virtual columns are not computed for triggers
#
CREATE TABLE t1 (
a INTEGER UNSIGNED NULL DEFAULT NULL,
b INTEGER UNSIGNED GENERATED ALWAYS AS (a) VIRTUAL
);
CREATE TABLE t2 (c INTEGER UNSIGNED NOT NULL);
CREATE TRIGGER t1_ins_aft
AFTER INSERT
ON t1
FOR EACH ROW
BEGIN
INSERT INTO t2 (c) VALUES (NEW.b);
END |
CREATE TRIGGER t1_del_bef
BEFORE DELETE
ON t1
FOR EACH ROW
BEGIN
INSERT INTO t2 (c) VALUES (OLD.b);
END |
INSERT INTO t1 (a) VALUES (1), (2), (3);
SELECT * FROM t2;
c
1
2
3
DELETE FROM t1;
SELECT * FROM t2;
c
1
2
3
1
2
3
DROP TRIGGER t1_ins_aft;
DROP TRIGGER t1_del_bef;
DROP TABLE t1,t2;
......@@ -85,3 +85,43 @@ a b c
300 30 30
drop table t1,t2;
drop procedure p1;
#
# Bug mdev-3845: values of virtual columns are not computed for triggers
#
CREATE TABLE t1 (
a INTEGER UNSIGNED NULL DEFAULT NULL,
b INTEGER UNSIGNED GENERATED ALWAYS AS (a) VIRTUAL
);
CREATE TABLE t2 (c INTEGER UNSIGNED NOT NULL);
CREATE TRIGGER t1_ins_aft
AFTER INSERT
ON t1
FOR EACH ROW
BEGIN
INSERT INTO t2 (c) VALUES (NEW.b);
END |
CREATE TRIGGER t1_del_bef
BEFORE DELETE
ON t1
FOR EACH ROW
BEGIN
INSERT INTO t2 (c) VALUES (OLD.b);
END |
INSERT INTO t1 (a) VALUES (1), (2), (3);
SELECT * FROM t2;
c
1
2
3
DELETE FROM t1;
SELECT * FROM t2;
c
1
2
3
1
2
3
DROP TRIGGER t1_ins_aft;
DROP TRIGGER t1_del_bef;
DROP TABLE t1,t2;
......@@ -1366,7 +1366,8 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length,
bool allow_rowid, uint *cached_field_index_ptr);
Field *
find_field_in_table_sef(TABLE *table, const char *name);
int update_virtual_fields(THD *thd, TABLE *table, bool ignore_stored= FALSE);
int update_virtual_fields(THD *thd, TABLE *table,
enum enum_vcol_update_mode vcol_update_mode= VCOL_UPDATE_FOR_READ);
#endif /* MYSQL_SERVER */
......
......@@ -8460,7 +8460,9 @@ fill_record(THD * thd, List<Item> &fields, List<Item> &values,
{
if (vcol_table->vfield)
{
if (update_virtual_fields(thd, vcol_table, TRUE))
if (update_virtual_fields(thd, vcol_table,
vcol_table->triggers ? VCOL_UPDATE_ALL :
VCOL_UPDATE_FOR_WRITE))
goto err;
}
}
......@@ -8524,7 +8526,9 @@ fill_record_n_invoke_before_triggers(THD *thd, List<Item> &fields,
if (item_field && item_field->field &&
(table= item_field->field->table) &&
table->vfield)
result= update_virtual_fields(thd, table, TRUE);
result= update_virtual_fields(thd, table,
table->triggers ? VCOL_UPDATE_ALL :
VCOL_UPDATE_FOR_WRITE);
}
}
return result;
......@@ -8604,7 +8608,10 @@ fill_record(THD *thd, Field **ptr, List<Item> &values, bool ignore_errors)
}
/* Update virtual fields*/
thd->abort_on_warning= FALSE;
if (table->vfield && update_virtual_fields(thd, table, TRUE))
if (table->vfield &&
update_virtual_fields(thd, table,
table->triggers ? VCOL_UPDATE_ALL :
VCOL_UPDATE_FOR_WRITE))
goto err;
thd->abort_on_warning= abort_on_warning_saved;
DBUG_RETURN(thd->is_error());
......@@ -8657,7 +8664,9 @@ fill_record_n_invoke_before_triggers(THD *thd, Field **ptr,
{
TABLE *table= (*ptr)->table;
if (table->vfield)
result= update_virtual_fields(thd, table, TRUE);
result= update_virtual_fields(thd, table,
table->triggers ? VCOL_UPDATE_ALL :
VCOL_UPDATE_FOR_WRITE);
}
return result;
......
......@@ -313,7 +313,9 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
while (!(error=info.read_record(&info)) && !thd->killed &&
! thd->is_error())
{
update_virtual_fields(thd, table);
update_virtual_fields(thd, table,
triggers_applicable ? VCOL_UPDATE_ALL :
VCOL_UPDATE_FOR_READ);
thd->examined_row_count++;
// thd->is_error() is tested to disallow delete row on error
if (!select || select->skip_record(thd) > 0)
......
......@@ -8066,7 +8066,7 @@ copy_data_between_tables(TABLE *from,TABLE *to,
copy_ptr->do_copy(copy_ptr);
}
prev_insert_id= to->file->next_insert_id;
update_virtual_fields(thd, to, TRUE);
update_virtual_fields(thd, to, VCOL_UPDATE_FOR_WRITE);
if (thd->is_error())
{
error= 1;
......
......@@ -490,7 +490,9 @@ int mysql_update(THD *thd,
while (!(error=info.read_record(&info)) && !thd->killed)
{
update_virtual_fields(thd, table);
update_virtual_fields(thd, table,
table->triggers ? VCOL_UPDATE_ALL :
VCOL_UPDATE_FOR_READ);
thd->examined_row_count++;
if (!select || (error= select->skip_record(thd)) > 0)
{
......@@ -605,7 +607,9 @@ int mysql_update(THD *thd,
while (!(error=info.read_record(&info)) && !thd->killed)
{
update_virtual_fields(thd, table);
update_virtual_fields(thd, table,
table->triggers ? VCOL_UPDATE_ALL :
VCOL_UPDATE_FOR_READ);
thd->examined_row_count++;
if (!select || select->skip_record(thd) > 0)
{
......
......@@ -5500,22 +5500,25 @@ size_t max_row_length(TABLE *table, const uchar *data)
@param thd Thread handle
@param table The TABLE object
@param for_write Requests to compute only fields needed for write
@param vcol_update_mode Specifies what virtual column are computed
@details
The function computes the values of the virtual columns of the table and
stores them in the table record buffer.
Only fields from vcol_set are computed, and, when the flag for_write is not
set to TRUE, a virtual field is computed only if it's not stored.
The flag for_write is set to TRUE for row insert/update operations.
If vcol_update_mode is set to VCOL_UPDATE_ALL then all virtual column are
computed. Otherwise, only fields from vcol_set are computed: all of them,
if vcol_update_mode is set to VCOL_UPDATE_FOR_WRITE, and, only those with
the stored_in_db flag set to false, if vcol_update_mode is equal to
VCOL_UPDATE_FOR_READ.
@retval
0 Success
@retval
>0 Error occurred when storing a virtual field value
*/
int update_virtual_fields(THD *thd, TABLE *table, bool for_write)
int update_virtual_fields(THD *thd, TABLE *table,
enum enum_vcol_update_mode vcol_update_mode)
{
DBUG_ENTER("update_virtual_fields");
Field **vfield_ptr, *vfield;
......@@ -5529,9 +5532,9 @@ int update_virtual_fields(THD *thd, TABLE *table, bool for_write)
{
vfield= (*vfield_ptr);
DBUG_ASSERT(vfield->vcol_info && vfield->vcol_info->expr_item);
/* Only update those fields that are marked in the vcol_set bitmap */
if (bitmap_is_set(table->vcol_set, vfield->field_index) &&
(for_write || !vfield->stored_in_db))
if ((bitmap_is_set(table->vcol_set, vfield->field_index) &&
(vcol_update_mode == VCOL_UPDATE_FOR_WRITE || !vfield->stored_in_db)) ||
vcol_update_mode == VCOL_UPDATE_ALL)
{
/* Compute the actual value of the virtual fields */
error= vfield->vcol_info->expr_item->save_in_field(vfield, 0);
......
......@@ -156,6 +156,13 @@ enum frm_type_enum
enum release_type { RELEASE_NORMAL, RELEASE_WAIT_FOR_DROP };
enum enum_vcol_update_mode
{
VCOL_UPDATE_FOR_READ= 0,
VCOL_UPDATE_FOR_WRITE,
VCOL_UPDATE_ALL
};
typedef struct st_filesort_info
{
IO_CACHE *io_cache; /* If sorted through filesort */
......
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