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:
Submodule libmariadb updated: 7de639518f...544b6f1d12
@@ -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`
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
27
sql/field.cc
27
sql/field.cc
@@ -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);
|
||||||
|
|||||||
23
sql/field.h
23
sql/field.h
@@ -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)))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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()); }
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 &&
|
||||||
|
|||||||
@@ -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()))
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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> ¬_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)
|
||||||
{
|
{
|
||||||
|
|||||||
28
sql/table.cc
28
sql/table.cc
@@ -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);
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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 */
|
||||||
|
|||||||
Reference in New Issue
Block a user