1
0
mirror of https://github.com/MariaDB/server.git synced 2025-11-25 17:25:02 +03:00

Merge 10.2 into 10.3

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

View File

@@ -3357,18 +3357,26 @@ a b
drop table t1; drop table t1;
set sql_mode=default; set sql_mode=default;
create table t1 (a int default b, b int default 4, t text); 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 t1 (b, t) values (5, '1 column is omitted');
insert into t1 values (default, 5, '2 column gets DEFAULT, keyword'); insert t1 values (default, 5, '2 column gets DEFAULT, keyword');
insert into t1 values (default(a), 5, '3 column gets DEFAULT(a), expression'); insert 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 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 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; select * from t1 order by t;
a b t a b t
5 5 1 column is omitted 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 3 column gets DEFAULT(a), expression
4 5 4 also expression DEFAULT(0)+0 4 5 4 also expression DEFAULT(0)+0
4 5 5 the value of the DEFAULT(a), that is b 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; drop table t1;
create table t1 (col1 int default(-(default(col1)))); create table t1 (col1 int default(-(default(col1))));
ERROR 01000: Expression for field `col1` is referring to uninitialized field `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) # 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); 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 t1 (b, t) values (5, '1 column is omitted');
insert into t1 values (default, 5, '2 column gets DEFAULT, keyword'); insert t1 values (default, 5, '2 column gets DEFAULT, keyword');
insert into t1 values (default(a), 5, '3 column gets DEFAULT(a), expression'); insert 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 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 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; select * from t1 order by t;
drop table t1; 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 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 2001-09-09 04:46:40.000000 2001-09-09 04:46:40.000000
DROP TABLE t1; 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 $timestamp=TIMESTAMP(6);
let $datetime=DATETIME(6); let $datetime=DATETIME(6);
source 'include/function_defaults.inc'; 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); insert into t2 values(1);
select a,b from t2; select a,b from t2;
a b a b
12 1 NULL 1
drop table t1,t2; drop table t1,t2;
create table t1 (a int invisible, b int, c int); create table t1 (a int invisible, b int, c int);
create table t2 (a int, b int, d 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_notify_cmd : MDEV-13549 Galera test failures
galera_var_slave_threads : MDEV-19746 Galera test failures because of wsrep_slave_threads identification 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_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 partition : MDEV-19958 Galera test failure on galera.partition
query_cache: MDEV-15805 Test failure on galera.query_cache query_cache: MDEV-15805 Test failure on galera.query_cache

View File

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

View File

@@ -3,6 +3,7 @@
# Flush any open myisam tables from previous tests # Flush any open myisam tables from previous tests
FLUSH TABLES; FLUSH TABLES;
call mtr.add_suppression("Found 1 prepared XA transactions");
--echo # --echo #
--echo # MDEV-13797 InnoDB may hang if shutdown is initiated soon after startup --echo # MDEV-13797 InnoDB may hang if shutdown is initiated soon after startup
@@ -23,6 +24,15 @@ dec $c;
COMMIT; COMMIT;
let $c = $trx; 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) while ($c)
{ {
connect (con$c,localhost,root,,); connect (con$c,localhost,root,,);
@@ -53,12 +63,17 @@ FLUSH TABLES;
# Perform a slow shutdown in order to roll back all recovered transactions # Perform a slow shutdown in order to roll back all recovered transactions
# and to avoid locking conflicts with the DROP TABLE below. # and to avoid locking conflicts with the DROP TABLE below.
XA RECOVER;
--disable_query_log --disable_query_log
SET GLOBAL innodb_fast_shutdown=0; SET GLOBAL innodb_fast_shutdown=0;
--source include/restart_mysqld.inc --source include/restart_mysqld.inc
--disable_query_log --disable_query_log
let $c = $trx; let $c = $trx;
disconnect con$c;
XA ROLLBACK 'x';
eval DROP TABLE t$c;
dec $c;
while ($c) while ($c)
{ {
disconnect con$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_row_index_choice : MDEV-11666
rpl_parallel2 : fails after MDEV-16172 rpl_parallel2 : fails after MDEV-16172
rpl_semi_sync_after_sync : fails after MDEV-16172 rpl_semi_sync_after_sync : fails after MDEV-16172
mdev_17588: MDEV-20137
rpl_slave_grp_exec: MDEV-10514 rpl_slave_grp_exec: MDEV-10514

View File

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

View File

@@ -1,23 +1,28 @@
--source include/master-slave.inc --source include/master-slave.inc
--source include/have_innodb.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 --connection master
create table t1 (a int) engine=innodb; create table t1 (a int) engine=innodb;
create table t2 (a int); create table t2 (a int);
create table t3 (a int) engine=innodb; create table t3 (a int) engine=innodb;
--source include/save_master_gtid.inc --save_master_pos
--connection slave --connection slave
# Using ER_UNKNOWN_STORAGE_ENGINE wont work # Using ER_UNKNOWN_STORAGE_ENGINE wont work
let $slave_sql_errno= 1286; let $slave_sql_errno= 1286;
--source include/wait_for_slave_sql_error.inc --source include/wait_for_slave_sql_error_and_skip.inc
--let $status_items= Last_Error --sync_with_master
--source include/show_slave_status.inc
STOP SLAVE IO_THREAD; --let $assert_text= Status should be 'Slave has read all relay log...'
source include/wait_for_slave_to_stop.inc; --let $assert_cond= "[SHOW SLAVE STATUS, Slave_SQL_Running_State, 1]"
SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; #Like "Slave has read all relay log%"
--source include/start_slave.inc --source include/rpl_assert.inc
--source include/sync_with_master_gtid.inc
show tables; show tables;
show create table t2; show create table t2;
--error ER_NO_SUCH_TABLE --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) 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); 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); 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 virtual my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const
{ DBUG_ASSERT(0); return 0; } { DBUG_ASSERT(0); return 0; }
@@ -1010,14 +1009,6 @@ public:
return get_timestamp(ptr, sec_part); 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 binary() const { return 1; }
virtual bool zero_pack() const { return 1; } virtual bool zero_pack() const { return 1; }
virtual enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; } virtual enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; }
@@ -2686,13 +2677,6 @@ public:
void sql_type(String &str) const; void sql_type(String &str) const;
bool zero_pack() const { return 0; } bool zero_pack() const { return 0; }
int set_time(); 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 */ /* 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(const uchar *pos, ulong *sec_part) const;
my_time_t get_timestamp(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) bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
{ return Field_datetime::get_TIME(ltime, ptr, fuzzydate); } { return Field_datetime::get_TIME(ltime, ptr, fuzzydate); }
int set_time(); 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, uchar *pack(uchar* to, const uchar *from,
uint max_length __attribute__((unused))) uint max_length __attribute__((unused)))
{ {

View File

@@ -802,9 +802,9 @@ struct xid_t {
char data[XIDDATASIZE]; // not \0-terminated ! char data[XIDDATASIZE]; // not \0-terminated !
xid_t() {} /* Remove gcc warning */ 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); } { 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); } { return !is_null() && g == gtrid_length && b == bqual_length && !memcmp(d, data, g+b); }
void set(struct xid_t *xid) void set(struct xid_t *xid)
{ memcpy(this, xid, xid->length()); } { 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); 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 == return field_arg->save_in_field_default_value(context->error_processor ==
&view_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); int err_code= item->save_in_field(field, 0);
field->table->copy_blobs= copy_blobs_saved; field->table->copy_blobs= copy_blobs_saved;
field->set_explicit_default(item); field->set_has_explicit_value();
return err_code < 0; 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] - rfield->move_field_offset((my_ptrdiff_t) (table->record[1] -
table->record[0])); table->record[0]));
} }
rfield->set_explicit_default(value); rfield->set_has_explicit_value();
} }
if (update && thd->variables.sql_mode & MODE_SIMULTANEOUS_ASSIGNMENT) 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 && if (update)
table_arg->update_default_fields(0, ignore_errors)) table_arg->evaluate_update_default_function();
goto err; else
if (table_arg->default_field &&
table_arg->update_default_fields(ignore_errors))
goto err;
/* Update virtual fields */ /* Update virtual fields */
if (table_arg->vfield && if (table_arg->vfield &&
table_arg->update_virtual_fields(table_arg->file, VCOL_UPDATE_FOR_WRITE)) 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_iterator_fast<Item> v(values);
List<TABLE> tbl_list; List<TABLE> tbl_list;
bool all_fields_have_values= true;
Item *value; Item *value;
Field *field; Field *field;
bool abort_on_warning_saved= thd->abort_on_warning; 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); DBUG_ASSERT(field->table == table);
if (unlikely(field->invisible)) if (unlikely(field->invisible))
{
all_fields_have_values= false;
continue; continue;
}
else else
value=v++; value=v++;
@@ -8617,11 +8617,8 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List<Item> &values,
else else
if (value->save_in_field(field, 0) < 0) if (value->save_in_field(field, 0) < 0)
goto err; 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 */ /* Update virtual fields */
thd->abort_on_warning= FALSE; thd->abort_on_warning= FALSE;
if (table->vfield && 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. be updated as if this is an UPDATE.
*/ */
if (different_records && table->default_field) if (different_records && table->default_field)
{ table->evaluate_update_default_function();
if (table->update_default_fields(1, info->ignore))
goto err;
}
/* CHECK OPTION for VIEW ... ON DUPLICATE KEY UPDATE ... */ /* CHECK OPTION for VIEW ... ON DUPLICATE KEY UPDATE ... */
res= info->table_list->view_check_option(table->in_use, info->ignore); 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 thd->count_cuted_fields= CHECK_FIELD_WARN; // Calculate cuted fields
store_values(values); store_values(values);
if (table->default_field && if (table->default_field &&
unlikely(table->update_default_fields(0, info.ignore))) unlikely(table->update_default_fields(info.ignore)))
DBUG_RETURN(1); DBUG_RETURN(1);
thd->count_cuted_fields= CHECK_FIELD_ERROR_FOR_NULL; thd->count_cuted_fields= CHECK_FIELD_ERROR_FOR_NULL;
if (unlikely(thd->is_error())) if (unlikely(thd->is_error()))

View File

@@ -9819,7 +9819,7 @@ do_continue:;
thd->count_cuted_fields= CHECK_FIELD_EXPRESSION; thd->count_cuted_fields= CHECK_FIELD_EXPRESSION;
altered_table->reset_default_fields(); altered_table->reset_default_fields();
if (altered_table->default_field && if (altered_table->default_field &&
altered_table->update_default_fields(0, 1)) altered_table->update_default_fields(true))
goto err_new_table_cleanup; goto err_new_table_cleanup;
thd->count_cuted_fields= CHECK_FIELD_IGNORE; 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; prev_insert_id= to->file->next_insert_id;
if (to->default_field) if (to->default_field)
to->update_default_fields(0, ignore); to->update_default_fields(ignore);
if (to->vfield) if (to->vfield)
to->update_virtual_fields(to->file, VCOL_UPDATE_FOR_WRITE); 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)); 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 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 (including NULL bits) not in the write_set may not have been read and
can therefore not be compared. 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++) for (Field **ptr= table->field ; *ptr != NULL; ptr++)
{ {
Field *field= *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()) if (field->real_maybe_null())
{ {
@@ -111,8 +114,9 @@ bool compare_record(const TABLE *table)
/* Compare updated fields */ /* Compare updated fields */
for (Field **ptr= table->field ; *ptr ; ptr++) for (Field **ptr= table->field ; *ptr ; ptr++)
{ {
if (bitmap_is_set(table->write_set, (*ptr)->field_index) && Field *field= *ptr;
(*ptr)->cmp_binary_offset(table->s->rec_buff_length)) if (field->has_explicit_value() && !field->vcol_info &&
field->cmp_binary_offset(table->s->rec_buff_length))
return TRUE; return TRUE;
} }
return FALSE; return FALSE;
@@ -897,11 +901,6 @@ update_begin:
if (!can_compare_record || compare_record(table)) 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)) != if ((res= table_list->view_check_option(thd, ignore)) !=
VIEW_CHECK_OK) VIEW_CHECK_OK)
{ {
@@ -2379,10 +2378,6 @@ int multi_update::send_data(List<Item> &not_used_values)
{ {
int error; 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)) != if ((error= cur_table->view_check_option(thd, ignore)) !=
VIEW_CHECK_OK) VIEW_CHECK_OK)
{ {
@@ -2699,6 +2694,10 @@ int multi_update::do_updates()
copy_field_ptr->to_field->set_has_explicit_value(); 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 && if (table->triggers &&
table->triggers->process_triggers(thd, TRG_EVENT_UPDATE, table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
TRG_ACTION_BEFORE, TRUE)) TRG_ACTION_BEFORE, TRUE))
@@ -2707,12 +2706,6 @@ int multi_update::do_updates()
if (!can_compare_record || compare_record(table)) if (!can_compare_record || compare_record(table))
{ {
int error; 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)) != if ((error= cur_table->view_check_option(thd, ignore)) !=
VIEW_CHECK_OK) 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. 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; Query_arena backup_arena;
Field **field_ptr; Field **field_ptr;
@@ -7950,14 +7950,9 @@ int TABLE::update_default_fields(bool update_command, bool ignore_errors)
*/ */
if (!field->has_explicit_value()) if (!field->has_explicit_value())
{ {
if (!update_command) if (field->default_value &&
{ (field->default_value->flags || field->flags & BLOB_FLAG))
if (field->default_value && res|= (field->default_value->expr->save_in_field(field, 0) < 0);
(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 (!ignore_errors && res) if (!ignore_errors && res)
{ {
my_error(ER_CALCULATING_DEFAULT_VALUE, MYF(0), field->field_name.str); 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() void TABLE::vers_update_fields()
{ {
bitmap_set_bit(write_set, vers_start_field()->field_index); bitmap_set_bit(write_set, vers_start_field()->field_index);

View File

@@ -1489,7 +1489,8 @@ public:
ulong actual_key_flags(KEY *keyinfo); ulong actual_key_flags(KEY *keyinfo);
int update_virtual_field(Field *vf); int update_virtual_field(Field *vf);
int update_virtual_fields(handler *h, enum_vcol_update_mode update_mode); 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(); void reset_default_fields();
inline ha_rows stat_records() { return used_stat_records; } 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 buf_block_t* block, /*!< in: buffer block containing rec */
const rec_t* rec, /*!< in: record */ const rec_t* rec, /*!< in: record */
lock_mode lock_mode);/*!< in: LOCK_S or LOCK_X */ lock_mode lock_mode);/*!< in: LOCK_S or LOCK_X */
/*********************************************************************//**
Releases a transaction's locks, and releases possible other transactions /** Release the explicit locks of a committing transaction,
waiting because of these locks. Change the state of the transaction to and release possible other transactions waiting because of these locks. */
TRX_STATE_COMMITTED_IN_MEMORY. */ void lock_trx_release_locks(trx_t* trx);
void
lock_trx_release_locks(
/*===================*/
trx_t* trx); /*!< in/out: transaction */
/*********************************************************************//** /*********************************************************************//**
Calculates the fold value of a page file address: used in inserting or 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] rec secondary index record
@param[in] index secondary index @param[in] index secondary index
@param[in] offsets rec_get_offsets(rec, 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 */ @retval NULL if the record was committed */
trx_t* trx_t*
row_vers_impl_x_locked( 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->read_only || !trx->rsegs.m_redo.rseg);
ut_ad(!trx_is_autocommit_non_locking(trx)); ut_ad(!trx_is_autocommit_non_locking(trx));
/* trx->state can be anything except TRX_STATE_NOT_STARTED */
mutex_enter(&trx->mutex); mutex_enter(&trx->mutex);
ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE) || 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_RECOVERED) ||
trx_state_eq(trx, TRX_STATE_PREPARED)); trx_state_eq(trx, TRX_STATE_PREPARED));
mutex_exit(&trx->mutex); mutex_exit(&trx->mutex);
@@ -940,7 +942,7 @@ public:
/** /**
Takes MVCC snapshot. 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. in ids.
For details about get_rw_trx_hash_version() != get_max_trx_id() spin 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 */ XID* xid_list, /*!< in/out: prepared transactions */
uint len); /*!< in: number of slots in xid_list */ uint len); /*!< in: number of slots in xid_list */
/*******************************************************************//** /** Look up an X/Open distributed transaction in XA PREPARE state.
This function is used to find one X/Open XA distributed transaction @param[in] xid X/Open XA transaction identifier
which is in the prepared state @return transaction on match (the trx_t::xid will be invalidated);
@return trx or NULL; on match, the trx->xid will be invalidated; note that the trx may have been committed before the caller acquires
note that the trx may have been committed, unless the caller is trx_t::mutex
holding lock_sys.mutex */ @retval NULL if no match */
trx_t * trx_t* trx_get_trx_by_xid(const XID* xid);
trx_get_trx_by_xid(
/*===============*/
XID* xid); /*!< in: X/Open XA transaction identifier */
/**********************************************************************//** /**********************************************************************//**
If required, flushes the log to disk if we called trx_commit_for_mysql() If required, flushes the log to disk if we called trx_commit_for_mysql()
with trx->flush_log_later == TRUE. */ with trx->flush_log_later == TRUE. */
@@ -467,6 +464,9 @@ Check transaction state */
ut_ad(!(t)->read_view.is_open()); \ ut_ad(!(t)->read_view.is_open()); \
ut_ad((t)->lock.wait_thr == NULL); \ ut_ad((t)->lock.wait_thr == NULL); \
ut_ad(UT_LIST_GET_LEN((t)->lock.trx_locks) == 0); \ 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); \ ut_ad((t)->dict_operation == TRX_DICT_OP_NONE); \
} while(0) } while(0)
@@ -701,8 +701,8 @@ so without holding any mutex. The following are exceptions to this:
* trx_rollback_resurrected() may access resurrected (connectionless) * trx_rollback_resurrected() may access resurrected (connectionless)
transactions while the system is already processing new user transactions while the system is already processing new user
transactions. The trx_sys.mutex prevents a race condition between it transactions. The trx_sys.mutex and trx->is_recovered prevent
and lock_trx_release_locks() [invoked by trx_commit()]. a race condition between it and trx_commit().
* trx_print_low() may access transactions not associated with the current * trx_print_low() may access transactions not associated with the current
thread. The caller must be holding lock_sys.mutex. 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 * The locking code (in particular, lock_deadlock_recursive() and
lock_rec_convert_impl_to_expl()) will access transactions associated lock_rec_convert_impl_to_expl()) will access transactions associated
to other connections. The locks of transactions are protected by 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.*/ /** Represents an instance of rollback segment along with its state variables.*/
struct trx_undo_ptr_t { struct trx_undo_ptr_t {
@@ -837,26 +837,19 @@ public:
ACTIVE->COMMITTED is possible when the transaction is in ACTIVE->COMMITTED is possible when the transaction is in
rw_trx_hash. rw_trx_hash.
Transitions to COMMITTED are protected by both lock_sys.mutex Transitions to COMMITTED are protected by trx_t::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. */
trx_state_t state; 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 ReadView read_view; /*!< consistent read view used in the
transaction, or NULL if not yet set */ transaction, or NULL if not yet set */
trx_lock_t lock; /*!< Information about the transaction trx_lock_t lock; /*!< Information about the transaction
locks and state. Protected by locks and state. Protected by
trx->mutex or lock_sys.mutex lock_sys.mutex (insertions also
or both */ by trx_t::mutex). */
bool is_recovered; /*!< 0=normal transaction,
1=recovered, must be rolled back,
protected by trx_sys.mutex when
trx is in rw_trx_hash */
/* These fields are not protected by any mutex. */ /* These fields are not protected by any mutex. */
const char* op_info; /*!< English text describing the const char* op_info; /*!< English text describing the
@@ -1116,6 +1109,12 @@ public:
return flush_observer; 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() bool is_referenced()
{ {

View File

@@ -4740,7 +4740,7 @@ lock_trx_table_locks_find(
{ {
bool found = false; bool found = false;
trx_mutex_enter(trx); ut_ad(trx_mutex_own(trx));
for (lock_list::const_iterator it = trx->lock.table_locks.begin(), for (lock_list::const_iterator it = trx->lock.table_locks.begin(),
end = trx->lock.table_locks.end(); it != end; ++it) { 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); ut_a(lock->un_member.tab_lock.table != NULL);
} }
trx_mutex_exit(trx);
return(found); return(found);
} }
@@ -4785,25 +4783,23 @@ lock_table_queue_validate(
lock != NULL; lock != NULL;
lock = UT_LIST_GET_NEXT(un_member.tab_lock.locks, lock)) { lock = UT_LIST_GET_NEXT(un_member.tab_lock.locks, lock)) {
/* Transaction state may change from ACTIVE to PREPARED. /* lock->trx->state cannot change from or to NOT_STARTED
State change to COMMITTED is not possible while we are while we are holding the trx_sys.mutex. It may change
holding lock_sys.mutex: it is done by lock_trx_release_locks() from ACTIVE or PREPARED to PREPARED or COMMITTED. */
under lock_sys.mutex protection. trx_mutex_enter(lock->trx);
Transaction in NOT_STARTED state cannot hold locks, and
lock->trx->state can only move to NOT_STARTED from COMMITTED. */
check_trx_state(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( ut_a(!lock_table_other_has_incompatible(
lock->trx, 0, table, lock->trx, 0, table,
lock_get_mode(lock))); lock_get_mode(lock)));
} else { } else {
ut_a(lock_table_has_to_wait_in_queue(lock)); ut_a(lock_table_has_to_wait_in_queue(lock));
} }
ut_a(lock_trx_table_locks_find(lock->trx, lock)); ut_a(lock_trx_table_locks_find(lock->trx, lock));
trx_mutex_exit(lock->trx);
} }
return(TRUE); return(TRUE);
@@ -4850,42 +4846,41 @@ lock_rec_queue_validate(
lock != NULL; lock != NULL;
lock = lock_rec_get_next_const(heap_no, lock)) { 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)); ut_ad(!trx_is_ac_nl_ro(lock->trx));
ut_ad(trx_state_eq(lock->trx,
if (lock_get_wait(lock)) { TRX_STATE_COMMITTED_IN_MEMORY)
ut_a(lock_rec_has_to_wait_in_queue(lock)); || !lock_get_wait(lock)
} || lock_rec_has_to_wait_in_queue(lock));
trx_mutex_exit(lock->trx);
if (index != NULL) {
ut_a(lock->index == index);
}
} }
goto func_exit; func_exit:
if (!locked_lock_trx_sys) {
lock_mutex_exit();
}
return true;
} }
ut_ad(page_rec_is_leaf(rec)); 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)) { mutex_enter(&impl_trx->mutex);
/* Unlike the non-debug code, this invariant can only succeed ut_ad(impl_trx->state != TRX_STATE_NOT_STARTED);
if the check and assertion are covered by the lock mutex. */ if (impl_trx->state == TRX_STATE_COMMITTED_IN_MEMORY) {
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) {
} else if (const lock_t* other_lock } else if (const lock_t* other_lock
= lock_rec_other_has_expl_req( = lock_rec_other_has_expl_req(
LOCK_S, block, true, heap_no, 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, ut_ad(lock_rec_has_expl(LOCK_X | LOCK_REC_NOT_GAP,
block, heap_no, impl_trx)); block, heap_no, impl_trx));
} }
mutex_exit(&impl_trx->mutex);
} }
for (lock = lock_rec_get_first(lock_sys.rec_hash, block, heap_no); 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 || ut_ad(innodb_lock_schedule_algorithm == INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS ||
lock_queue_validate(lock)); lock_queue_validate(lock));
func_exit: goto func_exit;
if (!locked_lock_trx_sys) {
lock_mutex_exit();
}
return(TRUE);
} }
/*********************************************************************//** /*********************************************************************//**
@@ -5398,30 +5390,24 @@ lock_rec_convert_impl_to_expl_for_trx(
trx_t* trx, /*!< in/out: active transaction */ trx_t* trx, /*!< in/out: active transaction */
ulint heap_no)/*!< in: rec heap number to lock */ ulint heap_no)/*!< in: rec heap number to lock */
{ {
ut_ad(trx->is_referenced());
ut_ad(page_rec_is_leaf(rec)); ut_ad(page_rec_is_leaf(rec));
ut_ad(!rec_is_metadata(rec, index)); ut_ad(!rec_is_metadata(rec, index));
DEBUG_SYNC_C("before_lock_rec_convert_impl_to_expl_for_trx"); DEBUG_SYNC_C("before_lock_rec_convert_impl_to_expl_for_trx");
lock_mutex_enter(); lock_mutex_enter();
trx_mutex_enter(trx);
ut_ad(trx->is_referenced());
ut_ad(!trx_state_eq(trx, TRX_STATE_NOT_STARTED)); ut_ad(!trx_state_eq(trx, TRX_STATE_NOT_STARTED));
if (!trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY) if (!trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY)
&& !lock_rec_has_expl(LOCK_X | LOCK_REC_NOT_GAP, && !lock_rec_has_expl(LOCK_X | LOCK_REC_NOT_GAP,
block, heap_no, trx)) { block, heap_no, trx)) {
lock_rec_add_to_queue(LOCK_REC | LOCK_X | LOCK_REC_NOT_GAP,
ulint type_mode; block, heap_no, index, trx, true);
type_mode = (LOCK_REC | LOCK_X | LOCK_REC_NOT_GAP);
lock_rec_add_to_queue(
type_mode, block, heap_no, index, trx, FALSE);
} }
lock_mutex_exit(); lock_mutex_exit();
trx_mutex_exit(trx);
trx->release_reference(); trx->release_reference();
DEBUG_SYNC_C("after_lock_rec_convert_impl_to_expl_for_trx"); 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); mutex_enter(&element->mutex);
if (element->trx) if (element->trx)
{ {
lock_t *expl_lock= lock_rec_has_expl(LOCK_S | LOCK_REC_NOT_GAP, arg->block, trx_mutex_enter(element->trx);
arg->heap_no, 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 An explicit lock is held by trx other than the trx holding the implicit
lock. lock.
*/ */
ut_ad(!expl_lock || expl_lock->trx == arg->impl_trx); ut_ad(!expl_lock || expl_lock->trx == arg->impl_trx);
trx_mutex_exit(element->trx);
} }
mutex_exit(&element->mutex); mutex_exit(&element->mutex);
return 0; 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)); ut_ad(!page_rec_is_metadata(rec));
lock_mutex_enter(); lock_mutex_enter();
ut_ad(trx->is_referenced()); 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); trx_mutex_enter(trx);
const trx_state_t state = trx->state; const trx_state_t state = trx->state;
trx_mutex_exit(trx); trx_mutex_exit(trx);
@@ -6248,89 +6235,24 @@ lock_unlock_table_autoinc(
} }
} }
/*********************************************************************//** /** Release the explicit locks of a committing transaction,
Releases a transaction's locks, and releases possible other transactions and release possible other transactions waiting because of these locks. */
waiting because of these locks. Change the state of the transaction to void lock_trx_release_locks(trx_t* trx)
TRX_STATE_COMMITTED_IN_MEMORY. */
void
lock_trx_release_locks(
/*===================*/
trx_t* trx) /*!< in/out: transaction */
{ {
check_trx_state(trx); ut_ad(UT_LIST_GET_LEN(trx->lock.trx_locks));
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();
}
lock_mutex_enter();
lock_release(trx);
trx->lock.n_rec_locks = 0; trx->lock.n_rec_locks = 0;
/* We don't remove the locks one by one from the vector for /* We don't remove the locks one by one from the vector for
efficiency reasons. We simply reset it because we would have efficiency reasons. We simply reset it because we would have
released all the locks anyway. */ released all the locks anyway. */
trx->lock.table_locks.clear(); trx->lock.table_locks.clear();
ut_a(UT_LIST_GET_LEN(trx->lock.trx_locks) == 0); ut_ad(UT_LIST_GET_LEN(trx->lock.trx_locks) == 0);
ut_a(ib_vector_is_empty(trx->autoinc_locks)); ut_ad(ib_vector_is_empty(trx->autoinc_locks));
ut_a(trx->lock.table_locks.empty()); lock_mutex_exit();
mem_heap_empty(trx->lock.lock_heap); 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); mutex_enter(&element->mutex);
if (element->trx) if (element->trx)
{ {
trx_mutex_enter(element->trx);
check_trx_state(element->trx); check_trx_state(element->trx);
for (const lock_t *lock= UT_LIST_GET_FIRST(element->trx->lock.trx_locks); if (element->trx->state != TRX_STATE_COMMITTED_IN_MEMORY)
lock != NULL;
lock= UT_LIST_GET_NEXT(trx_locks, lock))
{ {
ut_ad(lock->trx == element->trx); for (const lock_t *lock= UT_LIST_GET_FIRST(element->trx->lock.trx_locks);
if (lock_get_type_low(lock) == LOCK_REC) lock != NULL;
lock= UT_LIST_GET_NEXT(trx_locks, lock))
{ {
ut_ad(!dict_index_is_online_ddl(lock->index) || ut_ad(lock->trx == element->trx);
dict_index_is_clust(lock->index)); if (lock_get_type_low(lock) == LOCK_REC)
ut_ad(lock->index->table != table); {
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); mutex_exit(&element->mutex);
return 0; return 0;
@@ -6585,7 +6512,7 @@ DeadlockChecker::start_print()
if (srv_print_all_deadlocks) { if (srv_print_all_deadlocks) {
ib::info() << "Transactions deadlock detected, dumping" 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] index secondary index
@param[in] offsets rec_get_offsets(rec, index) @param[in] offsets rec_get_offsets(rec, index)
@param[in,out] mtr mini-transaction @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 */ @retval NULL if the record was committed */
UNIV_INLINE UNIV_INLINE
trx_t* trx_t*
@@ -90,9 +91,6 @@ row_vers_impl_x_locked_low(
mtr_t* mtr) mtr_t* mtr)
{ {
trx_id_t trx_id; trx_id_t trx_id;
ulint comp;
ulint rec_del;
const rec_t* version;
rec_t* prev_version = NULL; rec_t* prev_version = NULL;
ulint* clust_offsets; ulint* clust_offsets;
mem_heap_t* heap; 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(index->table == clust_index->table);
ut_ad(!!comp == dict_table_is_comp(index->table)); ut_ad(!!comp == dict_table_is_comp(index->table));
ut_ad(!comp == !page_rec_is_comp(clust_rec)); 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)) { if (dict_index_has_virtual(index)) {
ulint n_ext; ulint n_ext;
@@ -174,7 +172,7 @@ row_vers_impl_x_locked_low(
modify rec, and does not necessarily have an implicit x-lock modify rec, and does not necessarily have an implicit x-lock
on rec. */ on rec. */
for (version = clust_rec;; version = prev_version) { for (const rec_t* version = clust_rec;; version = prev_version) {
row_ext_t* ext; row_ext_t* ext;
dtuple_t* row; dtuple_t* row;
dtuple_t* entry; dtuple_t* entry;
@@ -194,16 +192,24 @@ row_vers_impl_x_locked_low(
heap, &prev_version, NULL, heap, &prev_version, NULL,
dict_index_has_virtual(index) ? &vrow : NULL, 0); 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 /* The oldest visible clustered index version must not be
delete-marked, because we never start a transaction by delete-marked, because we never start a transaction by
inserting a delete-marked record. */ inserting a delete-marked record. */
ut_ad(prev_version ut_ad(committed || prev_version
|| !rec_get_deleted_flag(version, comp) || !rec_get_deleted_flag(version, comp));
|| !trx_sys.is_registered(caller_trx, trx_id));
/* Free version and clust_offsets. */ /* Free version and clust_offsets. */
mem_heap_free(old_heap); mem_heap_free(old_heap);
if (committed) {
goto not_locked;
}
if (prev_version == NULL) { if (prev_version == NULL) {
/* We reached the oldest visible version without /* 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 or updated, the leaf page record always is
created with a clear delete-mark flag. created with a clear delete-mark flag.
(We never insert a delete-marked record.) */ (We never insert a delete-marked record.) */
not_locked:
trx->release_reference(); trx->release_reference();
trx = 0; trx = 0;
} }
@@ -349,14 +356,14 @@ result_check:
if (trx->id != prev_trx_id) { if (trx->id != prev_trx_id) {
/* prev_version was the first version modified by /* prev_version was the first version modified by
the trx_id transaction: no implicit x-lock */ the trx_id transaction: no implicit x-lock */
goto not_locked;
trx->release_reference();
trx = 0;
break;
} }
} }
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) { if (v_heap != NULL) {
mem_heap_free(v_heap); mem_heap_free(v_heap);
@@ -372,7 +379,8 @@ index record.
@param[in] rec secondary index record @param[in] rec secondary index record
@param[in] index secondary index @param[in] index secondary index
@param[in] offsets rec_get_offsets(rec, 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 */ @retval NULL if the record was committed */
trx_t* trx_t*
row_vers_impl_x_locked( 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); mutex_enter(&element->mutex);
if (trx_t *trx= element->trx) 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); mutex_enter(&trx->mutex);
if (trx->is_recovered && trx_state_eq(trx, TRX_STATE_ACTIVE)) if (trx->is_recovered && trx_state_eq(trx, TRX_STATE_ACTIVE))
trx_list->push_back(trx); trx_list->push_back(trx);

View File

@@ -460,10 +460,57 @@ void trx_free(trx_t*& trx)
trx = NULL; 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. */ /** At shutdown, frees a transaction object. */
void void
trx_free_at_shutdown(trx_t *trx) trx_free_at_shutdown(trx_t *trx)
{ {
trx_mutex_enter(trx);
ut_ad(trx->is_recovered); ut_ad(trx->is_recovered);
ut_a(trx_state_eq(trx, TRX_STATE_PREPARED) ut_a(trx_state_eq(trx, TRX_STATE_PREPARED)
|| trx_state_eq(trx, TRX_STATE_PREPARED_RECOVERED) || 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)))); && !srv_undo_sources && srv_fast_shutdown))));
ut_a(trx->magic_n == TRX_MAGIC_N); 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); trx_undo_free_at_shutdown(trx);
ut_a(!trx->read_only); ut_a(!trx->read_only);
DBUG_LOG("trx", "Free prepared: " << trx); DBUG_LOG("trx", "Free prepared: " << trx);
trx->state = TRX_STATE_NOT_STARTED; trx->state = TRX_STATE_NOT_STARTED;
ut_ad(!UT_LIST_GET_LEN(trx->lock.trx_locks));
/* 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();
trx->id = 0; trx->id = 0;
trx_free(trx); trx_free(trx);
@@ -1308,8 +1350,8 @@ trx_commit_in_memory(
/* Note: We are asserting without holding the lock mutex. But /* Note: We are asserting without holding the lock mutex. But
that is OK because this transaction is not waiting and cannot that is OK because this transaction is not waiting and cannot
be rolled back and no new locks can (or should not) be added be rolled back and no new locks can (or should) be added
becuase it is flagged as a non-locking read-only transaction. */ because it is flagged as a non-locking read-only transaction. */
ut_a(UT_LIST_GET_LEN(trx->lock.trx_locks) == 0); 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); DBUG_LOG("trx", "Autocommit in memory: " << trx);
trx->state = TRX_STATE_NOT_STARTED; trx->state = TRX_STATE_NOT_STARTED;
} else { } else {
if (trx->id > 0) { trx_mutex_enter(trx);
/* For consistent snapshot, we need to remove current trx->commit_state();
transaction from rw_trx_hash before doing commit and trx_mutex_exit(trx);
releasing locks. */
if (trx->id) {
trx_sys.deregister_rw(trx); 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"); 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); MONITOR_INC(MONITOR_TRX_RO_COMMIT);
} else { } else {
trx_update_mod_tables_timestamp(trx); trx_update_mod_tables_timestamp(trx);
MONITOR_INC(MONITOR_TRX_RW_COMMIT); MONITOR_INC(MONITOR_TRX_RW_COMMIT);
} }
trx->id = 0;
} }
ut_ad(!trx->rsegs.m_redo.undo); 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 struct trx_get_trx_by_xid_callback_arg
{ {
XID *xid; const XID *xid;
trx_t *trx; 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); mutex_enter(&element->mutex);
if (trx_t *trx= element->trx) if (trx_t *trx= element->trx)
{ {
trx_mutex_enter(trx);
if (trx->is_recovered && if (trx->is_recovered &&
(trx_state_eq(trx, TRX_STATE_PREPARED) || (trx_state_eq(trx, TRX_STATE_PREPARED) ||
trx_state_eq(trx, TRX_STATE_PREPARED_RECOVERED)) && 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; arg->trx= trx;
found= 1; found= 1;
} }
trx_mutex_exit(trx);
} }
mutex_exit(&element->mutex); mutex_exit(&element->mutex);
return found; return found;
} }
/** Look up an X/Open distributed transaction in XA PREPARE state.
/** @param[in] xid X/Open XA transaction identifier
Finds PREPARED XA transaction by xid. @return transaction on match (the trx_t::xid will be invalidated);
note that the trx may have been committed before the caller acquires
trx may have been committed, unless the caller is holding lock_sys.mutex. trx_t::mutex
@retval NULL if no match */
@param[in] xid X/Open XA transaction identifier trx_t* trx_get_trx_by_xid(const XID* xid)
@return trx or NULL; on match, the trx->xid will be invalidated;
*/
trx_t *trx_get_trx_by_xid(XID *xid)
{ {
trx_get_trx_by_xid_callback_arg arg= { xid, 0 }; 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)); TRX_STATE_COMMITTED_IN_MEMORY));
/* fall through */ /* fall through */
case TRX_UNDO_ACTIVE: case TRX_UNDO_ACTIVE:
/* lock_trx_release_locks() assigns /* trx_t::commit_state() assigns
trx->state = TRX_STATE_COMMITTED_IN_MEMORY. */ trx->state = TRX_STATE_COMMITTED_IN_MEMORY. */
ut_a(!srv_was_started ut_a(!srv_was_started
|| srv_read_only_mode || srv_read_only_mode
@@ -1661,7 +1661,7 @@ trx_undo_free_at_shutdown(trx_t *trx)
TRX_STATE_COMMITTED_IN_MEMORY)); TRX_STATE_COMMITTED_IN_MEMORY));
/* fall through */ /* fall through */
case TRX_UNDO_ACTIVE: case TRX_UNDO_ACTIVE:
/* lock_trx_release_locks() assigns /* trx_t::commit_state() assigns
trx->state = TRX_STATE_COMMITTED_IN_MEMORY. */ trx->state = TRX_STATE_COMMITTED_IN_MEMORY. */
ut_a(!srv_was_started ut_a(!srv_was_started
|| srv_read_only_mode || srv_read_only_mode

View File

@@ -2785,7 +2785,20 @@ static void reset_thd_trn(THD *thd, MARIA_HA *first_table)
THD_TRN= NULL; THD_TRN= NULL;
for (MARIA_HA *table= first_table; table ; for (MARIA_HA *table= first_table; table ;
table= table->trn_next) table= table->trn_next)
{
_ma_reset_trn_for_table(table); _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; DBUG_VOID_RETURN;
} }
@@ -3252,7 +3265,10 @@ static int maria_commit(handlerton *hton __attribute__ ((unused)),
THD *thd, bool all) THD *thd, bool all)
{ {
TRN *trn= THD_TRN; TRN *trn= THD_TRN;
int res;
MARIA_HA *used_instances= (MARIA_HA*) trn->used_instances;
DBUG_ENTER("maria_commit"); DBUG_ENTER("maria_commit");
trnman_reset_locked_tables(trn, 0); trnman_reset_locked_tables(trn, 0);
trnman_set_flags(trn, trnman_get_flags(trn) & ~TRN_STATE_INFO_LOGGED); 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)) && if ((thd->variables.option_bits & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) &&
!all) !all)
DBUG_RETURN(0); // end of statement DBUG_RETURN(0); // end of statement
reset_thd_trn(thd, (MARIA_HA*) trn->used_instances); res= ma_commit(trn);
DBUG_RETURN(ma_commit(trn)); // end of transaction 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 */ invalidator_by_filename invalidator; /* query cache invalidator */
ulonglong last_auto_increment; /* auto value at start of statement */ ulonglong last_auto_increment; /* auto value at start of statement */
ulonglong row_changes; /* Incremented for each change */ ulonglong row_changes; /* Incremented for each change */
ulonglong start_row_changes; /* Row changes since start trans */
ulong this_unique; /* uniq filenumber or thread */ ulong this_unique; /* uniq filenumber or thread */
ulong last_unique; /* last unique number */ ulong last_unique; /* last unique number */
ulong this_loop; /* counter for this open */ ulong this_loop; /* counter for this open */