Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
M
MariaDB
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nexedi
MariaDB
Commits
35b00a18
Commit
35b00a18
authored
Nov 06, 2009
by
Alexander Nozdrin
Browse files
Options
Browse Files
Download
Plain Diff
Manual merge from mysql-trunk-merge.
parents
f5622359
409160e4
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
532 additions
and
2 deletions
+532
-2
mysql-test/r/innodb_lock_wait_timeout_1.result
mysql-test/r/innodb_lock_wait_timeout_1.result
+305
-0
mysql-test/t/innodb_lock_wait_timeout_1.test
mysql-test/t/innodb_lock_wait_timeout_1.test
+155
-0
sql/item_subselect.cc
sql/item_subselect.cc
+1
-0
sql/records.cc
sql/records.cc
+2
-0
sql/records.h
sql/records.h
+4
-0
sql/sql_select.cc
sql/sql_select.cc
+58
-2
sql/sql_select.h
sql/sql_select.h
+7
-0
No files found.
mysql-test/r/innodb_lock_wait_timeout_1.result
View file @
35b00a18
...
...
@@ -48,5 +48,310 @@ commit;
set autocommit=default;
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
#
mysql-test/t/innodb_lock_wait_timeout_1.test
View file @
35b00a18
...
...
@@ -70,6 +70,161 @@ commit;
set
autocommit
=
default
;
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
# End of 5.1 tests
--
echo
#
sql/item_subselect.cc
View file @
35b00a18
...
...
@@ -1956,6 +1956,7 @@ int subselect_single_select_engine::exec()
tab
->
read_record
.
record
=
tab
->
table
->
record
[
0
];
tab
->
read_record
.
thd
=
join
->
thd
;
tab
->
read_record
.
ref_length
=
tab
->
table
->
file
->
ref_length
;
tab
->
read_record
.
unlock_row
=
rr_unlock_row
;
*
(
last_changed_tab
++
)
=
tab
;
break
;
}
...
...
sql/records.cc
View file @
35b00a18
...
...
@@ -67,6 +67,7 @@ void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table,
info
->
file
=
table
->
file
;
info
->
record
=
table
->
record
[
0
];
info
->
print_error
=
print_error
;
info
->
unlock_row
=
rr_unlock_row
;
table
->
status
=
0
;
/* And it's always found */
if
(
!
table
->
file
->
inited
)
...
...
@@ -192,6 +193,7 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
}
info
->
select
=
select
;
info
->
print_error
=
print_error
;
info
->
unlock_row
=
rr_unlock_row
;
info
->
ignore_not_found_rows
=
0
;
table
->
status
=
0
;
/* And it's always found */
...
...
sql/records.h
View file @
35b00a18
...
...
@@ -43,11 +43,13 @@ class SQL_SELECT;
struct
READ_RECORD
{
typedef
int
(
*
Read_func
)(
READ_RECORD
*
);
typedef
void
(
*
Unlock_row_func
)(
st_join_table
*
);
typedef
int
(
*
Setup_func
)(
struct
st_join_table
*
);
TABLE
*
table
;
/* Head-form */
handler
*
file
;
TABLE
**
forms
;
/* head and ref forms */
Unlock_row_func
unlock_row
;
Read_func
read_record
;
THD
*
thd
;
SQL_SELECT
*
select
;
...
...
@@ -72,4 +74,6 @@ void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table,
bool
print_error
,
uint
idx
);
void
end_read_record
(
READ_RECORD
*
info
);
void
rr_unlock_row
(
st_join_table
*
tab
);
#endif
/* SQL_RECORDS_H */
sql/sql_select.cc
View file @
35b00a18
...
...
@@ -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_const
(
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_last_key
(
JOIN_TAB
*
tab
);
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_err
=
1
;
j
->
ref
.
has_record
=
FALSE
;
j
->
ref
.
null_rejecting
=
0
;
j
->
ref
.
use_count
=
0
;
keyuse
=
org_keyuse
;
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
);
}
/**
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
make_join_readinfo
(
JOIN
*
join
,
ulonglong
options
)
{
...
...
@@ -6584,6 +6601,7 @@ make_join_readinfo(JOIN *join, ulonglong options)
TABLE
*
table
=
tab
->
table
;
tab
->
read_record
.
table
=
table
;
tab
->
read_record
.
file
=
table
->
file
;
tab
->
read_record
.
unlock_row
=
rr_unlock_row
;
tab
->
next_select
=
sub_select
;
/* normal select */
/*
...
...
@@ -6629,6 +6647,7 @@ make_join_readinfo(JOIN *join, ulonglong options)
delete
tab
->
quick
;
tab
->
quick
=
0
;
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
;
if
(
table
->
covering_keys
.
is_set
(
tab
->
ref
.
key
)
&&
!
table
->
no_keyread
)
...
...
@@ -11472,7 +11491,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
else
{
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
...
...
@@ -11483,7 +11502,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
*/
join
->
examined_rows
++
;
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
;
}
...
...
@@ -11843,18 +11862,55 @@ join_read_key(JOIN_TAB *tab)
table
->
status
=
STATUS_NOT_FOUND
;
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
],
tab
->
ref
.
key_buff
,
make_prev_keypart_map
(
tab
->
ref
.
key_parts
),
HA_READ_KEY_EXACT
);
if
(
error
&&
error
!=
HA_ERR_KEY_NOT_FOUND
&&
error
!=
HA_ERR_END_OF_FILE
)
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
;
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
...
...
sql/sql_select.h
View file @
35b00a18
...
...
@@ -61,6 +61,8 @@ class store_key;
typedef
struct
st_table_ref
{
bool
key_err
;
/** True if something was read into buffer in join_read_key. */
bool
has_record
;
uint
key_parts
;
///< num of ...
uint
key_length
;
///< length of key_buff
int
key
;
///< key no
...
...
@@ -88,6 +90,11 @@ typedef struct st_table_ref
table_map
depend_map
;
///< Table depends on these tables.
/* null byte position in the key_buf. Used for REF_OR_NULL optimization */
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
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment