Manual merge from mysql-trunk-merge.
This commit is contained in:
commit
35b00a182e
@ -48,5 +48,310 @@ commit;
|
|||||||
set autocommit=default;
|
set autocommit=default;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
#
|
#
|
||||||
|
# Bug#41756 Strange error messages about locks from InnoDB
|
||||||
|
#
|
||||||
|
drop table if exists t1;
|
||||||
|
# In the default transaction isolation mode, and/or with
|
||||||
|
# innodb_locks_unsafe_for_binlog=OFF, handler::unlock_row()
|
||||||
|
# in InnoDB does nothing.
|
||||||
|
# Thus in order to reproduce the condition that led to the
|
||||||
|
# warning, one needs to relax isolation by either
|
||||||
|
# setting a weaker tx_isolation value, or by turning on
|
||||||
|
# the unsafe replication switch.
|
||||||
|
# For testing purposes, choose to tweak the isolation level,
|
||||||
|
# since it's settable at runtime, unlike
|
||||||
|
# innodb_locks_unsafe_for_binlog, which is
|
||||||
|
# only a command-line switch.
|
||||||
|
#
|
||||||
|
set @@session.tx_isolation="read-committed";
|
||||||
|
# Prepare data. We need a table with a unique index,
|
||||||
|
# for join_read_key to be used. The other column
|
||||||
|
# allows to control what passes WHERE clause filter.
|
||||||
|
create table t1 (a int primary key, b int) engine=innodb;
|
||||||
|
# Let's make sure t1 has sufficient amount of rows
|
||||||
|
# to exclude JT_ALL access method when reading it,
|
||||||
|
# i.e. make sure that JT_EQ_REF(a) is always preferred.
|
||||||
|
insert into t1 values (1,1), (2,null), (3,1), (4,1),
|
||||||
|
(5,1), (6,1), (7,1), (8,1), (9,1), (10,1),
|
||||||
|
(11,1), (12,1), (13,1), (14,1), (15,1),
|
||||||
|
(16,1), (17,1), (18,1), (19,1), (20,1);
|
||||||
|
#
|
||||||
|
# Demonstrate that for the SELECT statement
|
||||||
|
# used later in the test JT_EQ_REF access method is used.
|
||||||
|
#
|
||||||
|
explain
|
||||||
|
select 1 from t1 natural join (select 2 as a, 1 as b union all
|
||||||
|
select 2 as a, 2 as b) as t2 for update;
|
||||||
|
id 1
|
||||||
|
select_type PRIMARY
|
||||||
|
table <derived2>
|
||||||
|
type ALL
|
||||||
|
possible_keys NULL
|
||||||
|
key NULL
|
||||||
|
key_len NULL
|
||||||
|
ref NULL
|
||||||
|
rows 2
|
||||||
|
Extra
|
||||||
|
id 1
|
||||||
|
select_type PRIMARY
|
||||||
|
table t1
|
||||||
|
type eq_ref
|
||||||
|
possible_keys PRIMARY
|
||||||
|
key PRIMARY
|
||||||
|
key_len 4
|
||||||
|
ref t2.a
|
||||||
|
rows 1
|
||||||
|
Extra Using where
|
||||||
|
id 2
|
||||||
|
select_type DERIVED
|
||||||
|
table NULL
|
||||||
|
type NULL
|
||||||
|
possible_keys NULL
|
||||||
|
key NULL
|
||||||
|
key_len NULL
|
||||||
|
ref NULL
|
||||||
|
rows NULL
|
||||||
|
Extra No tables used
|
||||||
|
id 3
|
||||||
|
select_type UNION
|
||||||
|
table NULL
|
||||||
|
type NULL
|
||||||
|
possible_keys NULL
|
||||||
|
key NULL
|
||||||
|
key_len NULL
|
||||||
|
ref NULL
|
||||||
|
rows NULL
|
||||||
|
Extra No tables used
|
||||||
|
id NULL
|
||||||
|
select_type UNION RESULT
|
||||||
|
table <union2,3>
|
||||||
|
type ALL
|
||||||
|
possible_keys NULL
|
||||||
|
key NULL
|
||||||
|
key_len NULL
|
||||||
|
ref NULL
|
||||||
|
rows NULL
|
||||||
|
Extra
|
||||||
|
#
|
||||||
|
# Demonstrate that the reported SELECT statement
|
||||||
|
# no longer produces warnings.
|
||||||
|
#
|
||||||
|
select 1 from t1 natural join (select 2 as a, 1 as b union all
|
||||||
|
select 2 as a, 2 as b) as t2 for update;
|
||||||
|
1
|
||||||
|
commit;
|
||||||
|
#
|
||||||
|
# Demonstrate that due to lack of inter-sweep "reset" function,
|
||||||
|
# we keep some non-matching records locked, even though we know
|
||||||
|
# we could unlock them.
|
||||||
|
# To do that, show that if there is only one distinct value
|
||||||
|
# for a in t2 (a=2), we will keep record (2,null) in t1 locked.
|
||||||
|
# But if we add another value for "a" to t2, say 6,
|
||||||
|
# join_read_key cache will be pruned at least once,
|
||||||
|
# and thus record (2, null) in t1 will get unlocked.
|
||||||
|
#
|
||||||
|
begin;
|
||||||
|
select 1 from t1 natural join (select 2 as a, 1 as b union all
|
||||||
|
select 2 as a, 2 as b) as t2 for update;
|
||||||
|
1
|
||||||
|
#
|
||||||
|
# Switching to connection con1
|
||||||
|
# We should be able to delete all records from t1 except (2, null),
|
||||||
|
# since they were not locked.
|
||||||
|
begin;
|
||||||
|
# Delete in series of 3 records so that full scan
|
||||||
|
# is not used and we're not blocked on record (2,null)
|
||||||
|
delete from t1 where a in (1,3,4);
|
||||||
|
delete from t1 where a in (5,6,7);
|
||||||
|
delete from t1 where a in (8,9,10);
|
||||||
|
delete from t1 where a in (11,12,13);
|
||||||
|
delete from t1 where a in (14,15,16);
|
||||||
|
delete from t1 where a in (17,18);
|
||||||
|
delete from t1 where a in (19,20);
|
||||||
|
#
|
||||||
|
# Record (2, null) is locked. This is actually unnecessary,
|
||||||
|
# because the previous select returned no rows.
|
||||||
|
# Just demonstrate the effect.
|
||||||
|
#
|
||||||
|
delete from t1;
|
||||||
|
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
|
||||||
|
rollback;
|
||||||
|
#
|
||||||
|
# Switching to connection default
|
||||||
|
#
|
||||||
|
# Show that the original contents of t1 is intact:
|
||||||
|
select * from t1;
|
||||||
|
a b
|
||||||
|
1 1
|
||||||
|
2 NULL
|
||||||
|
3 1
|
||||||
|
4 1
|
||||||
|
5 1
|
||||||
|
6 1
|
||||||
|
7 1
|
||||||
|
8 1
|
||||||
|
9 1
|
||||||
|
10 1
|
||||||
|
11 1
|
||||||
|
12 1
|
||||||
|
13 1
|
||||||
|
14 1
|
||||||
|
15 1
|
||||||
|
16 1
|
||||||
|
17 1
|
||||||
|
18 1
|
||||||
|
19 1
|
||||||
|
20 1
|
||||||
|
commit;
|
||||||
|
#
|
||||||
|
# Have a one more record in t2 to show that
|
||||||
|
# if join_read_key cache is purned, the current
|
||||||
|
# row under the cursor is unlocked (provided, this row didn't
|
||||||
|
# match the partial WHERE clause, of course).
|
||||||
|
# Sic: the result of this test dependent on the order of retrieval
|
||||||
|
# of records --echo # from the derived table, if !
|
||||||
|
# We use DELETE to disable the JOIN CACHE. This DELETE modifies no
|
||||||
|
# records. It also should leave no InnoDB row locks.
|
||||||
|
#
|
||||||
|
begin;
|
||||||
|
delete t1.* from t1 natural join (select 2 as a, 2 as b union all
|
||||||
|
select 0 as a, 0 as b) as t2;
|
||||||
|
# Demonstrate that nothing was deleted form t1
|
||||||
|
select * from t1;
|
||||||
|
a b
|
||||||
|
1 1
|
||||||
|
2 NULL
|
||||||
|
3 1
|
||||||
|
4 1
|
||||||
|
5 1
|
||||||
|
6 1
|
||||||
|
7 1
|
||||||
|
8 1
|
||||||
|
9 1
|
||||||
|
10 1
|
||||||
|
11 1
|
||||||
|
12 1
|
||||||
|
13 1
|
||||||
|
14 1
|
||||||
|
15 1
|
||||||
|
16 1
|
||||||
|
17 1
|
||||||
|
18 1
|
||||||
|
19 1
|
||||||
|
20 1
|
||||||
|
#
|
||||||
|
# Switching to connection con1
|
||||||
|
begin;
|
||||||
|
# Since there is another distinct record in the derived table
|
||||||
|
# the previous matching record in t1 -- (2,null) -- was unlocked.
|
||||||
|
delete from t1;
|
||||||
|
# We will need the contents of the table again.
|
||||||
|
rollback;
|
||||||
|
select * from t1;
|
||||||
|
a b
|
||||||
|
1 1
|
||||||
|
2 NULL
|
||||||
|
3 1
|
||||||
|
4 1
|
||||||
|
5 1
|
||||||
|
6 1
|
||||||
|
7 1
|
||||||
|
8 1
|
||||||
|
9 1
|
||||||
|
10 1
|
||||||
|
11 1
|
||||||
|
12 1
|
||||||
|
13 1
|
||||||
|
14 1
|
||||||
|
15 1
|
||||||
|
16 1
|
||||||
|
17 1
|
||||||
|
18 1
|
||||||
|
19 1
|
||||||
|
20 1
|
||||||
|
commit;
|
||||||
|
#
|
||||||
|
# Switching to connection default
|
||||||
|
rollback;
|
||||||
|
begin;
|
||||||
|
#
|
||||||
|
# Before this patch, we could wrongly unlock a record
|
||||||
|
# that was cached and later used in a join. Demonstrate that
|
||||||
|
# this is no longer the case.
|
||||||
|
# Sic: this test is also order-dependent (i.e. the
|
||||||
|
# the bug would show up only if the first record in the union
|
||||||
|
# is retreived and processed first.
|
||||||
|
#
|
||||||
|
# Verify that JT_EQ_REF is used.
|
||||||
|
explain
|
||||||
|
select 1 from t1 natural join (select 3 as a, 2 as b union all
|
||||||
|
select 3 as a, 1 as b) as t2 for update;
|
||||||
|
id 1
|
||||||
|
select_type PRIMARY
|
||||||
|
table <derived2>
|
||||||
|
type ALL
|
||||||
|
possible_keys NULL
|
||||||
|
key NULL
|
||||||
|
key_len NULL
|
||||||
|
ref NULL
|
||||||
|
rows 2
|
||||||
|
Extra
|
||||||
|
id 1
|
||||||
|
select_type PRIMARY
|
||||||
|
table t1
|
||||||
|
type eq_ref
|
||||||
|
possible_keys PRIMARY
|
||||||
|
key PRIMARY
|
||||||
|
key_len 4
|
||||||
|
ref t2.a
|
||||||
|
rows 1
|
||||||
|
Extra Using where
|
||||||
|
id 2
|
||||||
|
select_type DERIVED
|
||||||
|
table NULL
|
||||||
|
type NULL
|
||||||
|
possible_keys NULL
|
||||||
|
key NULL
|
||||||
|
key_len NULL
|
||||||
|
ref NULL
|
||||||
|
rows NULL
|
||||||
|
Extra No tables used
|
||||||
|
id 3
|
||||||
|
select_type UNION
|
||||||
|
table NULL
|
||||||
|
type NULL
|
||||||
|
possible_keys NULL
|
||||||
|
key NULL
|
||||||
|
key_len NULL
|
||||||
|
ref NULL
|
||||||
|
rows NULL
|
||||||
|
Extra No tables used
|
||||||
|
id NULL
|
||||||
|
select_type UNION RESULT
|
||||||
|
table <union2,3>
|
||||||
|
type ALL
|
||||||
|
possible_keys NULL
|
||||||
|
key NULL
|
||||||
|
key_len NULL
|
||||||
|
ref NULL
|
||||||
|
rows NULL
|
||||||
|
Extra
|
||||||
|
# Lock the record.
|
||||||
|
select 1 from t1 natural join (select 3 as a, 2 as b union all
|
||||||
|
select 3 as a, 1 as b) as t2 for update;
|
||||||
|
1
|
||||||
|
1
|
||||||
|
# Switching to connection con1
|
||||||
|
#
|
||||||
|
# We should not be able to delete record (3,1) from t1,
|
||||||
|
# (previously it was possible).
|
||||||
|
#
|
||||||
|
delete from t1 where a=3;
|
||||||
|
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
|
||||||
|
# Switching to connection default
|
||||||
|
commit;
|
||||||
|
set @@session.tx_isolation=default;
|
||||||
|
drop table t1;
|
||||||
|
#
|
||||||
# End of 5.1 tests
|
# End of 5.1 tests
|
||||||
#
|
#
|
||||||
|
@ -70,6 +70,161 @@ commit;
|
|||||||
set autocommit=default;
|
set autocommit=default;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Bug#41756 Strange error messages about locks from InnoDB
|
||||||
|
--echo #
|
||||||
|
--disable_warnings
|
||||||
|
drop table if exists t1;
|
||||||
|
--enable_warnings
|
||||||
|
--echo # In the default transaction isolation mode, and/or with
|
||||||
|
--echo # innodb_locks_unsafe_for_binlog=OFF, handler::unlock_row()
|
||||||
|
--echo # in InnoDB does nothing.
|
||||||
|
--echo # Thus in order to reproduce the condition that led to the
|
||||||
|
--echo # warning, one needs to relax isolation by either
|
||||||
|
--echo # setting a weaker tx_isolation value, or by turning on
|
||||||
|
--echo # the unsafe replication switch.
|
||||||
|
--echo # For testing purposes, choose to tweak the isolation level,
|
||||||
|
--echo # since it's settable at runtime, unlike
|
||||||
|
--echo # innodb_locks_unsafe_for_binlog, which is
|
||||||
|
--echo # only a command-line switch.
|
||||||
|
--echo #
|
||||||
|
set @@session.tx_isolation="read-committed";
|
||||||
|
|
||||||
|
--echo # Prepare data. We need a table with a unique index,
|
||||||
|
--echo # for join_read_key to be used. The other column
|
||||||
|
--echo # allows to control what passes WHERE clause filter.
|
||||||
|
create table t1 (a int primary key, b int) engine=innodb;
|
||||||
|
--echo # Let's make sure t1 has sufficient amount of rows
|
||||||
|
--echo # to exclude JT_ALL access method when reading it,
|
||||||
|
--echo # i.e. make sure that JT_EQ_REF(a) is always preferred.
|
||||||
|
insert into t1 values (1,1), (2,null), (3,1), (4,1),
|
||||||
|
(5,1), (6,1), (7,1), (8,1), (9,1), (10,1),
|
||||||
|
(11,1), (12,1), (13,1), (14,1), (15,1),
|
||||||
|
(16,1), (17,1), (18,1), (19,1), (20,1);
|
||||||
|
--echo #
|
||||||
|
--echo # Demonstrate that for the SELECT statement
|
||||||
|
--echo # used later in the test JT_EQ_REF access method is used.
|
||||||
|
--echo #
|
||||||
|
--vertical_results
|
||||||
|
explain
|
||||||
|
select 1 from t1 natural join (select 2 as a, 1 as b union all
|
||||||
|
select 2 as a, 2 as b) as t2 for update;
|
||||||
|
--horizontal_results
|
||||||
|
--echo #
|
||||||
|
--echo # Demonstrate that the reported SELECT statement
|
||||||
|
--echo # no longer produces warnings.
|
||||||
|
--echo #
|
||||||
|
select 1 from t1 natural join (select 2 as a, 1 as b union all
|
||||||
|
select 2 as a, 2 as b) as t2 for update;
|
||||||
|
commit;
|
||||||
|
--echo #
|
||||||
|
--echo # Demonstrate that due to lack of inter-sweep "reset" function,
|
||||||
|
--echo # we keep some non-matching records locked, even though we know
|
||||||
|
--echo # we could unlock them.
|
||||||
|
--echo # To do that, show that if there is only one distinct value
|
||||||
|
--echo # for a in t2 (a=2), we will keep record (2,null) in t1 locked.
|
||||||
|
--echo # But if we add another value for "a" to t2, say 6,
|
||||||
|
--echo # join_read_key cache will be pruned at least once,
|
||||||
|
--echo # and thus record (2, null) in t1 will get unlocked.
|
||||||
|
--echo #
|
||||||
|
begin;
|
||||||
|
select 1 from t1 natural join (select 2 as a, 1 as b union all
|
||||||
|
select 2 as a, 2 as b) as t2 for update;
|
||||||
|
connect (con1,localhost,root,,);
|
||||||
|
--echo #
|
||||||
|
--echo # Switching to connection con1
|
||||||
|
connection con1;
|
||||||
|
--echo # We should be able to delete all records from t1 except (2, null),
|
||||||
|
--echo # since they were not locked.
|
||||||
|
begin;
|
||||||
|
--echo # Delete in series of 3 records so that full scan
|
||||||
|
--echo # is not used and we're not blocked on record (2,null)
|
||||||
|
delete from t1 where a in (1,3,4);
|
||||||
|
delete from t1 where a in (5,6,7);
|
||||||
|
delete from t1 where a in (8,9,10);
|
||||||
|
delete from t1 where a in (11,12,13);
|
||||||
|
delete from t1 where a in (14,15,16);
|
||||||
|
delete from t1 where a in (17,18);
|
||||||
|
delete from t1 where a in (19,20);
|
||||||
|
--echo #
|
||||||
|
--echo # Record (2, null) is locked. This is actually unnecessary,
|
||||||
|
--echo # because the previous select returned no rows.
|
||||||
|
--echo # Just demonstrate the effect.
|
||||||
|
--echo #
|
||||||
|
--error ER_LOCK_WAIT_TIMEOUT
|
||||||
|
delete from t1;
|
||||||
|
rollback;
|
||||||
|
--echo #
|
||||||
|
--echo # Switching to connection default
|
||||||
|
connection default;
|
||||||
|
--echo #
|
||||||
|
--echo # Show that the original contents of t1 is intact:
|
||||||
|
select * from t1;
|
||||||
|
commit;
|
||||||
|
--echo #
|
||||||
|
--echo # Have a one more record in t2 to show that
|
||||||
|
--echo # if join_read_key cache is purned, the current
|
||||||
|
--echo # row under the cursor is unlocked (provided, this row didn't
|
||||||
|
--echo # match the partial WHERE clause, of course).
|
||||||
|
--echo # Sic: the result of this test dependent on the order of retrieval
|
||||||
|
--echo # of records --echo # from the derived table, if !
|
||||||
|
--echo # We use DELETE to disable the JOIN CACHE. This DELETE modifies no
|
||||||
|
--echo # records. It also should leave no InnoDB row locks.
|
||||||
|
--echo #
|
||||||
|
begin;
|
||||||
|
delete t1.* from t1 natural join (select 2 as a, 2 as b union all
|
||||||
|
select 0 as a, 0 as b) as t2;
|
||||||
|
--echo # Demonstrate that nothing was deleted form t1
|
||||||
|
select * from t1;
|
||||||
|
--echo #
|
||||||
|
--echo # Switching to connection con1
|
||||||
|
connection con1;
|
||||||
|
begin;
|
||||||
|
--echo # Since there is another distinct record in the derived table
|
||||||
|
--echo # the previous matching record in t1 -- (2,null) -- was unlocked.
|
||||||
|
delete from t1;
|
||||||
|
--echo # We will need the contents of the table again.
|
||||||
|
rollback;
|
||||||
|
select * from t1;
|
||||||
|
commit;
|
||||||
|
--echo #
|
||||||
|
--echo # Switching to connection default
|
||||||
|
connection default;
|
||||||
|
rollback;
|
||||||
|
begin;
|
||||||
|
--echo #
|
||||||
|
--echo # Before this patch, we could wrongly unlock a record
|
||||||
|
--echo # that was cached and later used in a join. Demonstrate that
|
||||||
|
--echo # this is no longer the case.
|
||||||
|
--echo # Sic: this test is also order-dependent (i.e. the
|
||||||
|
--echo # the bug would show up only if the first record in the union
|
||||||
|
--echo # is retreived and processed first.
|
||||||
|
--echo #
|
||||||
|
--echo # Verify that JT_EQ_REF is used.
|
||||||
|
--vertical_results
|
||||||
|
explain
|
||||||
|
select 1 from t1 natural join (select 3 as a, 2 as b union all
|
||||||
|
select 3 as a, 1 as b) as t2 for update;
|
||||||
|
--horizontal_results
|
||||||
|
--echo # Lock the record.
|
||||||
|
select 1 from t1 natural join (select 3 as a, 2 as b union all
|
||||||
|
select 3 as a, 1 as b) as t2 for update;
|
||||||
|
--echo # Switching to connection con1
|
||||||
|
connection con1;
|
||||||
|
--echo #
|
||||||
|
--echo # We should not be able to delete record (3,1) from t1,
|
||||||
|
--echo # (previously it was possible).
|
||||||
|
--echo #
|
||||||
|
--error ER_LOCK_WAIT_TIMEOUT
|
||||||
|
delete from t1 where a=3;
|
||||||
|
--echo # Switching to connection default
|
||||||
|
connection default;
|
||||||
|
commit;
|
||||||
|
|
||||||
|
disconnect con1;
|
||||||
|
set @@session.tx_isolation=default;
|
||||||
|
drop table t1;
|
||||||
|
|
||||||
--echo #
|
--echo #
|
||||||
--echo # End of 5.1 tests
|
--echo # End of 5.1 tests
|
||||||
--echo #
|
--echo #
|
||||||
|
@ -1956,6 +1956,7 @@ int subselect_single_select_engine::exec()
|
|||||||
tab->read_record.record= tab->table->record[0];
|
tab->read_record.record= tab->table->record[0];
|
||||||
tab->read_record.thd= join->thd;
|
tab->read_record.thd= join->thd;
|
||||||
tab->read_record.ref_length= tab->table->file->ref_length;
|
tab->read_record.ref_length= tab->table->file->ref_length;
|
||||||
|
tab->read_record.unlock_row= rr_unlock_row;
|
||||||
*(last_changed_tab++)= tab;
|
*(last_changed_tab++)= tab;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -67,6 +67,7 @@ void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table,
|
|||||||
info->file= table->file;
|
info->file= table->file;
|
||||||
info->record= table->record[0];
|
info->record= table->record[0];
|
||||||
info->print_error= print_error;
|
info->print_error= print_error;
|
||||||
|
info->unlock_row= rr_unlock_row;
|
||||||
|
|
||||||
table->status=0; /* And it's always found */
|
table->status=0; /* And it's always found */
|
||||||
if (!table->file->inited)
|
if (!table->file->inited)
|
||||||
@ -192,6 +193,7 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
|
|||||||
}
|
}
|
||||||
info->select=select;
|
info->select=select;
|
||||||
info->print_error=print_error;
|
info->print_error=print_error;
|
||||||
|
info->unlock_row= rr_unlock_row;
|
||||||
info->ignore_not_found_rows= 0;
|
info->ignore_not_found_rows= 0;
|
||||||
table->status=0; /* And it's always found */
|
table->status=0; /* And it's always found */
|
||||||
|
|
||||||
|
@ -43,11 +43,13 @@ class SQL_SELECT;
|
|||||||
struct READ_RECORD
|
struct READ_RECORD
|
||||||
{
|
{
|
||||||
typedef int (*Read_func)(READ_RECORD*);
|
typedef int (*Read_func)(READ_RECORD*);
|
||||||
|
typedef void (*Unlock_row_func)(st_join_table *);
|
||||||
typedef int (*Setup_func)(struct st_join_table*);
|
typedef int (*Setup_func)(struct st_join_table*);
|
||||||
|
|
||||||
TABLE *table; /* Head-form */
|
TABLE *table; /* Head-form */
|
||||||
handler *file;
|
handler *file;
|
||||||
TABLE **forms; /* head and ref forms */
|
TABLE **forms; /* head and ref forms */
|
||||||
|
Unlock_row_func unlock_row;
|
||||||
Read_func read_record;
|
Read_func read_record;
|
||||||
THD *thd;
|
THD *thd;
|
||||||
SQL_SELECT *select;
|
SQL_SELECT *select;
|
||||||
@ -72,4 +74,6 @@ void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table,
|
|||||||
bool print_error, uint idx);
|
bool print_error, uint idx);
|
||||||
void end_read_record(READ_RECORD *info);
|
void end_read_record(READ_RECORD *info);
|
||||||
|
|
||||||
|
void rr_unlock_row(st_join_table *tab);
|
||||||
|
|
||||||
#endif /* SQL_RECORDS_H */
|
#endif /* SQL_RECORDS_H */
|
||||||
|
@ -150,6 +150,7 @@ static int join_read_const_table(JOIN_TAB *tab, POSITION *pos);
|
|||||||
static int join_read_system(JOIN_TAB *tab);
|
static int join_read_system(JOIN_TAB *tab);
|
||||||
static int join_read_const(JOIN_TAB *tab);
|
static int join_read_const(JOIN_TAB *tab);
|
||||||
static int join_read_key(JOIN_TAB *tab);
|
static int join_read_key(JOIN_TAB *tab);
|
||||||
|
static void join_read_key_unlock_row(st_join_table *tab);
|
||||||
static int join_read_always_key(JOIN_TAB *tab);
|
static int join_read_always_key(JOIN_TAB *tab);
|
||||||
static int join_read_last_key(JOIN_TAB *tab);
|
static int join_read_last_key(JOIN_TAB *tab);
|
||||||
static int join_no_more_records(READ_RECORD *info);
|
static int join_no_more_records(READ_RECORD *info);
|
||||||
@ -5736,7 +5737,9 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse,
|
|||||||
}
|
}
|
||||||
j->ref.key_buff2=j->ref.key_buff+ALIGN_SIZE(length);
|
j->ref.key_buff2=j->ref.key_buff+ALIGN_SIZE(length);
|
||||||
j->ref.key_err=1;
|
j->ref.key_err=1;
|
||||||
|
j->ref.has_record= FALSE;
|
||||||
j->ref.null_rejecting= 0;
|
j->ref.null_rejecting= 0;
|
||||||
|
j->ref.use_count= 0;
|
||||||
keyuse=org_keyuse;
|
keyuse=org_keyuse;
|
||||||
|
|
||||||
store_key **ref_key= j->ref.key_copy;
|
store_key **ref_key= j->ref.key_copy;
|
||||||
@ -6569,6 +6572,20 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
|
|||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
The default implementation of unlock-row method of READ_RECORD,
|
||||||
|
used in all access methods.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void rr_unlock_row(st_join_table *tab)
|
||||||
|
{
|
||||||
|
READ_RECORD *info= &tab->read_record;
|
||||||
|
info->file->unlock_row();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
make_join_readinfo(JOIN *join, ulonglong options)
|
make_join_readinfo(JOIN *join, ulonglong options)
|
||||||
{
|
{
|
||||||
@ -6584,6 +6601,7 @@ make_join_readinfo(JOIN *join, ulonglong options)
|
|||||||
TABLE *table=tab->table;
|
TABLE *table=tab->table;
|
||||||
tab->read_record.table= table;
|
tab->read_record.table= table;
|
||||||
tab->read_record.file=table->file;
|
tab->read_record.file=table->file;
|
||||||
|
tab->read_record.unlock_row= rr_unlock_row;
|
||||||
tab->next_select=sub_select; /* normal select */
|
tab->next_select=sub_select; /* normal select */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -6629,6 +6647,7 @@ make_join_readinfo(JOIN *join, ulonglong options)
|
|||||||
delete tab->quick;
|
delete tab->quick;
|
||||||
tab->quick=0;
|
tab->quick=0;
|
||||||
tab->read_first_record= join_read_key;
|
tab->read_first_record= join_read_key;
|
||||||
|
tab->read_record.unlock_row= join_read_key_unlock_row;
|
||||||
tab->read_record.read_record= join_no_more_records;
|
tab->read_record.read_record= join_no_more_records;
|
||||||
if (table->covering_keys.is_set(tab->ref.key) &&
|
if (table->covering_keys.is_set(tab->ref.key) &&
|
||||||
!table->no_keyread)
|
!table->no_keyread)
|
||||||
@ -11472,7 +11491,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
join->thd->warning_info->inc_current_row_for_warning();
|
join->thd->warning_info->inc_current_row_for_warning();
|
||||||
join_tab->read_record.file->unlock_row();
|
join_tab->read_record.unlock_row(join_tab);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -11483,7 +11502,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
|
|||||||
*/
|
*/
|
||||||
join->examined_rows++;
|
join->examined_rows++;
|
||||||
join->thd->warning_info->inc_current_row_for_warning();
|
join->thd->warning_info->inc_current_row_for_warning();
|
||||||
join_tab->read_record.file->unlock_row();
|
join_tab->read_record.unlock_row(join_tab);
|
||||||
}
|
}
|
||||||
return NESTED_LOOP_OK;
|
return NESTED_LOOP_OK;
|
||||||
}
|
}
|
||||||
@ -11843,18 +11862,55 @@ join_read_key(JOIN_TAB *tab)
|
|||||||
table->status=STATUS_NOT_FOUND;
|
table->status=STATUS_NOT_FOUND;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
Moving away from the current record. Unlock the row
|
||||||
|
in the handler if it did not match the partial WHERE.
|
||||||
|
*/
|
||||||
|
if (tab->ref.has_record && tab->ref.use_count == 0)
|
||||||
|
{
|
||||||
|
tab->read_record.file->unlock_row();
|
||||||
|
tab->ref.has_record= FALSE;
|
||||||
|
}
|
||||||
error=table->file->index_read_map(table->record[0],
|
error=table->file->index_read_map(table->record[0],
|
||||||
tab->ref.key_buff,
|
tab->ref.key_buff,
|
||||||
make_prev_keypart_map(tab->ref.key_parts),
|
make_prev_keypart_map(tab->ref.key_parts),
|
||||||
HA_READ_KEY_EXACT);
|
HA_READ_KEY_EXACT);
|
||||||
if (error && error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
|
if (error && error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
|
||||||
return report_error(table, error);
|
return report_error(table, error);
|
||||||
|
|
||||||
|
if (! error)
|
||||||
|
{
|
||||||
|
tab->ref.has_record= TRUE;
|
||||||
|
tab->ref.use_count= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (table->status == 0)
|
||||||
|
{
|
||||||
|
DBUG_ASSERT(tab->ref.has_record);
|
||||||
|
tab->ref.use_count++;
|
||||||
}
|
}
|
||||||
table->null_row=0;
|
table->null_row=0;
|
||||||
return table->status ? -1 : 0;
|
return table->status ? -1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Since join_read_key may buffer a record, do not unlock
|
||||||
|
it if it was not used in this invocation of join_read_key().
|
||||||
|
Only count locks, thus remembering if the record was left unused,
|
||||||
|
and unlock already when pruning the current value of
|
||||||
|
TABLE_REF buffer.
|
||||||
|
@sa join_read_key()
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void
|
||||||
|
join_read_key_unlock_row(st_join_table *tab)
|
||||||
|
{
|
||||||
|
DBUG_ASSERT(tab->ref.use_count);
|
||||||
|
if (tab->ref.use_count)
|
||||||
|
tab->ref.use_count--;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
ref access method implementation: "read_first" function
|
ref access method implementation: "read_first" function
|
||||||
|
|
||||||
|
@ -61,6 +61,8 @@ class store_key;
|
|||||||
typedef struct st_table_ref
|
typedef struct st_table_ref
|
||||||
{
|
{
|
||||||
bool key_err;
|
bool key_err;
|
||||||
|
/** True if something was read into buffer in join_read_key. */
|
||||||
|
bool has_record;
|
||||||
uint key_parts; ///< num of ...
|
uint key_parts; ///< num of ...
|
||||||
uint key_length; ///< length of key_buff
|
uint key_length; ///< length of key_buff
|
||||||
int key; ///< key no
|
int key; ///< key no
|
||||||
@ -88,6 +90,11 @@ typedef struct st_table_ref
|
|||||||
table_map depend_map; ///< Table depends on these tables.
|
table_map depend_map; ///< Table depends on these tables.
|
||||||
/* null byte position in the key_buf. Used for REF_OR_NULL optimization */
|
/* null byte position in the key_buf. Used for REF_OR_NULL optimization */
|
||||||
uchar *null_ref_key;
|
uchar *null_ref_key;
|
||||||
|
/*
|
||||||
|
The number of times the record associated with this key was used
|
||||||
|
in the join.
|
||||||
|
*/
|
||||||
|
ha_rows use_count;
|
||||||
} TABLE_REF;
|
} TABLE_REF;
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user