Merge 10.2 into 10.3

This commit is contained in:
Marko Mäkelä 2019-09-04 17:52:04 +03:00
commit 537f8594a6
33 changed files with 459 additions and 377 deletions

@ -1 +1 @@
Subproject commit 7de639518ffe56a99ac805654381d17f42796be2
Subproject commit 544b6f1d12f0e5b2a141129075ff2d64feb0e4c9

View File

@ -3357,18 +3357,26 @@ a b
drop table t1;
set sql_mode=default;
create table t1 (a int default b, b int default 4, t text);
insert into t1 (b, t) values (5, '1 column is omitted');
insert into t1 values (default, 5, '2 column gets DEFAULT, keyword');
insert into t1 values (default(a), 5, '3 column gets DEFAULT(a), expression');
insert into t1 values (default(a)+0, 5, '4 also expression DEFAULT(0)+0');
insert into t1 values (b, 5, '5 the value of the DEFAULT(a), that is b');
insert t1 (b, t) values (5, '1 column is omitted');
insert t1 values (default, 5, '2 column gets DEFAULT, keyword');
insert t1 values (default(a), 5, '3 column gets DEFAULT(a), expression');
insert t1 values (default(a)+0, 5, '4 also expression DEFAULT(0)+0');
insert t1 values (b, 5, '5 the value of the DEFAULT(a), that is b');
insert t1 (t,b,a) values ('6 reversed, column gets DEFAULT, keyword', 5, default);
insert t1 (t,b,a) values ('7 reversed, column gets DEFAULT(a), expression', 5, default(a));
insert t1 (t,b,a) values ('8 reversed, also expression DEFAULT(0)+0', 5, default(a)+0);
insert t1 (t,b,a) values ('9 reversed, the value of the DEFAULT(a), that is b', 5, b);
select * from t1 order by t;
a b t
5 5 1 column is omitted
5 5 2 column gets DEFAULT, keyword
4 5 2 column gets DEFAULT, keyword
4 5 3 column gets DEFAULT(a), expression
4 5 4 also expression DEFAULT(0)+0
4 5 5 the value of the DEFAULT(a), that is b
5 5 6 reversed, column gets DEFAULT, keyword
5 5 7 reversed, column gets DEFAULT(a), expression
5 5 8 reversed, also expression DEFAULT(0)+0
5 5 9 reversed, the value of the DEFAULT(a), that is b
drop table t1;
create table t1 (col1 int default(-(default(col1))));
ERROR 01000: Expression for field `col1` is referring to uninitialized field `col1`

View File

@ -2073,11 +2073,16 @@ set sql_mode=default;
# MDEV-10201 Bad results for CREATE TABLE t1 (a INT DEFAULT b, b INT DEFAULT 4)
#
create table t1 (a int default b, b int default 4, t text);
insert into t1 (b, t) values (5, '1 column is omitted');
insert into t1 values (default, 5, '2 column gets DEFAULT, keyword');
insert into t1 values (default(a), 5, '3 column gets DEFAULT(a), expression');
insert into t1 values (default(a)+0, 5, '4 also expression DEFAULT(0)+0');
insert into t1 values (b, 5, '5 the value of the DEFAULT(a), that is b');
insert t1 (b, t) values (5, '1 column is omitted');
insert t1 values (default, 5, '2 column gets DEFAULT, keyword');
insert t1 values (default(a), 5, '3 column gets DEFAULT(a), expression');
insert t1 values (default(a)+0, 5, '4 also expression DEFAULT(0)+0');
insert t1 values (b, 5, '5 the value of the DEFAULT(a), that is b');
# and the same in a different order
insert t1 (t,b,a) values ('6 reversed, column gets DEFAULT, keyword', 5, default);
insert t1 (t,b,a) values ('7 reversed, column gets DEFAULT(a), expression', 5, default(a));
insert t1 (t,b,a) values ('8 reversed, also expression DEFAULT(0)+0', 5, default(a)+0);
insert t1 (t,b,a) values ('9 reversed, the value of the DEFAULT(a), that is b', 5, b);
select * from t1 order by t;
drop table t1;

View File

@ -3093,3 +3093,55 @@ a b
1999-12-01 11:22:33.000000 1999-12-01 11:22:33.000000
2001-09-09 04:46:40.000000 2001-09-09 04:46:40.000000
DROP TABLE t1;
create table t1 (t timestamp, i int, v timestamp as (t) virtual, key(v));
insert t1 (t,i) values ('2006-03-01 23:59:59',1);
update t1 set i = 2;
check table t1;
Table Op Msg_type Msg_text
test.t1 check status OK
drop table t1;
create table t1 (t timestamp, i int);
create trigger tr1 before update on t1 for each row set @new:=new.t;
insert t1 (t,i) values ('2006-03-01 23:59:59', 1);
update t1 set i = 2;
select if(@new = t, 'correct', 'wrong') from t1;
if(@new = t, 'correct', 'wrong')
correct
drop table t1;
create table t1 (i int, j int as (i));
create trigger tr1 before update on t1 for each row set @new:=new.j;
insert t1 (i) values (1);
update t1, t1 as t2 set t1.i = 2;
select if(@new = j, 'correct', 'wrong') from t1;
if(@new = j, 'correct', 'wrong')
correct
drop table t1;
create table t1 (a int, b varchar(20) default 'foo');
insert t1 values (1,'bla'),(2, 'bar');
select * from t1;
a b
1 bla
2 bar
update t1 set b=default where a=1;
select * from t1;
a b
1 foo
2 bar
drop table t1;
create table t1 (
a int,
b timestamp default '2010-10-10 10:10:10' on update now(),
c varchar(100) default 'x');
insert t1 (a) values (1),(2);
select * from t1;
a b c
1 2010-10-10 10:10:10 x
2 2010-10-10 10:10:10 x
set timestamp=unix_timestamp('2011-11-11 11-11-11');
update t1 set b=default, c=default(b) where a=1;
select * from t1;
a b c
1 2010-10-10 10:10:10 2010-10-10 10:10:10
2 2010-10-10 10:10:10 x
drop table t1;
set timestamp=default;

View File

@ -19,3 +19,51 @@ let $now=NOW(6);
let $timestamp=TIMESTAMP(6);
let $datetime=DATETIME(6);
source 'include/function_defaults.inc';
#
# MDEV-20403 Assertion `0' or Assertion `btr_validate_index(index, 0)' failed in row_upd_sec_index_entry or error code 126: Index is corrupted upon UPDATE with TIMESTAMP..ON UPDATE
#
# ON UPDATE NOW and indexed virtual columns
create table t1 (t timestamp, i int, v timestamp as (t) virtual, key(v));
insert t1 (t,i) values ('2006-03-01 23:59:59',1);
update t1 set i = 2;
check table t1;
drop table t1;
# ON UPDATE NOW and triggers
create table t1 (t timestamp, i int);
create trigger tr1 before update on t1 for each row set @new:=new.t;
insert t1 (t,i) values ('2006-03-01 23:59:59', 1);
update t1 set i = 2;
select if(@new = t, 'correct', 'wrong') from t1;
drop table t1;
# triggers, virtual columns, multi-update
create table t1 (i int, j int as (i));
create trigger tr1 before update on t1 for each row set @new:=new.j;
insert t1 (i) values (1);
update t1, t1 as t2 set t1.i = 2;
select if(@new = j, 'correct', 'wrong') from t1;
drop table t1;
# SET xxx=DEFAULT
create table t1 (a int, b varchar(20) default 'foo');
insert t1 values (1,'bla'),(2, 'bar');
select * from t1;
update t1 set b=default where a=1;
select * from t1;
drop table t1;
# ON UPDATE NOW and SET xxx=DEFAULT
create table t1 (
a int,
b timestamp default '2010-10-10 10:10:10' on update now(),
c varchar(100) default 'x');
insert t1 (a) values (1),(2);
select * from t1;
set timestamp=unix_timestamp('2011-11-11 11-11-11');
update t1 set b=default, c=default(b) where a=1;
select * from t1;
drop table t1;
set timestamp=default;

View File

@ -538,7 +538,7 @@ a b
insert into t2 values(1);
select a,b from t2;
a b
12 1
NULL 1
drop table t1,t2;
create table t1 (a int invisible, b int, c int);
create table t2 (a int, b int, d int);

View File

@ -33,6 +33,7 @@ galera_var_node_address : MDEV-17151 Galera test failure
galera_var_notify_cmd : MDEV-13549 Galera test failures
galera_var_slave_threads : MDEV-19746 Galera test failures because of wsrep_slave_threads identification
galera_sst_mariabackup_encrypt_with_key : MDEV-19926 Galera SST tests fail
galera_wan : MDEV-17259: Test failure on galera.galera_wan
galera_var_node_address : MDEV-20485 Galera test failure on galera.galera_var_node_address
galera_wan : MDEV-17259 Test failure on galera.galera_wan
partition : MDEV-19958 Galera test failure on galera.partition
query_cache: MDEV-15805 Test failure on galera.query_cache

View File

@ -1,4 +1,5 @@
FLUSH TABLES;
call mtr.add_suppression("Found 1 prepared XA transactions");
#
# MDEV-13797 InnoDB may hang if shutdown is initiated soon after startup
# while rolling back recovered incomplete transactions
@ -8,10 +9,12 @@ BEGIN;
COMMIT;
connect con$c,localhost,root,,;
CREATE TABLE t8 (a SERIAL, b INT UNIQUE, c INT UNIQUE) ENGINE=InnoDB;
BEGIN;
XA START 'x';
INSERT INTO t8 (a) SELECT NULL FROM t;
UPDATE t8 SET a=a+100, b=a;
DELETE FROM t8;
XA END 'x';
XA PREPARE 'x';
connect con$c,localhost,root,,;
CREATE TABLE t7 (a SERIAL, b INT UNIQUE, c INT UNIQUE) ENGINE=InnoDB;
BEGIN;
@ -63,4 +66,7 @@ connection default;
SET GLOBAL innodb_flush_log_at_trx_commit=1;
CREATE TABLE u(a SERIAL) ENGINE=INNODB;
FLUSH TABLES;
XA RECOVER;
formatID gtrid_length bqual_length data
1 1 0 x
DROP TABLE t,u;

View File

@ -3,6 +3,7 @@
# Flush any open myisam tables from previous tests
FLUSH TABLES;
call mtr.add_suppression("Found 1 prepared XA transactions");
--echo #
--echo # MDEV-13797 InnoDB may hang if shutdown is initiated soon after startup
@ -23,6 +24,15 @@ dec $c;
COMMIT;
let $c = $trx;
connect (con$c,localhost,root,,);
eval CREATE TABLE t$c (a SERIAL, b INT UNIQUE, c INT UNIQUE) ENGINE=InnoDB;
XA START 'x';
eval INSERT INTO t$c (a) SELECT NULL FROM t;
eval UPDATE t$c SET a=a+$size, b=a;
eval DELETE FROM t$c;
XA END 'x';
XA PREPARE 'x';
dec $c;
while ($c)
{
connect (con$c,localhost,root,,);
@ -53,12 +63,17 @@ FLUSH TABLES;
# Perform a slow shutdown in order to roll back all recovered transactions
# and to avoid locking conflicts with the DROP TABLE below.
XA RECOVER;
--disable_query_log
SET GLOBAL innodb_fast_shutdown=0;
--source include/restart_mysqld.inc
--disable_query_log
let $c = $trx;
disconnect con$c;
XA ROLLBACK 'x';
eval DROP TABLE t$c;
dec $c;
while ($c)
{
disconnect con$c;

View File

@ -17,5 +17,4 @@ rpl_row_binlog_max_cache_size : MDEV-11092
rpl_row_index_choice : MDEV-11666
rpl_parallel2 : fails after MDEV-16172
rpl_semi_sync_after_sync : fails after MDEV-16172
mdev_17588: MDEV-20137
rpl_slave_grp_exec: MDEV-10514

View File

@ -1,18 +1,16 @@
include/master-slave.inc
[connection master]
connection slave;
include/stop_slave.inc
CHANGE MASTER TO master_use_gtid=slave_pos;
include/start_slave.inc
connection master;
create table t1 (a int) engine=innodb;
create table t2 (a int);
create table t3 (a int) engine=innodb;
include/save_master_gtid.inc
connection slave;
include/wait_for_slave_sql_error.inc [errno=1286]
Last_Error = 'Error 'Unknown storage engine 'innodb'' on query. Default database: 'test'. Query: 'create table t1 (a int) engine=innodb''
STOP SLAVE IO_THREAD;
include/wait_for_slave_to_stop.inc
SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1;
include/start_slave.inc
include/sync_with_master_gtid.inc
include/wait_for_slave_sql_error_and_skip.inc [errno=1286]
# Asserted this: Status should be 'Slave has read all relay log...'
show tables;
Tables_in_test
t2

View File

@ -1,23 +1,28 @@
--source include/master-slave.inc
--source include/have_innodb.inc
--connection slave
--source include/stop_slave.inc
CHANGE MASTER TO master_use_gtid=slave_pos;
--source include/start_slave.inc
--connection master
create table t1 (a int) engine=innodb;
create table t2 (a int);
create table t3 (a int) engine=innodb;
--source include/save_master_gtid.inc
--save_master_pos
--connection slave
# Using ER_UNKNOWN_STORAGE_ENGINE wont work
let $slave_sql_errno= 1286;
--source include/wait_for_slave_sql_error.inc
--let $status_items= Last_Error
--source include/show_slave_status.inc
STOP SLAVE IO_THREAD;
source include/wait_for_slave_to_stop.inc;
SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1;
--source include/start_slave.inc
--source include/sync_with_master_gtid.inc
--source include/wait_for_slave_sql_error_and_skip.inc
--sync_with_master
--let $assert_text= Status should be 'Slave has read all relay log...'
--let $assert_cond= "[SHOW SLAVE STATUS, Slave_SQL_Running_State, 1]"
#Like "Slave has read all relay log%"
--source include/rpl_assert.inc
show tables;
show create table t2;
--error ER_NO_SUCH_TABLE

View File

@ -11422,33 +11422,6 @@ key_map Field::get_possible_keys()
}
/**
Mark the field as having an explicit default value.
@param value if available, the value that the field is being set to
@note
Fields that have an explicit default value should not be updated
automatically via the DEFAULT or ON UPDATE functions. The functions
that deal with data change functionality (INSERT/UPDATE/LOAD),
determine if there is an explicit value for each field before performing
the data change, and call this method to mark the field.
If the 'value' parameter is NULL, then the field is marked unconditionally
as having an explicit value. If 'value' is not NULL, then it can be further
analyzed to check if it really should count as a value.
*/
bool Field::set_explicit_default(Item *value)
{
if (value->type() == Item::DEFAULT_VALUE_ITEM &&
!((Item_default_value*)value)->arg)
return false;
set_has_explicit_value();
return true;
}
bool Field::validate_value_in_record_with_warn(THD *thd, const uchar *record)
{
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);

View File

@ -1001,7 +1001,6 @@ public:
{
bitmap_clear_bit(&table->has_value_set, field_index);
}
bool set_explicit_default(Item *value);
virtual my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const
{ DBUG_ASSERT(0); return 0; }
@ -1010,14 +1009,6 @@ public:
return get_timestamp(ptr, sec_part);
}
/**
Evaluates the @c UPDATE default function, if one exists, and stores the
result in the record buffer. If no such function exists for the column,
or the function is not valid for the column's data type, invoking this
function has no effect.
*/
virtual int evaluate_update_default_function() { return 0; }
virtual bool binary() const { return 1; }
virtual bool zero_pack() const { return 1; }
virtual enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; }
@ -2686,13 +2677,6 @@ public:
void sql_type(String &str) const;
bool zero_pack() const { return 0; }
int set_time();
int evaluate_update_default_function()
{
int res= 0;
if (has_update_default_function())
res= set_time();
return res;
}
/* Get TIMESTAMP field value as seconds since begging of Unix Epoch */
my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const;
my_time_t get_timestamp(ulong *sec_part) const
@ -3148,13 +3132,6 @@ public:
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
{ return Field_datetime::get_TIME(ltime, ptr, fuzzydate); }
int set_time();
int evaluate_update_default_function()
{
int res= 0;
if (has_update_default_function())
res= set_time();
return res;
}
uchar *pack(uchar* to, const uchar *from,
uint max_length __attribute__((unused)))
{

View File

@ -802,9 +802,9 @@ struct xid_t {
char data[XIDDATASIZE]; // not \0-terminated !
xid_t() {} /* Remove gcc warning */
bool eq(struct xid_t *xid)
bool eq(struct xid_t *xid) const
{ return !xid->is_null() && eq(xid->gtrid_length, xid->bqual_length, xid->data); }
bool eq(long g, long b, const char *d)
bool eq(long g, long b, const char *d) const
{ return !is_null() && g == gtrid_length && b == bqual_length && !memcmp(d, data, g+b); }
void set(struct xid_t *xid)
{ memcpy(this, xid, xid->length()); }

View File

@ -9403,8 +9403,6 @@ int Item_default_value::save_in_field(Field *field_arg, bool no_conversions)
return Item_field::save_in_field(field_arg, no_conversions);
}
if (field_arg->default_value && field_arg->default_value->flags)
return 0; // defaut fields will be set later, no need to do it twice
return field_arg->save_in_field_default_value(context->error_processor ==
&view_error_processor);
}
@ -9651,7 +9649,7 @@ bool Item_trigger_field::set_value(THD *thd, sp_rcontext * /*ctx*/, Item **it)
int err_code= item->save_in_field(field, 0);
field->table->copy_blobs= copy_blobs_saved;
field->set_explicit_default(item);
field->set_has_explicit_value();
return err_code < 0;
}

View File

@ -8343,7 +8343,7 @@ fill_record(THD *thd, TABLE *table_arg, List<Item> &fields, List<Item> &values,
rfield->move_field_offset((my_ptrdiff_t) (table->record[1] -
table->record[0]));
}
rfield->set_explicit_default(value);
rfield->set_has_explicit_value();
}
if (update && thd->variables.sql_mode & MODE_SIMULTANEOUS_ASSIGNMENT)
@ -8362,9 +8362,13 @@ fill_record(THD *thd, TABLE *table_arg, List<Item> &fields, List<Item> &values,
}
}
if (!update && table_arg->default_field &&
table_arg->update_default_fields(0, ignore_errors))
goto err;
if (update)
table_arg->evaluate_update_default_function();
else
if (table_arg->default_field &&
table_arg->update_default_fields(ignore_errors))
goto err;
/* Update virtual fields */
if (table_arg->vfield &&
table_arg->update_virtual_fields(table_arg->file, VCOL_UPDATE_FOR_WRITE))
@ -8554,7 +8558,6 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List<Item> &values,
{
List_iterator_fast<Item> v(values);
List<TABLE> tbl_list;
bool all_fields_have_values= true;
Item *value;
Field *field;
bool abort_on_warning_saved= thd->abort_on_warning;
@ -8585,10 +8588,7 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List<Item> &values,
DBUG_ASSERT(field->table == table);
if (unlikely(field->invisible))
{
all_fields_have_values= false;
continue;
}
else
value=v++;
@ -8617,11 +8617,8 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List<Item> &values,
else
if (value->save_in_field(field, 0) < 0)
goto err;
all_fields_have_values &= field->set_explicit_default(value);
field->set_has_explicit_value();
}
if (!all_fields_have_values && table->default_field &&
table->update_default_fields(0, ignore_errors))
goto err;
/* Update virtual fields */
thd->abort_on_warning= FALSE;
if (table->vfield &&

View File

@ -1835,10 +1835,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
be updated as if this is an UPDATE.
*/
if (different_records && table->default_field)
{
if (table->update_default_fields(1, info->ignore))
goto err;
}
table->evaluate_update_default_function();
/* CHECK OPTION for VIEW ... ON DUPLICATE KEY UPDATE ... */
res= info->table_list->view_check_option(table->in_use, info->ignore);
@ -3856,7 +3853,7 @@ int select_insert::send_data(List<Item> &values)
thd->count_cuted_fields= CHECK_FIELD_WARN; // Calculate cuted fields
store_values(values);
if (table->default_field &&
unlikely(table->update_default_fields(0, info.ignore)))
unlikely(table->update_default_fields(info.ignore)))
DBUG_RETURN(1);
thd->count_cuted_fields= CHECK_FIELD_ERROR_FOR_NULL;
if (unlikely(thd->is_error()))

View File

@ -9819,7 +9819,7 @@ do_continue:;
thd->count_cuted_fields= CHECK_FIELD_EXPRESSION;
altered_table->reset_default_fields();
if (altered_table->default_field &&
altered_table->update_default_fields(0, 1))
altered_table->update_default_fields(true))
goto err_new_table_cleanup;
thd->count_cuted_fields= CHECK_FIELD_IGNORE;
@ -10515,7 +10515,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
prev_insert_id= to->file->next_insert_id;
if (to->default_field)
to->update_default_fields(0, ignore);
to->update_default_fields(ignore);
if (to->vfield)
to->update_virtual_fields(to->file, VCOL_UPDATE_FOR_WRITE);

View File

@ -69,17 +69,20 @@ bool compare_record(const TABLE *table)
{
DBUG_ASSERT(records_are_comparable(table));
if ((table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) != 0)
if (table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ ||
table->s->has_update_default_function)
{
/*
Storage engine may not have read all columns of the record. Fields
(including NULL bits) not in the write_set may not have been read and
can therefore not be compared.
Or ON UPDATE DEFAULT NOW() could've changed field values, including
NULL bits.
*/
for (Field **ptr= table->field ; *ptr != NULL; ptr++)
{
Field *field= *ptr;
if (bitmap_is_set(table->write_set, field->field_index))
if (field->has_explicit_value() && !field->vcol_info)
{
if (field->real_maybe_null())
{
@ -111,8 +114,9 @@ bool compare_record(const TABLE *table)
/* Compare updated fields */
for (Field **ptr= table->field ; *ptr ; ptr++)
{
if (bitmap_is_set(table->write_set, (*ptr)->field_index) &&
(*ptr)->cmp_binary_offset(table->s->rec_buff_length))
Field *field= *ptr;
if (field->has_explicit_value() && !field->vcol_info &&
field->cmp_binary_offset(table->s->rec_buff_length))
return TRUE;
}
return FALSE;
@ -897,11 +901,6 @@ update_begin:
if (!can_compare_record || compare_record(table))
{
if (table->default_field && table->update_default_fields(1, ignore))
{
error= 1;
break;
}
if ((res= table_list->view_check_option(thd, ignore)) !=
VIEW_CHECK_OK)
{
@ -2379,10 +2378,6 @@ int multi_update::send_data(List<Item> &not_used_values)
{
int error;
if (table->default_field &&
unlikely(table->update_default_fields(1, ignore)))
DBUG_RETURN(1);
if ((error= cur_table->view_check_option(thd, ignore)) !=
VIEW_CHECK_OK)
{
@ -2699,6 +2694,10 @@ int multi_update::do_updates()
copy_field_ptr->to_field->set_has_explicit_value();
}
table->evaluate_update_default_function();
if (table->vfield &&
table->update_virtual_fields(table->file, VCOL_UPDATE_FOR_WRITE))
goto err2;
if (table->triggers &&
table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
TRG_ACTION_BEFORE, TRUE))
@ -2707,12 +2706,6 @@ int multi_update::do_updates()
if (!can_compare_record || compare_record(table))
{
int error;
if (table->default_field &&
(error= table->update_default_fields(1, ignore)))
goto err2;
if (table->vfield &&
table->update_virtual_fields(table->file, VCOL_UPDATE_FOR_WRITE))
goto err2;
if ((error= cur_table->view_check_option(thd, ignore)) !=
VIEW_CHECK_OK)
{

View File

@ -7930,7 +7930,7 @@ int TABLE::update_virtual_field(Field *vf)
ignore_errors == 0. If set then an error was generated.
*/
int TABLE::update_default_fields(bool update_command, bool ignore_errors)
int TABLE::update_default_fields(bool ignore_errors)
{
Query_arena backup_arena;
Field **field_ptr;
@ -7950,14 +7950,9 @@ int TABLE::update_default_fields(bool update_command, bool ignore_errors)
*/
if (!field->has_explicit_value())
{
if (!update_command)
{
if (field->default_value &&
(field->default_value->flags || field->flags & BLOB_FLAG))
res|= (field->default_value->expr->save_in_field(field, 0) < 0);
}
else
res|= field->evaluate_update_default_function();
if (field->default_value &&
(field->default_value->flags || field->flags & BLOB_FLAG))
res|= (field->default_value->expr->save_in_field(field, 0) < 0);
if (!ignore_errors && res)
{
my_error(ER_CALCULATING_DEFAULT_VALUE, MYF(0), field->field_name.str);
@ -7971,6 +7966,21 @@ int TABLE::update_default_fields(bool update_command, bool ignore_errors)
}
void TABLE::evaluate_update_default_function()
{
DBUG_ENTER("TABLE::evaluate_update_default_function");
if (s->has_update_default_function)
for (Field **field_ptr= default_field; *field_ptr ; field_ptr++)
{
Field *field= (*field_ptr);
if (!field->has_explicit_value() && field->has_update_default_function())
field->set_time();
}
DBUG_VOID_RETURN;
}
void TABLE::vers_update_fields()
{
bitmap_set_bit(write_set, vers_start_field()->field_index);

View File

@ -1489,7 +1489,8 @@ public:
ulong actual_key_flags(KEY *keyinfo);
int update_virtual_field(Field *vf);
int update_virtual_fields(handler *h, enum_vcol_update_mode update_mode);
int update_default_fields(bool update, bool ignore_errors);
int update_default_fields(bool ignore_errors);
void evaluate_update_default_function();
void reset_default_fields();
inline ha_rows stat_records() { return used_stat_records; }

View File

@ -482,14 +482,10 @@ lock_rec_unlock(
const buf_block_t* block, /*!< in: buffer block containing rec */
const rec_t* rec, /*!< in: record */
lock_mode lock_mode);/*!< in: LOCK_S or LOCK_X */
/*********************************************************************//**
Releases a transaction's locks, and releases possible other transactions
waiting because of these locks. Change the state of the transaction to
TRX_STATE_COMMITTED_IN_MEMORY. */
void
lock_trx_release_locks(
/*===================*/
trx_t* trx); /*!< in/out: transaction */
/** Release the explicit locks of a committing transaction,
and release possible other transactions waiting because of these locks. */
void lock_trx_release_locks(trx_t* trx);
/*********************************************************************//**
Calculates the fold value of a page file address: used in inserting or

View File

@ -44,7 +44,8 @@ index record.
@param[in] rec secondary index record
@param[in] index secondary index
@param[in] offsets rec_get_offsets(rec, index)
@return the active transaction; trx->release_reference() must be invoked
@return the active transaction; state must be rechecked after
trx_mutex_enter(), and trx->release_reference() must be invoked
@retval NULL if the record was committed */
trx_t*
row_vers_impl_x_locked(

View File

@ -514,8 +514,10 @@ class rw_trx_hash_t
{
ut_ad(!trx->read_only || !trx->rsegs.m_redo.rseg);
ut_ad(!trx_is_autocommit_non_locking(trx));
/* trx->state can be anything except TRX_STATE_NOT_STARTED */
mutex_enter(&trx->mutex);
ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE) ||
trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY) ||
trx_state_eq(trx, TRX_STATE_PREPARED_RECOVERED) ||
trx_state_eq(trx, TRX_STATE_PREPARED));
mutex_exit(&trx->mutex);
@ -940,7 +942,7 @@ public:
/**
Takes MVCC snapshot.
To reduce malloc probablility we reserver rw_trx_hash.size() + 32 elements
To reduce malloc probablility we reserve rw_trx_hash.size() + 32 elements
in ids.
For details about get_rw_trx_hash_version() != get_max_trx_id() spin

View File

@ -211,16 +211,13 @@ trx_recover_for_mysql(
/*==================*/
XID* xid_list, /*!< in/out: prepared transactions */
uint len); /*!< in: number of slots in xid_list */
/*******************************************************************//**
This function is used to find one X/Open XA distributed transaction
which is in the prepared state
@return trx or NULL; on match, the trx->xid will be invalidated;
note that the trx may have been committed, unless the caller is
holding lock_sys.mutex */
trx_t *
trx_get_trx_by_xid(
/*===============*/
XID* xid); /*!< in: X/Open XA transaction identifier */
/** Look up an X/Open distributed transaction in XA PREPARE state.
@param[in] xid X/Open XA transaction identifier
@return transaction on match (the trx_t::xid will be invalidated);
note that the trx may have been committed before the caller acquires
trx_t::mutex
@retval NULL if no match */
trx_t* trx_get_trx_by_xid(const XID* xid);
/**********************************************************************//**
If required, flushes the log to disk if we called trx_commit_for_mysql()
with trx->flush_log_later == TRUE. */
@ -467,6 +464,9 @@ Check transaction state */
ut_ad(!(t)->read_view.is_open()); \
ut_ad((t)->lock.wait_thr == NULL); \
ut_ad(UT_LIST_GET_LEN((t)->lock.trx_locks) == 0); \
ut_ad((t)->lock.table_locks.empty()); \
ut_ad(!(t)->autoinc_locks \
|| ib_vector_is_empty((t)->autoinc_locks)); \
ut_ad((t)->dict_operation == TRX_DICT_OP_NONE); \
} while(0)
@ -701,8 +701,8 @@ so without holding any mutex. The following are exceptions to this:
* trx_rollback_resurrected() may access resurrected (connectionless)
transactions while the system is already processing new user
transactions. The trx_sys.mutex prevents a race condition between it
and lock_trx_release_locks() [invoked by trx_commit()].
transactions. The trx_sys.mutex and trx->is_recovered prevent
a race condition between it and trx_commit().
* trx_print_low() may access transactions not associated with the current
thread. The caller must be holding lock_sys.mutex.
@ -713,7 +713,7 @@ must not be modified without holding trx->mutex.
* The locking code (in particular, lock_deadlock_recursive() and
lock_rec_convert_impl_to_expl()) will access transactions associated
to other connections. The locks of transactions are protected by
lock_sys.mutex and sometimes by trx->mutex. */
lock_sys.mutex (insertions also by trx->mutex). */
/** Represents an instance of rollback segment along with its state variables.*/
struct trx_undo_ptr_t {
@ -837,26 +837,19 @@ public:
ACTIVE->COMMITTED is possible when the transaction is in
rw_trx_hash.
Transitions to COMMITTED are protected by both lock_sys.mutex
and trx->mutex.
NOTE: Some of these state change constraints are an overkill,
currently only required for a consistent view for printing stats.
This unnecessarily adds a huge cost for the general case. */
Transitions to COMMITTED are protected by trx_t::mutex. */
trx_state_t state;
/** whether this is a recovered transaction that should be
rolled back by trx_rollback_or_clean_recovered().
Protected by trx_t::mutex for transactions that are in trx_sys. */
bool is_recovered;
ReadView read_view; /*!< consistent read view used in the
transaction, or NULL if not yet set */
trx_lock_t lock; /*!< Information about the transaction
locks and state. Protected by
trx->mutex or lock_sys.mutex
or both */
bool is_recovered; /*!< 0=normal transaction,
1=recovered, must be rolled back,
protected by trx_sys.mutex when
trx is in rw_trx_hash */
lock_sys.mutex (insertions also
by trx_t::mutex). */
/* These fields are not protected by any mutex. */
const char* op_info; /*!< English text describing the
@ -1116,6 +1109,12 @@ public:
return flush_observer;
}
/** Transition to committed state, to release implicit locks. */
inline void commit_state();
/** Release any explicit locks of a committing transaction. */
inline void release_locks();
bool is_referenced()
{

View File

@ -4740,7 +4740,7 @@ lock_trx_table_locks_find(
{
bool found = false;
trx_mutex_enter(trx);
ut_ad(trx_mutex_own(trx));
for (lock_list::const_iterator it = trx->lock.table_locks.begin(),
end = trx->lock.table_locks.end(); it != end; ++it) {
@ -4763,8 +4763,6 @@ lock_trx_table_locks_find(
ut_a(lock->un_member.tab_lock.table != NULL);
}
trx_mutex_exit(trx);
return(found);
}
@ -4785,25 +4783,23 @@ lock_table_queue_validate(
lock != NULL;
lock = UT_LIST_GET_NEXT(un_member.tab_lock.locks, lock)) {
/* Transaction state may change from ACTIVE to PREPARED.
State change to COMMITTED is not possible while we are
holding lock_sys.mutex: it is done by lock_trx_release_locks()
under lock_sys.mutex protection.
Transaction in NOT_STARTED state cannot hold locks, and
lock->trx->state can only move to NOT_STARTED from COMMITTED. */
/* lock->trx->state cannot change from or to NOT_STARTED
while we are holding the trx_sys.mutex. It may change
from ACTIVE or PREPARED to PREPARED or COMMITTED. */
trx_mutex_enter(lock->trx);
check_trx_state(lock->trx);
if (!lock_get_wait(lock)) {
if (lock->trx->state == TRX_STATE_COMMITTED_IN_MEMORY) {
} else if (!lock_get_wait(lock)) {
ut_a(!lock_table_other_has_incompatible(
lock->trx, 0, table,
lock_get_mode(lock)));
} else {
ut_a(lock_table_has_to_wait_in_queue(lock));
}
ut_a(lock_trx_table_locks_find(lock->trx, lock));
trx_mutex_exit(lock->trx);
}
return(TRUE);
@ -4850,42 +4846,41 @@ lock_rec_queue_validate(
lock != NULL;
lock = lock_rec_get_next_const(heap_no, lock)) {
ut_ad(!index || lock->index == index);
trx_mutex_enter(lock->trx);
ut_ad(!trx_is_ac_nl_ro(lock->trx));
if (lock_get_wait(lock)) {
ut_a(lock_rec_has_to_wait_in_queue(lock));
}
if (index != NULL) {
ut_a(lock->index == index);
}
ut_ad(trx_state_eq(lock->trx,
TRX_STATE_COMMITTED_IN_MEMORY)
|| !lock_get_wait(lock)
|| lock_rec_has_to_wait_in_queue(lock));
trx_mutex_exit(lock->trx);
}
goto func_exit;
func_exit:
if (!locked_lock_trx_sys) {
lock_mutex_exit();
}
return true;
}
ut_ad(page_rec_is_leaf(rec));
ut_ad(lock_mutex_own());
if (index == NULL) {
const trx_id_t impl_trx_id = index && index->is_primary()
? lock_clust_rec_some_has_impl(rec, index, offsets)
: 0;
/* Nothing we can do */
if (trx_t *impl_trx = impl_trx_id
? trx_sys.find(current_trx(), impl_trx_id, false)
: 0) {
/* impl_trx could have been committed before we
acquire its mutex, but not thereafter. */
} else if (dict_index_is_clust(index)) {
/* Unlike the non-debug code, this invariant can only succeed
if the check and assertion are covered by the lock mutex. */
const trx_id_t impl_trx_id = lock_clust_rec_some_has_impl(
rec, index, offsets);
const trx_t *impl_trx = impl_trx_id
? trx_sys.find(current_trx(), impl_trx_id, false)
: 0;
ut_ad(lock_mutex_own());
/* impl_trx cannot be committed until lock_mutex_exit()
because lock_trx_release_locks() acquires lock_sys.mutex */
if (!impl_trx) {
mutex_enter(&impl_trx->mutex);
ut_ad(impl_trx->state != TRX_STATE_NOT_STARTED);
if (impl_trx->state == TRX_STATE_COMMITTED_IN_MEMORY) {
} else if (const lock_t* other_lock
= lock_rec_other_has_expl_req(
LOCK_S, block, true, heap_no,
@ -4927,6 +4922,8 @@ lock_rec_queue_validate(
ut_ad(lock_rec_has_expl(LOCK_X | LOCK_REC_NOT_GAP,
block, heap_no, impl_trx));
}
mutex_exit(&impl_trx->mutex);
}
for (lock = lock_rec_get_first(lock_sys.rec_hash, block, heap_no);
@ -4971,12 +4968,7 @@ lock_rec_queue_validate(
ut_ad(innodb_lock_schedule_algorithm == INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS ||
lock_queue_validate(lock));
func_exit:
if (!locked_lock_trx_sys) {
lock_mutex_exit();
}
return(TRUE);
goto func_exit;
}
/*********************************************************************//**
@ -5398,30 +5390,24 @@ lock_rec_convert_impl_to_expl_for_trx(
trx_t* trx, /*!< in/out: active transaction */
ulint heap_no)/*!< in: rec heap number to lock */
{
ut_ad(trx->is_referenced());
ut_ad(page_rec_is_leaf(rec));
ut_ad(!rec_is_metadata(rec, index));
DEBUG_SYNC_C("before_lock_rec_convert_impl_to_expl_for_trx");
lock_mutex_enter();
trx_mutex_enter(trx);
ut_ad(trx->is_referenced());
ut_ad(!trx_state_eq(trx, TRX_STATE_NOT_STARTED));
if (!trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY)
&& !lock_rec_has_expl(LOCK_X | LOCK_REC_NOT_GAP,
block, heap_no, trx)) {
ulint type_mode;
type_mode = (LOCK_REC | LOCK_X | LOCK_REC_NOT_GAP);
lock_rec_add_to_queue(
type_mode, block, heap_no, index, trx, FALSE);
lock_rec_add_to_queue(LOCK_REC | LOCK_X | LOCK_REC_NOT_GAP,
block, heap_no, index, trx, true);
}
lock_mutex_exit();
trx_mutex_exit(trx);
trx->release_reference();
DEBUG_SYNC_C("after_lock_rec_convert_impl_to_expl_for_trx");
@ -5444,13 +5430,17 @@ static my_bool lock_rec_other_trx_holds_expl_callback(
mutex_enter(&element->mutex);
if (element->trx)
{
lock_t *expl_lock= lock_rec_has_expl(LOCK_S | LOCK_REC_NOT_GAP, arg->block,
arg->heap_no, element->trx);
trx_mutex_enter(element->trx);
ut_ad(element->trx->state != TRX_STATE_NOT_STARTED);
lock_t *expl_lock= element->trx->state == TRX_STATE_COMMITTED_IN_MEMORY
? NULL : lock_rec_has_expl(LOCK_S | LOCK_REC_NOT_GAP, arg->block,
arg->heap_no, element->trx);
/*
An explicit lock is held by trx other than the trx holding the implicit
lock.
*/
ut_ad(!expl_lock || expl_lock->trx == arg->impl_trx);
trx_mutex_exit(element->trx);
}
mutex_exit(&element->mutex);
return 0;
@ -5480,9 +5470,6 @@ static void lock_rec_other_trx_holds_expl(trx_t *caller_trx, trx_t *trx,
ut_ad(!page_rec_is_metadata(rec));
lock_mutex_enter();
ut_ad(trx->is_referenced());
/* Prevent a data race with trx_prepare(), which could change the
state from ACTIVE to PREPARED. Other state changes should be
blocked by lock_mutex_own() and trx->is_referenced(). */
trx_mutex_enter(trx);
const trx_state_t state = trx->state;
trx_mutex_exit(trx);
@ -6248,89 +6235,24 @@ lock_unlock_table_autoinc(
}
}
/*********************************************************************//**
Releases a transaction's locks, and releases possible other transactions
waiting because of these locks. Change the state of the transaction to
TRX_STATE_COMMITTED_IN_MEMORY. */
void
lock_trx_release_locks(
/*===================*/
trx_t* trx) /*!< in/out: transaction */
/** Release the explicit locks of a committing transaction,
and release possible other transactions waiting because of these locks. */
void lock_trx_release_locks(trx_t* trx)
{
check_trx_state(trx);
ut_ad(trx_state_eq(trx, TRX_STATE_PREPARED)
|| trx_state_eq(trx, TRX_STATE_PREPARED_RECOVERED)
|| trx_state_eq(trx, TRX_STATE_ACTIVE));
bool release_lock = UT_LIST_GET_LEN(trx->lock.trx_locks) > 0;
/* Don't take lock_sys.mutex if trx didn't acquire any lock. */
if (release_lock) {
/* The transition of trx->state to TRX_STATE_COMMITTED_IN_MEMORY
is protected by both the lock_sys.mutex and the trx->mutex. */
lock_mutex_enter();
}
/* The following assignment makes the transaction committed in memory
and makes its changes to data visible to other transactions.
NOTE that there is a small discrepancy from the strict formal
visibility rules here: a human user of the database can see
modifications made by another transaction T even before the necessary
log segment has been flushed to the disk. If the database happens to
crash before the flush, the user has seen modifications from T which
will never be a committed transaction. However, any transaction T2
which sees the modifications of the committing transaction T, and
which also itself makes modifications to the database, will get an lsn
larger than the committing transaction T. In the case where the log
flush fails, and T never gets committed, also T2 will never get
committed. */
/*--------------------------------------*/
trx_mutex_enter(trx);
trx->state = TRX_STATE_COMMITTED_IN_MEMORY;
trx_mutex_exit(trx);
/*--------------------------------------*/
if (trx->is_referenced()) {
ut_a(release_lock);
lock_mutex_exit();
while (trx->is_referenced()) {
DEBUG_SYNC_C("waiting_trx_is_not_referenced");
/** Doing an implicit to explicit conversion
should not be expensive. */
ut_delay(srv_spin_wait_delay);
}
lock_mutex_enter();
}
ut_ad(!trx->is_referenced());
if (release_lock) {
lock_release(trx);
lock_mutex_exit();
}
ut_ad(UT_LIST_GET_LEN(trx->lock.trx_locks));
lock_mutex_enter();
lock_release(trx);
trx->lock.n_rec_locks = 0;
/* We don't remove the locks one by one from the vector for
efficiency reasons. We simply reset it because we would have
released all the locks anyway. */
trx->lock.table_locks.clear();
ut_a(UT_LIST_GET_LEN(trx->lock.trx_locks) == 0);
ut_a(ib_vector_is_empty(trx->autoinc_locks));
ut_a(trx->lock.table_locks.empty());
ut_ad(UT_LIST_GET_LEN(trx->lock.trx_locks) == 0);
ut_ad(ib_vector_is_empty(trx->autoinc_locks));
lock_mutex_exit();
mem_heap_empty(trx->lock.lock_heap);
}
@ -6403,21 +6325,26 @@ static my_bool lock_table_locks_lookup(rw_trx_hash_element_t *element,
mutex_enter(&element->mutex);
if (element->trx)
{
trx_mutex_enter(element->trx);
check_trx_state(element->trx);
for (const lock_t *lock= UT_LIST_GET_FIRST(element->trx->lock.trx_locks);
lock != NULL;
lock= UT_LIST_GET_NEXT(trx_locks, lock))
if (element->trx->state != TRX_STATE_COMMITTED_IN_MEMORY)
{
ut_ad(lock->trx == element->trx);
if (lock_get_type_low(lock) == LOCK_REC)
for (const lock_t *lock= UT_LIST_GET_FIRST(element->trx->lock.trx_locks);
lock != NULL;
lock= UT_LIST_GET_NEXT(trx_locks, lock))
{
ut_ad(!dict_index_is_online_ddl(lock->index) ||
dict_index_is_clust(lock->index));
ut_ad(lock->index->table != table);
ut_ad(lock->trx == element->trx);
if (lock_get_type_low(lock) == LOCK_REC)
{
ut_ad(!dict_index_is_online_ddl(lock->index) ||
lock->index->is_primary());
ut_ad(lock->index->table != table);
}
else
ut_ad(lock->un_member.tab_lock.table != table);
}
else
ut_ad(lock->un_member.tab_lock.table != table);
}
trx_mutex_exit(element->trx);
}
mutex_exit(&element->mutex);
return 0;
@ -6585,7 +6512,7 @@ DeadlockChecker::start_print()
if (srv_print_all_deadlocks) {
ib::info() << "Transactions deadlock detected, dumping"
<< " detailed information.";
" detailed information.";
}
}

View File

@ -76,7 +76,8 @@ index record.
@param[in] index secondary index
@param[in] offsets rec_get_offsets(rec, index)
@param[in,out] mtr mini-transaction
@return the active transaction; trx->release_reference() must be invoked
@return the active transaction; state must be rechecked after
trx_mutex_enter(), and trx->release_reference() must be invoked
@retval NULL if the record was committed */
UNIV_INLINE
trx_t*
@ -90,9 +91,6 @@ row_vers_impl_x_locked_low(
mtr_t* mtr)
{
trx_id_t trx_id;
ulint comp;
ulint rec_del;
const rec_t* version;
rec_t* prev_version = NULL;
ulint* clust_offsets;
mem_heap_t* heap;
@ -144,12 +142,12 @@ row_vers_impl_x_locked_low(
}
}
comp = page_rec_is_comp(rec);
const ulint comp = page_rec_is_comp(rec);
ut_ad(index->table == clust_index->table);
ut_ad(!!comp == dict_table_is_comp(index->table));
ut_ad(!comp == !page_rec_is_comp(clust_rec));
rec_del = rec_get_deleted_flag(rec, comp);
const ulint rec_del = rec_get_deleted_flag(rec, comp);
if (dict_index_has_virtual(index)) {
ulint n_ext;
@ -174,7 +172,7 @@ row_vers_impl_x_locked_low(
modify rec, and does not necessarily have an implicit x-lock
on rec. */
for (version = clust_rec;; version = prev_version) {
for (const rec_t* version = clust_rec;; version = prev_version) {
row_ext_t* ext;
dtuple_t* row;
dtuple_t* entry;
@ -194,16 +192,24 @@ row_vers_impl_x_locked_low(
heap, &prev_version, NULL,
dict_index_has_virtual(index) ? &vrow : NULL, 0);
trx_mutex_enter(trx);
const bool committed = trx_state_eq(
trx, TRX_STATE_COMMITTED_IN_MEMORY);
trx_mutex_exit(trx);
/* The oldest visible clustered index version must not be
delete-marked, because we never start a transaction by
inserting a delete-marked record. */
ut_ad(prev_version
|| !rec_get_deleted_flag(version, comp)
|| !trx_sys.is_registered(caller_trx, trx_id));
ut_ad(committed || prev_version
|| !rec_get_deleted_flag(version, comp));
/* Free version and clust_offsets. */
mem_heap_free(old_heap);
if (committed) {
goto not_locked;
}
if (prev_version == NULL) {
/* We reached the oldest visible version without
@ -223,6 +229,7 @@ row_vers_impl_x_locked_low(
or updated, the leaf page record always is
created with a clear delete-mark flag.
(We never insert a delete-marked record.) */
not_locked:
trx->release_reference();
trx = 0;
}
@ -349,14 +356,14 @@ result_check:
if (trx->id != prev_trx_id) {
/* prev_version was the first version modified by
the trx_id transaction: no implicit x-lock */
trx->release_reference();
trx = 0;
break;
goto not_locked;
}
}
DBUG_PRINT("info", ("Implicit lock is held by trx:" TRX_ID_FMT, trx_id));
if (trx) {
DBUG_PRINT("info", ("Implicit lock is held by trx:" TRX_ID_FMT,
trx_id));
}
if (v_heap != NULL) {
mem_heap_free(v_heap);
@ -372,7 +379,8 @@ index record.
@param[in] rec secondary index record
@param[in] index secondary index
@param[in] offsets rec_get_offsets(rec, index)
@return the active transaction; trx->release_reference() must be invoked
@return the active transaction; state must be rechecked after
trx_mutex_enter(), and trx->release_reference() must be invoked
@retval NULL if the record was committed */
trx_t*
row_vers_impl_x_locked(

View File

@ -764,6 +764,10 @@ static my_bool trx_rollback_recovered_callback(rw_trx_hash_element_t *element,
mutex_enter(&element->mutex);
if (trx_t *trx= element->trx)
{
/* The trx->is_recovered flag and trx->state are set
atomically under the protection of the trx->mutex in
trx_t::commit_state(). We do not want to accidentally clean up
a non-recovered transaction here. */
mutex_enter(&trx->mutex);
if (trx->is_recovered && trx_state_eq(trx, TRX_STATE_ACTIVE))
trx_list->push_back(trx);

View File

@ -460,10 +460,57 @@ void trx_free(trx_t*& trx)
trx = NULL;
}
/** Transition to committed state, to release implicit locks. */
inline void trx_t::commit_state()
{
/* This makes the transaction committed in memory and makes its
changes to data visible to other transactions. NOTE that there is a
small discrepancy from the strict formal visibility rules here: a
user of the database can see modifications made by another
transaction T even before the necessary redo log segment has been
flushed to the disk. If the database happens to crash before the
flush, the user has seen modifications from T which will never be a
committed transaction. However, any transaction T2 which sees the
modifications of the committing transaction T, and which also itself
makes modifications to the database, will get an lsn larger than the
committing transaction T. In the case where the log flush fails, and
T never gets committed, also T2 will never get committed. */
ut_ad(trx_mutex_own(this));
ut_ad(state != TRX_STATE_NOT_STARTED);
ut_ad(state != TRX_STATE_COMMITTED_IN_MEMORY
|| (is_recovered && !UT_LIST_GET_LEN(lock.trx_locks)));
state= TRX_STATE_COMMITTED_IN_MEMORY;
/* If the background thread trx_rollback_or_clean_recovered()
is still active then there is a chance that the rollback
thread may see this trx as COMMITTED_IN_MEMORY and goes ahead
to clean it up calling trx_cleanup_at_db_startup(). This can
happen in the case we are committing a trx here that is left
in PREPARED state during the crash. Note that commit of the
rollback of a PREPARED trx happens in the recovery thread
while the rollback of other transactions happen in the
background thread. To avoid this race we unconditionally unset
the is_recovered flag. */
is_recovered= false;
ut_ad(id || !is_referenced());
}
/** Release any explicit locks of a committing transaction. */
inline void trx_t::release_locks()
{
DBUG_ASSERT(state == TRX_STATE_COMMITTED_IN_MEMORY);
if (UT_LIST_GET_LEN(lock.trx_locks))
lock_trx_release_locks(this);
else
lock.table_locks.clear(); // Work around a bug
}
/** At shutdown, frees a transaction object. */
void
trx_free_at_shutdown(trx_t *trx)
{
trx_mutex_enter(trx);
ut_ad(trx->is_recovered);
ut_a(trx_state_eq(trx, TRX_STATE_PREPARED)
|| trx_state_eq(trx, TRX_STATE_PREPARED_RECOVERED)
@ -477,21 +524,16 @@ trx_free_at_shutdown(trx_t *trx)
&& !srv_undo_sources && srv_fast_shutdown))));
ut_a(trx->magic_n == TRX_MAGIC_N);
lock_trx_release_locks(trx);
trx->commit_state();
trx_mutex_exit(trx);
trx->release_locks();
trx_undo_free_at_shutdown(trx);
ut_a(!trx->read_only);
DBUG_LOG("trx", "Free prepared: " << trx);
trx->state = TRX_STATE_NOT_STARTED;
/* Undo trx_resurrect_table_locks(). */
lock_trx_lock_list_init(&trx->lock.trx_locks);
/* Note: This vector is not guaranteed to be empty because the
transaction was never committed and therefore lock_trx_release()
was not called. */
trx->lock.table_locks.clear();
ut_ad(!UT_LIST_GET_LEN(trx->lock.trx_locks));
trx->id = 0;
trx_free(trx);
@ -1308,8 +1350,8 @@ trx_commit_in_memory(
/* Note: We are asserting without holding the lock mutex. But
that is OK because this transaction is not waiting and cannot
be rolled back and no new locks can (or should not) be added
becuase it is flagged as a non-locking read-only transaction. */
be rolled back and no new locks can (or should) be added
because it is flagged as a non-locking read-only transaction. */
ut_a(UT_LIST_GET_LEN(trx->lock.trx_locks) == 0);
@ -1327,30 +1369,35 @@ trx_commit_in_memory(
DBUG_LOG("trx", "Autocommit in memory: " << trx);
trx->state = TRX_STATE_NOT_STARTED;
} else {
if (trx->id > 0) {
/* For consistent snapshot, we need to remove current
transaction from rw_trx_hash before doing commit and
releasing locks. */
trx_mutex_enter(trx);
trx->commit_state();
trx_mutex_exit(trx);
if (trx->id) {
trx_sys.deregister_rw(trx);
/* Wait for any implicit-to-explicit lock
conversions to cease, so that there will be no
race condition in lock_release(). */
while (UNIV_UNLIKELY(trx->is_referenced())) {
ut_delay(srv_spin_wait_delay);
}
trx->release_locks();
trx->id = 0;
} else {
ut_ad(trx->read_only || !trx->rsegs.m_redo.rseg);
trx->release_locks();
}
lock_trx_release_locks(trx);
ut_ad(trx->read_only || !trx->rsegs.m_redo.rseg || trx->id);
/* Remove the transaction from the list of active
transactions now that it no longer holds any user locks. */
ut_ad(trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY));
DEBUG_SYNC_C("after_trx_committed_in_memory");
if (trx->read_only || trx->rsegs.m_redo.rseg == NULL) {
if (trx->read_only || !trx->rsegs.m_redo.rseg) {
MONITOR_INC(MONITOR_TRX_RO_COMMIT);
} else {
trx_update_mod_tables_timestamp(trx);
MONITOR_INC(MONITOR_TRX_RW_COMMIT);
}
trx->id = 0;
}
ut_ad(!trx->rsegs.m_redo.undo);
@ -2166,7 +2213,7 @@ int trx_recover_for_mysql(XID *xid_list, uint len)
struct trx_get_trx_by_xid_callback_arg
{
XID *xid;
const XID *xid;
trx_t *trx;
};
@ -2178,6 +2225,7 @@ static my_bool trx_get_trx_by_xid_callback(rw_trx_hash_element_t *element,
mutex_enter(&element->mutex);
if (trx_t *trx= element->trx)
{
trx_mutex_enter(trx);
if (trx->is_recovered &&
(trx_state_eq(trx, TRX_STATE_PREPARED) ||
trx_state_eq(trx, TRX_STATE_PREPARED_RECOVERED)) &&
@ -2194,23 +2242,19 @@ static my_bool trx_get_trx_by_xid_callback(rw_trx_hash_element_t *element,
arg->trx= trx;
found= 1;
}
trx_mutex_exit(trx);
}
mutex_exit(&element->mutex);
return found;
}
/**
Finds PREPARED XA transaction by xid.
trx may have been committed, unless the caller is holding lock_sys.mutex.
@param[in] xid X/Open XA transaction identifier
@return trx or NULL; on match, the trx->xid will be invalidated;
*/
trx_t *trx_get_trx_by_xid(XID *xid)
/** Look up an X/Open distributed transaction in XA PREPARE state.
@param[in] xid X/Open XA transaction identifier
@return transaction on match (the trx_t::xid will be invalidated);
note that the trx may have been committed before the caller acquires
trx_t::mutex
@retval NULL if no match */
trx_t* trx_get_trx_by_xid(const XID* xid)
{
trx_get_trx_by_xid_callback_arg arg= { xid, 0 };

View File

@ -1634,7 +1634,7 @@ trx_undo_free_at_shutdown(trx_t *trx)
TRX_STATE_COMMITTED_IN_MEMORY));
/* fall through */
case TRX_UNDO_ACTIVE:
/* lock_trx_release_locks() assigns
/* trx_t::commit_state() assigns
trx->state = TRX_STATE_COMMITTED_IN_MEMORY. */
ut_a(!srv_was_started
|| srv_read_only_mode
@ -1661,7 +1661,7 @@ trx_undo_free_at_shutdown(trx_t *trx)
TRX_STATE_COMMITTED_IN_MEMORY));
/* fall through */
case TRX_UNDO_ACTIVE:
/* lock_trx_release_locks() assigns
/* trx_t::commit_state() assigns
trx->state = TRX_STATE_COMMITTED_IN_MEMORY. */
ut_a(!srv_was_started
|| srv_read_only_mode

View File

@ -2785,7 +2785,20 @@ static void reset_thd_trn(THD *thd, MARIA_HA *first_table)
THD_TRN= NULL;
for (MARIA_HA *table= first_table; table ;
table= table->trn_next)
{
_ma_reset_trn_for_table(table);
/*
If table has changed by this statement, invalidate it from the query
cache
*/
if (table->row_changes != table->start_row_changes)
{
table->start_row_changes= table->row_changes;
DBUG_ASSERT(table->s->chst_invalidator != NULL);
(*table->s->chst_invalidator)(table->s->data_file_name.str);
}
}
DBUG_VOID_RETURN;
}
@ -3252,7 +3265,10 @@ static int maria_commit(handlerton *hton __attribute__ ((unused)),
THD *thd, bool all)
{
TRN *trn= THD_TRN;
int res;
MARIA_HA *used_instances= (MARIA_HA*) trn->used_instances;
DBUG_ENTER("maria_commit");
trnman_reset_locked_tables(trn, 0);
trnman_set_flags(trn, trnman_get_flags(trn) & ~TRN_STATE_INFO_LOGGED);
@ -3260,8 +3276,9 @@ static int maria_commit(handlerton *hton __attribute__ ((unused)),
if ((thd->variables.option_bits & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) &&
!all)
DBUG_RETURN(0); // end of statement
reset_thd_trn(thd, (MARIA_HA*) trn->used_instances);
DBUG_RETURN(ma_commit(trn)); // end of transaction
res= ma_commit(trn);
reset_thd_trn(thd, used_instances);
DBUG_RETURN(res);
}

View File

@ -641,6 +641,7 @@ struct st_maria_handler
invalidator_by_filename invalidator; /* query cache invalidator */
ulonglong last_auto_increment; /* auto value at start of statement */
ulonglong row_changes; /* Incremented for each change */
ulonglong start_row_changes; /* Row changes since start trans */
ulong this_unique; /* uniq filenumber or thread */
ulong last_unique; /* last unique number */
ulong this_loop; /* counter for this open */