Fix for bugs #5892/6182/8751/8758/10994 (based on Antony's patch)
"Triggers have the wrong namespace" "Triggers: duplicate names allowed" "Triggers: CREATE TRIGGER does not accept fully qualified names" "SHOW TRIGGERS"
This commit is contained in:
parent
bff3507b1d
commit
8a3e723b74
@ -48,6 +48,7 @@ TABLE_PRIVILEGES
|
|||||||
COLUMN_PRIVILEGES
|
COLUMN_PRIVILEGES
|
||||||
TABLE_CONSTRAINTS
|
TABLE_CONSTRAINTS
|
||||||
KEY_COLUMN_USAGE
|
KEY_COLUMN_USAGE
|
||||||
|
TRIGGERS
|
||||||
columns_priv
|
columns_priv
|
||||||
db
|
db
|
||||||
func
|
func
|
||||||
@ -77,6 +78,7 @@ c table_name
|
|||||||
TABLES TABLES
|
TABLES TABLES
|
||||||
TABLE_PRIVILEGES TABLE_PRIVILEGES
|
TABLE_PRIVILEGES TABLE_PRIVILEGES
|
||||||
TABLE_CONSTRAINTS TABLE_CONSTRAINTS
|
TABLE_CONSTRAINTS TABLE_CONSTRAINTS
|
||||||
|
TRIGGERS TRIGGERS
|
||||||
tables_priv tables_priv
|
tables_priv tables_priv
|
||||||
time_zone time_zone
|
time_zone time_zone
|
||||||
time_zone_leap_second time_zone_leap_second
|
time_zone_leap_second time_zone_leap_second
|
||||||
@ -94,6 +96,7 @@ c table_name
|
|||||||
TABLES TABLES
|
TABLES TABLES
|
||||||
TABLE_PRIVILEGES TABLE_PRIVILEGES
|
TABLE_PRIVILEGES TABLE_PRIVILEGES
|
||||||
TABLE_CONSTRAINTS TABLE_CONSTRAINTS
|
TABLE_CONSTRAINTS TABLE_CONSTRAINTS
|
||||||
|
TRIGGERS TRIGGERS
|
||||||
tables_priv tables_priv
|
tables_priv tables_priv
|
||||||
time_zone time_zone
|
time_zone time_zone
|
||||||
time_zone_leap_second time_zone_leap_second
|
time_zone_leap_second time_zone_leap_second
|
||||||
@ -111,6 +114,7 @@ c table_name
|
|||||||
TABLES TABLES
|
TABLES TABLES
|
||||||
TABLE_PRIVILEGES TABLE_PRIVILEGES
|
TABLE_PRIVILEGES TABLE_PRIVILEGES
|
||||||
TABLE_CONSTRAINTS TABLE_CONSTRAINTS
|
TABLE_CONSTRAINTS TABLE_CONSTRAINTS
|
||||||
|
TRIGGERS TRIGGERS
|
||||||
tables_priv tables_priv
|
tables_priv tables_priv
|
||||||
time_zone time_zone
|
time_zone time_zone
|
||||||
time_zone_leap_second time_zone_leap_second
|
time_zone_leap_second time_zone_leap_second
|
||||||
@ -577,6 +581,7 @@ Tables_in_information_schema (T%)
|
|||||||
TABLES
|
TABLES
|
||||||
TABLE_PRIVILEGES
|
TABLE_PRIVILEGES
|
||||||
TABLE_CONSTRAINTS
|
TABLE_CONSTRAINTS
|
||||||
|
TRIGGERS
|
||||||
create database information_schema;
|
create database information_schema;
|
||||||
ERROR HY000: Can't create database 'information_schema'; database exists
|
ERROR HY000: Can't create database 'information_schema'; database exists
|
||||||
use information_schema;
|
use information_schema;
|
||||||
@ -585,6 +590,7 @@ Tables_in_information_schema (T%) Table_type
|
|||||||
TABLES TEMPORARY
|
TABLES TEMPORARY
|
||||||
TABLE_PRIVILEGES TEMPORARY
|
TABLE_PRIVILEGES TEMPORARY
|
||||||
TABLE_CONSTRAINTS TEMPORARY
|
TABLE_CONSTRAINTS TEMPORARY
|
||||||
|
TRIGGERS TEMPORARY
|
||||||
create table t1(a int);
|
create table t1(a int);
|
||||||
ERROR 42S02: Unknown table 't1' in information_schema
|
ERROR 42S02: Unknown table 't1' in information_schema
|
||||||
use test;
|
use test;
|
||||||
@ -596,6 +602,7 @@ Tables_in_information_schema (T%)
|
|||||||
TABLES
|
TABLES
|
||||||
TABLE_PRIVILEGES
|
TABLE_PRIVILEGES
|
||||||
TABLE_CONSTRAINTS
|
TABLE_CONSTRAINTS
|
||||||
|
TRIGGERS
|
||||||
select table_name from tables where table_name='user';
|
select table_name from tables where table_name='user';
|
||||||
table_name
|
table_name
|
||||||
user
|
user
|
||||||
@ -690,7 +697,7 @@ CREATE TABLE t_crashme ( f1 BIGINT);
|
|||||||
CREATE VIEW a1 (t_CRASHME) AS SELECT f1 FROM t_crashme GROUP BY f1;
|
CREATE VIEW a1 (t_CRASHME) AS SELECT f1 FROM t_crashme GROUP BY f1;
|
||||||
CREATE VIEW a2 AS SELECT t_CRASHME FROM a1;
|
CREATE VIEW a2 AS SELECT t_CRASHME FROM a1;
|
||||||
count(*)
|
count(*)
|
||||||
100
|
101
|
||||||
drop view a2, a1;
|
drop view a2, a1;
|
||||||
drop table t_crashme;
|
drop table t_crashme;
|
||||||
select table_schema,table_name, column_name from
|
select table_schema,table_name, column_name from
|
||||||
@ -701,6 +708,8 @@ information_schema COLUMNS COLUMN_TYPE
|
|||||||
information_schema ROUTINES ROUTINE_DEFINITION
|
information_schema ROUTINES ROUTINE_DEFINITION
|
||||||
information_schema ROUTINES SQL_MODE
|
information_schema ROUTINES SQL_MODE
|
||||||
information_schema VIEWS VIEW_DEFINITION
|
information_schema VIEWS VIEW_DEFINITION
|
||||||
|
information_schema TRIGGERS ACTION_CONDITION
|
||||||
|
information_schema TRIGGERS ACTION_STATEMENT
|
||||||
select table_name, column_name, data_type from information_schema.columns
|
select table_name, column_name, data_type from information_schema.columns
|
||||||
where data_type = 'datetime';
|
where data_type = 'datetime';
|
||||||
table_name column_name data_type
|
table_name column_name data_type
|
||||||
@ -709,6 +718,7 @@ TABLES UPDATE_TIME datetime
|
|||||||
TABLES CHECK_TIME datetime
|
TABLES CHECK_TIME datetime
|
||||||
ROUTINES CREATED datetime
|
ROUTINES CREATED datetime
|
||||||
ROUTINES LAST_ALTERED datetime
|
ROUTINES LAST_ALTERED datetime
|
||||||
|
TRIGGERS CREATED datetime
|
||||||
SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES A
|
SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES A
|
||||||
WHERE NOT EXISTS
|
WHERE NOT EXISTS
|
||||||
(SELECT * FROM INFORMATION_SCHEMA.COLUMNS B
|
(SELECT * FROM INFORMATION_SCHEMA.COLUMNS B
|
||||||
@ -755,8 +765,71 @@ delete from mysql.db where user='mysqltest_4';
|
|||||||
flush privileges;
|
flush privileges;
|
||||||
SELECT table_schema, count(*) FROM information_schema.TABLES GROUP BY TABLE_SCHEMA;
|
SELECT table_schema, count(*) FROM information_schema.TABLES GROUP BY TABLE_SCHEMA;
|
||||||
table_schema count(*)
|
table_schema count(*)
|
||||||
information_schema 15
|
information_schema 16
|
||||||
mysql 17
|
mysql 17
|
||||||
|
create table t1 (i int, j int);
|
||||||
|
create trigger trg1 before insert on t1 for each row
|
||||||
|
begin
|
||||||
|
if new.j > 10 then
|
||||||
|
set new.j := 10;
|
||||||
|
end if;
|
||||||
|
end|
|
||||||
|
create trigger trg2 before update on t1 for each row
|
||||||
|
begin
|
||||||
|
if old.i % 2 = 0 then
|
||||||
|
set new.j := -1;
|
||||||
|
end if;
|
||||||
|
end|
|
||||||
|
create trigger trg3 after update on t1 for each row
|
||||||
|
begin
|
||||||
|
if new.j = -1 then
|
||||||
|
set @fired:= "Yes";
|
||||||
|
end if;
|
||||||
|
end|
|
||||||
|
show triggers;
|
||||||
|
Trigger Event Table Statement Timing Created
|
||||||
|
trg1 INSERT t1
|
||||||
|
begin
|
||||||
|
if new.j > 10 then
|
||||||
|
set new.j := 10;
|
||||||
|
end if;
|
||||||
|
end BEFORE NULL
|
||||||
|
trg2 UPDATE t1
|
||||||
|
begin
|
||||||
|
if old.i % 2 = 0 then
|
||||||
|
set new.j := -1;
|
||||||
|
end if;
|
||||||
|
end BEFORE NULL
|
||||||
|
trg3 UPDATE t1
|
||||||
|
begin
|
||||||
|
if new.j = -1 then
|
||||||
|
set @fired:= "Yes";
|
||||||
|
end if;
|
||||||
|
end AFTER NULL
|
||||||
|
select * from information_schema.triggers;
|
||||||
|
TRIGGER_CATALOG TRIGGER_SCHEMA TRIGGER_NAME EVENT_MANIPULATION EVENT_OBJECT_CATALOG EVENT_OBJECT_SCHEMA EVENT_OBJECT_TABLE ACTION_ORDER ACTION_CONDITION ACTION_STATEMENT ACTION_ORIENTATION ACTION_TIMING ACTION_REFERENCE_OLD_TABLE ACTION_REFERENCE_NEW_TABLE ACTION_REFERENCE_OLD_ROW ACTION_REFERENCE_NEW_ROW CREATED
|
||||||
|
NULL test trg1 INSERT NULL test t1 0 NULL
|
||||||
|
begin
|
||||||
|
if new.j > 10 then
|
||||||
|
set new.j := 10;
|
||||||
|
end if;
|
||||||
|
end ROW BEFORE NULL NULL OLD NEW NULL
|
||||||
|
NULL test trg2 UPDATE NULL test t1 0 NULL
|
||||||
|
begin
|
||||||
|
if old.i % 2 = 0 then
|
||||||
|
set new.j := -1;
|
||||||
|
end if;
|
||||||
|
end ROW BEFORE NULL NULL OLD NEW NULL
|
||||||
|
NULL test trg3 UPDATE NULL test t1 0 NULL
|
||||||
|
begin
|
||||||
|
if new.j = -1 then
|
||||||
|
set @fired:= "Yes";
|
||||||
|
end if;
|
||||||
|
end ROW AFTER NULL NULL OLD NEW NULL
|
||||||
|
drop trigger trg1;
|
||||||
|
drop trigger trg2;
|
||||||
|
drop trigger trg3;
|
||||||
|
drop table t1;
|
||||||
create database mysqltest;
|
create database mysqltest;
|
||||||
create table mysqltest.t1 (f1 int, f2 int);
|
create table mysqltest.t1 (f1 int, f2 int);
|
||||||
create table mysqltest.t2 (f1 int);
|
create table mysqltest.t2 (f1 int);
|
||||||
|
@ -16,11 +16,13 @@ TABLE_PRIVILEGES
|
|||||||
COLUMN_PRIVILEGES
|
COLUMN_PRIVILEGES
|
||||||
TABLE_CONSTRAINTS
|
TABLE_CONSTRAINTS
|
||||||
KEY_COLUMN_USAGE
|
KEY_COLUMN_USAGE
|
||||||
|
TRIGGERS
|
||||||
show tables from INFORMATION_SCHEMA like 'T%';
|
show tables from INFORMATION_SCHEMA like 'T%';
|
||||||
Tables_in_information_schema (T%)
|
Tables_in_information_schema (T%)
|
||||||
TABLES
|
TABLES
|
||||||
TABLE_PRIVILEGES
|
TABLE_PRIVILEGES
|
||||||
TABLE_CONSTRAINTS
|
TABLE_CONSTRAINTS
|
||||||
|
TRIGGERS
|
||||||
create database `inf%`;
|
create database `inf%`;
|
||||||
use `inf%`;
|
use `inf%`;
|
||||||
show tables;
|
show tables;
|
||||||
|
@ -237,7 +237,7 @@ select * from t1;
|
|||||||
a
|
a
|
||||||
10
|
10
|
||||||
delete from t1;
|
delete from t1;
|
||||||
drop trigger t1.trg;
|
drop trigger trg;
|
||||||
insert into t1 values (1);
|
insert into t1 values (1);
|
||||||
select * from t1;
|
select * from t1;
|
||||||
a
|
a
|
||||||
@ -248,7 +248,7 @@ master-bin.000002 # Query 1 # use `mysqltest1`; delete from t1
|
|||||||
master-bin.000002 # Query 1 # use `mysqltest1`; create trigger trg before insert on t1 for each row set new.a= 10
|
master-bin.000002 # Query 1 # use `mysqltest1`; create trigger trg before insert on t1 for each row set new.a= 10
|
||||||
master-bin.000002 # Query 1 # use `mysqltest1`; insert into t1 values (1)
|
master-bin.000002 # Query 1 # use `mysqltest1`; insert into t1 values (1)
|
||||||
master-bin.000002 # Query 1 # use `mysqltest1`; delete from t1
|
master-bin.000002 # Query 1 # use `mysqltest1`; delete from t1
|
||||||
master-bin.000002 # Query 1 # use `mysqltest1`; drop trigger t1.trg
|
master-bin.000002 # Query 1 # use `mysqltest1`; drop trigger trg
|
||||||
master-bin.000002 # Query 1 # use `mysqltest1`; insert into t1 values (1)
|
master-bin.000002 # Query 1 # use `mysqltest1`; insert into t1 values (1)
|
||||||
select * from t1;
|
select * from t1;
|
||||||
a
|
a
|
||||||
|
@ -12,13 +12,13 @@ insert into t1 values (1);
|
|||||||
select @a;
|
select @a;
|
||||||
@a
|
@a
|
||||||
1
|
1
|
||||||
drop trigger t1.trg;
|
drop trigger trg;
|
||||||
create trigger trg before insert on t1 for each row set @a:=new.i;
|
create trigger trg before insert on t1 for each row set @a:=new.i;
|
||||||
insert into t1 values (123);
|
insert into t1 values (123);
|
||||||
select @a;
|
select @a;
|
||||||
@a
|
@a
|
||||||
123
|
123
|
||||||
drop trigger t1.trg;
|
drop trigger trg;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
create table t1 (i int not null, j int);
|
create table t1 (i int not null, j int);
|
||||||
create trigger trg before insert on t1 for each row
|
create trigger trg before insert on t1 for each row
|
||||||
@ -33,7 +33,7 @@ select * from t1|
|
|||||||
i j
|
i j
|
||||||
1 10
|
1 10
|
||||||
2 3
|
2 3
|
||||||
drop trigger t1.trg|
|
drop trigger trg|
|
||||||
drop table t1|
|
drop table t1|
|
||||||
create table t1 (i int not null primary key);
|
create table t1 (i int not null primary key);
|
||||||
create trigger trg after insert on t1 for each row
|
create trigger trg after insert on t1 for each row
|
||||||
@ -43,7 +43,7 @@ insert into t1 values (2),(3),(4),(5);
|
|||||||
select @a;
|
select @a;
|
||||||
@a
|
@a
|
||||||
2:3:4:5
|
2:3:4:5
|
||||||
drop trigger t1.trg;
|
drop trigger trg;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
create table t1 (aid int not null primary key, balance int not null default 0);
|
create table t1 (aid int not null primary key, balance int not null default 0);
|
||||||
insert into t1 values (1, 1000), (2,3000);
|
insert into t1 values (1, 1000), (2,3000);
|
||||||
@ -65,7 +65,7 @@ Too big change for aid = 2
|
|||||||
aid balance
|
aid balance
|
||||||
1 1500
|
1 1500
|
||||||
2 3000
|
2 3000
|
||||||
drop trigger t1.trg|
|
drop trigger trg|
|
||||||
drop table t1|
|
drop table t1|
|
||||||
create table t1 (i int);
|
create table t1 (i int);
|
||||||
insert into t1 values (1),(2),(3),(4);
|
insert into t1 values (1),(2),(3),(4);
|
||||||
@ -76,7 +76,7 @@ update t1 set i=3;
|
|||||||
select @total_change;
|
select @total_change;
|
||||||
@total_change
|
@total_change
|
||||||
2
|
2
|
||||||
drop trigger t1.trg;
|
drop trigger trg;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
create table t1 (i int);
|
create table t1 (i int);
|
||||||
insert into t1 values (1),(2),(3),(4);
|
insert into t1 values (1),(2),(3),(4);
|
||||||
@ -87,7 +87,7 @@ delete from t1 where i <= 3;
|
|||||||
select @del_sum;
|
select @del_sum;
|
||||||
@del_sum
|
@del_sum
|
||||||
6
|
6
|
||||||
drop trigger t1.trg;
|
drop trigger trg;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
create table t1 (i int);
|
create table t1 (i int);
|
||||||
insert into t1 values (1),(2),(3),(4);
|
insert into t1 values (1),(2),(3),(4);
|
||||||
@ -97,7 +97,7 @@ delete from t1 where i <> 0;
|
|||||||
select @del;
|
select @del;
|
||||||
@del
|
@del
|
||||||
1
|
1
|
||||||
drop trigger t1.trg;
|
drop trigger trg;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
create table t1 (i int, j int);
|
create table t1 (i int, j int);
|
||||||
create trigger trg1 before insert on t1 for each row
|
create trigger trg1 before insert on t1 for each row
|
||||||
@ -137,9 +137,9 @@ i j
|
|||||||
1 20
|
1 20
|
||||||
2 -1
|
2 -1
|
||||||
3 20
|
3 20
|
||||||
drop trigger t1.trg1;
|
drop trigger trg1;
|
||||||
drop trigger t1.trg2;
|
drop trigger trg2;
|
||||||
drop trigger t1.trg3;
|
drop trigger trg3;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
create table t1 (id int not null primary key, data int);
|
create table t1 (id int not null primary key, data int);
|
||||||
create trigger t1_bi before insert on t1 for each row
|
create trigger t1_bi before insert on t1 for each row
|
||||||
@ -197,7 +197,7 @@ select * from t2;
|
|||||||
event
|
event
|
||||||
INSERT INTO t1 id=1 data='one'
|
INSERT INTO t1 id=1 data='one'
|
||||||
INSERT INTO t1 id=2 data='two'
|
INSERT INTO t1 id=2 data='two'
|
||||||
drop trigger t1.t1_ai;
|
drop trigger t1_ai;
|
||||||
create trigger t1_bi before insert on t1 for each row
|
create trigger t1_bi before insert on t1 for each row
|
||||||
begin
|
begin
|
||||||
if exists (select id from t3 where id=new.fk) then
|
if exists (select id from t3 where id=new.fk) then
|
||||||
@ -271,6 +271,7 @@ id copy
|
|||||||
3 NULL
|
3 NULL
|
||||||
drop table t1, t2;
|
drop table t1, t2;
|
||||||
create table t1 (i int);
|
create table t1 (i int);
|
||||||
|
create table t3 (i int);
|
||||||
create trigger trg before insert on t1 for each row set @a:= old.i;
|
create trigger trg before insert on t1 for each row set @a:= old.i;
|
||||||
ERROR HY000: There is no OLD row in on INSERT trigger
|
ERROR HY000: There is no OLD row in on INSERT trigger
|
||||||
create trigger trg before delete on t1 for each row set @a:= new.i;
|
create trigger trg before delete on t1 for each row set @a:= new.i;
|
||||||
@ -292,14 +293,19 @@ create trigger trg after insert on t1 for each row set @a:=1;
|
|||||||
ERROR HY000: Trigger already exists
|
ERROR HY000: Trigger already exists
|
||||||
create trigger trg2 before insert on t1 for each row set @a:=1;
|
create trigger trg2 before insert on t1 for each row set @a:=1;
|
||||||
ERROR HY000: Trigger already exists
|
ERROR HY000: Trigger already exists
|
||||||
drop trigger t1.trg;
|
create trigger trg before insert on t3 for each row set @a:=1;
|
||||||
drop trigger t1.trg;
|
ERROR HY000: Trigger already exists
|
||||||
|
create trigger trg2 before insert on t3 for each row set @a:=1;
|
||||||
|
drop trigger trg2;
|
||||||
|
drop trigger trg;
|
||||||
|
drop trigger trg;
|
||||||
ERROR HY000: Trigger does not exist
|
ERROR HY000: Trigger does not exist
|
||||||
create view v1 as select * from t1;
|
create view v1 as select * from t1;
|
||||||
create trigger trg before insert on v1 for each row set @a:=1;
|
create trigger trg before insert on v1 for each row set @a:=1;
|
||||||
ERROR HY000: 'test.v1' is not BASE TABLE
|
ERROR HY000: 'test.v1' is not BASE TABLE
|
||||||
drop view v1;
|
drop view v1;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
drop table t3;
|
||||||
create temporary table t1 (i int);
|
create temporary table t1 (i int);
|
||||||
create trigger trg before insert on t1 for each row set @a:=1;
|
create trigger trg before insert on t1 for each row set @a:=1;
|
||||||
ERROR HY000: Trigger's 't1' is view or temporary table
|
ERROR HY000: Trigger's 't1' is view or temporary table
|
||||||
@ -307,7 +313,7 @@ drop table t1;
|
|||||||
create table t1 (x1col char);
|
create table t1 (x1col char);
|
||||||
create trigger tx1 before insert on t1 for each row set new.x1col = 'x';
|
create trigger tx1 before insert on t1 for each row set new.x1col = 'x';
|
||||||
insert into t1 values ('y');
|
insert into t1 values ('y');
|
||||||
drop trigger t1.tx1;
|
drop trigger tx1;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
create table t1 (i int) engine=myisam;
|
create table t1 (i int) engine=myisam;
|
||||||
insert into t1 values (1), (2);
|
insert into t1 values (1), (2);
|
||||||
@ -318,8 +324,8 @@ delete from t1;
|
|||||||
select @del_before, @del_after;
|
select @del_before, @del_after;
|
||||||
@del_before @del_after
|
@del_before @del_after
|
||||||
3 3
|
3 3
|
||||||
drop trigger t1.trg1;
|
drop trigger trg1;
|
||||||
drop trigger t1.trg2;
|
drop trigger trg2;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
create table t1 (a int);
|
create table t1 (a int);
|
||||||
create trigger trg1 before insert on t1 for each row set new.a= 10;
|
create trigger trg1 before insert on t1 for each row set new.a= 10;
|
||||||
@ -336,6 +342,15 @@ create table t1 (i int);
|
|||||||
create trigger trg1 before insert on t1 for each row set @a:= 1;
|
create trigger trg1 before insert on t1 for each row set @a:= 1;
|
||||||
drop database mysqltest;
|
drop database mysqltest;
|
||||||
use test;
|
use test;
|
||||||
|
create database mysqltest;
|
||||||
|
create table mysqltest.t1 (i int);
|
||||||
|
create trigger trg1 before insert on mysqltest.t1 for each row set @a:= 1;
|
||||||
|
ERROR HY000: Trigger in wrong schema
|
||||||
|
use mysqltest;
|
||||||
|
create trigger test.trg1 before insert on t1 for each row set @a:= 1;
|
||||||
|
ERROR HY000: Trigger in wrong schema
|
||||||
|
drop database mysqltest;
|
||||||
|
use test;
|
||||||
create table t1 (i int, j int default 10, k int not null, key (k));
|
create table t1 (i int, j int default 10, k int not null, key (k));
|
||||||
create table t2 (i int);
|
create table t2 (i int);
|
||||||
insert into t1 (i, k) values (1, 1);
|
insert into t1 (i, k) values (1, 1);
|
||||||
@ -549,7 +564,7 @@ i k
|
|||||||
1 1
|
1 1
|
||||||
2 2
|
2 2
|
||||||
alter table t1 add primary key (i);
|
alter table t1 add primary key (i);
|
||||||
drop trigger t1.bi;
|
drop trigger bi;
|
||||||
insert into t1 values (2, 4) on duplicate key update k= k + 10;
|
insert into t1 values (2, 4) on duplicate key update k= k + 10;
|
||||||
ERROR 42S22: Unknown column 'bt' in 'NEW'
|
ERROR 42S22: Unknown column 'bt' in 'NEW'
|
||||||
select * from t1;
|
select * from t1;
|
||||||
@ -578,5 +593,5 @@ create trigger t1_bu before update on t1 for each row set new.col1= bug5893();
|
|||||||
drop function bug5893;
|
drop function bug5893;
|
||||||
update t1 set col2 = 4;
|
update t1 set col2 = 4;
|
||||||
ERROR 42000: FUNCTION test.bug5893 does not exist
|
ERROR 42000: FUNCTION test.bug5893 does not exist
|
||||||
drop trigger t1.t1_bu;
|
drop trigger t1_bu;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
@ -1245,7 +1245,7 @@ select * from v1;
|
|||||||
s1
|
s1
|
||||||
select * from t1;
|
select * from t1;
|
||||||
s1
|
s1
|
||||||
drop trigger t1.t1_bi;
|
drop trigger t1_bi;
|
||||||
drop view v1;
|
drop view v1;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
create table t1 (s1 tinyint);
|
create table t1 (s1 tinyint);
|
||||||
|
@ -505,6 +505,41 @@ flush privileges;
|
|||||||
#
|
#
|
||||||
SELECT table_schema, count(*) FROM information_schema.TABLES GROUP BY TABLE_SCHEMA;
|
SELECT table_schema, count(*) FROM information_schema.TABLES GROUP BY TABLE_SCHEMA;
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# TRIGGERS table test
|
||||||
|
#
|
||||||
|
create table t1 (i int, j int);
|
||||||
|
|
||||||
|
delimiter |;
|
||||||
|
create trigger trg1 before insert on t1 for each row
|
||||||
|
begin
|
||||||
|
if new.j > 10 then
|
||||||
|
set new.j := 10;
|
||||||
|
end if;
|
||||||
|
end|
|
||||||
|
create trigger trg2 before update on t1 for each row
|
||||||
|
begin
|
||||||
|
if old.i % 2 = 0 then
|
||||||
|
set new.j := -1;
|
||||||
|
end if;
|
||||||
|
end|
|
||||||
|
create trigger trg3 after update on t1 for each row
|
||||||
|
begin
|
||||||
|
if new.j = -1 then
|
||||||
|
set @fired:= "Yes";
|
||||||
|
end if;
|
||||||
|
end|
|
||||||
|
delimiter ;|
|
||||||
|
show triggers;
|
||||||
|
select * from information_schema.triggers;
|
||||||
|
|
||||||
|
drop trigger trg1;
|
||||||
|
drop trigger trg2;
|
||||||
|
drop trigger trg3;
|
||||||
|
drop table t1;
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Bug #10964 Information Schema:Authorization check on privilege tables is improper
|
# Bug #10964 Information Schema:Authorization check on privilege tables is improper
|
||||||
#
|
#
|
||||||
|
@ -249,7 +249,7 @@ select * from t1;
|
|||||||
|
|
||||||
connection master;
|
connection master;
|
||||||
delete from t1;
|
delete from t1;
|
||||||
drop trigger t1.trg;
|
drop trigger trg;
|
||||||
insert into t1 values (1);
|
insert into t1 values (1);
|
||||||
select * from t1;
|
select * from t1;
|
||||||
--replace_column 2 # 5 #
|
--replace_column 2 # 5 #
|
||||||
|
@ -17,13 +17,13 @@ set @a:=0;
|
|||||||
select @a;
|
select @a;
|
||||||
insert into t1 values (1);
|
insert into t1 values (1);
|
||||||
select @a;
|
select @a;
|
||||||
drop trigger t1.trg;
|
drop trigger trg;
|
||||||
|
|
||||||
# let us test simple trigger reading some values
|
# let us test simple trigger reading some values
|
||||||
create trigger trg before insert on t1 for each row set @a:=new.i;
|
create trigger trg before insert on t1 for each row set @a:=new.i;
|
||||||
insert into t1 values (123);
|
insert into t1 values (123);
|
||||||
select @a;
|
select @a;
|
||||||
drop trigger t1.trg;
|
drop trigger trg;
|
||||||
|
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ end|
|
|||||||
insert into t1 (i) values (1)|
|
insert into t1 (i) values (1)|
|
||||||
insert into t1 (i,j) values (2, 3)|
|
insert into t1 (i,j) values (2, 3)|
|
||||||
select * from t1|
|
select * from t1|
|
||||||
drop trigger t1.trg|
|
drop trigger trg|
|
||||||
drop table t1|
|
drop table t1|
|
||||||
delimiter ;|
|
delimiter ;|
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ create trigger trg after insert on t1 for each row
|
|||||||
set @a:="";
|
set @a:="";
|
||||||
insert into t1 values (2),(3),(4),(5);
|
insert into t1 values (2),(3),(4),(5);
|
||||||
select @a;
|
select @a;
|
||||||
drop trigger t1.trg;
|
drop trigger trg;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
|
||||||
# PS doesn't work with multi-row statements
|
# PS doesn't work with multi-row statements
|
||||||
@ -75,7 +75,7 @@ set @update_failed:=""|
|
|||||||
update t1 set balance=1500|
|
update t1 set balance=1500|
|
||||||
select @update_failed;
|
select @update_failed;
|
||||||
select * from t1|
|
select * from t1|
|
||||||
drop trigger t1.trg|
|
drop trigger trg|
|
||||||
drop table t1|
|
drop table t1|
|
||||||
delimiter ;|
|
delimiter ;|
|
||||||
--enable_ps_protocol
|
--enable_ps_protocol
|
||||||
@ -88,7 +88,7 @@ create trigger trg after update on t1 for each row
|
|||||||
set @total_change:=0;
|
set @total_change:=0;
|
||||||
update t1 set i=3;
|
update t1 set i=3;
|
||||||
select @total_change;
|
select @total_change;
|
||||||
drop trigger t1.trg;
|
drop trigger trg;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
|
||||||
# Before delete trigger
|
# Before delete trigger
|
||||||
@ -100,7 +100,7 @@ create trigger trg before delete on t1 for each row
|
|||||||
set @del_sum:= 0;
|
set @del_sum:= 0;
|
||||||
delete from t1 where i <= 3;
|
delete from t1 where i <= 3;
|
||||||
select @del_sum;
|
select @del_sum;
|
||||||
drop trigger t1.trg;
|
drop trigger trg;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
|
||||||
# After delete trigger.
|
# After delete trigger.
|
||||||
@ -111,7 +111,7 @@ create trigger trg after delete on t1 for each row set @del:= 1;
|
|||||||
set @del:= 0;
|
set @del:= 0;
|
||||||
delete from t1 where i <> 0;
|
delete from t1 where i <> 0;
|
||||||
select @del;
|
select @del;
|
||||||
drop trigger t1.trg;
|
drop trigger trg;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
|
||||||
# Several triggers on one table
|
# Several triggers on one table
|
||||||
@ -145,9 +145,9 @@ update t1 set j= 20;
|
|||||||
select @fired;
|
select @fired;
|
||||||
select * from t1;
|
select * from t1;
|
||||||
|
|
||||||
drop trigger t1.trg1;
|
drop trigger trg1;
|
||||||
drop trigger t1.trg2;
|
drop trigger trg2;
|
||||||
drop trigger t1.trg3;
|
drop trigger trg3;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
|
||||||
|
|
||||||
@ -212,7 +212,7 @@ create trigger t1_ai after insert on t1 for each row
|
|||||||
insert into t1 (id, data) values (1, "one"), (2, "two");
|
insert into t1 (id, data) values (1, "one"), (2, "two");
|
||||||
select * from t1;
|
select * from t1;
|
||||||
select * from t2;
|
select * from t2;
|
||||||
drop trigger t1.t1_ai;
|
drop trigger t1_ai;
|
||||||
# Trigger which uses couple of tables (and partially emulates FK constraint)
|
# Trigger which uses couple of tables (and partially emulates FK constraint)
|
||||||
delimiter |;
|
delimiter |;
|
||||||
create trigger t1_bi before insert on t1 for each row
|
create trigger t1_bi before insert on t1 for each row
|
||||||
@ -282,6 +282,7 @@ drop table t1, t2;
|
|||||||
# Test of wrong column specifiers in triggers
|
# Test of wrong column specifiers in triggers
|
||||||
#
|
#
|
||||||
create table t1 (i int);
|
create table t1 (i int);
|
||||||
|
create table t3 (i int);
|
||||||
|
|
||||||
--error 1363
|
--error 1363
|
||||||
create trigger trg before insert on t1 for each row set @a:= old.i;
|
create trigger trg before insert on t1 for each row set @a:= old.i;
|
||||||
@ -301,7 +302,7 @@ create trigger trg before update on t1 for each row set @a:=old.j;
|
|||||||
|
|
||||||
#
|
#
|
||||||
# Let us test various trigger creation errors
|
# Let us test various trigger creation errors
|
||||||
#
|
# Also quickly test table namespace (bug#5892/6182)
|
||||||
#
|
#
|
||||||
--error 1146
|
--error 1146
|
||||||
create trigger trg before insert on t2 for each row set @a:=1;
|
create trigger trg before insert on t2 for each row set @a:=1;
|
||||||
@ -311,10 +312,14 @@ create trigger trg before insert on t1 for each row set @a:=1;
|
|||||||
create trigger trg after insert on t1 for each row set @a:=1;
|
create trigger trg after insert on t1 for each row set @a:=1;
|
||||||
--error 1359
|
--error 1359
|
||||||
create trigger trg2 before insert on t1 for each row set @a:=1;
|
create trigger trg2 before insert on t1 for each row set @a:=1;
|
||||||
drop trigger t1.trg;
|
--error 1359
|
||||||
|
create trigger trg before insert on t3 for each row set @a:=1;
|
||||||
|
create trigger trg2 before insert on t3 for each row set @a:=1;
|
||||||
|
drop trigger trg2;
|
||||||
|
drop trigger trg;
|
||||||
|
|
||||||
--error 1360
|
--error 1360
|
||||||
drop trigger t1.trg;
|
drop trigger trg;
|
||||||
|
|
||||||
create view v1 as select * from t1;
|
create view v1 as select * from t1;
|
||||||
--error 1347
|
--error 1347
|
||||||
@ -322,6 +327,7 @@ create trigger trg before insert on v1 for each row set @a:=1;
|
|||||||
drop view v1;
|
drop view v1;
|
||||||
|
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
drop table t3;
|
||||||
|
|
||||||
create temporary table t1 (i int);
|
create temporary table t1 (i int);
|
||||||
--error 1361
|
--error 1361
|
||||||
@ -339,7 +345,7 @@ drop table t1;
|
|||||||
create table t1 (x1col char);
|
create table t1 (x1col char);
|
||||||
create trigger tx1 before insert on t1 for each row set new.x1col = 'x';
|
create trigger tx1 before insert on t1 for each row set new.x1col = 'x';
|
||||||
insert into t1 values ('y');
|
insert into t1 values ('y');
|
||||||
drop trigger t1.tx1;
|
drop trigger tx1;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -355,8 +361,8 @@ create trigger trg2 after delete on t1 for each row set @del_after:= @del_after
|
|||||||
set @del_before:=0, @del_after:= 0;
|
set @del_before:=0, @del_after:= 0;
|
||||||
delete from t1;
|
delete from t1;
|
||||||
select @del_before, @del_after;
|
select @del_before, @del_after;
|
||||||
drop trigger t1.trg1;
|
drop trigger trg1;
|
||||||
drop trigger t1.trg2;
|
drop trigger trg2;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
|
||||||
# Test for bug #5859 "DROP TABLE does not drop triggers". Trigger should not
|
# Test for bug #5859 "DROP TABLE does not drop triggers". Trigger should not
|
||||||
@ -378,6 +384,19 @@ create trigger trg1 before insert on t1 for each row set @a:= 1;
|
|||||||
drop database mysqltest;
|
drop database mysqltest;
|
||||||
use test;
|
use test;
|
||||||
|
|
||||||
|
# Test for bug #8791
|
||||||
|
# "Triggers: Allowed to create triggers on a subject table in a different DB".
|
||||||
|
create database mysqltest;
|
||||||
|
create table mysqltest.t1 (i int);
|
||||||
|
--error 1429
|
||||||
|
create trigger trg1 before insert on mysqltest.t1 for each row set @a:= 1;
|
||||||
|
use mysqltest;
|
||||||
|
--error 1429
|
||||||
|
create trigger test.trg1 before insert on t1 for each row set @a:= 1;
|
||||||
|
drop database mysqltest;
|
||||||
|
use test;
|
||||||
|
|
||||||
|
|
||||||
# Test for bug #5860 "Multi-table UPDATE does not activate update triggers"
|
# Test for bug #5860 "Multi-table UPDATE does not activate update triggers"
|
||||||
# We will also test how delete triggers wor for multi-table DELETE.
|
# We will also test how delete triggers wor for multi-table DELETE.
|
||||||
create table t1 (i int, j int default 10, k int not null, key (k));
|
create table t1 (i int, j int default 10, k int not null, key (k));
|
||||||
@ -559,7 +578,7 @@ select * from t1;
|
|||||||
# To test properly code-paths different from those that are used
|
# To test properly code-paths different from those that are used
|
||||||
# in ordinary INSERT we need to drop "before insert" trigger.
|
# in ordinary INSERT we need to drop "before insert" trigger.
|
||||||
alter table t1 add primary key (i);
|
alter table t1 add primary key (i);
|
||||||
drop trigger t1.bi;
|
drop trigger bi;
|
||||||
--error 1054
|
--error 1054
|
||||||
insert into t1 values (2, 4) on duplicate key update k= k + 10;
|
insert into t1 values (2, 4) on duplicate key update k= k + 10;
|
||||||
select * from t1;
|
select * from t1;
|
||||||
@ -589,5 +608,5 @@ drop function bug5893;
|
|||||||
--error 1305
|
--error 1305
|
||||||
update t1 set col2 = 4;
|
update t1 set col2 = 4;
|
||||||
# This should not crash server too.
|
# This should not crash server too.
|
||||||
drop trigger t1.t1_bu;
|
drop trigger t1_bu;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
@ -1182,7 +1182,7 @@ create view v1 as select * from t1 where s1 <> 127 with check option;
|
|||||||
insert into v1 values (0);
|
insert into v1 values (0);
|
||||||
select * from v1;
|
select * from v1;
|
||||||
select * from t1;
|
select * from t1;
|
||||||
drop trigger t1.t1_bi;
|
drop trigger t1_bi;
|
||||||
drop view v1;
|
drop view v1;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
|
||||||
|
@ -2434,6 +2434,7 @@ TYPELIB *ha_known_exts(void)
|
|||||||
|
|
||||||
known_extensions_id= mysys_usage_id;
|
known_extensions_id= mysys_usage_id;
|
||||||
found_exts.push_back((char*) triggers_file_ext);
|
found_exts.push_back((char*) triggers_file_ext);
|
||||||
|
found_exts.push_back((char*) trigname_file_ext);
|
||||||
for (types= sys_table_types; types->type; types++)
|
for (types= sys_table_types; types->type; types++)
|
||||||
{
|
{
|
||||||
if (*types->value == SHOW_OPTION_YES)
|
if (*types->value == SHOW_OPTION_YES)
|
||||||
|
@ -1781,7 +1781,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
enum trg_action_time_type
|
enum trg_action_time_type
|
||||||
{
|
{
|
||||||
TRG_ACTION_BEFORE= 0, TRG_ACTION_AFTER= 1
|
TRG_ACTION_BEFORE= 0, TRG_ACTION_AFTER= 1, TRG_ACTION_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1789,7 +1789,7 @@ enum trg_action_time_type
|
|||||||
*/
|
*/
|
||||||
enum trg_event_type
|
enum trg_event_type
|
||||||
{
|
{
|
||||||
TRG_EVENT_INSERT= 0 , TRG_EVENT_UPDATE= 1, TRG_EVENT_DELETE= 2
|
TRG_EVENT_INSERT= 0 , TRG_EVENT_UPDATE= 1, TRG_EVENT_DELETE= 2, TRG_EVENT_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
class Table_triggers_list;
|
class Table_triggers_list;
|
||||||
|
@ -497,6 +497,7 @@ static SYMBOL symbols[] = {
|
|||||||
{ "TRAILING", SYM(TRAILING)},
|
{ "TRAILING", SYM(TRAILING)},
|
||||||
{ "TRANSACTION", SYM(TRANSACTION_SYM)},
|
{ "TRANSACTION", SYM(TRANSACTION_SYM)},
|
||||||
{ "TRIGGER", SYM(TRIGGER_SYM)},
|
{ "TRIGGER", SYM(TRIGGER_SYM)},
|
||||||
|
{ "TRIGGERS", SYM(TRIGGERS_SYM)},
|
||||||
{ "TRUE", SYM(TRUE_SYM)},
|
{ "TRUE", SYM(TRUE_SYM)},
|
||||||
{ "TRUNCATE", SYM(TRUNCATE_SYM)},
|
{ "TRUNCATE", SYM(TRUNCATE_SYM)},
|
||||||
{ "TYPE", SYM(TYPE_SYM)},
|
{ "TYPE", SYM(TYPE_SYM)},
|
||||||
|
@ -1082,6 +1082,7 @@ extern const char **errmesg; /* Error messages */
|
|||||||
extern const char *myisam_recover_options_str;
|
extern const char *myisam_recover_options_str;
|
||||||
extern const char *in_left_expr_name, *in_additional_cond;
|
extern const char *in_left_expr_name, *in_additional_cond;
|
||||||
extern const char * const triggers_file_ext;
|
extern const char * const triggers_file_ext;
|
||||||
|
extern const char * const trigname_file_ext;
|
||||||
extern Eq_creator eq_creator;
|
extern Eq_creator eq_creator;
|
||||||
extern Ne_creator ne_creator;
|
extern Ne_creator ne_creator;
|
||||||
extern Gt_creator gt_creator;
|
extern Gt_creator gt_creator;
|
||||||
|
@ -5722,6 +5722,7 @@ struct show_var_st status_vars[]= {
|
|||||||
{"Com_show_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STATUS]), SHOW_LONG_STATUS},
|
{"Com_show_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STATUS]), SHOW_LONG_STATUS},
|
||||||
{"Com_show_storage_engines", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STORAGE_ENGINES]), SHOW_LONG_STATUS},
|
{"Com_show_storage_engines", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STORAGE_ENGINES]), SHOW_LONG_STATUS},
|
||||||
{"Com_show_tables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLES]), SHOW_LONG_STATUS},
|
{"Com_show_tables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLES]), SHOW_LONG_STATUS},
|
||||||
|
{"Com_show_triggers", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TRIGGERS]), SHOW_LONG_STATUS},
|
||||||
{"Com_show_variables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_VARIABLES]), SHOW_LONG_STATUS},
|
{"Com_show_variables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_VARIABLES]), SHOW_LONG_STATUS},
|
||||||
{"Com_show_warnings", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_WARNS]), SHOW_LONG_STATUS},
|
{"Com_show_warnings", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_WARNS]), SHOW_LONG_STATUS},
|
||||||
{"Com_slave_start", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SLAVE_START]), SHOW_LONG_STATUS},
|
{"Com_slave_start", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SLAVE_START]), SHOW_LONG_STATUS},
|
||||||
|
@ -5370,3 +5370,5 @@ ER_SCALE_BIGGER_THAN_PRECISION 42000 S1009
|
|||||||
eng "Scale may not be larger than the precision (column '%-.64s')."
|
eng "Scale may not be larger than the precision (column '%-.64s')."
|
||||||
ER_WRONG_LOCK_OF_SYSTEM_TABLE
|
ER_WRONG_LOCK_OF_SYSTEM_TABLE
|
||||||
eng "You can't combine write-locking of system '%-.64s.%-.64s' table with other tables"
|
eng "You can't combine write-locking of system '%-.64s.%-.64s' table with other tables"
|
||||||
|
ER_TRG_IN_WRONG_SCHEMA
|
||||||
|
eng "Trigger in wrong schema"
|
||||||
|
@ -1442,8 +1442,8 @@ sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
|
|||||||
{
|
{
|
||||||
Sroutine_hash_entry **last_cached_routine_ptr=
|
Sroutine_hash_entry **last_cached_routine_ptr=
|
||||||
(Sroutine_hash_entry **)lex->sroutines_list.next;
|
(Sroutine_hash_entry **)lex->sroutines_list.next;
|
||||||
for (int i= 0; i < 3; i++)
|
for (int i= 0; i < (int)TRG_EVENT_MAX; i++)
|
||||||
for (int j= 0; j < 2; j++)
|
for (int j= 0; j < (int)TRG_ACTION_MAX; j++)
|
||||||
if (triggers->bodies[i][j])
|
if (triggers->bodies[i][j])
|
||||||
{
|
{
|
||||||
(void)triggers->bodies[i][j]->add_used_tables_to_table_list(thd,
|
(void)triggers->bodies[i][j]->add_used_tables_to_table_list(thd,
|
||||||
|
@ -1740,7 +1740,7 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db,
|
|||||||
!my_strcasecmp(system_charset_info, name, "proc"))
|
!my_strcasecmp(system_charset_info, name, "proc"))
|
||||||
entry->s->system_table= 1;
|
entry->s->system_table= 1;
|
||||||
|
|
||||||
if (Table_triggers_list::check_n_load(thd, db, name, entry))
|
if (Table_triggers_list::check_n_load(thd, db, name, entry, 0))
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -57,6 +57,7 @@ enum enum_sql_command {
|
|||||||
SQLCOM_SHOW_PROCESSLIST, SQLCOM_SHOW_MASTER_STAT, SQLCOM_SHOW_SLAVE_STAT,
|
SQLCOM_SHOW_PROCESSLIST, SQLCOM_SHOW_MASTER_STAT, SQLCOM_SHOW_SLAVE_STAT,
|
||||||
SQLCOM_SHOW_GRANTS, SQLCOM_SHOW_CREATE, SQLCOM_SHOW_CHARSETS,
|
SQLCOM_SHOW_GRANTS, SQLCOM_SHOW_CREATE, SQLCOM_SHOW_CHARSETS,
|
||||||
SQLCOM_SHOW_COLLATIONS, SQLCOM_SHOW_CREATE_DB, SQLCOM_SHOW_TABLE_STATUS,
|
SQLCOM_SHOW_COLLATIONS, SQLCOM_SHOW_CREATE_DB, SQLCOM_SHOW_TABLE_STATUS,
|
||||||
|
SQLCOM_SHOW_TRIGGERS,
|
||||||
|
|
||||||
SQLCOM_LOAD,SQLCOM_SET_OPTION,SQLCOM_LOCK_TABLES,SQLCOM_UNLOCK_TABLES,
|
SQLCOM_LOAD,SQLCOM_SET_OPTION,SQLCOM_LOCK_TABLES,SQLCOM_UNLOCK_TABLES,
|
||||||
SQLCOM_GRANT,
|
SQLCOM_GRANT,
|
||||||
|
@ -2104,6 +2104,7 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
|
|||||||
case SCH_TABLE_NAMES:
|
case SCH_TABLE_NAMES:
|
||||||
case SCH_TABLES:
|
case SCH_TABLES:
|
||||||
case SCH_VIEWS:
|
case SCH_VIEWS:
|
||||||
|
case SCH_TRIGGERS:
|
||||||
#ifdef DONT_ALLOW_SHOW_COMMANDS
|
#ifdef DONT_ALLOW_SHOW_COMMANDS
|
||||||
my_message(ER_NOT_ALLOWED_COMMAND,
|
my_message(ER_NOT_ALLOWED_COMMAND,
|
||||||
ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */
|
ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "sql_select.h" // For select_describe
|
#include "sql_select.h" // For select_describe
|
||||||
#include "repl_failsafe.h"
|
#include "repl_failsafe.h"
|
||||||
#include "sp_head.h"
|
#include "sp_head.h"
|
||||||
|
#include "sql_trigger.h"
|
||||||
#include <my_dir.h>
|
#include <my_dir.h>
|
||||||
|
|
||||||
#ifdef HAVE_BERKELEY_DB
|
#ifdef HAVE_BERKELEY_DB
|
||||||
@ -1696,6 +1697,7 @@ void get_index_field_values(LEX *lex, INDEX_FIELD_VALUES *index_field_values)
|
|||||||
break;
|
break;
|
||||||
case SQLCOM_SHOW_TABLES:
|
case SQLCOM_SHOW_TABLES:
|
||||||
case SQLCOM_SHOW_TABLE_STATUS:
|
case SQLCOM_SHOW_TABLE_STATUS:
|
||||||
|
case SQLCOM_SHOW_TRIGGERS:
|
||||||
index_field_values->db_value= lex->current_select->db;
|
index_field_values->db_value= lex->current_select->db;
|
||||||
index_field_values->table_value= wild;
|
index_field_values->table_value= wild;
|
||||||
break;
|
break;
|
||||||
@ -2963,6 +2965,73 @@ static int get_schema_constraints_record(THD *thd, struct st_table_list *tables,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool store_trigger(THD *thd, TABLE *table, const char *db,
|
||||||
|
const char *tname, LEX_STRING *trigger_name,
|
||||||
|
enum trg_event_type event,
|
||||||
|
enum trg_action_time_type timing,
|
||||||
|
LEX_STRING *trigger_stmt)
|
||||||
|
{
|
||||||
|
CHARSET_INFO *cs= system_charset_info;
|
||||||
|
restore_record(table, s->default_values);
|
||||||
|
table->field[1]->store(db, strlen(db), cs);
|
||||||
|
table->field[2]->store(trigger_name->str, trigger_name->length, cs);
|
||||||
|
table->field[3]->store(trg_event_type_names[event].str,
|
||||||
|
trg_event_type_names[event].length, cs);
|
||||||
|
table->field[5]->store(db, strlen(db), cs);
|
||||||
|
table->field[6]->store(tname, strlen(tname), cs);
|
||||||
|
table->field[9]->store(trigger_stmt->str, trigger_stmt->length, cs);
|
||||||
|
table->field[10]->store("ROW", 3, cs);
|
||||||
|
table->field[11]->store(trg_action_time_type_names[timing].str,
|
||||||
|
trg_action_time_type_names[timing].length, cs);
|
||||||
|
table->field[14]->store("OLD", 3, cs);
|
||||||
|
table->field[15]->store("NEW", 3, cs);
|
||||||
|
return schema_table_store_record(thd, table);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int get_schema_triggers_record(THD *thd, struct st_table_list *tables,
|
||||||
|
TABLE *table, bool res,
|
||||||
|
const char *base_name,
|
||||||
|
const char *file_name)
|
||||||
|
{
|
||||||
|
DBUG_ENTER("get_schema_triggers_record");
|
||||||
|
/*
|
||||||
|
res can be non zero value when processed table is a view or
|
||||||
|
error happened during opening of processed table.
|
||||||
|
*/
|
||||||
|
if (res)
|
||||||
|
{
|
||||||
|
if (!tables->view)
|
||||||
|
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
|
||||||
|
thd->net.last_errno, thd->net.last_error);
|
||||||
|
thd->clear_error();
|
||||||
|
DBUG_RETURN(0);
|
||||||
|
}
|
||||||
|
if (!tables->view && tables->table->triggers)
|
||||||
|
{
|
||||||
|
Table_triggers_list *triggers= tables->table->triggers;
|
||||||
|
int event, timing;
|
||||||
|
for (event= 0; event < (int)TRG_EVENT_MAX; event++)
|
||||||
|
{
|
||||||
|
for (timing= 0; timing < (int)TRG_ACTION_MAX; timing++)
|
||||||
|
{
|
||||||
|
LEX_STRING trigger_name;
|
||||||
|
LEX_STRING trigger_stmt;
|
||||||
|
if (triggers->get_trigger_info(thd, (enum trg_event_type) event,
|
||||||
|
(enum trg_action_time_type)timing,
|
||||||
|
&trigger_name, &trigger_stmt))
|
||||||
|
continue;
|
||||||
|
if (store_trigger(thd, table, base_name, file_name, &trigger_name,
|
||||||
|
(enum trg_event_type) event,
|
||||||
|
(enum trg_action_time_type) timing, &trigger_stmt))
|
||||||
|
DBUG_RETURN(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DBUG_RETURN(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void store_key_column_usage(TABLE *table, const char*db, const char *tname,
|
void store_key_column_usage(TABLE *table, const char*db, const char *tname,
|
||||||
const char *key_name, uint key_len,
|
const char *key_name, uint key_len,
|
||||||
const char *con_type, uint con_len, longlong idx)
|
const char *con_type, uint con_len, longlong idx)
|
||||||
@ -3847,6 +3916,29 @@ ST_FIELD_INFO open_tables_fields_info[]=
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
ST_FIELD_INFO triggers_fields_info[]=
|
||||||
|
{
|
||||||
|
{"TRIGGER_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
|
||||||
|
{"TRIGGER_SCHEMA",NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
|
||||||
|
{"TRIGGER_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Trigger"},
|
||||||
|
{"EVENT_MANIPULATION", 6, MYSQL_TYPE_STRING, 0, 0, "Event"},
|
||||||
|
{"EVENT_OBJECT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
|
||||||
|
{"EVENT_OBJECT_SCHEMA",NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
|
||||||
|
{"EVENT_OBJECT_TABLE", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table"},
|
||||||
|
{"ACTION_ORDER", 4, MYSQL_TYPE_LONG, 0, 0, 0},
|
||||||
|
{"ACTION_CONDITION", 65535, MYSQL_TYPE_STRING, 0, 1, 0},
|
||||||
|
{"ACTION_STATEMENT", 65535, MYSQL_TYPE_STRING, 0, 0, "Statement"},
|
||||||
|
{"ACTION_ORIENTATION", 9, MYSQL_TYPE_STRING, 0, 0, 0},
|
||||||
|
{"ACTION_TIMING", 6, MYSQL_TYPE_STRING, 0, 0, "Timing"},
|
||||||
|
{"ACTION_REFERENCE_OLD_TABLE", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
|
||||||
|
{"ACTION_REFERENCE_NEW_TABLE", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
|
||||||
|
{"ACTION_REFERENCE_OLD_ROW", 3, MYSQL_TYPE_STRING, 0, 0, 0},
|
||||||
|
{"ACTION_REFERENCE_NEW_ROW", 3, MYSQL_TYPE_STRING, 0, 0, 0},
|
||||||
|
{"CREATED", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Created"},
|
||||||
|
{0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
ST_FIELD_INFO variables_fields_info[]=
|
ST_FIELD_INFO variables_fields_info[]=
|
||||||
{
|
{
|
||||||
{"Variable_name", 80, MYSQL_TYPE_STRING, 0, 0, "Variable_name"},
|
{"Variable_name", 80, MYSQL_TYPE_STRING, 0, 0, "Variable_name"},
|
||||||
@ -3897,6 +3989,8 @@ ST_SCHEMA_TABLE schema_tables[]=
|
|||||||
fill_open_tables, make_old_format, 0, -1, -1, 1},
|
fill_open_tables, make_old_format, 0, -1, -1, 1},
|
||||||
{"STATUS", variables_fields_info, create_schema_table, fill_status,
|
{"STATUS", variables_fields_info, create_schema_table, fill_status,
|
||||||
make_old_format, 0, -1, -1, 1},
|
make_old_format, 0, -1, -1, 1},
|
||||||
|
{"TRIGGERS", triggers_fields_info, create_schema_table,
|
||||||
|
get_all_tables, make_old_format, get_schema_triggers_record, 5, 6, 0},
|
||||||
{"VARIABLES", variables_fields_info, create_schema_table, fill_variables,
|
{"VARIABLES", variables_fields_info, create_schema_table, fill_variables,
|
||||||
make_old_format, 0, -1, -1, 1},
|
make_old_format, 0, -1, -1, 1},
|
||||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}
|
{0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
#include <hash.h>
|
#include <hash.h>
|
||||||
#include <myisam.h>
|
#include <myisam.h>
|
||||||
#include <my_dir.h>
|
#include <my_dir.h>
|
||||||
|
#include "sp_head.h"
|
||||||
|
#include "sql_trigger.h"
|
||||||
|
|
||||||
#ifdef __WIN__
|
#ifdef __WIN__
|
||||||
#include <io.h>
|
#include <io.h>
|
||||||
@ -290,16 +292,8 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
|
|||||||
if (!(new_error=my_delete(path,MYF(MY_WME))))
|
if (!(new_error=my_delete(path,MYF(MY_WME))))
|
||||||
{
|
{
|
||||||
some_tables_deleted=1;
|
some_tables_deleted=1;
|
||||||
/*
|
new_error= Table_triggers_list::drop_all_triggers(thd, db,
|
||||||
Destroy triggers for this table if there are any.
|
table->table_name);
|
||||||
|
|
||||||
We won't need this as soon as we will have new .FRM format,
|
|
||||||
in which we will store trigger definitions in the same .FRM
|
|
||||||
files as table descriptions.
|
|
||||||
*/
|
|
||||||
strmov(end, triggers_file_ext);
|
|
||||||
if (!access(path, F_OK))
|
|
||||||
new_error= my_delete(path, MYF(MY_WME));
|
|
||||||
}
|
}
|
||||||
error|= new_error;
|
error|= new_error;
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,45 @@ static File_option triggers_file_parameters[]=
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Structure representing contents of .TRN file which are used to support
|
||||||
|
database wide trigger namespace.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct st_trigname
|
||||||
|
{
|
||||||
|
LEX_STRING trigger_table;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const LEX_STRING trigname_file_type= {(char *)"TRIGGERNAME", 11};
|
||||||
|
|
||||||
|
const char * const trigname_file_ext= ".TRN";
|
||||||
|
|
||||||
|
static File_option trigname_file_parameters[]=
|
||||||
|
{
|
||||||
|
{{(char*)"trigger_table", 15}, offsetof(struct st_trigname, trigger_table),
|
||||||
|
FILE_OPTIONS_ESTRING},
|
||||||
|
{{0, 0}, 0, FILE_OPTIONS_STRING}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const LEX_STRING trg_action_time_type_names[]=
|
||||||
|
{
|
||||||
|
{ (char *) STRING_WITH_LEN("BEFORE") },
|
||||||
|
{ (char *) STRING_WITH_LEN("AFTER") }
|
||||||
|
};
|
||||||
|
|
||||||
|
const LEX_STRING trg_event_type_names[]=
|
||||||
|
{
|
||||||
|
{ (char *) STRING_WITH_LEN("INSERT") },
|
||||||
|
{ (char *) STRING_WITH_LEN("UPDATE") },
|
||||||
|
{ (char *) STRING_WITH_LEN("DELETE") }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static TABLE_LIST *add_table_for_trigger(THD *thd, sp_name *trig);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Create or drop trigger for table.
|
Create or drop trigger for table.
|
||||||
|
|
||||||
@ -69,6 +108,10 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
|
|||||||
But do we want this ?
|
But do we want this ?
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
if (!create &&
|
||||||
|
!(tables= add_table_for_trigger(thd, thd->lex->spname)))
|
||||||
|
DBUG_RETURN(TRUE);
|
||||||
|
|
||||||
/* We should have only one table in table list. */
|
/* We should have only one table in table list. */
|
||||||
DBUG_ASSERT(tables->next_global == 0);
|
DBUG_ASSERT(tables->next_global == 0);
|
||||||
|
|
||||||
@ -174,11 +217,22 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables)
|
|||||||
{
|
{
|
||||||
LEX *lex= thd->lex;
|
LEX *lex= thd->lex;
|
||||||
TABLE *table= tables->table;
|
TABLE *table= tables->table;
|
||||||
char dir_buff[FN_REFLEN], file_buff[FN_REFLEN];
|
char dir_buff[FN_REFLEN], file_buff[FN_REFLEN], trigname_buff[FN_REFLEN],
|
||||||
LEX_STRING dir, file;
|
trigname_path[FN_REFLEN];
|
||||||
|
LEX_STRING dir, file, trigname_file;
|
||||||
LEX_STRING *trg_def, *name;
|
LEX_STRING *trg_def, *name;
|
||||||
Item_trigger_field *trg_field;
|
Item_trigger_field *trg_field;
|
||||||
List_iterator_fast<LEX_STRING> it(names_list);
|
struct st_trigname trigname;
|
||||||
|
|
||||||
|
|
||||||
|
/* Trigger must be in the same schema as target table. */
|
||||||
|
if (my_strcasecmp(system_charset_info, table->s->db,
|
||||||
|
lex->spname->m_db.str ? lex->spname->m_db.str :
|
||||||
|
thd->db))
|
||||||
|
{
|
||||||
|
my_error(ER_TRG_IN_WRONG_SCHEMA, MYF(0));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* We don't allow creation of several triggers of the same type yet */
|
/* We don't allow creation of several triggers of the same type yet */
|
||||||
if (bodies[lex->trg_chistics.event][lex->trg_chistics.action_time])
|
if (bodies[lex->trg_chistics.event][lex->trg_chistics.action_time])
|
||||||
@ -187,17 +241,6 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Let us check if trigger with the same name exists */
|
|
||||||
while ((name= it++))
|
|
||||||
{
|
|
||||||
if (my_strcasecmp(system_charset_info, lex->ident.str,
|
|
||||||
name->str) == 0)
|
|
||||||
{
|
|
||||||
my_message(ER_TRG_ALREADY_EXISTS, ER(ER_TRG_ALREADY_EXISTS), MYF(0));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Let us check if all references to fields in old/new versions of row in
|
Let us check if all references to fields in old/new versions of row in
|
||||||
this trigger are ok.
|
this trigger are ok.
|
||||||
@ -234,6 +277,25 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables)
|
|||||||
file.length= strxnmov(file_buff, FN_REFLEN, tables->table_name,
|
file.length= strxnmov(file_buff, FN_REFLEN, tables->table_name,
|
||||||
triggers_file_ext, NullS) - file_buff;
|
triggers_file_ext, NullS) - file_buff;
|
||||||
file.str= file_buff;
|
file.str= file_buff;
|
||||||
|
trigname_file.length= strxnmov(trigname_buff, FN_REFLEN,
|
||||||
|
lex->spname->m_name.str,
|
||||||
|
trigname_file_ext, NullS) - trigname_buff;
|
||||||
|
trigname_file.str= trigname_buff;
|
||||||
|
strxnmov(trigname_path, FN_REFLEN, dir_buff, trigname_buff, NullS);
|
||||||
|
|
||||||
|
/* Use the filesystem to enforce trigger namespace constraints. */
|
||||||
|
if (!access(trigname_path, F_OK))
|
||||||
|
{
|
||||||
|
my_error(ER_TRG_ALREADY_EXISTS, MYF(0));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
trigname.trigger_table.str= tables->table_name;
|
||||||
|
trigname.trigger_table.length= tables->table_name_length;
|
||||||
|
|
||||||
|
if (sql_create_definition_file(&dir, &trigname_file, &trigname_file_type,
|
||||||
|
(gptr)&trigname, trigname_file_parameters, 0))
|
||||||
|
return 1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Soon we will invalidate table object and thus Table_triggers_list object
|
Soon we will invalidate table object and thus Table_triggers_list object
|
||||||
@ -246,13 +308,66 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables)
|
|||||||
if (!(trg_def= (LEX_STRING *)alloc_root(&table->mem_root,
|
if (!(trg_def= (LEX_STRING *)alloc_root(&table->mem_root,
|
||||||
sizeof(LEX_STRING))) ||
|
sizeof(LEX_STRING))) ||
|
||||||
definitions_list.push_back(trg_def, &table->mem_root))
|
definitions_list.push_back(trg_def, &table->mem_root))
|
||||||
return 1;
|
goto err_with_cleanup;
|
||||||
|
|
||||||
trg_def->str= thd->query;
|
trg_def->str= thd->query;
|
||||||
trg_def->length= thd->query_length;
|
trg_def->length= thd->query_length;
|
||||||
|
|
||||||
return sql_create_definition_file(&dir, &file, &triggers_file_type,
|
if (!sql_create_definition_file(&dir, &file, &triggers_file_type,
|
||||||
(gptr)this, triggers_file_parameters, 3);
|
(gptr)this, triggers_file_parameters, 3))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_with_cleanup:
|
||||||
|
my_delete(trigname_path, MYF(MY_WME));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Deletes the .TRG file for a table
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
rm_trigger_file()
|
||||||
|
path - char buffer of size FN_REFLEN to be used
|
||||||
|
for constructing path to .TRG file.
|
||||||
|
db - table's database name
|
||||||
|
table_name - table's name
|
||||||
|
|
||||||
|
RETURN VALUE
|
||||||
|
False - success
|
||||||
|
True - error
|
||||||
|
*/
|
||||||
|
|
||||||
|
static bool rm_trigger_file(char *path, char *db, char *table_name)
|
||||||
|
{
|
||||||
|
strxnmov(path, FN_REFLEN, mysql_data_home, "/", db, "/", table_name,
|
||||||
|
triggers_file_ext, NullS);
|
||||||
|
unpack_filename(path, path);
|
||||||
|
return my_delete(path, MYF(MY_WME));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Deletes the .TRN file for a trigger
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
rm_trigname_file()
|
||||||
|
path - char buffer of size FN_REFLEN to be used
|
||||||
|
for constructing path to .TRN file.
|
||||||
|
db - trigger's database name
|
||||||
|
table_name - trigger's name
|
||||||
|
|
||||||
|
RETURN VALUE
|
||||||
|
False - success
|
||||||
|
True - error
|
||||||
|
*/
|
||||||
|
|
||||||
|
static bool rm_trigname_file(char *path, char *db, char *trigger_name)
|
||||||
|
{
|
||||||
|
strxnmov(path, FN_REFLEN, mysql_data_home, "/", db, "/", trigger_name,
|
||||||
|
trigname_file_ext, NullS);
|
||||||
|
unpack_filename(path, path);
|
||||||
|
return my_delete(path, MYF(MY_WME));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -275,12 +390,13 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables)
|
|||||||
LEX_STRING *name;
|
LEX_STRING *name;
|
||||||
List_iterator_fast<LEX_STRING> it_name(names_list);
|
List_iterator_fast<LEX_STRING> it_name(names_list);
|
||||||
List_iterator<LEX_STRING> it_def(definitions_list);
|
List_iterator<LEX_STRING> it_def(definitions_list);
|
||||||
|
char path[FN_REFLEN];
|
||||||
|
|
||||||
while ((name= it_name++))
|
while ((name= it_name++))
|
||||||
{
|
{
|
||||||
it_def++;
|
it_def++;
|
||||||
|
|
||||||
if (my_strcasecmp(system_charset_info, lex->ident.str,
|
if (my_strcasecmp(system_charset_info, lex->spname->m_name.str,
|
||||||
name->str) == 0)
|
name->str) == 0)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@ -291,18 +407,14 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables)
|
|||||||
|
|
||||||
if (definitions_list.is_empty())
|
if (definitions_list.is_empty())
|
||||||
{
|
{
|
||||||
char path[FN_REFLEN];
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TODO: Probably instead of removing .TRG file we should move
|
TODO: Probably instead of removing .TRG file we should move
|
||||||
to archive directory but this should be done as part of
|
to archive directory but this should be done as part of
|
||||||
parse_file.cc functionality (because we will need it
|
parse_file.cc functionality (because we will need it
|
||||||
elsewhere).
|
elsewhere).
|
||||||
*/
|
*/
|
||||||
strxnmov(path, FN_REFLEN, mysql_data_home, "/", tables->db, "/",
|
if (rm_trigger_file(path, tables->db, tables->table_name))
|
||||||
tables->table_name, triggers_file_ext, NullS);
|
return 1;
|
||||||
unpack_filename(path, path);
|
|
||||||
return my_delete(path, MYF(MY_WME));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -317,10 +429,15 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables)
|
|||||||
triggers_file_ext, NullS) - file_buff;
|
triggers_file_ext, NullS) - file_buff;
|
||||||
file.str= file_buff;
|
file.str= file_buff;
|
||||||
|
|
||||||
return sql_create_definition_file(&dir, &file, &triggers_file_type,
|
if (sql_create_definition_file(&dir, &file, &triggers_file_type,
|
||||||
(gptr)this,
|
(gptr)this, triggers_file_parameters,
|
||||||
triggers_file_parameters, 3);
|
3))
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rm_trigname_file(path, tables->db, lex->spname->m_name.str))
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,8 +448,8 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables)
|
|||||||
|
|
||||||
Table_triggers_list::~Table_triggers_list()
|
Table_triggers_list::~Table_triggers_list()
|
||||||
{
|
{
|
||||||
for (int i= 0; i < 3; i++)
|
for (int i= 0; i < (int)TRG_EVENT_MAX; i++)
|
||||||
for (int j= 0; j < 2; j++)
|
for (int j= 0; j < (int)TRG_ACTION_MAX; j++)
|
||||||
delete bodies[i][j];
|
delete bodies[i][j];
|
||||||
|
|
||||||
if (record1_field)
|
if (record1_field)
|
||||||
@ -389,13 +506,16 @@ bool Table_triggers_list::prepare_record1_accessors(TABLE *table)
|
|||||||
db - table's database name
|
db - table's database name
|
||||||
table_name - table's name
|
table_name - table's name
|
||||||
table - pointer to table object
|
table - pointer to table object
|
||||||
|
names_only - stop after loading trigger names
|
||||||
|
|
||||||
RETURN VALUE
|
RETURN VALUE
|
||||||
False - success
|
False - success
|
||||||
True - error
|
True - error
|
||||||
*/
|
*/
|
||||||
|
|
||||||
bool Table_triggers_list::check_n_load(THD *thd, const char *db,
|
bool Table_triggers_list::check_n_load(THD *thd, const char *db,
|
||||||
const char *table_name, TABLE *table)
|
const char *table_name, TABLE *table,
|
||||||
|
bool names_only)
|
||||||
{
|
{
|
||||||
char path_buff[FN_REFLEN];
|
char path_buff[FN_REFLEN];
|
||||||
LEX_STRING path;
|
LEX_STRING path;
|
||||||
@ -451,7 +571,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
|
|||||||
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.
|
||||||
*/
|
*/
|
||||||
if (triggers->prepare_record1_accessors(table))
|
if (!names_only && triggers->prepare_record1_accessors(table))
|
||||||
DBUG_RETURN(1);
|
DBUG_RETURN(1);
|
||||||
|
|
||||||
List_iterator_fast<LEX_STRING> it(triggers->definitions_list);
|
List_iterator_fast<LEX_STRING> it(triggers->definitions_list);
|
||||||
@ -471,32 +591,20 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
|
|||||||
Free lex associated resources
|
Free lex associated resources
|
||||||
QQ: Do we really need all this stuff here ?
|
QQ: Do we really need all this stuff here ?
|
||||||
*/
|
*/
|
||||||
if (lex.sphead)
|
delete lex.sphead;
|
||||||
{
|
|
||||||
delete lex.sphead;
|
|
||||||
lex.sphead= 0;
|
|
||||||
}
|
|
||||||
goto err_with_lex_cleanup;
|
goto err_with_lex_cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
triggers->bodies[lex.trg_chistics.event]
|
triggers->bodies[lex.trg_chistics.event]
|
||||||
[lex.trg_chistics.action_time]= lex.sphead;
|
[lex.trg_chistics.action_time]= lex.sphead;
|
||||||
lex.sphead= 0;
|
if (triggers->names_list.push_back(&lex.sphead->m_name, &table->mem_root))
|
||||||
|
goto err_with_lex_cleanup;
|
||||||
|
|
||||||
if (!(trg_name_buff= alloc_root(&table->mem_root,
|
if (names_only)
|
||||||
sizeof(LEX_STRING) +
|
{
|
||||||
lex.ident.length + 1)))
|
lex_end(&lex);
|
||||||
goto err_with_lex_cleanup;
|
continue;
|
||||||
|
}
|
||||||
trg_name_str= (LEX_STRING *)trg_name_buff;
|
|
||||||
trg_name_buff+= sizeof(LEX_STRING);
|
|
||||||
memcpy(trg_name_buff, lex.ident.str,
|
|
||||||
lex.ident.length + 1);
|
|
||||||
trg_name_str->str= trg_name_buff;
|
|
||||||
trg_name_str->length= lex.ident.length;
|
|
||||||
|
|
||||||
if (triggers->names_list.push_back(trg_name_str, &table->mem_root))
|
|
||||||
goto err_with_lex_cleanup;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Let us bind Item_trigger_field objects representing access to fields
|
Let us bind Item_trigger_field objects representing access to fields
|
||||||
@ -537,3 +645,160 @@ err_with_lex_cleanup:
|
|||||||
|
|
||||||
DBUG_RETURN(1);
|
DBUG_RETURN(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Obtains and returns trigger metadata
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
get_trigger_info()
|
||||||
|
thd - current thread context
|
||||||
|
event - trigger event type
|
||||||
|
time_type - trigger action time
|
||||||
|
name - returns name of trigger
|
||||||
|
stmt - returns statement of trigger
|
||||||
|
|
||||||
|
RETURN VALUE
|
||||||
|
False - success
|
||||||
|
True - error
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool Table_triggers_list::get_trigger_info(THD *thd, trg_event_type event,
|
||||||
|
trg_action_time_type time_type,
|
||||||
|
LEX_STRING *trigger_name,
|
||||||
|
LEX_STRING *trigger_stmt)
|
||||||
|
{
|
||||||
|
sp_head *body;
|
||||||
|
DBUG_ENTER("get_trigger_info");
|
||||||
|
if ((body= bodies[event][time_type]))
|
||||||
|
{
|
||||||
|
*trigger_name= body->m_name;
|
||||||
|
*trigger_stmt= body->m_body;
|
||||||
|
DBUG_RETURN(0);
|
||||||
|
}
|
||||||
|
DBUG_RETURN(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Find trigger's table from trigger identifier and add it to
|
||||||
|
the statement table list.
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
mysql_table_for_trigger()
|
||||||
|
thd - current thread context
|
||||||
|
trig - identifier for trigger
|
||||||
|
|
||||||
|
RETURN VALUE
|
||||||
|
0 - error
|
||||||
|
# - pointer to TABLE_LIST object for the table
|
||||||
|
*/
|
||||||
|
|
||||||
|
static TABLE_LIST *add_table_for_trigger(THD *thd, sp_name *trig)
|
||||||
|
{
|
||||||
|
const char *db= !trig->m_db.str ? thd->db : trig->m_db.str;
|
||||||
|
LEX *lex= thd->lex;
|
||||||
|
char path_buff[FN_REFLEN];
|
||||||
|
LEX_STRING path;
|
||||||
|
File_parser *parser;
|
||||||
|
struct st_trigname trigname;
|
||||||
|
DBUG_ENTER("add_table_for_trigger");
|
||||||
|
|
||||||
|
strxnmov(path_buff, FN_REFLEN, mysql_data_home, "/", db, "/",
|
||||||
|
trig->m_name.str, trigname_file_ext, NullS);
|
||||||
|
path.length= unpack_filename(path_buff, path_buff);
|
||||||
|
path.str= path_buff;
|
||||||
|
|
||||||
|
if (access(path_buff, F_OK))
|
||||||
|
{
|
||||||
|
my_error(ER_TRG_DOES_NOT_EXIST, MYF(0));
|
||||||
|
DBUG_RETURN(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(parser= sql_parse_prepare(&path, thd->mem_root, 1)))
|
||||||
|
DBUG_RETURN(0);
|
||||||
|
|
||||||
|
if (strncmp(trigname_file_type.str, parser->type()->str,
|
||||||
|
parser->type()->length))
|
||||||
|
{
|
||||||
|
my_error(ER_WRONG_OBJECT, MYF(0), trig->m_name.str, trigname_file_ext,
|
||||||
|
"TRIGGERNAME");
|
||||||
|
DBUG_RETURN(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parser->parse((gptr)&trigname, thd->mem_root,
|
||||||
|
trigname_file_parameters, 1))
|
||||||
|
DBUG_RETURN(0);
|
||||||
|
|
||||||
|
/* We need to reset statement table list to be PS/SP friendly. */
|
||||||
|
lex->query_tables= 0;
|
||||||
|
lex->query_tables_last= &lex->query_tables;
|
||||||
|
DBUG_RETURN(sp_add_to_query_tables(thd, lex, db,
|
||||||
|
trigname.trigger_table.str, TL_WRITE));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Drop all triggers for table.
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
drop_all_triggers()
|
||||||
|
thd - current thread context
|
||||||
|
db - schema for table
|
||||||
|
name - name for table
|
||||||
|
|
||||||
|
NOTE
|
||||||
|
The calling thread should hold the LOCK_open mutex;
|
||||||
|
|
||||||
|
RETURN VALUE
|
||||||
|
False - success
|
||||||
|
True - error
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool Table_triggers_list::drop_all_triggers(THD *thd, char *db, char *name)
|
||||||
|
{
|
||||||
|
TABLE table;
|
||||||
|
char path[FN_REFLEN];
|
||||||
|
bool result= 0;
|
||||||
|
DBUG_ENTER("drop_all_triggers");
|
||||||
|
|
||||||
|
bzero(&table, sizeof(table));
|
||||||
|
init_alloc_root(&table.mem_root, 8192, 0);
|
||||||
|
|
||||||
|
safe_mutex_assert_owner(&LOCK_open);
|
||||||
|
|
||||||
|
if (Table_triggers_list::check_n_load(thd, db, name, &table, 1))
|
||||||
|
{
|
||||||
|
result= 1;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
if (table.triggers)
|
||||||
|
{
|
||||||
|
LEX_STRING *trigger;
|
||||||
|
List_iterator_fast<LEX_STRING> it_name(table.triggers->names_list);
|
||||||
|
|
||||||
|
while ((trigger= it_name++))
|
||||||
|
{
|
||||||
|
if (rm_trigname_file(path, db, trigger->str))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Instead of immediately bailing out with error if we were unable
|
||||||
|
to remove .TRN file we will try to drop other files.
|
||||||
|
*/
|
||||||
|
result= 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rm_trigger_file(path, db, name))
|
||||||
|
{
|
||||||
|
result= 1;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end:
|
||||||
|
if (table.triggers)
|
||||||
|
delete table.triggers;
|
||||||
|
free_root(&table.mem_root, MYF(0));
|
||||||
|
DBUG_RETURN(result);
|
||||||
|
}
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
class Table_triggers_list: public Sql_alloc
|
class Table_triggers_list: public Sql_alloc
|
||||||
{
|
{
|
||||||
/* Triggers as SPs grouped by event, action_time */
|
/* Triggers as SPs grouped by event, action_time */
|
||||||
sp_head *bodies[3][2];
|
sp_head *bodies[TRG_EVENT_MAX][TRG_ACTION_MAX];
|
||||||
/*
|
/*
|
||||||
Copy of TABLE::Field array with field pointers set to TABLE::record[1]
|
Copy of TABLE::Field array with field pointers set to TABLE::record[1]
|
||||||
buffer instead of TABLE::record[0] (used for OLD values in on UPDATE
|
buffer instead of TABLE::record[0] (used for OLD values in on UPDATE
|
||||||
@ -121,9 +121,13 @@ public:
|
|||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
bool get_trigger_info(THD *thd, trg_event_type event,
|
||||||
|
trg_action_time_type time_type,
|
||||||
|
LEX_STRING *trigger_name, LEX_STRING *trigger_stmt);
|
||||||
|
|
||||||
static bool check_n_load(THD *thd, const char *db, const char *table_name,
|
static bool check_n_load(THD *thd, const char *db, const char *table_name,
|
||||||
TABLE *table);
|
TABLE *table, bool names_only);
|
||||||
|
static bool drop_all_triggers(THD *thd, char *db, char *table_name);
|
||||||
|
|
||||||
bool has_delete_triggers()
|
bool has_delete_triggers()
|
||||||
{
|
{
|
||||||
@ -143,3 +147,6 @@ public:
|
|||||||
private:
|
private:
|
||||||
bool prepare_record1_accessors(TABLE *table);
|
bool prepare_record1_accessors(TABLE *table);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern const LEX_STRING trg_action_time_type_names[];
|
||||||
|
extern const LEX_STRING trg_event_type_names[];
|
||||||
|
@ -599,6 +599,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
|
|||||||
%token TRAILING
|
%token TRAILING
|
||||||
%token TRANSACTION_SYM
|
%token TRANSACTION_SYM
|
||||||
%token TRIGGER_SYM
|
%token TRIGGER_SYM
|
||||||
|
%token TRIGGERS_SYM
|
||||||
%token TRIM
|
%token TRIM
|
||||||
%token TRUE_SYM
|
%token TRUE_SYM
|
||||||
%token TRUNCATE_SYM
|
%token TRUNCATE_SYM
|
||||||
@ -1266,7 +1267,7 @@ create:
|
|||||||
}
|
}
|
||||||
opt_view_list AS select_init check_option
|
opt_view_list AS select_init check_option
|
||||||
{}
|
{}
|
||||||
| CREATE TRIGGER_SYM ident trg_action_time trg_event
|
| CREATE TRIGGER_SYM sp_name trg_action_time trg_event
|
||||||
ON table_ident FOR_SYM EACH_SYM ROW_SYM
|
ON table_ident FOR_SYM EACH_SYM ROW_SYM
|
||||||
{
|
{
|
||||||
LEX *lex= Lex;
|
LEX *lex= Lex;
|
||||||
@ -1285,6 +1286,7 @@ create:
|
|||||||
|
|
||||||
sp->m_type= TYPE_ENUM_TRIGGER;
|
sp->m_type= TYPE_ENUM_TRIGGER;
|
||||||
lex->sphead= sp;
|
lex->sphead= sp;
|
||||||
|
lex->spname= $3;
|
||||||
/*
|
/*
|
||||||
We have to turn of CLIENT_MULTI_QUERIES while parsing a
|
We have to turn of CLIENT_MULTI_QUERIES while parsing a
|
||||||
stored procedure, otherwise yylex will chop it into pieces
|
stored procedure, otherwise yylex will chop it into pieces
|
||||||
@ -1295,7 +1297,7 @@ create:
|
|||||||
|
|
||||||
bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
|
bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
|
||||||
lex->sphead->m_chistics= &lex->sp_chistics;
|
lex->sphead->m_chistics= &lex->sp_chistics;
|
||||||
lex->sphead->m_body_begin= lex->tok_start;
|
lex->sphead->m_body_begin= lex->ptr;
|
||||||
}
|
}
|
||||||
sp_proc_stmt
|
sp_proc_stmt
|
||||||
{
|
{
|
||||||
@ -1303,14 +1305,12 @@ create:
|
|||||||
sp_head *sp= lex->sphead;
|
sp_head *sp= lex->sphead;
|
||||||
|
|
||||||
lex->sql_command= SQLCOM_CREATE_TRIGGER;
|
lex->sql_command= SQLCOM_CREATE_TRIGGER;
|
||||||
sp->init_strings(YYTHD, lex, NULL);
|
sp->init_strings(YYTHD, lex, $3);
|
||||||
/* Restore flag if it was cleared above */
|
/* Restore flag if it was cleared above */
|
||||||
if (sp->m_old_cmq)
|
if (sp->m_old_cmq)
|
||||||
YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES;
|
YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES;
|
||||||
sp->restore_thd_mem_root(YYTHD);
|
sp->restore_thd_mem_root(YYTHD);
|
||||||
|
|
||||||
lex->ident= $3;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
We have to do it after parsing trigger body, because some of
|
We have to do it after parsing trigger body, because some of
|
||||||
sp_proc_stmt alternatives are not saving/restoring LEX, so
|
sp_proc_stmt alternatives are not saving/restoring LEX, so
|
||||||
@ -5919,19 +5919,11 @@ drop:
|
|||||||
lex->sql_command= SQLCOM_DROP_VIEW;
|
lex->sql_command= SQLCOM_DROP_VIEW;
|
||||||
lex->drop_if_exists= $3;
|
lex->drop_if_exists= $3;
|
||||||
}
|
}
|
||||||
| DROP TRIGGER_SYM ident '.' ident
|
| DROP TRIGGER_SYM sp_name
|
||||||
{
|
{
|
||||||
LEX *lex= Lex;
|
LEX *lex= Lex;
|
||||||
|
|
||||||
lex->sql_command= SQLCOM_DROP_TRIGGER;
|
lex->sql_command= SQLCOM_DROP_TRIGGER;
|
||||||
/* QQ: Could we loosen lock type in certain cases ? */
|
lex->spname= $3;
|
||||||
if (!lex->select_lex.add_table_to_list(YYTHD,
|
|
||||||
new Table_ident($3),
|
|
||||||
(LEX_STRING*) 0,
|
|
||||||
TL_OPTION_UPDATING,
|
|
||||||
TL_WRITE))
|
|
||||||
YYABORT;
|
|
||||||
lex->ident= $5;
|
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
@ -6296,6 +6288,15 @@ show_param:
|
|||||||
if (prepare_schema_table(YYTHD, lex, 0, SCH_TABLE_NAMES))
|
if (prepare_schema_table(YYTHD, lex, 0, SCH_TABLE_NAMES))
|
||||||
YYABORT;
|
YYABORT;
|
||||||
}
|
}
|
||||||
|
| opt_full TRIGGERS_SYM opt_db wild_and_where
|
||||||
|
{
|
||||||
|
LEX *lex= Lex;
|
||||||
|
lex->sql_command= SQLCOM_SELECT;
|
||||||
|
lex->orig_sql_command= SQLCOM_SHOW_TRIGGERS;
|
||||||
|
lex->select_lex.db= $3;
|
||||||
|
if (prepare_schema_table(YYTHD, lex, 0, SCH_TRIGGERS))
|
||||||
|
YYABORT;
|
||||||
|
}
|
||||||
| TABLE_SYM STATUS_SYM opt_db wild_and_where
|
| TABLE_SYM STATUS_SYM opt_db wild_and_where
|
||||||
{
|
{
|
||||||
LEX *lex= Lex;
|
LEX *lex= Lex;
|
||||||
@ -7590,6 +7591,7 @@ keyword_sp:
|
|||||||
| TEMPTABLE_SYM {}
|
| TEMPTABLE_SYM {}
|
||||||
| TEXT_SYM {}
|
| TEXT_SYM {}
|
||||||
| TRANSACTION_SYM {}
|
| TRANSACTION_SYM {}
|
||||||
|
| TRIGGERS_SYM {}
|
||||||
| TIMESTAMP {}
|
| TIMESTAMP {}
|
||||||
| TIMESTAMP_ADD {}
|
| TIMESTAMP_ADD {}
|
||||||
| TIMESTAMP_DIFF {}
|
| TIMESTAMP_DIFF {}
|
||||||
|
@ -282,7 +282,7 @@ enum enum_schema_tables
|
|||||||
SCH_COLLATION_CHARACTER_SET_APPLICABILITY, SCH_PROCEDURES, SCH_STATISTICS,
|
SCH_COLLATION_CHARACTER_SET_APPLICABILITY, SCH_PROCEDURES, SCH_STATISTICS,
|
||||||
SCH_VIEWS, SCH_USER_PRIVILEGES, SCH_SCHEMA_PRIVILEGES, SCH_TABLE_PRIVILEGES,
|
SCH_VIEWS, SCH_USER_PRIVILEGES, SCH_SCHEMA_PRIVILEGES, SCH_TABLE_PRIVILEGES,
|
||||||
SCH_COLUMN_PRIVILEGES, SCH_TABLE_CONSTRAINTS, SCH_KEY_COLUMN_USAGE,
|
SCH_COLUMN_PRIVILEGES, SCH_TABLE_CONSTRAINTS, SCH_KEY_COLUMN_USAGE,
|
||||||
SCH_TABLE_NAMES, SCH_OPEN_TABLES, SCH_STATUS, SCH_VARIABLES
|
SCH_TABLE_NAMES, SCH_OPEN_TABLES, SCH_STATUS, SCH_TRIGGERS, SCH_VARIABLES
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user