summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergei Golubchik <serg@mariadb.org>2017-01-03 14:55:29 +0100
committerSergei Golubchik <serg@mariadb.org>2017-02-13 18:12:04 +0100
commitcf003933785cbf88504bcf81001c776dabd64b15 (patch)
tree10a23e84cf9c4f62dc2b8455f639bc0a18bc5da2
parentcd4dd2b62dda31a4ea1da99ca6732ecb7ee0d628 (diff)
downloadmariadb-git-cf003933785cbf88504bcf81001c776dabd64b15.tar.gz
Race condition in DEFAULT() with expressions
Item_default_value::calculate was updating table->s->default_values, but it is supposed to be read-only
-rw-r--r--mysql-test/r/default_debug.result21
-rw-r--r--mysql-test/t/default_debug.test20
-rw-r--r--sql/item.cc22
3 files changed, 55 insertions, 8 deletions
diff --git a/mysql-test/r/default_debug.result b/mysql-test/r/default_debug.result
new file mode 100644
index 00000000000..3f5b0b7896c
--- /dev/null
+++ b/mysql-test/r/default_debug.result
@@ -0,0 +1,21 @@
+create table t1 (a int, b int default (a+1));
+insert t1 values (1,10), (2,20), (3,30);
+connect con1, localhost, root;
+select a,b,default(b) from t1;
+a b default(b)
+1 10 2
+2 20 3
+3 30 4
+set debug_sync='after_Item_default_value_calculate WAIT_FOR go';
+select a,b,default(b) from t1;
+connection default;
+set debug_sync='ha_write_row_start SIGNAL go';
+insert t1 values (100,default(b));
+connection con1;
+a b default(b)
+1 10 2
+2 20 3
+3 30 4
+connection default;
+drop table t1;
+set debug_sync='RESET';
diff --git a/mysql-test/t/default_debug.test b/mysql-test/t/default_debug.test
new file mode 100644
index 00000000000..847966bfa81
--- /dev/null
+++ b/mysql-test/t/default_debug.test
@@ -0,0 +1,20 @@
+#
+# Race condition in DEFAULT() with expressions
+#
+
+source include/have_debug_sync.inc;
+
+create table t1 (a int, b int default (a+1));
+insert t1 values (1,10), (2,20), (3,30);
+connect (con1, localhost, root);
+select a,b,default(b) from t1;
+set debug_sync='after_Item_default_value_calculate WAIT_FOR go';
+send select a,b,default(b) from t1;
+connection default;
+set debug_sync='ha_write_row_start SIGNAL go';
+insert t1 values (100,default(b));
+connection con1;
+reap;
+connection default;
+drop table t1;
+set debug_sync='RESET';
diff --git a/sql/item.cc b/sql/item.cc
index a2c59255b55..3b3e878f4d3 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -8758,17 +8758,22 @@ bool Item_default_value::fix_fields(THD *thd, Item **items)
goto error;
memcpy((void *)def_field, (void *)field_arg->field,
field_arg->field->size_of());
- def_field->move_field_offset((my_ptrdiff_t)
- (def_field->table->s->default_values -
- def_field->table->record[0]));
- set_field(def_field);
- if (field->default_value)
+ IF_DBUG(def_field->is_stat_field=1,); // a hack to fool ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED
+ if (def_field->default_value && def_field->default_value->flags)
{
- fix_session_vcol_expr_for_read(thd, field, field->default_value);
+ uchar *newptr= (uchar*) thd->alloc(1+def_field->pack_length());
+ if (!newptr)
+ goto error;
+ fix_session_vcol_expr_for_read(thd, def_field, def_field->default_value);
if (thd->mark_used_columns != MARK_COLUMNS_NONE)
- field->default_value->expr->walk(&Item::register_field_in_read_map, 1, 0);
- IF_DBUG(def_field->is_stat_field=1,); // a hack to fool ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED
+ def_field->default_value->expr->walk(&Item::register_field_in_read_map, 1, 0);
+ def_field->move_field(newptr+1, def_field->maybe_null() ? newptr : 0, 1);
}
+ else
+ def_field->move_field_offset((my_ptrdiff_t)
+ (def_field->table->s->default_values -
+ def_field->table->record[0]));
+ set_field(def_field);
return FALSE;
error:
@@ -8793,6 +8798,7 @@ void Item_default_value::calculate()
{
if (field->default_value)
field->set_default();
+ DEBUG_SYNC(field->table->in_use, "after_Item_default_value_calculate");
}
String *Item_default_value::val_str(String *str)