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
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
mariadb
Commits
675a4e8f
Commit
675a4e8f
authored
Jul 12, 2007
by
kostja@bodhi.(none)
Browse files
Options
Browse Files
Download
Plain Diff
Merge bk-internal.mysql.com:/home/bk/mysql-5.0-runtime
into bodhi.(none):/opt/local/work/mysql-5.0-26141-final
parents
825b5096
5ab4b6f1
Changes
18
Hide whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
1255 additions
and
58 deletions
+1255
-58
mysql-test/r/trigger-trans.result
mysql-test/r/trigger-trans.result
+59
-0
mysql-test/r/trigger.result
mysql-test/r/trigger.result
+457
-0
mysql-test/t/trigger-trans.test
mysql-test/t/trigger-trans.test
+81
-1
mysql-test/t/trigger.test
mysql-test/t/trigger.test
+365
-0
sql/item.h
sql/item.h
+0
-8
sql/sp.cc
sql/sp.cc
+37
-16
sql/sp_head.cc
sql/sp_head.cc
+44
-9
sql/sp_head.h
sql/sp_head.h
+6
-0
sql/sql_lex.cc
sql/sql_lex.cc
+21
-0
sql/sql_lex.h
sql/sql_lex.h
+2
-0
sql/sql_parse.cc
sql/sql_parse.cc
+3
-2
sql/sql_prepare.cc
sql/sql_prepare.cc
+1
-0
sql/sql_trigger.cc
sql/sql_trigger.cc
+16
-12
sql/sql_trigger.h
sql/sql_trigger.h
+0
-8
sql/sql_view.cc
sql/sql_view.cc
+13
-0
sql/sql_yacc.yy
sql/sql_yacc.yy
+2
-2
sql/table.cc
sql/table.cc
+129
-0
sql/table.h
sql/table.h
+19
-0
No files found.
mysql-test/r/trigger-trans.result
View file @
675a4e8f
...
@@ -82,3 +82,62 @@ ALICE 33 1 0
...
@@ -82,3 +82,62 @@ ALICE 33 1 0
THE CROWN 43 1 0
THE CROWN 43 1 0
THE PIE 53 1 1
THE PIE 53 1 1
drop table t1;
drop table t1;
Bug#26141 mixing table types in trigger causes full
table lock on innodb table
Ensure we do not open and lock tables for the triggers we do not
fire.
drop table if exists t1, t2, t3;
drop trigger if exists trg_bug26141_au;
drop trigger if exists trg_bug26141_ai;
create table t1 (c int primary key) engine=innodb;
create table t2 (c int) engine=myisam;
create table t3 (c int) engine=myisam;
insert into t1 (c) values (1);
create trigger trg_bug26141_ai after insert on t1
for each row
begin
insert into t2 (c) values (1);
# We need the 'sync' lock to synchronously wait in connection 2 till
# the moment when the trigger acquired all the locks.
select release_lock("lock_bug26141_sync") into @a;
# 1000 is time in seconds of lock wait timeout -- this is a way
# to cause a manageable sleep up to 1000 seconds
select get_lock("lock_bug26141_wait", 1000) into @a;
end|
create trigger trg_bug26141_au after update on t1
for each row
begin
insert into t3 (c) values (1);
end|
select get_lock("lock_bug26141_wait", 0);
get_lock("lock_bug26141_wait", 0)
1
select get_lock("lock_bug26141_sync", /* must not be priorly locked */ 0);
get_lock("lock_bug26141_sync", /* must not be priorly locked */ 0)
1
insert into t1 (c) values (2);
select get_lock("lock_bug26141_sync", 1000);
get_lock("lock_bug26141_sync", 1000)
1
update t1 set c=3 where c=1;
select release_lock("lock_bug26141_sync");
release_lock("lock_bug26141_sync")
1
select release_lock("lock_bug26141_wait");
release_lock("lock_bug26141_wait")
1
select * from t1;
c
2
3
select * from t2;
c
1
select * from t3;
c
1
drop table t1, t2, t3;
End of 5.0 tests
mysql-test/r/trigger.result
View file @
675a4e8f
...
@@ -1476,4 +1476,461 @@ DROP TRIGGER t1_test;
...
@@ -1476,4 +1476,461 @@ DROP TRIGGER t1_test;
DROP TABLE t1,t2;
DROP TABLE t1,t2;
SET SESSION LOW_PRIORITY_UPDATES=DEFAULT;
SET SESSION LOW_PRIORITY_UPDATES=DEFAULT;
SET GLOBAL LOW_PRIORITY_UPDATES=DEFAULT;
SET GLOBAL LOW_PRIORITY_UPDATES=DEFAULT;
Bug#28502 Triggers that update another innodb table will block
on X lock unnecessarily
Ensure we do not open and lock tables for triggers we do not fire.
drop table if exists t1, t2;
drop trigger if exists trg_bug28502_au;
create table t1 (id int, count int);
create table t2 (id int);
create trigger trg_bug28502_au before update on t2
for each row
begin
if (new.id is not null) then
update t1 set count= count + 1 where id = old.id;
end if;
end|
insert into t1 (id, count) values (1, 0);
lock table t1 write;
insert into t2 set id=1;
unlock tables;
update t2 set id=1 where id=1;
select * from t1;
id count
1 1
select * from t2;
id
1
drop table t1, t2;
Additionally, provide test coverage for triggers and
all MySQL data changing commands.
drop table if exists t1, t2, t1_op_log;
drop view if exists v1;
drop trigger if exists trg_bug28502_bi;
drop trigger if exists trg_bug28502_ai;
drop trigger if exists trg_bug28502_bu;
drop trigger if exists trg_bug28502_au;
drop trigger if exists trg_bug28502_bd;
drop trigger if exists trg_bug28502_ad;
create table t1 (id int primary key auto_increment, operation varchar(255));
create table t2 (id int primary key);
create table t1_op_log(operation varchar(255));
create view v1 as select * from t1;
create trigger trg_bug28502_bi before insert on t1
for each row
insert into t1_op_log (operation)
values (concat("Before INSERT, new=", new.operation));
create trigger trg_bug28502_ai after insert on t1
for each row
insert into t1_op_log (operation)
values (concat("After INSERT, new=", new.operation));
create trigger trg_bug28502_bu before update on t1
for each row
insert into t1_op_log (operation)
values (concat("Before UPDATE, new=", new.operation,
", old=", old.operation));
create trigger trg_bug28502_au after update on t1
for each row
insert into t1_op_log (operation)
values (concat("After UPDATE, new=", new.operation,
", old=", old.operation));
create trigger trg_bug28502_bd before delete on t1
for each row
insert into t1_op_log (operation)
values (concat("Before DELETE, old=", old.operation));
create trigger trg_bug28502_ad after delete on t1
for each row
insert into t1_op_log (operation)
values (concat("After DELETE, old=", old.operation));
insert into t1 (operation) values ("INSERT");
set @id=last_insert_id();
select * from t1;
id operation
1 INSERT
select * from t1_op_log;
operation
Before INSERT, new=INSERT
After INSERT, new=INSERT
truncate t1_op_log;
update t1 set operation="UPDATE" where id=@id;
select * from t1;
id operation
1 UPDATE
select * from t1_op_log;
operation
Before UPDATE, new=UPDATE, old=INSERT
After UPDATE, new=UPDATE, old=INSERT
truncate t1_op_log;
delete from t1 where id=@id;
select * from t1;
id operation
select * from t1_op_log;
operation
Before DELETE, old=UPDATE
After DELETE, old=UPDATE
truncate t1;
truncate t1_op_log;
insert into t1 (id, operation) values
(NULL, "INSERT ON DUPLICATE KEY UPDATE, inserting a new key")
on duplicate key update id=NULL, operation="Should never happen";
set @id=last_insert_id();
select * from t1;
id operation
1 INSERT ON DUPLICATE KEY UPDATE, inserting a new key
select * from t1_op_log;
operation
Before INSERT, new=INSERT ON DUPLICATE KEY UPDATE, inserting a new key
After INSERT, new=INSERT ON DUPLICATE KEY UPDATE, inserting a new key
truncate t1_op_log;
insert into t1 (id, operation) values
(@id, "INSERT ON DUPLICATE KEY UPDATE, the key value is the same")
on duplicate key update id=NULL,
operation="INSERT ON DUPLICATE KEY UPDATE, updating the duplicate";
select * from t1;
id operation
0 INSERT ON DUPLICATE KEY UPDATE, updating the duplicate
select * from t1_op_log;
operation
Before INSERT, new=INSERT ON DUPLICATE KEY UPDATE, the key value is the same
Before UPDATE, new=INSERT ON DUPLICATE KEY UPDATE, updating the duplicate, old=INSERT ON DUPLICATE KEY UPDATE, inserting a new key
After UPDATE, new=INSERT ON DUPLICATE KEY UPDATE, updating the duplicate, old=INSERT ON DUPLICATE KEY UPDATE, inserting a new key
truncate t1;
truncate t1_op_log;
replace into t1 values (NULL, "REPLACE, inserting a new key");
set @id=last_insert_id();
select * from t1;
id operation
1 REPLACE, inserting a new key
select * from t1_op_log;
operation
Before INSERT, new=REPLACE, inserting a new key
After INSERT, new=REPLACE, inserting a new key
truncate t1_op_log;
replace into t1 values (@id, "REPLACE, deleting the duplicate");
select * from t1;
id operation
1 REPLACE, deleting the duplicate
select * from t1_op_log;
operation
Before INSERT, new=REPLACE, deleting the duplicate
Before DELETE, old=REPLACE, inserting a new key
After DELETE, old=REPLACE, inserting a new key
After INSERT, new=REPLACE, deleting the duplicate
truncate t1;
truncate t1_op_log;
create table if not exists t1
select NULL, "CREATE TABLE ... SELECT, inserting a new key";
Warnings:
Note 1050 Table 't1' already exists
set @id=last_insert_id();
select * from t1;
id operation
1 CREATE TABLE ... SELECT, inserting a new key
select * from t1_op_log;
operation
Before INSERT, new=CREATE TABLE ... SELECT, inserting a new key
After INSERT, new=CREATE TABLE ... SELECT, inserting a new key
truncate t1_op_log;
create table if not exists t1 replace
select @id, "CREATE TABLE ... REPLACE SELECT, deleting a duplicate key";
Warnings:
Note 1050 Table 't1' already exists
select * from t1;
id operation
1 CREATE TABLE ... REPLACE SELECT, deleting a duplicate key
select * from t1_op_log;
operation
Before INSERT, new=CREATE TABLE ... REPLACE SELECT, deleting a duplicate key
Before DELETE, old=CREATE TABLE ... SELECT, inserting a new key
After DELETE, old=CREATE TABLE ... SELECT, inserting a new key
After INSERT, new=CREATE TABLE ... REPLACE SELECT, deleting a duplicate key
truncate t1;
truncate t1_op_log;
insert into t1 (id, operation)
select NULL, "INSERT ... SELECT, inserting a new key";
set @id=last_insert_id();
select * from t1;
id operation
1 INSERT ... SELECT, inserting a new key
select * from t1_op_log;
operation
Before INSERT, new=INSERT ... SELECT, inserting a new key
After INSERT, new=INSERT ... SELECT, inserting a new key
truncate t1_op_log;
insert into t1 (id, operation)
select @id,
"INSERT ... SELECT ... ON DUPLICATE KEY UPDATE, updating a duplicate"
on duplicate key update id=NULL,
operation="INSERT ... SELECT ... ON DUPLICATE KEY UPDATE, updating a duplicate";
select * from t1;
id operation
0 INSERT ... SELECT ... ON DUPLICATE KEY UPDATE, updating a duplicate
select * from t1_op_log;
operation
Before INSERT, new=INSERT ... SELECT ... ON DUPLICATE KEY UPDATE, updating a duplicate
Before UPDATE, new=INSERT ... SELECT ... ON DUPLICATE KEY UPDATE, updating a duplicate, old=INSERT ... SELECT, inserting a new key
After UPDATE, new=INSERT ... SELECT ... ON DUPLICATE KEY UPDATE, updating a duplicate, old=INSERT ... SELECT, inserting a new key
truncate t1;
truncate t1_op_log;
replace into t1 (id, operation)
select NULL, "REPLACE ... SELECT, inserting a new key";
set @id=last_insert_id();
select * from t1;
id operation
1 REPLACE ... SELECT, inserting a new key
select * from t1_op_log;
operation
Before INSERT, new=REPLACE ... SELECT, inserting a new key
After INSERT, new=REPLACE ... SELECT, inserting a new key
truncate t1_op_log;
replace into t1 (id, operation)
select @id, "REPLACE ... SELECT, deleting a duplicate";
select * from t1;
id operation
1 REPLACE ... SELECT, deleting a duplicate
select * from t1_op_log;
operation
Before INSERT, new=REPLACE ... SELECT, deleting a duplicate
Before DELETE, old=REPLACE ... SELECT, inserting a new key
After DELETE, old=REPLACE ... SELECT, inserting a new key
After INSERT, new=REPLACE ... SELECT, deleting a duplicate
truncate t1;
truncate t1_op_log;
insert into t1 (id, operation) values (1, "INSERT for multi-DELETE");
insert into t2 (id) values (1);
delete t1.*, t2.* from t1, t2 where t1.id=1;
select * from t1;
id operation
select * from t2;
id
select * from t1_op_log;
operation
Before INSERT, new=INSERT for multi-DELETE
After INSERT, new=INSERT for multi-DELETE
Before DELETE, old=INSERT for multi-DELETE
After DELETE, old=INSERT for multi-DELETE
truncate t1;
truncate t2;
truncate t1_op_log;
insert into t1 (id, operation) values (1, "INSERT for multi-UPDATE");
insert into t2 (id) values (1);
update t1, t2 set t1.id=2, operation="multi-UPDATE" where t1.id=1;
update t1, t2
set t2.id=3, operation="multi-UPDATE, SET for t2, but the trigger is fired" where t1.id=2;
select * from t1;
id operation
2 multi-UPDATE, SET for t2, but the trigger is fired
select * from t2;
id
3
select * from t1_op_log;
operation
Before INSERT, new=INSERT for multi-UPDATE
After INSERT, new=INSERT for multi-UPDATE
Before UPDATE, new=multi-UPDATE, old=INSERT for multi-UPDATE
After UPDATE, new=multi-UPDATE, old=INSERT for multi-UPDATE
Before UPDATE, new=multi-UPDATE, SET for t2, but the trigger is fired, old=multi-UPDATE
After UPDATE, new=multi-UPDATE, SET for t2, but the trigger is fired, old=multi-UPDATE
truncate table t1;
truncate table t2;
truncate table t1_op_log;
Now do the same but use a view instead of the base table.
insert into v1 (operation) values ("INSERT");
set @id=last_insert_id();
select * from t1;
id operation
1 INSERT
select * from t1_op_log;
operation
Before INSERT, new=INSERT
After INSERT, new=INSERT
truncate t1_op_log;
update v1 set operation="UPDATE" where id=@id;
select * from t1;
id operation
1 UPDATE
select * from t1_op_log;
operation
Before UPDATE, new=UPDATE, old=INSERT
After UPDATE, new=UPDATE, old=INSERT
truncate t1_op_log;
delete from v1 where id=@id;
select * from t1;
id operation
select * from t1_op_log;
operation
Before DELETE, old=UPDATE
After DELETE, old=UPDATE
truncate t1;
truncate t1_op_log;
insert into v1 (id, operation) values
(NULL, "INSERT ON DUPLICATE KEY UPDATE, inserting a new key")
on duplicate key update id=NULL, operation="Should never happen";
set @id=last_insert_id();
select * from t1;
id operation
1 INSERT ON DUPLICATE KEY UPDATE, inserting a new key
select * from t1_op_log;
operation
Before INSERT, new=INSERT ON DUPLICATE KEY UPDATE, inserting a new key
After INSERT, new=INSERT ON DUPLICATE KEY UPDATE, inserting a new key
truncate t1_op_log;
insert into v1 (id, operation) values
(@id, "INSERT ON DUPLICATE KEY UPDATE, the key value is the same")
on duplicate key update id=NULL,
operation="INSERT ON DUPLICATE KEY UPDATE, updating the duplicate";
select * from t1;
id operation
0 INSERT ON DUPLICATE KEY UPDATE, updating the duplicate
select * from t1_op_log;
operation
Before INSERT, new=INSERT ON DUPLICATE KEY UPDATE, the key value is the same
Before UPDATE, new=INSERT ON DUPLICATE KEY UPDATE, updating the duplicate, old=INSERT ON DUPLICATE KEY UPDATE, inserting a new key
After UPDATE, new=INSERT ON DUPLICATE KEY UPDATE, updating the duplicate, old=INSERT ON DUPLICATE KEY UPDATE, inserting a new key
truncate t1;
truncate t1_op_log;
replace into v1 values (NULL, "REPLACE, inserting a new key");
set @id=last_insert_id();
select * from t1;
id operation
1 REPLACE, inserting a new key
select * from t1_op_log;
operation
Before INSERT, new=REPLACE, inserting a new key
After INSERT, new=REPLACE, inserting a new key
truncate t1_op_log;
replace into v1 values (@id, "REPLACE, deleting the duplicate");
select * from t1;
id operation
1 REPLACE, deleting the duplicate
select * from t1_op_log;
operation
Before INSERT, new=REPLACE, deleting the duplicate
Before DELETE, old=REPLACE, inserting a new key
After DELETE, old=REPLACE, inserting a new key
After INSERT, new=REPLACE, deleting the duplicate
truncate t1;
truncate t1_op_log;
create table if not exists v1
select NULL, "CREATE TABLE ... SELECT, inserting a new key";
Warnings:
Note 1050 Table 'v1' already exists
set @id=last_insert_id();
select * from t1;
id operation
1 CREATE TABLE ... SELECT, inserting a new key
select * from t1_op_log;
operation
Before INSERT, new=CREATE TABLE ... SELECT, inserting a new key
After INSERT, new=CREATE TABLE ... SELECT, inserting a new key
truncate t1_op_log;
create table if not exists v1 replace
select @id, "CREATE TABLE ... REPLACE SELECT, deleting a duplicate key";
Warnings:
Note 1050 Table 'v1' already exists
select * from t1;
id operation
1 CREATE TABLE ... REPLACE SELECT, deleting a duplicate key
select * from t1_op_log;
operation
Before INSERT, new=CREATE TABLE ... REPLACE SELECT, deleting a duplicate key
Before DELETE, old=CREATE TABLE ... SELECT, inserting a new key
After DELETE, old=CREATE TABLE ... SELECT, inserting a new key
After INSERT, new=CREATE TABLE ... REPLACE SELECT, deleting a duplicate key
truncate t1;
truncate t1_op_log;
insert into v1 (id, operation)
select NULL, "INSERT ... SELECT, inserting a new key";
set @id=last_insert_id();
select * from t1;
id operation
1 INSERT ... SELECT, inserting a new key
select * from t1_op_log;
operation
Before INSERT, new=INSERT ... SELECT, inserting a new key
After INSERT, new=INSERT ... SELECT, inserting a new key
truncate t1_op_log;
insert into v1 (id, operation)
select @id,
"INSERT ... SELECT ... ON DUPLICATE KEY UPDATE, updating a duplicate"
on duplicate key update id=NULL,
operation="INSERT ... SELECT ... ON DUPLICATE KEY UPDATE, updating a duplicate";
select * from t1;
id operation
0 INSERT ... SELECT ... ON DUPLICATE KEY UPDATE, updating a duplicate
select * from t1_op_log;
operation
Before INSERT, new=INSERT ... SELECT ... ON DUPLICATE KEY UPDATE, updating a duplicate
Before UPDATE, new=INSERT ... SELECT ... ON DUPLICATE KEY UPDATE, updating a duplicate, old=INSERT ... SELECT, inserting a new key
After UPDATE, new=INSERT ... SELECT ... ON DUPLICATE KEY UPDATE, updating a duplicate, old=INSERT ... SELECT, inserting a new key
truncate t1;
truncate t1_op_log;
replace into v1 (id, operation)
select NULL, "REPLACE ... SELECT, inserting a new key";
set @id=last_insert_id();
select * from t1;
id operation
1 REPLACE ... SELECT, inserting a new key
select * from t1_op_log;
operation
Before INSERT, new=REPLACE ... SELECT, inserting a new key
After INSERT, new=REPLACE ... SELECT, inserting a new key
truncate t1_op_log;
replace into v1 (id, operation)
select @id, "REPLACE ... SELECT, deleting a duplicate";
select * from t1;
id operation
1 REPLACE ... SELECT, deleting a duplicate
select * from t1_op_log;
operation
Before INSERT, new=REPLACE ... SELECT, deleting a duplicate
Before DELETE, old=REPLACE ... SELECT, inserting a new key
After DELETE, old=REPLACE ... SELECT, inserting a new key
After INSERT, new=REPLACE ... SELECT, deleting a duplicate
truncate t1;
truncate t1_op_log;
insert into v1 (id, operation) values (1, "INSERT for multi-DELETE");
insert into t2 (id) values (1);
delete v1.*, t2.* from v1, t2 where v1.id=1;
select * from t1;
id operation
select * from t2;
id
select * from t1_op_log;
operation
Before INSERT, new=INSERT for multi-DELETE
After INSERT, new=INSERT for multi-DELETE
Before DELETE, old=INSERT for multi-DELETE
After DELETE, old=INSERT for multi-DELETE
truncate t1;
truncate t2;
truncate t1_op_log;
insert into v1 (id, operation) values (1, "INSERT for multi-UPDATE");
insert into t2 (id) values (1);
update v1, t2 set v1.id=2, operation="multi-UPDATE" where v1.id=1;
update v1, t2
set t2.id=3, operation="multi-UPDATE, SET for t2, but the trigger is fired" where v1.id=2;
select * from t1;
id operation
2 multi-UPDATE, SET for t2, but the trigger is fired
select * from t2;
id
3
select * from t1_op_log;
operation
Before INSERT, new=INSERT for multi-UPDATE
After INSERT, new=INSERT for multi-UPDATE
Before UPDATE, new=multi-UPDATE, old=INSERT for multi-UPDATE
After UPDATE, new=multi-UPDATE, old=INSERT for multi-UPDATE
Before UPDATE, new=multi-UPDATE, SET for t2, but the trigger is fired, old=multi-UPDATE
After UPDATE, new=multi-UPDATE, SET for t2, but the trigger is fired, old=multi-UPDATE
drop view v1;
drop table t1, t2, t1_op_log;
End of 5.0 tests
End of 5.0 tests
mysql-test/t/trigger-trans.test
View file @
675a4e8f
...
@@ -49,4 +49,84 @@ insert into t1 values ('The Pie', 50, 1, 1);
...
@@ -49,4 +49,84 @@ insert into t1 values ('The Pie', 50, 1, 1);
select
*
from
t1
;
select
*
from
t1
;
drop
table
t1
;
drop
table
t1
;
# End of 5.0 tests
--
echo
--
echo
Bug
#26141 mixing table types in trigger causes full
--
echo
table
lock
on
innodb
table
--
echo
--
echo
Ensure
we
do
not
open
and
lock
tables
for
the
triggers
we
do
not
--
echo
fire
.
--
echo
--
disable_warnings
drop
table
if
exists
t1
,
t2
,
t3
;
drop
trigger
if
exists
trg_bug26141_au
;
drop
trigger
if
exists
trg_bug26141_ai
;
--
enable_warnings
# Note, for InnoDB to allow concurrent UPDATE and INSERT the
# table must have a unique key.
create
table
t1
(
c
int
primary
key
)
engine
=
innodb
;
create
table
t2
(
c
int
)
engine
=
myisam
;
create
table
t3
(
c
int
)
engine
=
myisam
;
insert
into
t1
(
c
)
values
(
1
);
delimiter
|
;
create
trigger
trg_bug26141_ai
after
insert
on
t1
for
each
row
begin
insert
into
t2
(
c
)
values
(
1
);
# We need the 'sync' lock to synchronously wait in connection 2 till
# the moment when the trigger acquired all the locks.
select
release_lock
(
"lock_bug26141_sync"
)
into
@
a
;
# 1000 is time in seconds of lock wait timeout -- this is a way
# to cause a manageable sleep up to 1000 seconds
select
get_lock
(
"lock_bug26141_wait"
,
1000
)
into
@
a
;
end
|
create
trigger
trg_bug26141_au
after
update
on
t1
for
each
row
begin
insert
into
t3
(
c
)
values
(
1
);
end
|
delimiter
;
|
# Establish an alternative connection.
--
connect
(
connection_aux
,
localhost
,
root
,,
test
,,)
--
connect
(
connection_update
,
localhost
,
root
,,
test
,,)
connection
connection_aux
;
# Lock the wait lock, it must not be locked, so specify zero timeout.
select
get_lock
(
"lock_bug26141_wait"
,
0
);
#
connection
default
;
#
# Run the trigger synchronously
#
select
get_lock
(
"lock_bug26141_sync"
,
/* must not be priorly locked */
0
);
# Will acquire the table level locks, perform the insert into t2,
# release the sync lock and block on the wait lock.
send
insert
into
t1
(
c
)
values
(
2
);
connection
connection_update
;
# Wait for the trigger to acquire its locks and unlock the sync lock.
select
get_lock
(
"lock_bug26141_sync"
,
1000
);
#
# This must continue: after the fix for the bug, we do not
# open tables for t2, and with c=4 innobase allows the update
# to run concurrently with insert.
update
t1
set
c
=
3
where
c
=
1
;
select
release_lock
(
"lock_bug26141_sync"
);
connection
connection_aux
;
select
release_lock
(
"lock_bug26141_wait"
);
connection
default
;
reap
;
select
*
from
t1
;
select
*
from
t2
;
select
*
from
t3
;
# Drops the trigger as well.
drop
table
t1
,
t2
,
t3
;
disconnect
connection_update
;
disconnect
connection_aux
;
--
echo
End
of
5.0
tests
mysql-test/t/trigger.test
View file @
675a4e8f
...
@@ -1828,5 +1828,370 @@ DROP TRIGGER t1_test;
...
@@ -1828,5 +1828,370 @@ DROP TRIGGER t1_test;
DROP
TABLE
t1
,
t2
;
DROP
TABLE
t1
,
t2
;
SET
SESSION
LOW_PRIORITY_UPDATES
=
DEFAULT
;
SET
SESSION
LOW_PRIORITY_UPDATES
=
DEFAULT
;
SET
GLOBAL
LOW_PRIORITY_UPDATES
=
DEFAULT
;
SET
GLOBAL
LOW_PRIORITY_UPDATES
=
DEFAULT
;
--
echo
--
echo
Bug
#28502 Triggers that update another innodb table will block
--
echo
on
X
lock
unnecessarily
--
echo
--
echo
Ensure
we
do
not
open
and
lock
tables
for
triggers
we
do
not
fire
.
--
echo
--
disable_warnings
drop
table
if
exists
t1
,
t2
;
drop
trigger
if
exists
trg_bug28502_au
;
--
enable_warnings
create
table
t1
(
id
int
,
count
int
);
create
table
t2
(
id
int
);
delimiter
|
;
create
trigger
trg_bug28502_au
before
update
on
t2
for
each
row
begin
if
(
new
.
id
is
not
null
)
then
update
t1
set
count
=
count
+
1
where
id
=
old
.
id
;
end
if
;
end
|
delimiter
;
|
insert
into
t1
(
id
,
count
)
values
(
1
,
0
);
lock
table
t1
write
;
--
connect
(
connection_insert
,
localhost
,
root
,
,
test
,
,
)
connection
connection_insert
;
# Is expected to pass.
insert
into
t2
set
id
=
1
;
connection
default
;
unlock
tables
;
update
t2
set
id
=
1
where
id
=
1
;
select
*
from
t1
;
select
*
from
t2
;
# Will drop the trigger
drop
table
t1
,
t2
;
disconnect
connection_insert
;
--
echo
--
echo
Additionally
,
provide
test
coverage
for
triggers
and
--
echo
all
MySQL
data
changing
commands
.
--
echo
--
disable_warnings
drop
table
if
exists
t1
,
t2
,
t1_op_log
;
drop
view
if
exists
v1
;
drop
trigger
if
exists
trg_bug28502_bi
;
drop
trigger
if
exists
trg_bug28502_ai
;
drop
trigger
if
exists
trg_bug28502_bu
;
drop
trigger
if
exists
trg_bug28502_au
;
drop
trigger
if
exists
trg_bug28502_bd
;
drop
trigger
if
exists
trg_bug28502_ad
;
--
enable_warnings
create
table
t1
(
id
int
primary
key
auto_increment
,
operation
varchar
(
255
));
create
table
t2
(
id
int
primary
key
);
create
table
t1_op_log
(
operation
varchar
(
255
));
create
view
v1
as
select
*
from
t1
;
create
trigger
trg_bug28502_bi
before
insert
on
t1
for
each
row
insert
into
t1_op_log
(
operation
)
values
(
concat
(
"Before INSERT, new="
,
new
.
operation
));
create
trigger
trg_bug28502_ai
after
insert
on
t1
for
each
row
insert
into
t1_op_log
(
operation
)
values
(
concat
(
"After INSERT, new="
,
new
.
operation
));
create
trigger
trg_bug28502_bu
before
update
on
t1
for
each
row
insert
into
t1_op_log
(
operation
)
values
(
concat
(
"Before UPDATE, new="
,
new
.
operation
,
", old="
,
old
.
operation
));
create
trigger
trg_bug28502_au
after
update
on
t1
for
each
row
insert
into
t1_op_log
(
operation
)
values
(
concat
(
"After UPDATE, new="
,
new
.
operation
,
", old="
,
old
.
operation
));
create
trigger
trg_bug28502_bd
before
delete
on
t1
for
each
row
insert
into
t1_op_log
(
operation
)
values
(
concat
(
"Before DELETE, old="
,
old
.
operation
));
create
trigger
trg_bug28502_ad
after
delete
on
t1
for
each
row
insert
into
t1_op_log
(
operation
)
values
(
concat
(
"After DELETE, old="
,
old
.
operation
));
insert
into
t1
(
operation
)
values
(
"INSERT"
);
set
@
id
=
last_insert_id
();
select
*
from
t1
;
select
*
from
t1_op_log
;
truncate
t1_op_log
;
update
t1
set
operation
=
"UPDATE"
where
id
=@
id
;
select
*
from
t1
;
select
*
from
t1_op_log
;
truncate
t1_op_log
;
delete
from
t1
where
id
=@
id
;
select
*
from
t1
;
select
*
from
t1_op_log
;
truncate
t1
;
truncate
t1_op_log
;
insert
into
t1
(
id
,
operation
)
values
(
NULL
,
"INSERT ON DUPLICATE KEY UPDATE, inserting a new key"
)
on
duplicate
key
update
id
=
NULL
,
operation
=
"Should never happen"
;
set
@
id
=
last_insert_id
();
select
*
from
t1
;
select
*
from
t1_op_log
;
truncate
t1_op_log
;
insert
into
t1
(
id
,
operation
)
values
(
@
id
,
"INSERT ON DUPLICATE KEY UPDATE, the key value is the same"
)
on
duplicate
key
update
id
=
NULL
,
operation
=
"INSERT ON DUPLICATE KEY UPDATE, updating the duplicate"
;
select
*
from
t1
;
select
*
from
t1_op_log
;
truncate
t1
;
truncate
t1_op_log
;
replace
into
t1
values
(
NULL
,
"REPLACE, inserting a new key"
);
set
@
id
=
last_insert_id
();
select
*
from
t1
;
select
*
from
t1_op_log
;
truncate
t1_op_log
;
replace
into
t1
values
(
@
id
,
"REPLACE, deleting the duplicate"
);
select
*
from
t1
;
select
*
from
t1_op_log
;
truncate
t1
;
truncate
t1_op_log
;
create
table
if
not
exists
t1
select
NULL
,
"CREATE TABLE ... SELECT, inserting a new key"
;
set
@
id
=
last_insert_id
();
select
*
from
t1
;
select
*
from
t1_op_log
;
truncate
t1_op_log
;
create
table
if
not
exists
t1
replace
select
@
id
,
"CREATE TABLE ... REPLACE SELECT, deleting a duplicate key"
;
select
*
from
t1
;
select
*
from
t1_op_log
;
truncate
t1
;
truncate
t1_op_log
;
insert
into
t1
(
id
,
operation
)
select
NULL
,
"INSERT ... SELECT, inserting a new key"
;
set
@
id
=
last_insert_id
();
select
*
from
t1
;
select
*
from
t1_op_log
;
truncate
t1_op_log
;
insert
into
t1
(
id
,
operation
)
select
@
id
,
"INSERT ... SELECT ... ON DUPLICATE KEY UPDATE, updating a duplicate"
on
duplicate
key
update
id
=
NULL
,
operation
=
"INSERT ... SELECT ... ON DUPLICATE KEY UPDATE, updating a duplicate"
;
select
*
from
t1
;
select
*
from
t1_op_log
;
truncate
t1
;
truncate
t1_op_log
;
replace
into
t1
(
id
,
operation
)
select
NULL
,
"REPLACE ... SELECT, inserting a new key"
;
set
@
id
=
last_insert_id
();
select
*
from
t1
;
select
*
from
t1_op_log
;
truncate
t1_op_log
;
replace
into
t1
(
id
,
operation
)
select
@
id
,
"REPLACE ... SELECT, deleting a duplicate"
;
select
*
from
t1
;
select
*
from
t1_op_log
;
truncate
t1
;
truncate
t1_op_log
;
insert
into
t1
(
id
,
operation
)
values
(
1
,
"INSERT for multi-DELETE"
);
insert
into
t2
(
id
)
values
(
1
);
delete
t1
.*
,
t2
.*
from
t1
,
t2
where
t1
.
id
=
1
;
select
*
from
t1
;
select
*
from
t2
;
select
*
from
t1_op_log
;
truncate
t1
;
truncate
t2
;
truncate
t1_op_log
;
insert
into
t1
(
id
,
operation
)
values
(
1
,
"INSERT for multi-UPDATE"
);
insert
into
t2
(
id
)
values
(
1
);
update
t1
,
t2
set
t1
.
id
=
2
,
operation
=
"multi-UPDATE"
where
t1
.
id
=
1
;
update
t1
,
t2
set
t2
.
id
=
3
,
operation
=
"multi-UPDATE, SET for t2, but the trigger is fired"
where
t1
.
id
=
2
;
select
*
from
t1
;
select
*
from
t2
;
select
*
from
t1_op_log
;
truncate
table
t1
;
truncate
table
t2
;
truncate
table
t1_op_log
;
--
echo
--
echo
Now
do
the
same
but
use
a
view instead of the base table.
--echo
insert into v1 (operation) values ("INSERT")
;
set
@
id
=
last_insert_id
();
select
*
from
t1
;
select
*
from
t1_op_log
;
truncate
t1_op_log
;
update
v1
set
operation
=
"UPDATE"
where
id
=@
id
;
select
*
from
t1
;
select
*
from
t1_op_log
;
truncate
t1_op_log
;
delete
from
v1
where
id
=@
id
;
select
*
from
t1
;
select
*
from
t1_op_log
;
truncate
t1
;
truncate
t1_op_log
;
insert
into
v1
(
id
,
operation
)
values
(
NULL
,
"INSERT ON DUPLICATE KEY UPDATE, inserting a new key"
)
on
duplicate
key
update
id
=
NULL
,
operation
=
"Should never happen"
;
set
@
id
=
last_insert_id
();
select
*
from
t1
;
select
*
from
t1_op_log
;
truncate
t1_op_log
;
insert
into
v1
(
id
,
operation
)
values
(
@
id
,
"INSERT ON DUPLICATE KEY UPDATE, the key value is the same"
)
on
duplicate
key
update
id
=
NULL
,
operation
=
"INSERT ON DUPLICATE KEY UPDATE, updating the duplicate"
;
select
*
from
t1
;
select
*
from
t1_op_log
;
truncate
t1
;
truncate
t1_op_log
;
replace
into
v1
values
(
NULL
,
"REPLACE, inserting a new key"
);
set
@
id
=
last_insert_id
();
select
*
from
t1
;
select
*
from
t1_op_log
;
truncate
t1_op_log
;
replace
into
v1
values
(
@
id
,
"REPLACE, deleting the duplicate"
);
select
*
from
t1
;
select
*
from
t1_op_log
;
truncate
t1
;
truncate
t1_op_log
;
create
table
if
not
exists
v1
select
NULL
,
"CREATE TABLE ... SELECT, inserting a new key"
;
set
@
id
=
last_insert_id
();
select
*
from
t1
;
select
*
from
t1_op_log
;
truncate
t1_op_log
;
create
table
if
not
exists
v1
replace
select
@
id
,
"CREATE TABLE ... REPLACE SELECT, deleting a duplicate key"
;
select
*
from
t1
;
select
*
from
t1_op_log
;
truncate
t1
;
truncate
t1_op_log
;
insert
into
v1
(
id
,
operation
)
select
NULL
,
"INSERT ... SELECT, inserting a new key"
;
set
@
id
=
last_insert_id
();
select
*
from
t1
;
select
*
from
t1_op_log
;
truncate
t1_op_log
;
insert
into
v1
(
id
,
operation
)
select
@
id
,
"INSERT ... SELECT ... ON DUPLICATE KEY UPDATE, updating a duplicate"
on
duplicate
key
update
id
=
NULL
,
operation
=
"INSERT ... SELECT ... ON DUPLICATE KEY UPDATE, updating a duplicate"
;
select
*
from
t1
;
select
*
from
t1_op_log
;
truncate
t1
;
truncate
t1_op_log
;
replace
into
v1
(
id
,
operation
)
select
NULL
,
"REPLACE ... SELECT, inserting a new key"
;
set
@
id
=
last_insert_id
();
select
*
from
t1
;
select
*
from
t1_op_log
;
truncate
t1_op_log
;
replace
into
v1
(
id
,
operation
)
select
@
id
,
"REPLACE ... SELECT, deleting a duplicate"
;
select
*
from
t1
;
select
*
from
t1_op_log
;
truncate
t1
;
truncate
t1_op_log
;
insert
into
v1
(
id
,
operation
)
values
(
1
,
"INSERT for multi-DELETE"
);
insert
into
t2
(
id
)
values
(
1
);
delete
v1
.*
,
t2
.*
from
v1
,
t2
where
v1
.
id
=
1
;
select
*
from
t1
;
select
*
from
t2
;
select
*
from
t1_op_log
;
truncate
t1
;
truncate
t2
;
truncate
t1_op_log
;
insert
into
v1
(
id
,
operation
)
values
(
1
,
"INSERT for multi-UPDATE"
);
insert
into
t2
(
id
)
values
(
1
);
update
v1
,
t2
set
v1
.
id
=
2
,
operation
=
"multi-UPDATE"
where
v1
.
id
=
1
;
update
v1
,
t2
set
t2
.
id
=
3
,
operation
=
"multi-UPDATE, SET for t2, but the trigger is fired"
where
v1
.
id
=
2
;
select
*
from
t1
;
select
*
from
t2
;
select
*
from
t1_op_log
;
drop
view
v1
;
drop
table
t1
,
t2
,
t1_op_log
;
#
# TODO: test LOAD DATA INFILE
--
echo
End
of
5.0
tests
--
echo
End
of
5.0
tests
sql/item.h
View file @
675a4e8f
...
@@ -2309,14 +2309,6 @@ enum trg_action_time_type
...
@@ -2309,14 +2309,6 @@ enum trg_action_time_type
TRG_ACTION_BEFORE
=
0
,
TRG_ACTION_AFTER
=
1
,
TRG_ACTION_MAX
TRG_ACTION_BEFORE
=
0
,
TRG_ACTION_AFTER
=
1
,
TRG_ACTION_MAX
};
};
/*
Event on which trigger is invoked.
*/
enum
trg_event_type
{
TRG_EVENT_INSERT
=
0
,
TRG_EVENT_UPDATE
=
1
,
TRG_EVENT_DELETE
=
2
,
TRG_EVENT_MAX
};
class
Table_triggers_list
;
class
Table_triggers_list
;
/*
/*
...
...
sql/sp.cc
View file @
675a4e8f
...
@@ -440,6 +440,19 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp,
...
@@ -440,6 +440,19 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp,
lex_start
(
thd
);
lex_start
(
thd
);
thd
->
spcont
=
NULL
;
thd
->
spcont
=
NULL
;
ret
=
MYSQLparse
(
thd
);
ret
=
MYSQLparse
(
thd
);
if
(
ret
==
0
)
{
/*
Not strictly necessary to invoke this method here, since we know
that we've parsed CREATE PROCEDURE/FUNCTION and not an
UPDATE/DELETE/INSERT/REPLACE/LOAD/CREATE TABLE, but we try to
maintain the invariant that this method is called for each
distinct statement, in case its logic is extended with other
types of analyses in future.
*/
newlex
.
set_trg_event_type_for_tables
();
}
}
}
if
(
ret
||
thd
->
is_fatal_error
||
newlex
.
sphead
==
NULL
)
if
(
ret
||
thd
->
is_fatal_error
||
newlex
.
sphead
==
NULL
)
...
@@ -1742,31 +1755,39 @@ sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
...
@@ -1742,31 +1755,39 @@ sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
TABLE_LIST
*
table
)
TABLE_LIST
*
table
)
{
{
int
ret
=
0
;
int
ret
=
0
;
Table_triggers_list
*
triggers
=
table
->
table
->
triggers
;
if
(
add_used_routine
(
lex
,
thd
->
stmt_arena
,
&
triggers
->
sroutines_key
,
Sroutine_hash_entry
**
last_cached_routine_ptr
=
table
->
belong_to_view
))
(
Sroutine_hash_entry
**
)
lex
->
sroutines_list
.
next
;
if
(
static_cast
<
int
>
(
table
->
lock_type
)
>=
static_cast
<
int
>
(
TL_WRITE_ALLOW_WRITE
))
{
{
Sroutine_hash_entry
**
last_cached_routine_ptr
=
(
Sroutine_hash_entry
**
)
lex
->
sroutines_list
.
next
;
for
(
int
i
=
0
;
i
<
(
int
)
TRG_EVENT_MAX
;
i
++
)
for
(
int
i
=
0
;
i
<
(
int
)
TRG_EVENT_MAX
;
i
++
)
{
{
for
(
int
j
=
0
;
j
<
(
int
)
TRG_ACTION_MAX
;
j
++
)
if
(
table
->
trg_event_map
&
static_cast
<
uint8
>
(
1
<<
static_cast
<
int
>
(
i
)))
{
{
if
(
triggers
->
bodies
[
i
][
j
]
)
for
(
int
j
=
0
;
j
<
(
int
)
TRG_ACTION_MAX
;
j
++
)
{
{
(
void
)
triggers
->
bodies
[
i
][
j
]
->
/* We can have only one trigger per action type currently */
add_used_tables_to_table_list
(
thd
,
&
lex
->
query_tables_last
,
sp_head
*
trigger
=
table
->
table
->
triggers
->
bodies
[
i
][
j
];
table
->
belong_to_view
);
if
(
trigger
&&
sp_update_stmt_used_routines
(
thd
,
lex
,
add_used_routine
(
lex
,
thd
->
stmt_arena
,
&
trigger
->
m_sroutines_key
,
&
triggers
->
bodies
[
i
][
j
]
->
m_sroutines
,
table
->
belong_to_view
))
table
->
belong_to_view
);
{
trigger
->
add_used_tables_to_table_list
(
thd
,
&
lex
->
query_tables_last
,
table
->
belong_to_view
);
sp_update_stmt_used_routines
(
thd
,
lex
,
&
trigger
->
m_sroutines
,
table
->
belong_to_view
);
}
}
}
}
}
}
}
ret
=
sp_cache_routines_and_add_tables_aux
(
thd
,
lex
,
*
last_cached_routine_ptr
,
FALSE
,
NULL
);
}
}
ret
=
sp_cache_routines_and_add_tables_aux
(
thd
,
lex
,
*
last_cached_routine_ptr
,
FALSE
,
NULL
);
return
ret
;
return
ret
;
}
}
...
...
sql/sp_head.cc
View file @
675a4e8f
...
@@ -478,12 +478,35 @@ sp_head::init(LEX *lex)
...
@@ -478,12 +478,35 @@ sp_head::init(LEX *lex)
*/
*/
lex
->
trg_table_fields
.
empty
();
lex
->
trg_table_fields
.
empty
();
my_init_dynamic_array
(
&
m_instr
,
sizeof
(
sp_instr
*
),
16
,
8
);
my_init_dynamic_array
(
&
m_instr
,
sizeof
(
sp_instr
*
),
16
,
8
);
m_param_begin
=
m_param_end
=
m_body_begin
=
0
;
m_qname
.
str
=
m_db
.
str
=
m_name
.
str
=
m_params
.
str
=
m_param_begin
=
NULL
;
m_body
.
str
=
m_defstr
.
str
=
0
;
m_param_end
=
NULL
;
m_qname
.
length
=
m_db
.
length
=
m_name
.
length
=
m_params
.
length
=
m_body
.
length
=
m_defstr
.
length
=
0
;
m_body_begin
=
NULL
;
m_qname
.
str
=
NULL
;
m_qname
.
length
=
0
;
m_db
.
str
=
NULL
;
m_db
.
length
=
0
;
m_name
.
str
=
NULL
;
m_name
.
length
=
0
;
m_params
.
str
=
NULL
;
m_params
.
length
=
0
;
m_body
.
str
=
NULL
;
m_body
.
length
=
0
;
m_defstr
.
str
=
NULL
;
m_defstr
.
length
=
0
;
m_sroutines_key
.
str
=
NULL
;
m_sroutines_key
.
length
=
0
;
m_return_field_def
.
charset
=
NULL
;
m_return_field_def
.
charset
=
NULL
;
DBUG_VOID_RETURN
;
DBUG_VOID_RETURN
;
}
}
...
@@ -509,9 +532,14 @@ sp_head::init_sp_name(THD *thd, sp_name *spname)
...
@@ -509,9 +532,14 @@ sp_head::init_sp_name(THD *thd, sp_name *spname)
if
(
spname
->
m_qname
.
length
==
0
)
if
(
spname
->
m_qname
.
length
==
0
)
spname
->
init_qname
(
thd
);
spname
->
init_qname
(
thd
);
m_qname
.
length
=
spname
->
m_qname
.
length
;
m_sroutines_key
.
length
=
spname
->
m_sroutines_key
.
length
;
m_qname
.
str
=
strmake_root
(
thd
->
mem_root
,
spname
->
m_qname
.
str
,
m_sroutines_key
.
str
=
memdup_root
(
thd
->
mem_root
,
m_qname
.
length
);
spname
->
m_sroutines_key
.
str
,
spname
->
m_sroutines_key
.
length
+
1
);
m_sroutines_key
.
str
[
0
]
=
static_cast
<
char
>
(
m_type
);
m_qname
.
length
=
m_sroutines_key
.
length
-
1
;
m_qname
.
str
=
m_sroutines_key
.
str
+
1
;
DBUG_VOID_RETURN
;
DBUG_VOID_RETURN
;
}
}
...
@@ -1796,8 +1824,11 @@ sp_head::restore_lex(THD *thd)
...
@@ -1796,8 +1824,11 @@ sp_head::restore_lex(THD *thd)
{
{
DBUG_ENTER
(
"sp_head::restore_lex"
);
DBUG_ENTER
(
"sp_head::restore_lex"
);
LEX
*
sublex
=
thd
->
lex
;
LEX
*
sublex
=
thd
->
lex
;
LEX
*
oldlex
=
(
LEX
*
)
m_lex
.
pop
();
LEX
*
oldlex
;
sublex
->
set_trg_event_type_for_tables
();
oldlex
=
(
LEX
*
)
m_lex
.
pop
();
if
(
!
oldlex
)
if
(
!
oldlex
)
return
;
// Nothing to restore
return
;
// Nothing to restore
...
@@ -3429,6 +3460,7 @@ typedef struct st_sp_table
...
@@ -3429,6 +3460,7 @@ typedef struct st_sp_table
thr_lock_type
lock_type
;
/* lock type used for prelocking */
thr_lock_type
lock_type
;
/* lock type used for prelocking */
uint
lock_count
;
uint
lock_count
;
uint
query_lock_count
;
uint
query_lock_count
;
uint8
trg_event_map
;
}
SP_TABLE
;
}
SP_TABLE
;
byte
*
byte
*
...
@@ -3515,6 +3547,7 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check)
...
@@ -3515,6 +3547,7 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check)
tab
->
query_lock_count
++
;
tab
->
query_lock_count
++
;
if
(
tab
->
query_lock_count
>
tab
->
lock_count
)
if
(
tab
->
query_lock_count
>
tab
->
lock_count
)
tab
->
lock_count
++
;
tab
->
lock_count
++
;
tab
->
trg_event_map
|=
table
->
trg_event_map
;
}
}
else
else
{
{
...
@@ -3536,6 +3569,7 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check)
...
@@ -3536,6 +3569,7 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check)
tab
->
db_length
=
table
->
db_length
;
tab
->
db_length
=
table
->
db_length
;
tab
->
lock_type
=
table
->
lock_type
;
tab
->
lock_type
=
table
->
lock_type
;
tab
->
lock_count
=
tab
->
query_lock_count
=
1
;
tab
->
lock_count
=
tab
->
query_lock_count
=
1
;
tab
->
trg_event_map
=
table
->
trg_event_map
;
my_hash_insert
(
&
m_sptabs
,
(
byte
*
)
tab
);
my_hash_insert
(
&
m_sptabs
,
(
byte
*
)
tab
);
}
}
}
}
...
@@ -3613,6 +3647,7 @@ sp_head::add_used_tables_to_table_list(THD *thd,
...
@@ -3613,6 +3647,7 @@ sp_head::add_used_tables_to_table_list(THD *thd,
table
->
cacheable_table
=
1
;
table
->
cacheable_table
=
1
;
table
->
prelocking_placeholder
=
1
;
table
->
prelocking_placeholder
=
1
;
table
->
belong_to_view
=
belong_to_view
;
table
->
belong_to_view
=
belong_to_view
;
table
->
trg_event_map
=
stab
->
trg_event_map
;
/* Everyting else should be zeroed */
/* Everyting else should be zeroed */
...
...
sql/sp_head.h
View file @
675a4e8f
...
@@ -130,6 +130,12 @@ public:
...
@@ -130,6 +130,12 @@ public:
st_sp_chistics
*
m_chistics
;
st_sp_chistics
*
m_chistics
;
ulong
m_sql_mode
;
// For SHOW CREATE and execution
ulong
m_sql_mode
;
// For SHOW CREATE and execution
LEX_STRING
m_qname
;
// db.name
LEX_STRING
m_qname
;
// db.name
/**
Key representing routine in the set of stored routines used by statement.
[routine_type]db.name\0
@sa sp_name::m_sroutines_key
*/
LEX_STRING
m_sroutines_key
;
LEX_STRING
m_db
;
LEX_STRING
m_db
;
LEX_STRING
m_name
;
LEX_STRING
m_name
;
LEX_STRING
m_params
;
LEX_STRING
m_params
;
...
...
sql/sql_lex.cc
View file @
675a4e8f
...
@@ -2034,6 +2034,27 @@ void st_select_lex_unit::set_limit(SELECT_LEX *sl)
...
@@ -2034,6 +2034,27 @@ void st_select_lex_unit::set_limit(SELECT_LEX *sl)
}
}
/**
Update the parsed tree with information about triggers that
may be fired when executing this statement.
*/
void
st_lex
::
set_trg_event_type_for_tables
()
{
/*
Do not iterate over sub-selects, only the tables in the outermost
SELECT_LEX can be modified, if any.
*/
TABLE_LIST
*
tables
=
select_lex
.
get_table_list
();
while
(
tables
)
{
tables
->
set_trg_event_type
(
this
);
tables
=
tables
->
next_local
;
}
}
/*
/*
Unlink the first table from the global table list and the first table from
Unlink the first table from the global table list and the first table from
outer select (lex->select_lex) local list
outer select (lex->select_lex) local list
...
...
sql/sql_lex.h
View file @
675a4e8f
...
@@ -1188,6 +1188,8 @@ typedef struct st_lex : public Query_tables_list
...
@@ -1188,6 +1188,8 @@ typedef struct st_lex : public Query_tables_list
un
->
uncacheable
|=
cause
;
un
->
uncacheable
|=
cause
;
}
}
}
}
void
set_trg_event_type_for_tables
();
TABLE_LIST
*
unlink_first_table
(
bool
*
link_to_local
);
TABLE_LIST
*
unlink_first_table
(
bool
*
link_to_local
);
void
link_first_table_back
(
TABLE_LIST
*
first
,
bool
link_to_local
);
void
link_first_table_back
(
TABLE_LIST
*
first
,
bool
link_to_local
);
void
first_lists_tables_same
();
void
first_lists_tables_same
();
...
...
sql/sql_parse.cc
View file @
675a4e8f
...
@@ -6080,8 +6080,9 @@ void mysql_parse(THD *thd, const char *inBuf, uint length,
...
@@ -6080,8 +6080,9 @@ void mysql_parse(THD *thd, const char *inBuf, uint length,
(
thd
->
query_length
=
(
ulong
)(
lip
.
found_semicolon
-
thd
->
query
)))
(
thd
->
query_length
=
(
ulong
)(
lip
.
found_semicolon
-
thd
->
query
)))
thd
->
query_length
--
;
thd
->
query_length
--
;
/* Actually execute the query */
/* Actually execute the query */
mysql_execute_command
(
thd
);
lex
->
set_trg_event_type_for_tables
();
query_cache_end_of_result
(
thd
);
mysql_execute_command
(
thd
);
query_cache_end_of_result
(
thd
);
}
}
}
}
}
}
...
...
sql/sql_prepare.cc
View file @
675a4e8f
...
@@ -2826,6 +2826,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
...
@@ -2826,6 +2826,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
lex_start
(
thd
);
lex_start
(
thd
);
lex
->
safe_to_cache_query
=
FALSE
;
lex
->
safe_to_cache_query
=
FALSE
;
int
err
=
MYSQLparse
((
void
*
)
thd
);
int
err
=
MYSQLparse
((
void
*
)
thd
);
lex
->
set_trg_event_type_for_tables
();
error
=
err
||
thd
->
is_fatal_error
||
error
=
err
||
thd
->
is_fatal_error
||
thd
->
net
.
report_error
||
init_param_array
(
this
);
thd
->
net
.
report_error
||
init_param_array
(
this
);
...
...
sql/sql_trigger.cc
View file @
675a4e8f
...
@@ -942,17 +942,6 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
...
@@ -942,17 +942,6 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
table
->
triggers
=
triggers
;
table
->
triggers
=
triggers
;
/*
Construct key that will represent triggers for this table in the set
of routines used by statement.
*/
triggers
->
sroutines_key
.
length
=
1
+
strlen
(
db
)
+
1
+
strlen
(
table_name
)
+
1
;
if
(
!
(
triggers
->
sroutines_key
.
str
=
alloc_root
(
&
table
->
mem_root
,
triggers
->
sroutines_key
.
length
)))
DBUG_RETURN
(
1
);
triggers
->
sroutines_key
.
str
[
0
]
=
TYPE_ENUM_TRIGGER
;
strxmov
(
triggers
->
sroutines_key
.
str
+
1
,
db
,
"."
,
table_name
,
NullS
);
/*
/*
TODO: This could be avoided if there is no triggers
TODO: This could be avoided if there is no triggers
for UPDATE and DELETE.
for UPDATE and DELETE.
...
@@ -991,6 +980,15 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
...
@@ -991,6 +980,15 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
DBUG_ASSERT
(
lex
.
sphead
==
0
);
DBUG_ASSERT
(
lex
.
sphead
==
0
);
goto
err_with_lex_cleanup
;
goto
err_with_lex_cleanup
;
}
}
/*
Not strictly necessary to invoke this method here, since we know
that we've parsed CREATE TRIGGER and not an
UPDATE/DELETE/INSERT/REPLACE/LOAD/CREATE TABLE, but we try to
maintain the invariant that this method is called for each
distinct statement, in case its logic is extended with other
types of analyses in future.
*/
lex
.
set_trg_event_type_for_tables
();
lex
.
sphead
->
set_info
(
0
,
0
,
&
lex
.
sp_chistics
,
(
ulong
)
*
trg_sql_mode
);
lex
.
sphead
->
set_info
(
0
,
0
,
&
lex
.
sp_chistics
,
(
ulong
)
*
trg_sql_mode
);
...
@@ -1550,6 +1548,12 @@ bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event,
...
@@ -1550,6 +1548,12 @@ bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event,
new_field
=
record1_field
;
new_field
=
record1_field
;
old_field
=
trigger_table
->
field
;
old_field
=
trigger_table
->
field
;
}
}
/*
This trigger must have been processed by the pre-locking
algorithm.
*/
DBUG_ASSERT
(
trigger_table
->
pos_in_table_list
->
trg_event_map
&
static_cast
<
uint
>
(
1
<<
static_cast
<
int
>
(
event
)));
thd
->
reset_sub_statement_state
(
&
statement_state
,
SUB_STMT_TRIGGER
);
thd
->
reset_sub_statement_state
(
&
statement_state
,
SUB_STMT_TRIGGER
);
err_status
=
sp_trigger
->
execute_trigger
err_status
=
sp_trigger
->
execute_trigger
...
@@ -1568,7 +1572,7 @@ bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event,
...
@@ -1568,7 +1572,7 @@ bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event,
SYNOPSIS
SYNOPSIS
mark_fields_used()
mark_fields_used()
thd Current thread context
thd Current thread context
event Type of event triggers for which we are going to ins
pect
event Type of event triggers for which we are going to ins
DESCRIPTION
DESCRIPTION
This method marks fields of subject table which are read/set in its
This method marks fields of subject table which are read/set in its
...
...
sql/sql_trigger.h
View file @
675a4e8f
...
@@ -56,14 +56,6 @@ class Table_triggers_list: public Sql_alloc
...
@@ -56,14 +56,6 @@ class Table_triggers_list: public Sql_alloc
updating trigger definitions during RENAME TABLE.
updating trigger definitions during RENAME TABLE.
*/
*/
List
<
LEX_STRING
>
on_table_names_list
;
List
<
LEX_STRING
>
on_table_names_list
;
/*
Key representing triggers for this table in set of all stored
routines used by statement.
TODO: We won't need this member once triggers namespace will be
database-wide instead of table-wide because then we will be able
to use key based on sp_name as for other stored routines.
*/
LEX_STRING
sroutines_key
;
/*
/*
Grant information for each trigger (pair: subject table, trigger definer).
Grant information for each trigger (pair: subject table, trigger definer).
...
...
sql/sql_view.cc
View file @
675a4e8f
...
@@ -1153,7 +1153,20 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
...
@@ -1153,7 +1153,20 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
*/
*/
for
(
tbl
=
view_main_select_tables
;
tbl
;
tbl
=
tbl
->
next_local
)
for
(
tbl
=
view_main_select_tables
;
tbl
;
tbl
=
tbl
->
next_local
)
tbl
->
lock_type
=
table
->
lock_type
;
tbl
->
lock_type
=
table
->
lock_type
;
/*
If the view is mergeable, we might want to
INSERT/UPDATE/DELETE into tables of this view. Preserve the
original sql command and 'duplicates' of the outer lex.
This is used later in set_trg_event_type_for_command.
*/
lex
->
sql_command
=
old_lex
->
sql_command
;
lex
->
duplicates
=
old_lex
->
duplicates
;
}
}
/*
This method has a dependency on the proper lock type being set,
so in case of views should be called here.
*/
lex
->
set_trg_event_type_for_tables
();
/*
/*
If we are opening this view as part of implicit LOCK TABLES, then
If we are opening this view as part of implicit LOCK TABLES, then
...
...
sql/sql_yacc.yy
View file @
675a4e8f
...
@@ -9644,13 +9644,13 @@ trigger_tail:
...
@@ -9644,13 +9644,13 @@ trigger_tail:
MYSQL_YYABORT;
MYSQL_YYABORT;
sp->reset_thd_mem_root(thd);
sp->reset_thd_mem_root(thd);
sp->init(lex);
sp->init(lex);
sp->m_type= TYPE_ENUM_TRIGGER;
sp->init_sp_name(thd, $3);
sp->init_sp_name(thd, $3);
lex->stmt_definition_begin= $2;
lex->stmt_definition_begin= $2;
lex->ident.str= $7;
lex->ident.str= $7;
lex->ident.length= $10 - $7;
lex->ident.length= $10 - $7;
sp->m_type= TYPE_ENUM_TRIGGER;
lex->sphead= sp;
lex->sphead= sp;
lex->spname= $3;
lex->spname= $3;
/*
/*
...
@@ -9728,9 +9728,9 @@ sp_tail:
...
@@ -9728,9 +9728,9 @@ sp_tail:
sp= new sp_head();
sp= new sp_head();
sp->reset_thd_mem_root(YYTHD);
sp->reset_thd_mem_root(YYTHD);
sp->init(lex);
sp->init(lex);
sp->m_type= TYPE_ENUM_PROCEDURE;
sp->init_sp_name(YYTHD, $3);
sp->init_sp_name(YYTHD, $3);
sp->m_type= TYPE_ENUM_PROCEDURE;
lex->sphead= sp;
lex->sphead= sp;
/*
/*
* We have to turn of CLIENT_MULTI_QUERIES while parsing a
* We have to turn of CLIENT_MULTI_QUERIES while parsing a
...
...
sql/table.cc
View file @
675a4e8f
...
@@ -1776,6 +1776,135 @@ void st_table::reset_item_list(List<Item> *item_list) const
...
@@ -1776,6 +1776,135 @@ void st_table::reset_item_list(List<Item> *item_list) const
}
}
}
}
/**
Set the initial purpose of this TABLE_LIST object in the list of
used tables. We need to track this information on table-by-
table basis, since when this table becomes an element of the
pre-locked list, it's impossible to identify which SQL
sub-statement it has been originally used in.
E.g.:
User request: SELECT * FROM t1 WHERE f1();
FUNCTION f1(): DELETE FROM t2; RETURN 1;
BEFORE DELETE trigger on t2: INSERT INTO t3 VALUES (old.a);
For this user request, the pre-locked list will contain t1, t2, t3
table elements, each needed for different DML.
This method is called immediately after parsing for tables
of the table list of the top-level select lex.
The trigger event map is updated to reflect INSERT, UPDATE, DELETE,
REPLACE, LOAD DATA, CREATE TABLE .. SELECT, CREATE TABLE ..
REPLACE SELECT statements, and additionally ON DUPLICATE KEY UPDATE
clause.
*/
void
TABLE_LIST
::
set_trg_event_type
(
const
st_lex
*
lex
)
{
enum
trg_event_type
trg_event
;
/*
Some auxiliary operations
(e.g. GRANT processing) create TABLE_LIST instances outside
the parser. Additionally, some commands (e.g. OPTIMIZE) change
the lock type for a table only after parsing is done. Luckily,
these do not fire triggers and do not need to pre-load them.
For these TABLE_LISTs set_trg_event_type is never called, and
trg_event_map is always empty. That means that the pre-locking
algorithm will ignore triggers defined on these tables, if
any, and the execution will either fail with an assert in
sql_trigger.cc or with an error that a used table was not
pre-locked, in case of a production build.
TODO: this usage pattern creates unnecessary module dependencies
and should be rewritten to go through the parser.
Table list instances created outside the parser in most cases
refer to mysql.* system tables. It is not allowed to have
a trigger on a system table, but keeping track of
initialization provides extra safety in case this limitation
is circumvented.
*/
/*
This is a fast check to filter out statements that do
not change data, or tables on the right side, in case of
INSERT .. SELECT, CREATE TABLE .. SELECT and so on.
Here we also filter out OPTIMIZE statement and non-updateable
views, for which lock_type is TL_UNLOCK or TL_READ after
parsing.
*/
if
(
static_cast
<
int
>
(
lock_type
)
<
static_cast
<
int
>
(
TL_WRITE_ALLOW_WRITE
))
return
;
switch
(
lex
->
sql_command
)
{
/*
Basic INSERT. If there is an additional ON DUPLIATE KEY UPDATE
clause, it will be handled later in this method.
*/
case
SQLCOM_INSERT
:
/* fall through */
case
SQLCOM_INSERT_SELECT
:
/*
LOAD DATA ... INFILE is expected to fire BEFORE/AFTER INSERT
triggers.
If the statement also has REPLACE clause, it will be
handled later in this method.
*/
case
SQLCOM_LOAD
:
/* fall through */
/*
REPLACE is semantically equivalent to INSERT. In case
of a primary or unique key conflict, it deletes the old
record and inserts a new one. So we also may need to
fire ON DELETE triggers. This functionality is handled
later in this method.
*/
case
SQLCOM_REPLACE
:
/* fall through */
case
SQLCOM_REPLACE_SELECT
:
/*
CREATE TABLE ... SELECT defaults to INSERT if the table or
view already exists. REPLACE option of CREATE TABLE ...
REPLACE SELECT is handled later in this method.
*/
case
SQLCOM_CREATE_TABLE
:
trg_event
=
TRG_EVENT_INSERT
;
break
;
/* Basic update and multi-update */
case
SQLCOM_UPDATE
:
/* fall through */
case
SQLCOM_UPDATE_MULTI
:
trg_event
=
TRG_EVENT_UPDATE
;
break
;
/* Basic delete and multi-delete */
case
SQLCOM_DELETE
:
/* fall through */
case
SQLCOM_DELETE_MULTI
:
trg_event
=
TRG_EVENT_DELETE
;
break
;
default:
/*
OK to return, since value of 'duplicates' is irrelevant
for non-updating commands.
*/
return
;
}
trg_event_map
|=
static_cast
<
uint8
>
(
1
<<
static_cast
<
int
>
(
trg_event
));
switch
(
lex
->
duplicates
)
{
case
DUP_UPDATE
:
trg_event
=
TRG_EVENT_UPDATE
;
break
;
case
DUP_REPLACE
:
trg_event
=
TRG_EVENT_DELETE
;
break
;
case
DUP_ERROR
:
default:
return
;
}
trg_event_map
|=
static_cast
<
uint8
>
(
1
<<
static_cast
<
int
>
(
trg_event
));
}
/*
/*
calculate md5 of query
calculate md5 of query
...
...
sql/table.h
View file @
675a4e8f
...
@@ -59,6 +59,17 @@ enum tmp_table_type {NO_TMP_TABLE=0,
...
@@ -59,6 +59,17 @@ enum tmp_table_type {NO_TMP_TABLE=0,
NON_TRANSACTIONAL_TMP_TABLE
=
1
,
TRANSACTIONAL_TMP_TABLE
=
2
,
NON_TRANSACTIONAL_TMP_TABLE
=
1
,
TRANSACTIONAL_TMP_TABLE
=
2
,
SYSTEM_TMP_TABLE
=
3
};
SYSTEM_TMP_TABLE
=
3
};
/** Event on which trigger is invoked. */
enum
trg_event_type
{
TRG_EVENT_INSERT
=
0
,
TRG_EVENT_UPDATE
=
1
,
TRG_EVENT_DELETE
=
2
,
TRG_EVENT_MAX
};
enum
frm_type_enum
enum
frm_type_enum
{
{
FRMTYPE_ERROR
=
0
,
FRMTYPE_ERROR
=
0
,
...
@@ -702,6 +713,13 @@ struct TABLE_LIST
...
@@ -702,6 +713,13 @@ struct TABLE_LIST
*/
*/
bool
create
;
bool
create
;
/**
Indicates what triggers we need to pre-load for this TABLE_LIST
when opening an associated TABLE. This is filled after
the parsed tree is created.
*/
uint8
trg_event_map
;
enum
enum_schema_table_state
schema_table_state
;
enum
enum_schema_table_state
schema_table_state
;
void
calc_md5
(
char
*
buffer
);
void
calc_md5
(
char
*
buffer
);
void
set_underlying_merge
();
void
set_underlying_merge
();
...
@@ -752,6 +770,7 @@ struct TABLE_LIST
...
@@ -752,6 +770,7 @@ struct TABLE_LIST
void
reinit_before_use
(
THD
*
thd
);
void
reinit_before_use
(
THD
*
thd
);
Item_subselect
*
containing_subselect
();
Item_subselect
*
containing_subselect
();
void
set_trg_event_type
(
const
st_lex
*
lex
);
private:
private:
bool
prep_check_option
(
THD
*
thd
,
uint8
check_opt_type
);
bool
prep_check_option
(
THD
*
thd
,
uint8
check_opt_type
);
bool
prep_where
(
THD
*
thd
,
Item
**
conds
,
bool
no_where_clause
);
bool
prep_where
(
THD
*
thd
,
Item
**
conds
,
bool
no_where_clause
);
...
...
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