mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
Bug#13002783 PARTIALLY UNINITIALIZED CASCADE UPDATE VECTOR
In the ON UPDATE CASCADE clause of FOREIGN KEY constraints, the calculated update vector was not fully initialized. This bug was introduced in the InnoDB Plugin when implementing support for ROW_FORMAT=DYNAMIC. Additionally, the data type information was not initialized, but apparently it has never been needed in this case. Nevertheless, it is not good programming practice to pass uninitialized values around. calc_row_difference(): Declare the update field uninitialized in Valgrind. Copy the data type information as well, except when the field is SQL NULL. In the built-in InnoDB, initialize ufield->extern_storage = FALSE (an initialization bug that had gone unnoticed this far). The InnoDB Plugin and later have this flag to dfield_t and have always initialized it properly. row_ins_cascade_calc_update_vec(): Reduce the scope of some pointers. Initialize orig_len. (This caused the bug in InnoDB Plugin and later.) row_ins_foreign_check_on_constraint(): Simplify a condition. Declare the update vector uninitialized. rb:771 approved by Jimmy Yang
This commit is contained in:
@ -1293,6 +1293,20 @@ ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fail
|
|||||||
update t3 set t3.id=7 where t1.id =1 and t2.id = t1.id and t3.id = t2.id;
|
update t3 set t3.id=7 where t1.id =1 and t2.id = t1.id and t3.id = t2.id;
|
||||||
ERROR 42S22: Unknown column 't1.id' in 'where clause'
|
ERROR 42S22: Unknown column 't1.id' in 'where clause'
|
||||||
drop table t3,t2,t1;
|
drop table t3,t2,t1;
|
||||||
|
CREATE TABLE t1 (
|
||||||
|
c1 VARCHAR(8), c2 VARCHAR(8),
|
||||||
|
PRIMARY KEY (c1, c2)
|
||||||
|
) ENGINE=InnoDB;
|
||||||
|
CREATE TABLE t2 (
|
||||||
|
c0 INT PRIMARY KEY,
|
||||||
|
c1 VARCHAR(8) UNIQUE,
|
||||||
|
FOREIGN KEY (c1) REFERENCES t1 (c1) ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB;
|
||||||
|
INSERT INTO t1 VALUES ('old', 'somevalu'), ('other', 'anyvalue');
|
||||||
|
INSERT INTO t2 VALUES (10, 'old'), (20, 'other');
|
||||||
|
UPDATE t1 SET c1 = 'other' WHERE c1 = 'old';
|
||||||
|
ERROR 23000: Upholding foreign key constraints for table 't1', entry '', key 2 would lead to a duplicate entry
|
||||||
|
DROP TABLE t2,t1;
|
||||||
create table t1(
|
create table t1(
|
||||||
id int primary key,
|
id int primary key,
|
||||||
pid int,
|
pid int,
|
||||||
@ -1671,7 +1685,7 @@ variable_value - @innodb_rows_deleted_orig
|
|||||||
71
|
71
|
||||||
SELECT variable_value - @innodb_rows_inserted_orig FROM information_schema.global_status WHERE LOWER(variable_name) = 'innodb_rows_inserted';
|
SELECT variable_value - @innodb_rows_inserted_orig FROM information_schema.global_status WHERE LOWER(variable_name) = 'innodb_rows_inserted';
|
||||||
variable_value - @innodb_rows_inserted_orig
|
variable_value - @innodb_rows_inserted_orig
|
||||||
1063
|
1067
|
||||||
SELECT variable_value - @innodb_rows_updated_orig FROM information_schema.global_status WHERE LOWER(variable_name) = 'innodb_rows_updated';
|
SELECT variable_value - @innodb_rows_updated_orig FROM information_schema.global_status WHERE LOWER(variable_name) = 'innodb_rows_updated';
|
||||||
variable_value - @innodb_rows_updated_orig
|
variable_value - @innodb_rows_updated_orig
|
||||||
865
|
865
|
||||||
|
@ -1021,6 +1021,24 @@ update t1,t2,t3 set t3.id=5, t2.id=6, t1.id=7 where t1.id =1 and t2.id = t1.id
|
|||||||
update t3 set t3.id=7 where t1.id =1 and t2.id = t1.id and t3.id = t2.id;
|
update t3 set t3.id=7 where t1.id =1 and t2.id = t1.id and t3.id = t2.id;
|
||||||
drop table t3,t2,t1;
|
drop table t3,t2,t1;
|
||||||
|
|
||||||
|
# test ON UPDATE CASCADE
|
||||||
|
CREATE TABLE t1 (
|
||||||
|
c1 VARCHAR(8), c2 VARCHAR(8),
|
||||||
|
PRIMARY KEY (c1, c2)
|
||||||
|
) ENGINE=InnoDB;
|
||||||
|
|
||||||
|
CREATE TABLE t2 (
|
||||||
|
c0 INT PRIMARY KEY,
|
||||||
|
c1 VARCHAR(8) UNIQUE,
|
||||||
|
FOREIGN KEY (c1) REFERENCES t1 (c1) ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB;
|
||||||
|
|
||||||
|
INSERT INTO t1 VALUES ('old', 'somevalu'), ('other', 'anyvalue');
|
||||||
|
INSERT INTO t2 VALUES (10, 'old'), (20, 'other');
|
||||||
|
-- error ER_FOREIGN_DUPLICATE_KEY
|
||||||
|
UPDATE t1 SET c1 = 'other' WHERE c1 = 'old';
|
||||||
|
DROP TABLE t2,t1;
|
||||||
|
|
||||||
#
|
#
|
||||||
# test for recursion depth limit
|
# test for recursion depth limit
|
||||||
#
|
#
|
||||||
|
@ -1301,6 +1301,20 @@ ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fail
|
|||||||
update t3 set t3.id=7 where t1.id =1 and t2.id = t1.id and t3.id = t2.id;
|
update t3 set t3.id=7 where t1.id =1 and t2.id = t1.id and t3.id = t2.id;
|
||||||
ERROR 42S22: Unknown column 't1.id' in 'where clause'
|
ERROR 42S22: Unknown column 't1.id' in 'where clause'
|
||||||
drop table t3,t2,t1;
|
drop table t3,t2,t1;
|
||||||
|
CREATE TABLE t1 (
|
||||||
|
c1 VARCHAR(8), c2 VARCHAR(8),
|
||||||
|
PRIMARY KEY (c1, c2)
|
||||||
|
) ENGINE=InnoDB;
|
||||||
|
CREATE TABLE t2 (
|
||||||
|
c0 INT PRIMARY KEY,
|
||||||
|
c1 VARCHAR(8) UNIQUE,
|
||||||
|
FOREIGN KEY (c1) REFERENCES t1 (c1) ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB;
|
||||||
|
INSERT INTO t1 VALUES ('old', 'somevalu'), ('other', 'anyvalue');
|
||||||
|
INSERT INTO t2 VALUES (10, 'old'), (20, 'other');
|
||||||
|
UPDATE t1 SET c1 = 'other' WHERE c1 = 'old';
|
||||||
|
ERROR 23000: Upholding foreign key constraints for table 't1', entry '', key 2 would lead to a duplicate entry
|
||||||
|
DROP TABLE t2,t1;
|
||||||
create table t1(
|
create table t1(
|
||||||
id int primary key,
|
id int primary key,
|
||||||
pid int,
|
pid int,
|
||||||
@ -1679,7 +1693,7 @@ variable_value - @innodb_rows_deleted_orig
|
|||||||
71
|
71
|
||||||
SELECT variable_value - @innodb_rows_inserted_orig FROM information_schema.global_status WHERE LOWER(variable_name) = 'innodb_rows_inserted';
|
SELECT variable_value - @innodb_rows_inserted_orig FROM information_schema.global_status WHERE LOWER(variable_name) = 'innodb_rows_inserted';
|
||||||
variable_value - @innodb_rows_inserted_orig
|
variable_value - @innodb_rows_inserted_orig
|
||||||
1067
|
1071
|
||||||
SELECT variable_value - @innodb_rows_updated_orig FROM information_schema.global_status WHERE LOWER(variable_name) = 'innodb_rows_updated';
|
SELECT variable_value - @innodb_rows_updated_orig FROM information_schema.global_status WHERE LOWER(variable_name) = 'innodb_rows_updated';
|
||||||
variable_value - @innodb_rows_updated_orig
|
variable_value - @innodb_rows_updated_orig
|
||||||
866
|
866
|
||||||
|
@ -1040,6 +1040,24 @@ update t1,t2,t3 set t3.id=5, t2.id=6, t1.id=7 where t1.id =1 and t2.id = t1.id
|
|||||||
update t3 set t3.id=7 where t1.id =1 and t2.id = t1.id and t3.id = t2.id;
|
update t3 set t3.id=7 where t1.id =1 and t2.id = t1.id and t3.id = t2.id;
|
||||||
drop table t3,t2,t1;
|
drop table t3,t2,t1;
|
||||||
|
|
||||||
|
# test ON UPDATE CASCADE
|
||||||
|
CREATE TABLE t1 (
|
||||||
|
c1 VARCHAR(8), c2 VARCHAR(8),
|
||||||
|
PRIMARY KEY (c1, c2)
|
||||||
|
) ENGINE=InnoDB;
|
||||||
|
|
||||||
|
CREATE TABLE t2 (
|
||||||
|
c0 INT PRIMARY KEY,
|
||||||
|
c1 VARCHAR(8) UNIQUE,
|
||||||
|
FOREIGN KEY (c1) REFERENCES t1 (c1) ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB;
|
||||||
|
|
||||||
|
INSERT INTO t1 VALUES ('old', 'somevalu'), ('other', 'anyvalue');
|
||||||
|
INSERT INTO t2 VALUES (10, 'old'), (20, 'other');
|
||||||
|
-- error ER_FOREIGN_DUPLICATE_KEY
|
||||||
|
UPDATE t1 SET c1 = 'other' WHERE c1 = 'old';
|
||||||
|
DROP TABLE t2,t1;
|
||||||
|
|
||||||
#
|
#
|
||||||
# test for recursion depth limit
|
# test for recursion depth limit
|
||||||
#
|
#
|
||||||
|
@ -4264,14 +4264,16 @@ calc_row_difference(
|
|||||||
/* The field has changed */
|
/* The field has changed */
|
||||||
|
|
||||||
ufield = uvect->fields + n_changed;
|
ufield = uvect->fields + n_changed;
|
||||||
|
UNIV_MEM_INVALID(ufield, sizeof *ufield);
|
||||||
|
|
||||||
/* Let us use a dummy dfield to make the conversion
|
/* Let us use a dummy dfield to make the conversion
|
||||||
from the MySQL column format to the InnoDB format */
|
from the MySQL column format to the InnoDB format */
|
||||||
|
|
||||||
dict_col_copy_type_noninline(prebuilt->table->cols + i,
|
if (n_len != UNIV_SQL_NULL) {
|
||||||
|
dict_col_copy_type_noninline(
|
||||||
|
prebuilt->table->cols + i,
|
||||||
&dfield.type);
|
&dfield.type);
|
||||||
|
|
||||||
if (n_len != UNIV_SQL_NULL) {
|
|
||||||
buf = row_mysql_store_col_in_innobase_format(
|
buf = row_mysql_store_col_in_innobase_format(
|
||||||
&dfield,
|
&dfield,
|
||||||
(byte*)buf,
|
(byte*)buf,
|
||||||
@ -4282,11 +4284,13 @@ calc_row_difference(
|
|||||||
prebuilt->table));
|
prebuilt->table));
|
||||||
ufield->new_val.data = dfield.data;
|
ufield->new_val.data = dfield.data;
|
||||||
ufield->new_val.len = dfield.len;
|
ufield->new_val.len = dfield.len;
|
||||||
|
ufield->new_val.type = dfield.type;
|
||||||
} else {
|
} else {
|
||||||
ufield->new_val.data = NULL;
|
ufield->new_val.data = NULL;
|
||||||
ufield->new_val.len = UNIV_SQL_NULL;
|
ufield->new_val.len = UNIV_SQL_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ufield->extern_storage = FALSE;
|
||||||
ufield->exp = NULL;
|
ufield->exp = NULL;
|
||||||
ufield->field_no = dict_col_get_clust_pos_noninline(
|
ufield->field_no = dict_col_get_clust_pos_noninline(
|
||||||
&prebuilt->table->cols[i], clust_index);
|
&prebuilt->table->cols[i], clust_index);
|
||||||
|
@ -425,11 +425,9 @@ row_ins_cascade_calc_update_vec(
|
|||||||
dict_table_t* table = foreign->foreign_table;
|
dict_table_t* table = foreign->foreign_table;
|
||||||
dict_index_t* index = foreign->foreign_index;
|
dict_index_t* index = foreign->foreign_index;
|
||||||
upd_t* update;
|
upd_t* update;
|
||||||
upd_field_t* ufield;
|
|
||||||
dict_table_t* parent_table;
|
dict_table_t* parent_table;
|
||||||
dict_index_t* parent_index;
|
dict_index_t* parent_index;
|
||||||
upd_t* parent_update;
|
upd_t* parent_update;
|
||||||
upd_field_t* parent_ufield;
|
|
||||||
ulint n_fields_updated;
|
ulint n_fields_updated;
|
||||||
ulint parent_field_no;
|
ulint parent_field_no;
|
||||||
ulint i;
|
ulint i;
|
||||||
@ -465,12 +463,14 @@ row_ins_cascade_calc_update_vec(
|
|||||||
dict_index_get_nth_col_no(parent_index, i));
|
dict_index_get_nth_col_no(parent_index, i));
|
||||||
|
|
||||||
for (j = 0; j < parent_update->n_fields; j++) {
|
for (j = 0; j < parent_update->n_fields; j++) {
|
||||||
parent_ufield = parent_update->fields + j;
|
const upd_field_t* parent_ufield
|
||||||
|
= &parent_update->fields[j];
|
||||||
|
|
||||||
if (parent_ufield->field_no == parent_field_no) {
|
if (parent_ufield->field_no == parent_field_no) {
|
||||||
|
|
||||||
ulint min_size;
|
ulint min_size;
|
||||||
const dict_col_t* col;
|
const dict_col_t* col;
|
||||||
|
upd_field_t* ufield;
|
||||||
|
|
||||||
col = dict_index_get_nth_col(index, i);
|
col = dict_index_get_nth_col(index, i);
|
||||||
|
|
||||||
@ -975,10 +975,9 @@ row_ins_foreign_check_on_constraint(
|
|||||||
goto nonstandard_exit_func;
|
goto nonstandard_exit_func;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((node->is_delete
|
if (node->is_delete
|
||||||
&& (foreign->type & DICT_FOREIGN_ON_DELETE_SET_NULL))
|
? (foreign->type & DICT_FOREIGN_ON_DELETE_SET_NULL)
|
||||||
|| (!node->is_delete
|
: (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL)) {
|
||||||
&& (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL))) {
|
|
||||||
|
|
||||||
/* Build the appropriate update vector which sets
|
/* Build the appropriate update vector which sets
|
||||||
foreign->n_fields first fields in rec to SQL NULL */
|
foreign->n_fields first fields in rec to SQL NULL */
|
||||||
@ -987,6 +986,8 @@ row_ins_foreign_check_on_constraint(
|
|||||||
|
|
||||||
update->info_bits = 0;
|
update->info_bits = 0;
|
||||||
update->n_fields = foreign->n_fields;
|
update->n_fields = foreign->n_fields;
|
||||||
|
UNIV_MEM_INVALID(update->fields,
|
||||||
|
update->n_fields * sizeof *update->fields);
|
||||||
|
|
||||||
for (i = 0; i < foreign->n_fields; i++) {
|
for (i = 0; i < foreign->n_fields; i++) {
|
||||||
(update->fields + i)->field_no
|
(update->fields + i)->field_no
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
|
2011-10-25 The InnoDB Team
|
||||||
|
|
||||||
|
* handler/ha_innodb.cc, row/row0ins.c:
|
||||||
|
Fix Bug#13002783 PARTIALLY UNINITIALIZED CASCADE UPDATE VECTOR
|
||||||
|
|
||||||
2011-10-20 The InnoDB Team
|
2011-10-20 The InnoDB Team
|
||||||
|
|
||||||
* btr/brt0cur.c:
|
* btr/btr0cur.c:
|
||||||
Fix Bug#13116045 Compilation failure using GCC 4.6.1 in btr/btr0cur.c
|
Fix Bug#13116045 Compilation failure using GCC 4.6.1 in btr/btr0cur.c
|
||||||
|
|
||||||
2011-10-12 The InnoDB Team
|
2011-10-12 The InnoDB Team
|
||||||
|
@ -4971,14 +4971,15 @@ calc_row_difference(
|
|||||||
/* The field has changed */
|
/* The field has changed */
|
||||||
|
|
||||||
ufield = uvect->fields + n_changed;
|
ufield = uvect->fields + n_changed;
|
||||||
|
UNIV_MEM_INVALID(ufield, sizeof *ufield);
|
||||||
|
|
||||||
/* Let us use a dummy dfield to make the conversion
|
/* Let us use a dummy dfield to make the conversion
|
||||||
from the MySQL column format to the InnoDB format */
|
from the MySQL column format to the InnoDB format */
|
||||||
|
|
||||||
|
if (n_len != UNIV_SQL_NULL) {
|
||||||
dict_col_copy_type(prebuilt->table->cols + i,
|
dict_col_copy_type(prebuilt->table->cols + i,
|
||||||
dfield_get_type(&dfield));
|
dfield_get_type(&dfield));
|
||||||
|
|
||||||
if (n_len != UNIV_SQL_NULL) {
|
|
||||||
buf = row_mysql_store_col_in_innobase_format(
|
buf = row_mysql_store_col_in_innobase_format(
|
||||||
&dfield,
|
&dfield,
|
||||||
(byte*)buf,
|
(byte*)buf,
|
||||||
@ -4986,7 +4987,7 @@ calc_row_difference(
|
|||||||
new_mysql_row_col,
|
new_mysql_row_col,
|
||||||
col_pack_len,
|
col_pack_len,
|
||||||
dict_table_is_comp(prebuilt->table));
|
dict_table_is_comp(prebuilt->table));
|
||||||
dfield_copy_data(&ufield->new_val, &dfield);
|
dfield_copy(&ufield->new_val, &dfield);
|
||||||
} else {
|
} else {
|
||||||
dfield_set_null(&ufield->new_val);
|
dfield_set_null(&ufield->new_val);
|
||||||
}
|
}
|
||||||
|
@ -434,11 +434,9 @@ row_ins_cascade_calc_update_vec(
|
|||||||
dict_table_t* table = foreign->foreign_table;
|
dict_table_t* table = foreign->foreign_table;
|
||||||
dict_index_t* index = foreign->foreign_index;
|
dict_index_t* index = foreign->foreign_index;
|
||||||
upd_t* update;
|
upd_t* update;
|
||||||
upd_field_t* ufield;
|
|
||||||
dict_table_t* parent_table;
|
dict_table_t* parent_table;
|
||||||
dict_index_t* parent_index;
|
dict_index_t* parent_index;
|
||||||
upd_t* parent_update;
|
upd_t* parent_update;
|
||||||
upd_field_t* parent_ufield;
|
|
||||||
ulint n_fields_updated;
|
ulint n_fields_updated;
|
||||||
ulint parent_field_no;
|
ulint parent_field_no;
|
||||||
ulint i;
|
ulint i;
|
||||||
@ -474,13 +472,15 @@ row_ins_cascade_calc_update_vec(
|
|||||||
dict_index_get_nth_col_no(parent_index, i));
|
dict_index_get_nth_col_no(parent_index, i));
|
||||||
|
|
||||||
for (j = 0; j < parent_update->n_fields; j++) {
|
for (j = 0; j < parent_update->n_fields; j++) {
|
||||||
parent_ufield = parent_update->fields + j;
|
const upd_field_t* parent_ufield
|
||||||
|
= &parent_update->fields[j];
|
||||||
|
|
||||||
if (parent_ufield->field_no == parent_field_no) {
|
if (parent_ufield->field_no == parent_field_no) {
|
||||||
|
|
||||||
ulint min_size;
|
ulint min_size;
|
||||||
const dict_col_t* col;
|
const dict_col_t* col;
|
||||||
ulint ufield_len;
|
ulint ufield_len;
|
||||||
|
upd_field_t* ufield;
|
||||||
|
|
||||||
col = dict_index_get_nth_col(index, i);
|
col = dict_index_get_nth_col(index, i);
|
||||||
|
|
||||||
@ -493,6 +493,8 @@ row_ins_cascade_calc_update_vec(
|
|||||||
ufield->field_no
|
ufield->field_no
|
||||||
= dict_table_get_nth_col_pos(
|
= dict_table_get_nth_col_pos(
|
||||||
table, dict_col_get_no(col));
|
table, dict_col_get_no(col));
|
||||||
|
|
||||||
|
ufield->orig_len = 0;
|
||||||
ufield->exp = NULL;
|
ufield->exp = NULL;
|
||||||
|
|
||||||
ufield->new_val = parent_ufield->new_val;
|
ufield->new_val = parent_ufield->new_val;
|
||||||
@ -993,10 +995,9 @@ row_ins_foreign_check_on_constraint(
|
|||||||
goto nonstandard_exit_func;
|
goto nonstandard_exit_func;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((node->is_delete
|
if (node->is_delete
|
||||||
&& (foreign->type & DICT_FOREIGN_ON_DELETE_SET_NULL))
|
? (foreign->type & DICT_FOREIGN_ON_DELETE_SET_NULL)
|
||||||
|| (!node->is_delete
|
: (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL)) {
|
||||||
&& (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL))) {
|
|
||||||
|
|
||||||
/* Build the appropriate update vector which sets
|
/* Build the appropriate update vector which sets
|
||||||
foreign->n_fields first fields in rec to SQL NULL */
|
foreign->n_fields first fields in rec to SQL NULL */
|
||||||
@ -1005,6 +1006,8 @@ row_ins_foreign_check_on_constraint(
|
|||||||
|
|
||||||
update->info_bits = 0;
|
update->info_bits = 0;
|
||||||
update->n_fields = foreign->n_fields;
|
update->n_fields = foreign->n_fields;
|
||||||
|
UNIV_MEM_INVALID(update->fields,
|
||||||
|
update->n_fields * sizeof *update->fields);
|
||||||
|
|
||||||
for (i = 0; i < foreign->n_fields; i++) {
|
for (i = 0; i < foreign->n_fields; i++) {
|
||||||
upd_field_t* ufield = &update->fields[i];
|
upd_field_t* ufield = &update->fields[i];
|
||||||
|
Reference in New Issue
Block a user