diff options
author | unknown <monty@mishka.local> | 2004-09-15 22:36:57 +0300 |
---|---|---|
committer | unknown <monty@mishka.local> | 2004-09-15 22:36:57 +0300 |
commit | 4ff57e80f745412b9e4cea67fb51ad3ecd75995f (patch) | |
tree | c54782bf59455786d3d6b7abf02e40aaac69b1ff | |
parent | 8267fd7a714f27473a017f37b279452f0628985f (diff) | |
parent | 50226162fd5b1fa6f22238c62b62ce7426b30cfa (diff) | |
download | mariadb-git-4ff57e80f745412b9e4cea67fb51ad3ecd75995f.tar.gz |
Merge
BitKeeper/etc/logging_ok:
auto-union
sql/ha_berkeley.cc:
Auto merged
sql/ha_heap.cc:
Auto merged
sql/ha_innodb.cc:
Auto merged
sql/ha_myisam.cc:
Auto merged
sql/ha_ndbcluster.cc:
Auto merged
sql/handler.cc:
Auto merged
sql/mysql_priv.h:
Auto merged
sql/mysqld.cc:
Auto merged
sql/sql_class.cc:
Auto merged
sql/sql_help.cc:
Auto merged
sql/sql_insert.cc:
Auto merged
sql/sql_lex.cc:
Auto merged
sql/sql_table.cc:
Auto merged
sql/sql_class.h:
SCCS merged
sql/sql_parse.cc:
SCCS merged
95 files changed, 2881 insertions, 609 deletions
diff --git a/.bzrignore b/.bzrignore index be7211af9e2..1feab85d33d 100644 --- a/.bzrignore +++ b/.bzrignore @@ -440,6 +440,7 @@ libmysqld/sql_state.c libmysqld/sql_string.cc libmysqld/sql_table.cc libmysqld/sql_test.cc +libmysqld/sql_trigger.cc libmysqld/sql_udf.cc libmysqld/sql_union.cc libmysqld/sql_unions.cc diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok index f29057f1d38..abff7bb08e9 100644 --- a/BitKeeper/etc/logging_ok +++ b/BitKeeper/etc/logging_ok @@ -64,6 +64,7 @@ igor@rurik.mysql.com ingo@mysql.com jan@hundin.mysql.fi jani@a80-186-24-72.elisa-laajakaista.fi +jani@a80-186-8-224.elisa-laajakaista.fi jani@dsl-jkl1657.dial.inet.fi jani@dsl-kpogw4gb5.dial.inet.fi jani@hynda.(none) @@ -151,6 +152,7 @@ pem@per-erik-martins-dator.local peter@linux.local peter@mysql.com peterg@mysql.com +petr@mysql.com pgulutzan@linux.local pmartin@build.mysql2.com psergey@psergey-rh8.(none) diff --git a/include/my_sys.h b/include/my_sys.h index 0d722a899f6..cc40b7a7693 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -738,7 +738,7 @@ extern void my_free_lock(byte *ptr,myf flags); #define my_free_lock(A,B) my_free((A),(B)) #endif #define alloc_root_inited(A) ((A)->min_malloc != 0) -#define clear_alloc_root(A) { (A)->free= (A)->used= (A)->pre_alloc= 0; } +#define clear_alloc_root(A) bzero((void *) (A), sizeof(MEM_ROOT)) extern void init_alloc_root(MEM_ROOT *mem_root, uint block_size, uint pre_alloc_size); extern gptr alloc_root(MEM_ROOT *mem_root,unsigned int Size); @@ -788,6 +788,10 @@ extern void add_compiled_collation(CHARSET_INFO *cs); extern ulong escape_string_for_mysql(CHARSET_INFO *charset_info, char *to, const char *from, ulong length); +extern void thd_increment_bytes_sent(ulong length); +extern void thd_increment_bytes_received(ulong length); +extern void thd_increment_net_big_packet_count(ulong length); + #ifdef __WIN__ extern my_bool have_tcpip; /* Is set if tcpip is used */ #endif diff --git a/include/mysqld_error.h b/include/mysqld_error.h index 125a39b2249..cf032380e2f 100644 --- a/include/mysqld_error.h +++ b/include/mysqld_error.h @@ -375,4 +375,9 @@ #define ER_VIEW_INVALID 1356 #define ER_SP_NO_DROP_SP 1357 #define ER_SP_GOTO_IN_HNDLR 1358 -#define ER_ERROR_MESSAGES 359 +#define ER_TRG_ALREADY_EXISTS 1359 +#define ER_TRG_DOES_NOT_EXIST 1360 +#define ER_TRG_ON_VIEW_OR_TEMP_TABLE 1361 +#define ER_TRG_CANT_CHANGE_ROW 1362 +#define ER_TRG_NO_SUCH_ROW_IN_TRG 1363 +#define ER_ERROR_MESSAGES 364 diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am index f0cda9ae524..56762f5771e 100644 --- a/libmysqld/Makefile.am +++ b/libmysqld/Makefile.am @@ -59,7 +59,7 @@ sqlsources = derror.cc field.cc field_conv.cc strfunc.cc filesort.cc \ unireg.cc uniques.cc stacktrace.c sql_union.cc hash_filo.cc \ spatial.cc gstream.cc sql_help.cc tztime.cc protocol_cursor.cc \ sp_head.cc sp_pcontext.cc sp.cc sp_cache.cc sp_rcontext.cc \ - parse_file.cc sql_view.cc + parse_file.cc sql_view.cc sql_trigger.cc libmysqld_int_a_SOURCES= $(libmysqld_sources) $(libmysqlsources) $(sqlsources) libmysqld_a_SOURCES= diff --git a/mysql-test/r/lowercase_view.result b/mysql-test/r/lowercase_view.result new file mode 100644 index 00000000000..51f7dc758eb --- /dev/null +++ b/mysql-test/r/lowercase_view.result @@ -0,0 +1,24 @@ +drop table if exists t1Aa,t2Aa,v1Aa,v2Aa; +drop view if exists t1Aa,t2Aa,v1Aa,v2Aa; +drop database if exists MySQLTest; +create database MySQLTest; +use MySQLTest; +create table TaB (Field int); +create view ViE as select * from TAb; +show create table VIe; +Table Create Table +vie CREATE VIEW `mysqltest`.`vie` AS select `mysqltest`.`tab`.`Field` AS `Field` from `mysqltest`.`tab` +drop database MySQLTest; +use test; +create table t1Aa (col1 int); +create table t2Aa (col1 int); +create view v1Aa as select * from t1Aa; +create view v2Aa as select * from v1Aa; +update v2aA set col1 = (select max(col1) from v1aA); +ERROR HY000: You can't specify target table 'v2aa' for update in FROM clause +delete from v2aA where col1 = (select max(col1) from v1aA); +ERROR HY000: You can't specify target table 'v2aa' for update in FROM clause +insert into v2aA values ((select max(col1) from v1aA)); +ERROR HY000: You can't specify target table 'v2aa' for update in FROM clause +drop view v2Aa,v1Aa; +drop table t1Aa,t2Aa; diff --git a/mysql-test/r/merge.result b/mysql-test/r/merge.result index 5755033190b..7334d5acf4d 100644 --- a/mysql-test/r/merge.result +++ b/mysql-test/r/merge.result @@ -650,4 +650,6 @@ create table t3 engine=merge union=(t1, t2) select * from t1; ERROR HY000: You can't specify target table 't1' for update in FROM clause create table t3 engine=merge union=(t1, t2) select * from t2; ERROR HY000: You can't specify target table 't2' for update in FROM clause +create table t3 engine=merge union=(t1, t2) select (select max(a) from t2); +ERROR HY000: You can't specify target table 't2' for update in FROM clause drop table t1, t2; diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result index 55c34ca471f..540a5652197 100644 --- a/mysql-test/r/sp-error.result +++ b/mysql-test/r/sp-error.result @@ -485,4 +485,13 @@ create procedure bug4344() drop procedure bug4344| ERROR HY000: Can't drop a PROCEDURE from within another stored routine create procedure bug4344() drop function bug4344| ERROR HY000: Can't drop a FUNCTION from within another stored routine +drop procedure if exists bug3294| +create procedure bug3294() +begin +declare continue handler for sqlexception drop table t5; +drop table t5; +end| +call bug3294()| +ERROR 42S02: Unknown table 't5' +drop procedure bug3294| drop table t1| diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index f0f51945f36..f84b224b8e0 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -1820,6 +1820,35 @@ Ok Ok drop procedure bug5258| drop procedure bug5258_aux| +create function bug4487() returns char +begin +declare v char; +return v; +end| +Warnings: +Warning 1311 Referring to uninitialized variable v +select bug4487()| +bug4487() +NULL +Warnings: +Warning 1311 Referring to uninitialized variable v +drop function bug4487| +drop procedure if exists bug4941| +create procedure bug4941(out x int) +begin +declare c cursor for select i from t2 limit 1; +open c; +fetch c into x; +close c; +end| +insert into t2 values (null, null, null)| +set @x = 42| +call bug4941(@x)| +select @x| +@x +NULL +delete from t1| +drop procedure bug4941| drop table if exists fac| create table fac (n int unsigned not null primary key, f bigint unsigned)| create procedure ifac(n int unsigned) diff --git a/mysql-test/r/trigger.result b/mysql-test/r/trigger.result new file mode 100644 index 00000000000..a7a845fa3e0 --- /dev/null +++ b/mysql-test/r/trigger.result @@ -0,0 +1,171 @@ +drop table if exists t1, t2; +drop view if exists v1; +create table t1 (i int); +create trigger trg before insert on t1 for each row set @a:=1; +set @a:=0; +select @a; +@a +0 +insert into t1 values (1); +select @a; +@a +1 +drop trigger t1.trg; +create trigger trg before insert on t1 for each row set @a:=new.i; +insert into t1 values (123); +select @a; +@a +123 +drop trigger t1.trg; +drop table t1; +create table t1 (i int not null, j int); +create trigger trg before insert on t1 for each row +begin +if isnull(new.j) then +set new.j:= new.i * 10; +end if; +end| +insert into t1 (i) values (1)| +insert into t1 (i,j) values (2, 3)| +select * from t1| +i j +1 10 +2 3 +drop trigger t1.trg| +drop table t1| +create table t1 (i int not null primary key); +create trigger trg after insert on t1 for each row +set @a:= if(@a,concat(@a, ":", new.i), new.i); +set @a:=""; +insert into t1 values (2),(3),(4),(5); +select @a; +@a +2:3:4:5 +drop trigger t1.trg; +drop table t1; +create table t1 (aid int not null primary key, balance int not null default 0); +insert into t1 values (1, 1000), (2,3000); +create trigger trg before update on t1 for each row +begin +declare loc_err varchar(255); +if abs(new.balance - old.balance) > 1000 then +set new.balance:= old.balance; +set loc_err := concat("Too big change for aid = ", new.aid); +set @update_failed:= if(@update_failed, concat(@a, ":", loc_err), loc_err); +end if; +end| +set @update_failed:=""| +update t1 set balance=1500| +select @update_failed; +select * from t1| +@update_failed +Too big change for aid = 2 +aid balance +1 1500 +2 3000 +drop trigger t1.trg| +drop table t1| +create table t1 (i int); +insert into t1 values (1),(2),(3),(4); +create trigger trg after update on t1 for each row +set @total_change:=@total_change + new.i - old.i; +set @total_change:=0; +update t1 set i=3; +select @total_change; +@total_change +2 +drop trigger t1.trg; +drop table t1; +create table t1 (i int); +insert into t1 values (1),(2),(3),(4); +create trigger trg before delete on t1 for each row +set @del_sum:= @del_sum + old.i; +set @del_sum:= 0; +delete from t1 where i <= 3; +select @del_sum; +@del_sum +6 +drop trigger t1.trg; +drop table t1; +create table t1 (i int); +insert into t1 values (1),(2),(3),(4); +create trigger trg after delete on t1 for each row set @del:= 1; +set @del:= 0; +delete from t1 where i <> 0; +select @del; +@del +1 +drop trigger t1.trg; +drop table t1; +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| +set @fired:=""; +insert into t1 values (1,2),(2,3),(3,14); +select @fired; +@fired + +select * from t1; +i j +1 2 +2 3 +3 10 +update t1 set j= 20; +select @fired; +@fired +Yes +select * from t1; +i j +1 20 +2 -1 +3 20 +drop trigger t1.trg1; +drop trigger t1.trg2; +drop trigger t1.trg3; +drop table t1; +create table t1 (i int); +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 +create trigger trg before delete on t1 for each row set @a:= new.i; +ERROR HY000: There is no NEW row in on DELETE trigger +create trigger trg before update on t1 for each row set old.i:=1; +ERROR HY000: Updating of OLD row is not allowed in trigger +create trigger trg before delete on t1 for each row set new.i:=1; +ERROR HY000: There is no NEW row in on DELETE trigger +create trigger trg after update on t1 for each row set new.i:=1; +ERROR HY000: Updating of NEW row is not allowed in after trigger +create trigger trg before insert on t2 for each row set @a:=1; +ERROR 42S02: Table 'test.t2' doesn't exist +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; +ERROR HY000: Trigger already exists +create trigger trg2 before insert on t1 for each row set @a:=1; +ERROR HY000: Trigger already exists +drop trigger t1.trg; +drop trigger t1.trg; +ERROR HY000: Trigger does not exist +create view v1 as select * from t1; +create trigger trg before insert on v1 for each row set @a:=1; +ERROR HY000: Trigger's 'v1' is view or temporary table +drop view v1; +drop table t1; +create temporary table t1 (i int); +create trigger trg before insert on t1 for each row set @a:=1; +ERROR HY000: Trigger's 't1' is view or temporary table +drop table t1; diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 572db8a7324..2551977200e 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -55,7 +55,7 @@ Note 1003 select (`test`.`t1`.`b` + 1) AS `c` from `test`.`v1` create algorithm=temptable view v2 (c) as select b+1 from t1; show create table v2; Table Create Table -v2 CREATE ALGORITHM=TMPTABLE VIEW `test`.`v2` AS select (`test`.`t1`.`b` + 1) AS `c` from `test`.`t1` +v2 CREATE ALGORITHM=TEMPTABLE VIEW `test`.`v2` AS select (`test`.`t1`.`b` + 1) AS `c` from `test`.`t1` select c from v2; c 3 @@ -313,7 +313,7 @@ id select_type table type possible_keys key key_len ref rows Extra 2 DERIVED NULL NULL NULL NULL NULL NULL NULL no matching row in const table show create table mysqltest.v2; Table Create Table -v2 CREATE ALGORITHM=TMPTABLE VIEW `mysqltest`.`v2` AS select (`mysqltest`.`t1`.`a` + 1) AS `c`,(`mysqltest`.`t1`.`b` + 1) AS `d` from `mysqltest`.`t1` +v2 CREATE ALGORITHM=TEMPTABLE VIEW `mysqltest`.`v2` AS select (`mysqltest`.`t1`.`a` + 1) AS `c`,(`mysqltest`.`t1`.`b` + 1) AS `d` from `mysqltest`.`t1` explain select c from mysqltest.v3; ERROR HY000: EXPLAIN/SHOW can not be issued; lacking privileges for underlying table show create table mysqltest.v3; @@ -335,7 +335,7 @@ id select_type table type possible_keys key key_len ref rows Extra 2 DERIVED NULL NULL NULL NULL NULL NULL NULL no matching row in const table show create table mysqltest.v2; Table Create Table -v2 CREATE ALGORITHM=TMPTABLE VIEW `mysqltest`.`v2` AS select (`mysqltest`.`t1`.`a` + 1) AS `c`,(`mysqltest`.`t1`.`b` + 1) AS `d` from `mysqltest`.`t1` +v2 CREATE ALGORITHM=TEMPTABLE VIEW `mysqltest`.`v2` AS select (`mysqltest`.`t1`.`a` + 1) AS `c`,(`mysqltest`.`t1`.`b` + 1) AS `d` from `mysqltest`.`t1` explain select c from mysqltest.v3; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t2 system NULL NULL NULL NULL 0 const row not found @@ -348,7 +348,7 @@ id select_type table type possible_keys key key_len ref rows Extra 2 DERIVED NULL NULL NULL NULL NULL NULL NULL no matching row in const table show create table mysqltest.v4; Table Create Table -v4 CREATE ALGORITHM=TMPTABLE VIEW `mysqltest`.`v4` AS select (`mysqltest`.`t2`.`a` + 1) AS `c`,(`mysqltest`.`t2`.`b` + 1) AS `d` from `mysqltest`.`t2` +v4 CREATE ALGORITHM=TEMPTABLE VIEW `mysqltest`.`v4` AS select (`mysqltest`.`t2`.`a` + 1) AS `c`,(`mysqltest`.`t2`.`b` + 1) AS `d` from `mysqltest`.`t2` revoke all privileges on mysqltest.* from mysqltest_1@localhost; delete from mysql.user where user='mysqltest_1'; drop database mysqltest; @@ -1270,3 +1270,44 @@ s1 7 drop view v1; drop table t1; +create table t1 (col1 int); +create table t2 (col1 int); +create view v1 as select * from t1; +create view v2 as select * from v1; +update v2 set col1 = (select max(col1) from v1); +ERROR HY000: You can't specify target table 'v2' for update in FROM clause +delete from v2 where col1 = (select max(col1) from v1); +ERROR HY000: You can't specify target table 'v2' for update in FROM clause +insert into v2 values ((select max(col1) from v1)); +ERROR HY000: You can't specify target table 'v2' for update in FROM clause +drop view v2,v1; +drop table t1,t2; +create table t1 (s1 int); +create view v1 as select * from t1; +handler v1 open as xx; +ERROR HY000: 'test.v1' is not BASE TABLE +drop view v1; +drop table t1; +create table t1(a int); +insert into t1 values (0), (1), (2), (3); +create table t2 (a int); +insert into t2 select a from t1 where a > 1; +create view v1 as select a from t1 where a > 1; +select * from t1 left join (t2 as t, v1) on v1.a=t1.a; +a a a +0 NULL NULL +1 NULL NULL +2 2 2 +2 3 2 +3 2 3 +3 3 3 +select * from t1 left join (t2 as t, t2) on t2.a=t1.a; +a a a +0 NULL NULL +1 NULL NULL +2 2 2 +2 3 2 +3 2 3 +3 3 3 +drop view v1; +drop table t1; diff --git a/mysql-test/t/lowercase_view-master.opt b/mysql-test/t/lowercase_view-master.opt new file mode 100644 index 00000000000..62ab6dad1e0 --- /dev/null +++ b/mysql-test/t/lowercase_view-master.opt @@ -0,0 +1 @@ +--lower_case_table_names=1 diff --git a/mysql-test/t/lowercase_view.test b/mysql-test/t/lowercase_view.test new file mode 100644 index 00000000000..2a2757650ae --- /dev/null +++ b/mysql-test/t/lowercase_view.test @@ -0,0 +1,34 @@ +--disable_warnings +drop table if exists t1Aa,t2Aa,v1Aa,v2Aa; +drop view if exists t1Aa,t2Aa,v1Aa,v2Aa; +drop database if exists MySQLTest; +--enable_warnings + +# +# different cases in VIEW +# +create database MySQLTest; +use MySQLTest; +create table TaB (Field int); +create view ViE as select * from TAb; +show create table VIe; +drop database MySQLTest; +use test; + +# +# test of updating and fetching from the same table check +# +create table t1Aa (col1 int); +create table t2Aa (col1 int); +create view v1Aa as select * from t1Aa; +create view v2Aa as select * from v1Aa; +-- error 1093 +update v2aA set col1 = (select max(col1) from v1aA); +#update v2aA,t2aA set v2aA.col1 = (select max(col1) from v1aA) where v2aA.col1 = t2aA.col1; +-- error 1093 +delete from v2aA where col1 = (select max(col1) from v1aA); +#delete v2aA from v2aA,t2aA where (select max(col1) from v1aA) > 0 and v2aA.col1 = t2aA.col1; +-- error 1093 +insert into v2aA values ((select max(col1) from v1aA)); +drop view v2Aa,v1Aa; +drop table t1Aa,t2Aa; diff --git a/mysql-test/t/merge.test b/mysql-test/t/merge.test index 9580c1ab44c..9d367260049 100644 --- a/mysql-test/t/merge.test +++ b/mysql-test/t/merge.test @@ -284,4 +284,6 @@ insert into t2 values (1); create table t3 engine=merge union=(t1, t2) select * from t1; --error 1093 create table t3 engine=merge union=(t1, t2) select * from t2; +--error 1093 +create table t3 engine=merge union=(t1, t2) select (select max(a) from t2); drop table t1, t2; diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test index 32c146141b5..46700293578 100644 --- a/mysql-test/t/sp-error.test +++ b/mysql-test/t/sp-error.test @@ -5,7 +5,7 @@ # Make sure we don't have any procedures left. delete from mysql.proc; -# A test "global" procedures, i.e. not belonging to any database. +# A test of "global" procedures, i.e. not belonging to any database. create function .f1() returns int return 1; create procedure .p1() select 1, database(); create procedure p1() select 2, database(); @@ -650,6 +650,22 @@ create procedure bug4344() drop procedure bug4344| --error 1357 create procedure bug4344() drop function bug4344| +# +# BUG#3294: Stored procedure crash if table dropped before use +# (Actually, when an error occurs within an error handler.) +--disable_warnings +drop procedure if exists bug3294| +--enable_warnings +create procedure bug3294() +begin + declare continue handler for sqlexception drop table t5; + drop table t5; +end| + +--error 1051 +call bug3294()| +drop procedure bug3294| + drop table t1| diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 05d38d25956..e2c82c9f0da 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -1987,6 +1987,40 @@ call bug5258_aux()| drop procedure bug5258| drop procedure bug5258_aux| +# +# BUG#4487: Stored procedure connection aborted if uninitialized char +# +create function bug4487() returns char +begin + declare v char; + return v; +end| + +select bug4487()| +drop function bug4487| + + +# +# BUG#4941: Stored procedure crash fetching null value into variable. +# +--disable_warnings +drop procedure if exists bug4941| +--enable_warnings +create procedure bug4941(out x int) +begin + declare c cursor for select i from t2 limit 1; + open c; + fetch c into x; + close c; +end| + +insert into t2 values (null, null, null)| +set @x = 42| +call bug4941(@x)| +select @x| +delete from t1| +drop procedure bug4941| + # # Some "real" examples diff --git a/mysql-test/t/trigger.test b/mysql-test/t/trigger.test new file mode 100644 index 00000000000..8922f73661e --- /dev/null +++ b/mysql-test/t/trigger.test @@ -0,0 +1,195 @@ +# +# Basic triggers test +# + +--disable_warnings +drop table if exists t1, t2; +drop view if exists v1; +--enable_warnings + +create table t1 (i int); + +# let us test some very simple trigger +create trigger trg before insert on t1 for each row set @a:=1; +set @a:=0; +select @a; +insert into t1 values (1); +select @a; +drop trigger t1.trg; + +# let us test simple trigger reading some values +create trigger trg before insert on t1 for each row set @a:=new.i; +insert into t1 values (123); +select @a; +drop trigger t1.trg; + +drop table t1; + +# Let us test before insert trigger +# Such triggers can be used for setting complex default values +create table t1 (i int not null, j int); +delimiter |; +create trigger trg before insert on t1 for each row +begin + if isnull(new.j) then + set new.j:= new.i * 10; + end if; +end| +insert into t1 (i) values (1)| +insert into t1 (i,j) values (2, 3)| +select * from t1| +drop trigger t1.trg| +drop table t1| +delimiter ;| + +# After insert trigger +# Useful for aggregating data +create table t1 (i int not null primary key); +create trigger trg after insert on t1 for each row + set @a:= if(@a,concat(@a, ":", new.i), new.i); +set @a:=""; +insert into t1 values (2),(3),(4),(5); +select @a; +drop trigger t1.trg; +drop table t1; + +# Before update trigger +# (In future we will achieve this via proper error handling in triggers) +create table t1 (aid int not null primary key, balance int not null default 0); +insert into t1 values (1, 1000), (2,3000); +delimiter |; +create trigger trg before update on t1 for each row +begin + declare loc_err varchar(255); + if abs(new.balance - old.balance) > 1000 then + set new.balance:= old.balance; + set loc_err := concat("Too big change for aid = ", new.aid); + set @update_failed:= if(@update_failed, concat(@a, ":", loc_err), loc_err); + end if; +end| +set @update_failed:=""| +update t1 set balance=1500| +select @update_failed; +select * from t1| +drop trigger t1.trg| +drop table t1| +delimiter ;| + +# After update trigger +create table t1 (i int); +insert into t1 values (1),(2),(3),(4); +create trigger trg after update on t1 for each row + set @total_change:=@total_change + new.i - old.i; +set @total_change:=0; +update t1 set i=3; +select @total_change; +drop trigger t1.trg; +drop table t1; + +# Before delete trigger +# This can be used for aggregation too :) +create table t1 (i int); +insert into t1 values (1),(2),(3),(4); +create trigger trg before delete on t1 for each row + set @del_sum:= @del_sum + old.i; +set @del_sum:= 0; +delete from t1 where i <= 3; +select @del_sum; +drop trigger t1.trg; +drop table t1; + +# After delete trigger. +# Just run out of imagination. +create table t1 (i int); +insert into t1 values (1),(2),(3),(4); +create trigger trg after delete on t1 for each row set @del:= 1; +set @del:= 0; +delete from t1 where i <> 0; +select @del; +drop trigger t1.trg; +drop table t1; + +# Several triggers on one table +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 ;| +set @fired:=""; +insert into t1 values (1,2),(2,3),(3,14); +select @fired; +select * from t1; +update t1 set j= 20; +select @fired; +select * from t1; + +drop trigger t1.trg1; +drop trigger t1.trg2; +drop trigger t1.trg3; +drop table t1; + + +# +# Test of wrong column specifiers in triggers +# +create table t1 (i int); + +--error 1363 +create trigger trg before insert on t1 for each row set @a:= old.i; +--error 1363 +create trigger trg before delete on t1 for each row set @a:= new.i; +--error 1362 +create trigger trg before update on t1 for each row set old.i:=1; +--error 1363 +create trigger trg before delete on t1 for each row set new.i:=1; +--error 1362 +create trigger trg after update on t1 for each row set new.i:=1; +# TODO: We should also test wrong field names here, we don't do it now +# because proper error handling is not in place yet. + + +# +# Let us test various trigger creation errors +# +# +--error 1146 +create trigger trg before insert on t2 for each row set @a:=1; + +create trigger trg before insert on t1 for each row set @a:=1; +--error 1359 +create trigger trg after insert on t1 for each row set @a:=1; +--error 1359 +create trigger trg2 before insert on t1 for each row set @a:=1; +drop trigger t1.trg; + +--error 1360 +drop trigger t1.trg; + +create view v1 as select * from t1; +--error 1361 +create trigger trg before insert on v1 for each row set @a:=1; +drop view v1; + +drop table t1; + +create temporary table t1 (i int); +--error 1361 +create trigger trg before insert on t1 for each row set @a:=1; +drop table t1; diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index 7d05dcb9ef8..682646a6c02 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -1230,3 +1230,44 @@ insert into v1 values (1) on duplicate key update s1 = 7; select * from t1; drop view v1; drop table t1; + +# +# test of updating and fetching from the same table check +# +create table t1 (col1 int); +create table t2 (col1 int); +create view v1 as select * from t1; +create view v2 as select * from v1; +-- error 1093 +update v2 set col1 = (select max(col1) from v1); +#update v2,t2 set v2.col1 = (select max(col1) from v1) where v2.col1 = t2.col1; +-- error 1093 +delete from v2 where col1 = (select max(col1) from v1); +#delete v2 from v2,t2 where (select max(col1) from v1) > 0 and v2.col1 = t2.col1; +-- error 1093 +insert into v2 values ((select max(col1) from v1)); +drop view v2,v1; +drop table t1,t2; + +# +# HANDLER with VIEW +# +create table t1 (s1 int); +create view v1 as select * from t1; +-- error 1347 +handler v1 open as xx; +drop view v1; +drop table t1; + +# +# view with WHERE in nested join +# +create table t1(a int); +insert into t1 values (0), (1), (2), (3); +create table t2 (a int); +insert into t2 select a from t1 where a > 1; +create view v1 as select a from t1 where a > 1; +select * from t1 left join (t2 as t, v1) on v1.a=t1.a; +select * from t1 left join (t2 as t, t2) on t2.a=t1.a; +drop view v1; +drop table t1; diff --git a/mysys/default.c b/mysys/default.c index a8343c6a21d..198a6402b8b 100644 --- a/mysys/default.c +++ b/mysys/default.c @@ -119,8 +119,6 @@ static int search_files(const char *conf_file, int *argc, char ***argv, int error= 0; DBUG_ENTER("search_files"); - args_used= 0; - /* Check if we want to force the use a specific default file */ forced_default_file= 0; if (*argc >= 2) @@ -128,12 +126,12 @@ static int search_files(const char *conf_file, int *argc, char ***argv, if (is_prefix(argv[0][1],"--defaults-file=")) { forced_default_file= strchr(argv[0][1],'=') + 1; - *args_used++; + (*args_used)++; } else if (is_prefix(argv[0][1],"--defaults-extra-file=")) { defaults_extra_file= strchr(argv[0][1],'=') + 1; - *args_used++; + (*args_used)++; } } diff --git a/scripts/mysql_fix_privilege_tables.sql b/scripts/mysql_fix_privilege_tables.sql index 18dfe14bc45..5b335862584 100644 --- a/scripts/mysql_fix_privilege_tables.sql +++ b/scripts/mysql_fix_privilege_tables.sql @@ -144,6 +144,13 @@ alter table user comment='Users and global privileges'; alter table func comment='User defined functions'; alter table tables_priv comment='Table privileges'; alter table columns_priv comment='Column privileges'; + +# +# Detect whether we had Create_view_priv +# +SET @hadCreateViewPriv:=0; +SELECT @hadCreateViewPriv:=1 FROM user WHERE Create_view_priv LIKE '%'; + # # Create VIEWs privileges (v5.0) # @@ -159,6 +166,11 @@ ALTER TABLE host ADD Show_view_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Cre ALTER TABLE user ADD Show_view_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Create_view_priv; # +# Assign create/show view privileges to people who have create provileges +# +UPDATE user SET Create_view_priv=Create_priv, Show_view_priv=Create_priv where user<>"" AND @hadCreateViewPriv = 0; + +# # Create some possible missing tables # CREATE TABLE IF NOT EXISTS help_topic ( diff --git a/sql/Makefile.am b/sql/Makefile.am index 7a55367c717..2c99d42f3ee 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -61,7 +61,7 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ spatial.h gstream.h client_settings.h tzfile.h \ tztime.h \ sp_head.h sp_pcontext.h sp_rcontext.h sp.h sp_cache.h \ - parse_file.h sql_view.h \ + parse_file.h sql_view.h sql_trigger.h \ examples/ha_example.h examples/ha_archive.h \ examples/ha_tina.h @@ -97,7 +97,7 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc \ gstream.cc spatial.cc sql_help.cc protocol_cursor.cc \ tztime.cc my_time.c \ sp_head.cc sp_pcontext.cc sp_rcontext.cc sp.cc \ - sp_cache.cc parse_file.cc \ + sp_cache.cc parse_file.cc sql_trigger.cc \ examples/ha_example.cc examples/ha_archive.cc \ examples/ha_tina.cc gen_lex_hash_SOURCES = gen_lex_hash.cc diff --git a/sql/filesort.cc b/sql/filesort.cc index fe2b3850197..ef8148616e5 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -163,11 +163,11 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, if (select && select->quick) { - statistic_increment(filesort_range_count, &LOCK_status); + statistic_increment(thd->status_var.filesort_range_count, &LOCK_status); } else { - statistic_increment(filesort_scan_count, &LOCK_status); + statistic_increment(thd->status_var.filesort_scan_count, &LOCK_status); } #ifdef CAN_TRUST_RANGE if (select && select->quick && select->quick->records > 0L) @@ -280,7 +280,8 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, if (error) my_error(ER_FILSORT_ABORT,MYF(ME_ERROR+ME_WAITTANG)); else - statistic_add(filesort_rows, (ulong) records, &LOCK_status); + statistic_add(thd->status_var.filesort_rows, + (ulong) records, &LOCK_status); *examined_rows= param.examined_rows; #ifdef SKIP_DBUG_IN_FILESORT DBUG_POP(); /* Ok to DBUG */ @@ -874,7 +875,8 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, THD::killed_state not_killable; DBUG_ENTER("merge_buffers"); - statistic_increment(filesort_merge_passes, &LOCK_status); + statistic_increment(current_thd->status_var.filesort_merge_passes, + &LOCK_status); if (param->not_killable) { killed= ¬_killable; diff --git a/sql/ha_berkeley.cc b/sql/ha_berkeley.cc index 9f93437bc32..bbe73ee8674 100644 --- a/sql/ha_berkeley.cc +++ b/sql/ha_berkeley.cc @@ -855,7 +855,7 @@ int ha_berkeley::write_row(byte * record) int error; DBUG_ENTER("write_row"); - statistic_increment(ha_write_count,&LOCK_status); + statistic_increment(table->in_use->status_var.ha_write_count, &LOCK_status); if (table->timestamp_default_now) update_timestamp(record+table->timestamp_default_now-1); if (table->next_number_field && record == table->record[0]) @@ -1099,10 +1099,11 @@ int ha_berkeley::update_row(const byte * old_row, byte * new_row) DB_TXN *sub_trans; ulong thd_options = table->tmp_table == NO_TMP_TABLE ? table->in_use->options : 0; bool primary_key_changed; + DBUG_ENTER("update_row"); LINT_INIT(error); - statistic_increment(ha_update_count,&LOCK_status); + statistic_increment(table->in_use->status_var.ha_update_count,&LOCK_status); if (table->timestamp_on_update_now) update_timestamp(new_row+table->timestamp_on_update_now-1); @@ -1292,7 +1293,7 @@ int ha_berkeley::delete_row(const byte * record) key_map keys=table->keys_in_use; ulong thd_options = table->tmp_table == NO_TMP_TABLE ? table->in_use->options : 0; DBUG_ENTER("delete_row"); - statistic_increment(ha_delete_count,&LOCK_status); + statistic_increment(table->in_use->status_var.ha_delete_count,&LOCK_status); if ((error=pack_row(&row, record, 0))) DBUG_RETURN((error)); /* purecov: inspected */ @@ -1439,7 +1440,7 @@ int ha_berkeley::read_row(int error, char *buf, uint keynr, DBT *row, int ha_berkeley::index_read_idx(byte * buf, uint keynr, const byte * key, uint key_len, enum ha_rkey_function find_flag) { - statistic_increment(ha_read_key_count,&LOCK_status); + statistic_increment(table->in_use->status_var.ha_read_key_count,&LOCK_status); DBUG_ENTER("index_read_idx"); current_row.flags=DB_DBT_REALLOC; active_index=MAX_KEY; @@ -1458,9 +1459,10 @@ int ha_berkeley::index_read(byte * buf, const byte * key, int error; KEY *key_info= &table->key_info[active_index]; int do_prev= 0; + DBUG_ENTER("ha_berkeley::index_read"); - statistic_increment(ha_read_key_count,&LOCK_status); + statistic_increment(table->in_use->status_var.ha_read_key_count,&LOCK_status); bzero((char*) &row,sizeof(row)); if (find_flag == HA_READ_BEFORE_KEY) { @@ -1529,7 +1531,8 @@ int ha_berkeley::index_read_last(byte * buf, const byte * key, uint key_len) KEY *key_info= &table->key_info[active_index]; DBUG_ENTER("ha_berkeley::index_read"); - statistic_increment(ha_read_key_count,&LOCK_status); + statistic_increment(table->in_use->status_var.ha_read_key_count, + &LOCK_status); bzero((char*) &row,sizeof(row)); /* read of partial key */ @@ -1553,7 +1556,8 @@ int ha_berkeley::index_next(byte * buf) { DBT row; DBUG_ENTER("index_next"); - statistic_increment(ha_read_next_count,&LOCK_status); + statistic_increment(table->in_use->status_var.ha_read_next_count, + &LOCK_status); bzero((char*) &row,sizeof(row)); DBUG_RETURN(read_row(cursor->c_get(cursor, &last_key, &row, DB_NEXT), (char*) buf, active_index, &row, &last_key, 1)); @@ -1564,7 +1568,8 @@ int ha_berkeley::index_next_same(byte * buf, const byte *key, uint keylen) DBT row; int error; DBUG_ENTER("index_next_same"); - statistic_increment(ha_read_next_count,&LOCK_status); + statistic_increment(table->in_use->status_var.ha_read_next_count, + &LOCK_status); bzero((char*) &row,sizeof(row)); if (keylen == table->key_info[active_index].key_length) error=read_row(cursor->c_get(cursor, &last_key, &row, DB_NEXT_DUP), @@ -1584,7 +1589,8 @@ int ha_berkeley::index_prev(byte * buf) { DBT row; DBUG_ENTER("index_prev"); - statistic_increment(ha_read_prev_count,&LOCK_status); + statistic_increment(table->in_use->status_var.ha_read_prev_count, + &LOCK_status); bzero((char*) &row,sizeof(row)); DBUG_RETURN(read_row(cursor->c_get(cursor, &last_key, &row, DB_PREV), (char*) buf, active_index, &row, &last_key, 1)); @@ -1595,7 +1601,8 @@ int ha_berkeley::index_first(byte * buf) { DBT row; DBUG_ENTER("index_first"); - statistic_increment(ha_read_first_count,&LOCK_status); + statistic_increment(table->in_use->status_var.ha_read_first_count, + &LOCK_status); bzero((char*) &row,sizeof(row)); DBUG_RETURN(read_row(cursor->c_get(cursor, &last_key, &row, DB_FIRST), (char*) buf, active_index, &row, &last_key, 1)); @@ -1605,7 +1612,8 @@ int ha_berkeley::index_last(byte * buf) { DBT row; DBUG_ENTER("index_last"); - statistic_increment(ha_read_last_count,&LOCK_status); + statistic_increment(table->in_use->status_var.ha_read_last_count, + &LOCK_status); bzero((char*) &row,sizeof(row)); DBUG_RETURN(read_row(cursor->c_get(cursor, &last_key, &row, DB_LAST), (char*) buf, active_index, &row, &last_key, 0)); @@ -1627,7 +1635,8 @@ int ha_berkeley::rnd_next(byte *buf) { DBT row; DBUG_ENTER("rnd_next"); - statistic_increment(ha_read_rnd_next_count,&LOCK_status); + statistic_increment(table->in_use->status_var.ha_read_rnd_next_count, + &LOCK_status); bzero((char*) &row,sizeof(row)); DBUG_RETURN(read_row(cursor->c_get(cursor, &last_key, &row, DB_NEXT), (char*) buf, primary_key, &row, &last_key, 1)); @@ -1658,9 +1667,10 @@ DBT *ha_berkeley::get_pos(DBT *to, byte *pos) int ha_berkeley::rnd_pos(byte * buf, byte *pos) { DBT db_pos; - statistic_increment(ha_read_rnd_count,&LOCK_status); + DBUG_ENTER("ha_berkeley::rnd_pos"); - + statistic_increment(table->in_use->status_var.ha_read_rnd_count, + &LOCK_status); active_index= MAX_KEY; DBUG_RETURN(read_row(file->get(file, transaction, get_pos(&db_pos, pos), diff --git a/sql/ha_heap.cc b/sql/ha_heap.cc index 7340a6973b5..6dc5e85e1d4 100644 --- a/sql/ha_heap.cc +++ b/sql/ha_heap.cc @@ -86,7 +86,7 @@ void ha_heap::set_keys_for_scanning(void) int ha_heap::write_row(byte * buf) { - statistic_increment(ha_write_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_write_count,&LOCK_status); if (table->timestamp_default_now) update_timestamp(buf+table->timestamp_default_now-1); if (table->next_number_field && buf == table->record[0]) @@ -96,7 +96,7 @@ int ha_heap::write_row(byte * buf) int ha_heap::update_row(const byte * old_data, byte * new_data) { - statistic_increment(ha_update_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_update_count,&LOCK_status); if (table->timestamp_on_update_now) update_timestamp(new_data+table->timestamp_on_update_now-1); return heap_update(file,old_data,new_data); @@ -104,7 +104,7 @@ int ha_heap::update_row(const byte * old_data, byte * new_data) int ha_heap::delete_row(const byte * buf) { - statistic_increment(ha_delete_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_delete_count,&LOCK_status); return heap_delete(file,buf); } @@ -112,7 +112,7 @@ int ha_heap::index_read(byte * buf, const byte * key, uint key_len, enum ha_rkey_function find_flag) { DBUG_ASSERT(inited==INDEX); - statistic_increment(ha_read_key_count, &LOCK_status); + statistic_increment(current_thd->status_var.ha_read_key_count, &LOCK_status); int error = heap_rkey(file,buf,active_index, key, key_len, find_flag); table->status = error ? STATUS_NOT_FOUND : 0; return error; @@ -121,7 +121,7 @@ int ha_heap::index_read(byte * buf, const byte * key, uint key_len, int ha_heap::index_read_last(byte *buf, const byte *key, uint key_len) { DBUG_ASSERT(inited==INDEX); - statistic_increment(ha_read_key_count, &LOCK_status); + statistic_increment(current_thd->status_var.ha_read_key_count, &LOCK_status); int error= heap_rkey(file, buf, active_index, key, key_len, HA_READ_PREFIX_LAST); table->status= error ? STATUS_NOT_FOUND : 0; @@ -131,7 +131,7 @@ int ha_heap::index_read_last(byte *buf, const byte *key, uint key_len) int ha_heap::index_read_idx(byte * buf, uint index, const byte * key, uint key_len, enum ha_rkey_function find_flag) { - statistic_increment(ha_read_key_count, &LOCK_status); + statistic_increment(current_thd->status_var.ha_read_key_count, &LOCK_status); int error = heap_rkey(file, buf, index, key, key_len, find_flag); table->status = error ? STATUS_NOT_FOUND : 0; return error; @@ -140,7 +140,7 @@ int ha_heap::index_read_idx(byte * buf, uint index, const byte * key, int ha_heap::index_next(byte * buf) { DBUG_ASSERT(inited==INDEX); - statistic_increment(ha_read_next_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_next_count,&LOCK_status); int error=heap_rnext(file,buf); table->status=error ? STATUS_NOT_FOUND: 0; return error; @@ -149,7 +149,7 @@ int ha_heap::index_next(byte * buf) int ha_heap::index_prev(byte * buf) { DBUG_ASSERT(inited==INDEX); - statistic_increment(ha_read_prev_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_prev_count,&LOCK_status); int error=heap_rprev(file,buf); table->status=error ? STATUS_NOT_FOUND: 0; return error; @@ -158,7 +158,8 @@ int ha_heap::index_prev(byte * buf) int ha_heap::index_first(byte * buf) { DBUG_ASSERT(inited==INDEX); - statistic_increment(ha_read_first_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_first_count, + &LOCK_status); int error=heap_rfirst(file, buf, active_index); table->status=error ? STATUS_NOT_FOUND: 0; return error; @@ -167,7 +168,7 @@ int ha_heap::index_first(byte * buf) int ha_heap::index_last(byte * buf) { DBUG_ASSERT(inited==INDEX); - statistic_increment(ha_read_last_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_last_count,&LOCK_status); int error=heap_rlast(file, buf, active_index); table->status=error ? STATUS_NOT_FOUND: 0; return error; @@ -180,7 +181,8 @@ int ha_heap::rnd_init(bool scan) int ha_heap::rnd_next(byte *buf) { - statistic_increment(ha_read_rnd_next_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_rnd_next_count, + &LOCK_status); int error=heap_scan(file, buf); table->status=error ? STATUS_NOT_FOUND: 0; return error; @@ -190,7 +192,7 @@ int ha_heap::rnd_pos(byte * buf, byte *pos) { int error; HEAP_PTR position; - statistic_increment(ha_read_rnd_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_rnd_count, &LOCK_status); memcpy_fixed((char*) &position,pos,sizeof(HEAP_PTR)); error=heap_rrnd(file, buf, position); table->status=error ? STATUS_NOT_FOUND: 0; diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 8da04ed0ab1..4d068cfc0ce 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -2218,7 +2218,8 @@ ha_innobase::write_row( ut_error; } - statistic_increment(ha_write_count, &LOCK_status); + statistic_increment(current_thd->status_var.ha_write_count, + &LOCK_status); if (table->timestamp_default_now) update_timestamp(record + table->timestamp_default_now - 1); @@ -2748,7 +2749,8 @@ ha_innobase::index_read( ut_ad(prebuilt->trx == (trx_t*) current_thd->transaction.all.innobase_tid); - statistic_increment(ha_read_key_count, &LOCK_status); + statistic_increment(current_thd->status_var.ha_read_key_count, + &LOCK_status); if (last_query_id != user_thd->query_id) { prebuilt->sql_stat_start = TRUE; @@ -2854,7 +2856,8 @@ ha_innobase::change_active_index( { row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; KEY* key=0; - statistic_increment(ha_read_key_count, &LOCK_status); + statistic_increment(current_thd->status_var.ha_read_key_count, + &LOCK_status); DBUG_ENTER("change_active_index"); ut_ad(user_thd == current_thd); @@ -2986,7 +2989,8 @@ ha_innobase::index_next( mysql_byte* buf) /* in/out: buffer for next row in MySQL format */ { - statistic_increment(ha_read_next_count, &LOCK_status); + statistic_increment(current_thd->status_var.ha_read_next_count, + &LOCK_status); return(general_fetch(buf, ROW_SEL_NEXT, 0)); } @@ -3003,7 +3007,8 @@ ha_innobase::index_next_same( const mysql_byte* key, /* in: key value */ uint keylen) /* in: key value length */ { - statistic_increment(ha_read_next_count, &LOCK_status); + statistic_increment(current_thd->status_var.ha_read_next_count, + &LOCK_status); return(general_fetch(buf, ROW_SEL_NEXT, last_match_mode)); } @@ -3037,7 +3042,8 @@ ha_innobase::index_first( int error; DBUG_ENTER("index_first"); - statistic_increment(ha_read_first_count, &LOCK_status); + statistic_increment(current_thd->status_var.ha_read_first_count, + &LOCK_status); error = index_read(buf, NULL, 0, HA_READ_AFTER_KEY); @@ -3063,7 +3069,8 @@ ha_innobase::index_last( int error; DBUG_ENTER("index_first"); - statistic_increment(ha_read_last_count, &LOCK_status); + statistic_increment(current_thd->status_var.ha_read_last_count, + &LOCK_status); error = index_read(buf, NULL, 0, HA_READ_BEFORE_KEY); @@ -3128,7 +3135,8 @@ ha_innobase::rnd_next( int error; DBUG_ENTER("rnd_next"); - statistic_increment(ha_read_rnd_next_count, &LOCK_status); + statistic_increment(current_thd->status_var.ha_read_rnd_next_count, + &LOCK_status); if (start_of_scan) { error = index_first(buf); @@ -3164,7 +3172,8 @@ ha_innobase::rnd_pos( DBUG_ENTER("rnd_pos"); DBUG_DUMP("key", (char*) pos, ref_length); - statistic_increment(ha_read_rnd_count, &LOCK_status); + statistic_increment(current_thd->status_var.ha_read_rnd_count, + &LOCK_status); ut_ad(prebuilt->trx == (trx_t*) current_thd->transaction.all.innobase_tid); diff --git a/sql/ha_isam.cc b/sql/ha_isam.cc index 2fd75462329..201230c8aab 100644 --- a/sql/ha_isam.cc +++ b/sql/ha_isam.cc @@ -69,7 +69,7 @@ uint ha_isam::min_record_length(uint options) const int ha_isam::write_row(byte * buf) { - statistic_increment(ha_write_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_write_count, &LOCK_status); if (table->timestamp_default_now) update_timestamp(buf+table->timestamp_default_now-1); if (table->next_number_field && buf == table->record[0]) @@ -79,7 +79,7 @@ int ha_isam::write_row(byte * buf) int ha_isam::update_row(const byte * old_data, byte * new_data) { - statistic_increment(ha_update_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_update_count, &LOCK_status); if (table->timestamp_on_update_now) update_timestamp(new_data+table->timestamp_on_update_now-1); return !nisam_update(file,old_data,new_data) ? 0 : my_errno ? my_errno : -1; @@ -87,14 +87,14 @@ int ha_isam::update_row(const byte * old_data, byte * new_data) int ha_isam::delete_row(const byte * buf) { - statistic_increment(ha_delete_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_delete_count, &LOCK_status); return !nisam_delete(file,buf) ? 0 : my_errno ? my_errno : -1; } int ha_isam::index_read(byte * buf, const byte * key, uint key_len, enum ha_rkey_function find_flag) { - statistic_increment(ha_read_key_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_key_count, &LOCK_status); int error=nisam_rkey(file, buf, active_index, key, key_len, find_flag); table->status=error ? STATUS_NOT_FOUND: 0; return !error ? 0 : my_errno ? my_errno : -1; @@ -103,7 +103,7 @@ int ha_isam::index_read(byte * buf, const byte * key, int ha_isam::index_read_idx(byte * buf, uint index, const byte * key, uint key_len, enum ha_rkey_function find_flag) { - statistic_increment(ha_read_key_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_key_count, &LOCK_status); int error=nisam_rkey(file, buf, index, key, key_len, find_flag); table->status=error ? STATUS_NOT_FOUND: 0; return !error ? 0 : my_errno ? my_errno : -1; @@ -111,7 +111,7 @@ int ha_isam::index_read_idx(byte * buf, uint index, const byte * key, int ha_isam::index_read_last(byte * buf, const byte * key, uint key_len) { - statistic_increment(ha_read_key_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_key_count, &LOCK_status); int error=nisam_rkey(file, buf, active_index, key, key_len, HA_READ_PREFIX_LAST); table->status=error ? STATUS_NOT_FOUND: 0; @@ -120,7 +120,8 @@ int ha_isam::index_read_last(byte * buf, const byte * key, uint key_len) int ha_isam::index_next(byte * buf) { - statistic_increment(ha_read_next_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_next_count, + &LOCK_status); int error=nisam_rnext(file,buf,active_index); table->status=error ? STATUS_NOT_FOUND: 0; return !error ? 0 : my_errno ? my_errno : HA_ERR_END_OF_FILE; @@ -128,7 +129,8 @@ int ha_isam::index_next(byte * buf) int ha_isam::index_prev(byte * buf) { - statistic_increment(ha_read_prev_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_prev_count, + &LOCK_status); int error=nisam_rprev(file,buf, active_index); table->status=error ? STATUS_NOT_FOUND: 0; return !error ? 0 : my_errno ? my_errno : HA_ERR_END_OF_FILE; @@ -136,7 +138,8 @@ int ha_isam::index_prev(byte * buf) int ha_isam::index_first(byte * buf) { - statistic_increment(ha_read_first_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_first_count, + &LOCK_status); int error=nisam_rfirst(file, buf, active_index); table->status=error ? STATUS_NOT_FOUND: 0; return !error ? 0 : my_errno ? my_errno : HA_ERR_END_OF_FILE; @@ -144,7 +147,8 @@ int ha_isam::index_first(byte * buf) int ha_isam::index_last(byte * buf) { - statistic_increment(ha_read_last_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_last_count, + &LOCK_status); int error=nisam_rlast(file, buf, active_index); table->status=error ? STATUS_NOT_FOUND: 0; return !error ? 0 : my_errno ? my_errno : HA_ERR_END_OF_FILE; @@ -157,7 +161,8 @@ int ha_isam::rnd_init(bool scan) int ha_isam::rnd_next(byte *buf) { - statistic_increment(ha_read_rnd_next_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_rnd_next_count, + &LOCK_status); int error=nisam_rrnd(file, buf, NI_POS_ERROR); table->status=error ? STATUS_NOT_FOUND: 0; return !error ? 0 : my_errno ? my_errno : -1; @@ -165,7 +170,8 @@ int ha_isam::rnd_next(byte *buf) int ha_isam::rnd_pos(byte * buf, byte *pos) { - statistic_increment(ha_read_rnd_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_rnd_count, + &LOCK_status); int error=nisam_rrnd(file, buf, (ulong) ha_get_ptr(pos,ref_length)); table->status=error ? STATUS_NOT_FOUND: 0; return !error ? 0 : my_errno ? my_errno : -1; diff --git a/sql/ha_isammrg.cc b/sql/ha_isammrg.cc index 20e2b4db423..19983e4b257 100644 --- a/sql/ha_isammrg.cc +++ b/sql/ha_isammrg.cc @@ -77,7 +77,7 @@ int ha_isammrg::write_row(byte * buf) int ha_isammrg::update_row(const byte * old_data, byte * new_data) { - statistic_increment(ha_update_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_update_count, &LOCK_status); if (table->timestamp_on_update_now) update_timestamp(new_data+table->timestamp_on_update_now-1); return !mrg_update(file,old_data,new_data) ? 0 : my_errno ? my_errno : -1; @@ -85,7 +85,7 @@ int ha_isammrg::update_row(const byte * old_data, byte * new_data) int ha_isammrg::delete_row(const byte * buf) { - statistic_increment(ha_delete_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_delete_count, &LOCK_status); return !mrg_delete(file,buf) ? 0 : my_errno ? my_errno : -1; } @@ -128,7 +128,8 @@ int ha_isammrg::rnd_init(bool scan) int ha_isammrg::rnd_next(byte *buf) { - statistic_increment(ha_read_rnd_next_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_rnd_next_count, + &LOCK_status); int error=mrg_rrnd(file, buf, ~(mrg_off_t) 0); table->status=error ? STATUS_NOT_FOUND: 0; return !error ? 0 : my_errno ? my_errno : -1; @@ -136,7 +137,7 @@ int ha_isammrg::rnd_next(byte *buf) int ha_isammrg::rnd_pos(byte * buf, byte *pos) { - statistic_increment(ha_read_rnd_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_rnd_count, &LOCK_status); int error=mrg_rrnd(file, buf, (ulong) ha_get_ptr(pos,ref_length)); table->status=error ? STATUS_NOT_FOUND: 0; return !error ? 0 : my_errno ? my_errno : -1; diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc index 938f3a40629..7e86633da89 100644 --- a/sql/ha_myisam.cc +++ b/sql/ha_myisam.cc @@ -249,7 +249,7 @@ int ha_myisam::close(void) int ha_myisam::write_row(byte * buf) { - statistic_increment(ha_write_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_write_count,&LOCK_status); /* If we have a timestamp column, update it to the current time */ if (table->timestamp_default_now) @@ -1070,7 +1070,7 @@ bool ha_myisam::is_crashed() const int ha_myisam::update_row(const byte * old_data, byte * new_data) { - statistic_increment(ha_update_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_update_count,&LOCK_status); if (table->timestamp_on_update_now) update_timestamp(new_data+table->timestamp_on_update_now-1); return mi_update(file,old_data,new_data); @@ -1078,7 +1078,7 @@ int ha_myisam::update_row(const byte * old_data, byte * new_data) int ha_myisam::delete_row(const byte * buf) { - statistic_increment(ha_delete_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_delete_count,&LOCK_status); return mi_delete(file,buf); } @@ -1086,7 +1086,7 @@ int ha_myisam::index_read(byte * buf, const byte * key, uint key_len, enum ha_rkey_function find_flag) { DBUG_ASSERT(inited==INDEX); - statistic_increment(ha_read_key_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_key_count,&LOCK_status); int error=mi_rkey(file,buf,active_index, key, key_len, find_flag); table->status=error ? STATUS_NOT_FOUND: 0; return error; @@ -1095,7 +1095,7 @@ int ha_myisam::index_read(byte * buf, const byte * key, int ha_myisam::index_read_idx(byte * buf, uint index, const byte * key, uint key_len, enum ha_rkey_function find_flag) { - statistic_increment(ha_read_key_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_key_count,&LOCK_status); int error=mi_rkey(file,buf,index, key, key_len, find_flag); table->status=error ? STATUS_NOT_FOUND: 0; return error; @@ -1104,7 +1104,7 @@ int ha_myisam::index_read_idx(byte * buf, uint index, const byte * key, int ha_myisam::index_read_last(byte * buf, const byte * key, uint key_len) { DBUG_ASSERT(inited==INDEX); - statistic_increment(ha_read_key_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_key_count,&LOCK_status); int error=mi_rkey(file,buf,active_index, key, key_len, HA_READ_PREFIX_LAST); table->status=error ? STATUS_NOT_FOUND: 0; return error; @@ -1113,7 +1113,7 @@ int ha_myisam::index_read_last(byte * buf, const byte * key, uint key_len) int ha_myisam::index_next(byte * buf) { DBUG_ASSERT(inited==INDEX); - statistic_increment(ha_read_next_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_next_count,&LOCK_status); int error=mi_rnext(file,buf,active_index); table->status=error ? STATUS_NOT_FOUND: 0; return error; @@ -1122,7 +1122,7 @@ int ha_myisam::index_next(byte * buf) int ha_myisam::index_prev(byte * buf) { DBUG_ASSERT(inited==INDEX); - statistic_increment(ha_read_prev_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_prev_count,&LOCK_status); int error=mi_rprev(file,buf, active_index); table->status=error ? STATUS_NOT_FOUND: 0; return error; @@ -1131,7 +1131,8 @@ int ha_myisam::index_prev(byte * buf) int ha_myisam::index_first(byte * buf) { DBUG_ASSERT(inited==INDEX); - statistic_increment(ha_read_first_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_first_count, + &LOCK_status); int error=mi_rfirst(file, buf, active_index); table->status=error ? STATUS_NOT_FOUND: 0; return error; @@ -1140,7 +1141,7 @@ int ha_myisam::index_first(byte * buf) int ha_myisam::index_last(byte * buf) { DBUG_ASSERT(inited==INDEX); - statistic_increment(ha_read_last_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_last_count,&LOCK_status); int error=mi_rlast(file, buf, active_index); table->status=error ? STATUS_NOT_FOUND: 0; return error; @@ -1151,7 +1152,7 @@ int ha_myisam::index_next_same(byte * buf, uint length __attribute__((unused))) { DBUG_ASSERT(inited==INDEX); - statistic_increment(ha_read_next_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_next_count,&LOCK_status); int error=mi_rnext_same(file,buf); table->status=error ? STATUS_NOT_FOUND: 0; return error; @@ -1167,7 +1168,8 @@ int ha_myisam::rnd_init(bool scan) int ha_myisam::rnd_next(byte *buf) { - statistic_increment(ha_read_rnd_next_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_rnd_next_count, + &LOCK_status); int error=mi_scan(file, buf); table->status=error ? STATUS_NOT_FOUND: 0; return error; @@ -1180,7 +1182,7 @@ int ha_myisam::restart_rnd_next(byte *buf, byte *pos) int ha_myisam::rnd_pos(byte * buf, byte *pos) { - statistic_increment(ha_read_rnd_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_rnd_count,&LOCK_status); int error=mi_rrnd(file, buf, ha_get_ptr(pos,ref_length)); table->status=error ? STATUS_NOT_FOUND: 0; return error; @@ -1589,7 +1591,8 @@ int ha_myisam::ft_read(byte * buf) if (!ft_handler) return -1; - thread_safe_increment(ha_read_next_count,&LOCK_status); // why ? + thread_safe_increment(current_thd->status_var.ha_read_next_count, + &LOCK_status); // why ? error=ft_handler->please->read_next(ft_handler,(char*) buf); diff --git a/sql/ha_myisammrg.cc b/sql/ha_myisammrg.cc index 895d025d6fc..29a31571380 100644 --- a/sql/ha_myisammrg.cc +++ b/sql/ha_myisammrg.cc @@ -81,7 +81,7 @@ int ha_myisammrg::close(void) int ha_myisammrg::write_row(byte * buf) { - statistic_increment(ha_write_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_write_count,&LOCK_status); if (table->timestamp_default_now) update_timestamp(buf+table->timestamp_default_now-1); if (table->next_number_field && buf == table->record[0]) @@ -91,7 +91,7 @@ int ha_myisammrg::write_row(byte * buf) int ha_myisammrg::update_row(const byte * old_data, byte * new_data) { - statistic_increment(ha_update_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_update_count,&LOCK_status); if (table->timestamp_on_update_now) update_timestamp(new_data+table->timestamp_on_update_now); return myrg_update(file,old_data,new_data); @@ -99,14 +99,14 @@ int ha_myisammrg::update_row(const byte * old_data, byte * new_data) int ha_myisammrg::delete_row(const byte * buf) { - statistic_increment(ha_delete_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_delete_count,&LOCK_status); return myrg_delete(file,buf); } int ha_myisammrg::index_read(byte * buf, const byte * key, uint key_len, enum ha_rkey_function find_flag) { - statistic_increment(ha_read_key_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_key_count,&LOCK_status); int error=myrg_rkey(file,buf,active_index, key, key_len, find_flag); table->status=error ? STATUS_NOT_FOUND: 0; return error; @@ -115,7 +115,7 @@ int ha_myisammrg::index_read(byte * buf, const byte * key, int ha_myisammrg::index_read_idx(byte * buf, uint index, const byte * key, uint key_len, enum ha_rkey_function find_flag) { - statistic_increment(ha_read_key_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_key_count,&LOCK_status); int error=myrg_rkey(file,buf,index, key, key_len, find_flag); table->status=error ? STATUS_NOT_FOUND: 0; return error; @@ -123,7 +123,7 @@ int ha_myisammrg::index_read_idx(byte * buf, uint index, const byte * key, int ha_myisammrg::index_read_last(byte * buf, const byte * key, uint key_len) { - statistic_increment(ha_read_key_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_key_count,&LOCK_status); int error=myrg_rkey(file,buf,active_index, key, key_len, HA_READ_PREFIX_LAST); table->status=error ? STATUS_NOT_FOUND: 0; @@ -132,7 +132,7 @@ int ha_myisammrg::index_read_last(byte * buf, const byte * key, uint key_len) int ha_myisammrg::index_next(byte * buf) { - statistic_increment(ha_read_next_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_next_count,&LOCK_status); int error=myrg_rnext(file,buf,active_index); table->status=error ? STATUS_NOT_FOUND: 0; return error; @@ -140,7 +140,7 @@ int ha_myisammrg::index_next(byte * buf) int ha_myisammrg::index_prev(byte * buf) { - statistic_increment(ha_read_prev_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_prev_count,&LOCK_status); int error=myrg_rprev(file,buf, active_index); table->status=error ? STATUS_NOT_FOUND: 0; return error; @@ -148,7 +148,8 @@ int ha_myisammrg::index_prev(byte * buf) int ha_myisammrg::index_first(byte * buf) { - statistic_increment(ha_read_first_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_first_count, + &LOCK_status); int error=myrg_rfirst(file, buf, active_index); table->status=error ? STATUS_NOT_FOUND: 0; return error; @@ -156,7 +157,7 @@ int ha_myisammrg::index_first(byte * buf) int ha_myisammrg::index_last(byte * buf) { - statistic_increment(ha_read_last_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_last_count,&LOCK_status); int error=myrg_rlast(file, buf, active_index); table->status=error ? STATUS_NOT_FOUND: 0; return error; @@ -166,7 +167,7 @@ int ha_myisammrg::index_next_same(byte * buf, const byte *key __attribute__((unused)), uint length __attribute__((unused))) { - statistic_increment(ha_read_next_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_next_count,&LOCK_status); int error=myrg_rnext_same(file,buf); table->status=error ? STATUS_NOT_FOUND: 0; return error; @@ -179,7 +180,8 @@ int ha_myisammrg::rnd_init(bool scan) int ha_myisammrg::rnd_next(byte *buf) { - statistic_increment(ha_read_rnd_next_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_rnd_next_count, + &LOCK_status); int error=myrg_rrnd(file, buf, HA_OFFSET_ERROR); table->status=error ? STATUS_NOT_FOUND: 0; return error; @@ -187,7 +189,7 @@ int ha_myisammrg::rnd_next(byte *buf) int ha_myisammrg::rnd_pos(byte * buf, byte *pos) { - statistic_increment(ha_read_rnd_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_rnd_count,&LOCK_status); int error=myrg_rrnd(file, buf, ha_get_ptr(pos,ref_length)); table->status=error ? STATUS_NOT_FOUND: 0; return error; diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 8b7b5e94965..1837500d8f7 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -1367,9 +1367,11 @@ int ha_ndbcluster::write_row(byte *record) NdbConnection *trans= m_active_trans; NdbOperation *op; int res; + THD *thd= current_thd; + DBUG_ENTER("write_row"); - statistic_increment(ha_write_count,&LOCK_status); + statistic_increment(thd->status_var.ha_write_count, &LOCK_status); if (table->timestamp_default_now) update_timestamp(record+table->timestamp_default_now-1); has_auto_increment= (table->next_number_field && record == table->record[0]); @@ -1499,7 +1501,7 @@ int ha_ndbcluster::update_row(const byte *old_data, byte *new_data) uint i; DBUG_ENTER("update_row"); - statistic_increment(ha_update_count,&LOCK_status); + statistic_increment(thd->status_var.ha_update_count, &LOCK_status); if (table->timestamp_on_update_now) update_timestamp(new_data+table->timestamp_on_update_now-1); @@ -1606,12 +1608,13 @@ int ha_ndbcluster::update_row(const byte *old_data, byte *new_data) int ha_ndbcluster::delete_row(const byte *record) { + THD *thd= current_thd; NdbConnection *trans= m_active_trans; NdbResultSet* cursor= m_active_cursor; NdbOperation *op; DBUG_ENTER("delete_row"); - statistic_increment(ha_delete_count,&LOCK_status); + statistic_increment(thd->status_var.ha_delete_count,&LOCK_status); if (cursor) { @@ -1926,7 +1929,7 @@ int ha_ndbcluster::index_read_idx(byte *buf, uint index_no, const byte *key, uint key_len, enum ha_rkey_function find_flag) { - statistic_increment(ha_read_key_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_key_count, &LOCK_status); DBUG_ENTER("index_read_idx"); DBUG_PRINT("enter", ("index_no: %u, key_len: %u", index_no, key_len)); index_init(index_no); @@ -1939,7 +1942,8 @@ int ha_ndbcluster::index_next(byte *buf) DBUG_ENTER("index_next"); int error= 1; - statistic_increment(ha_read_next_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_next_count, + &LOCK_status); DBUG_RETURN(next_result(buf)); } @@ -1947,7 +1951,8 @@ int ha_ndbcluster::index_next(byte *buf) int ha_ndbcluster::index_prev(byte *buf) { DBUG_ENTER("index_prev"); - statistic_increment(ha_read_prev_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_prev_count, + &LOCK_status); DBUG_RETURN(1); } @@ -1955,7 +1960,8 @@ int ha_ndbcluster::index_prev(byte *buf) int ha_ndbcluster::index_first(byte *buf) { DBUG_ENTER("index_first"); - statistic_increment(ha_read_first_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_first_count, + &LOCK_status); DBUG_RETURN(1); } @@ -1963,7 +1969,7 @@ int ha_ndbcluster::index_first(byte *buf) int ha_ndbcluster::index_last(byte *buf) { DBUG_ENTER("index_last"); - statistic_increment(ha_read_last_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_last_count,&LOCK_status); DBUG_RETURN(1); } @@ -2077,7 +2083,8 @@ int ha_ndbcluster::rnd_end() int ha_ndbcluster::rnd_next(byte *buf) { DBUG_ENTER("rnd_next"); - statistic_increment(ha_read_rnd_next_count, &LOCK_status); + statistic_increment(current_thd->status_var.ha_read_rnd_next_count, + &LOCK_status); if (!m_active_cursor) DBUG_RETURN(full_table_scan(buf)); @@ -2095,7 +2102,8 @@ int ha_ndbcluster::rnd_next(byte *buf) int ha_ndbcluster::rnd_pos(byte *buf, byte *pos) { DBUG_ENTER("rnd_pos"); - statistic_increment(ha_read_rnd_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_rnd_count, + &LOCK_status); // The primary key for the record is stored in pos // Perform a pk_read using primary key "index" DBUG_RETURN(pk_read(pos, ref_length, buf)); diff --git a/sql/handler.cc b/sql/handler.cc index 8a480b0f131..c3513ad819f 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -54,11 +54,7 @@ static int NEAR_F delete_file(const char *name,const char *ext,int extflag); -ulong ha_read_count, ha_write_count, ha_delete_count, ha_update_count, - ha_read_key_count, ha_read_next_count, ha_read_prev_count, - ha_read_first_count, ha_read_last_count, - ha_commit_count, ha_rollback_count, - ha_read_rnd_count, ha_read_rnd_next_count, ha_discover_count; +ulong ha_read_count, ha_discover_count; static SHOW_COMP_OPTION have_yes= SHOW_OPTION_YES; @@ -563,7 +559,7 @@ int ha_commit_trans(THD *thd, THD_TRANS* trans) thd->variables.tx_isolation=thd->session_tx_isolation; if (operation_done) { - statistic_increment(ha_commit_count,&LOCK_status); + statistic_increment(thd->status_var.ha_commit_count,&LOCK_status); thd->transaction.cleanup(); } if (need_start_waiters) @@ -652,7 +648,7 @@ int ha_rollback_trans(THD *thd, THD_TRANS *trans) } thd->variables.tx_isolation=thd->session_tx_isolation; if (operation_done) - statistic_increment(ha_rollback_count,&LOCK_status); + statistic_increment(thd->status_var.ha_rollback_count,&LOCK_status); } #endif /* USING_TRANSACTIONS */ DBUG_RETURN(error); @@ -725,7 +721,7 @@ int ha_rollback_to_savepoint(THD *thd, char *savepoint_name) operation_done=1; #endif if (operation_done) - statistic_increment(ha_rollback_count,&LOCK_status); + statistic_increment(thd->status_var.ha_rollback_count,&LOCK_status); } #endif /* USING_TRANSACTIONS */ @@ -920,7 +916,7 @@ int handler::read_first_row(byte * buf, uint primary_key) register int error; DBUG_ENTER("handler::read_first_row"); - statistic_increment(ha_read_first_count,&LOCK_status); + statistic_increment(current_thd->status_var.ha_read_first_count,&LOCK_status); /* If there is very few deleted rows in the table, find the first row by diff --git a/sql/item.cc b/sql/item.cc index 18ae7efd852..cbb760156b3 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -24,6 +24,8 @@ #include "my_dir.h" #include "sp_rcontext.h" #include "sql_acl.h" +#include "sp_head.h" +#include "sql_trigger.h" static void mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current, @@ -463,6 +465,24 @@ const char *Item_ident::full_name() const void Item_ident::print(String *str) { THD *thd= current_thd; + char d_name_buff[MAX_ALIAS_NAME], t_name_buff[MAX_ALIAS_NAME]; + const char *d_name= db_name, *t_name= table_name; + if (lower_case_table_names) + { + if (table_name && table_name[0]) + { + strmov(t_name_buff, table_name); + my_casedn_str(files_charset_info, t_name_buff); + t_name= t_name_buff; + } + if (db_name && db_name[0]) + { + strmov(d_name_buff, db_name); + my_casedn_str(files_charset_info, d_name_buff); + d_name= d_name_buff; + } + } + if (!table_name || !field_name) { const char *nm= field_name ? field_name : name ? name : "tmp_field"; @@ -471,9 +491,9 @@ void Item_ident::print(String *str) } if (db_name && db_name[0]) { - append_identifier(thd, str, db_name, strlen(db_name)); + append_identifier(thd, str, d_name, strlen(d_name)); str->append('.'); - append_identifier(thd, str, table_name, strlen(table_name)); + append_identifier(thd, str, t_name, strlen(t_name)); str->append('.'); append_identifier(thd, str, field_name, strlen(field_name)); } @@ -481,7 +501,7 @@ void Item_ident::print(String *str) { if (table_name[0]) { - append_identifier(thd, str, table_name, strlen(table_name)); + append_identifier(thd, str, t_name, strlen(t_name)); str->append('.'); append_identifier(thd, str, field_name, strlen(field_name)); } @@ -2444,6 +2464,97 @@ void Item_insert_value::print(String *str) str->append(')'); } + +/* + Bind item representing field of row being changed in trigger + to appropriate Field object. + + SYNOPSIS + setup_field() + thd - current thread context + table - table of trigger (and where we looking for fields) + event - type of trigger event + + NOTE + This function does almost the same as fix_fields() for Item_field + but is invoked during trigger definition parsing and takes TABLE + object as its argument. + + RETURN VALUES + 0 ok + 1 field was not found. +*/ +bool Item_trigger_field::setup_field(THD *thd, TABLE *table, + enum trg_event_type event) +{ + bool result= 1; + uint field_idx= (uint)-1; + bool save_set_query_id= thd->set_query_id; + + /* TODO: Think more about consequences of this step. */ + thd->set_query_id= 0; + + if (find_field_in_real_table(thd, table, field_name, + strlen(field_name), 0, 0, + &field_idx)) + { + field= (row_version == OLD_ROW && event == TRG_EVENT_UPDATE) ? + table->triggers->old_field[field_idx] : + table->field[field_idx]; + result= 0; + } + + thd->set_query_id= save_set_query_id; + + return result; +} + + +bool Item_trigger_field::eq(const Item *item, bool binary_cmp) const +{ + return item->type() == TRIGGER_FIELD_ITEM && + row_version == ((Item_trigger_field *)item)->row_version && + !my_strcasecmp(system_charset_info, field_name, + ((Item_trigger_field *)item)->field_name); +} + + +bool Item_trigger_field::fix_fields(THD *thd, + TABLE_LIST *table_list, + Item **items) +{ + /* + Since trigger is object tightly associated with TABLE object most + of its set up can be performed during trigger loading i.e. trigger + parsing! So we have little to do in fix_fields. :) + FIXME may be we still should bother about permissions here. + */ + DBUG_ASSERT(fixed == 0); + // QQ: May be this should be moved to setup_field? + set_field(field); + fixed= 1; + return 0; +} + + +void Item_trigger_field::print(String *str) +{ + str->append((row_version == NEW_ROW) ? "NEW" : "OLD", 3); + str->append('.'); + str->append(field_name); +} + + +void Item_trigger_field::cleanup() +{ + /* + Since special nature of Item_trigger_field we should not do most of + things from Item_field::cleanup() or Item_ident::cleanup() here. + */ + Item::cleanup(); +} + + /* If item is a const function, calculate it and return a const item The original item is freed if not returned diff --git a/sql/item.h b/sql/item.h index 1420a89c76a..a237e7ce6f5 100644 --- a/sql/item.h +++ b/sql/item.h @@ -105,7 +105,7 @@ public: PROC_ITEM,COND_ITEM, REF_ITEM, FIELD_STD_ITEM, FIELD_VARIANCE_ITEM, INSERT_VALUE_ITEM, SUBSELECT_ITEM, ROW_ITEM, CACHE_ITEM, TYPE_HOLDER, - PARAM_ITEM}; + PARAM_ITEM, TRIGGER_FIELD_ITEM}; enum cond_result { COND_UNDEF,COND_OK,COND_TRUE,COND_FALSE }; @@ -434,12 +434,13 @@ public: friend bool insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name, const char *table_name, List_iterator<Item> *it, - bool any_privileges); + bool any_privileges, bool allocate_view_names); }; class Item_field :public Item_ident { +protected: void set_field(Field *field); public: Field *field,*result_field; @@ -1198,6 +1199,57 @@ public: } }; + +/* + We need this two enums here instead of sql_lex.h because + at least one of them is used by Item_trigger_field interface. + + Time when trigger is invoked (i.e. before or after row actually + inserted/updated/deleted). +*/ +enum trg_action_time_type +{ + TRG_ACTION_BEFORE= 0, TRG_ACTION_AFTER= 1 +}; + +/* + Event on which trigger is invoked. +*/ +enum trg_event_type +{ + TRG_EVENT_INSERT= 0 , TRG_EVENT_UPDATE= 1, TRG_EVENT_DELETE= 2 +}; + +/* + Represents NEW/OLD version of field of row which is + changed/read in trigger. + + Note: For this item actual binding to Field object happens not during + fix_fields() (like for Item_field) but during parsing of trigger + definition, when table is opened, with special setup_field() call. +*/ +class Item_trigger_field : public Item_field +{ +public: + /* Is this item represents row from NEW or OLD row ? */ + enum row_version_type {OLD_ROW, NEW_ROW}; + row_version_type row_version; + + Item_trigger_field(row_version_type row_ver_par, + const char *field_name_par): + Item_field((const char *)NULL, (const char *)NULL, field_name_par), + row_version(row_ver_par) + {} + bool setup_field(THD *thd, TABLE *table, enum trg_event_type event); + enum Type type() const { return TRIGGER_FIELD_ITEM; } + bool eq(const Item *item, bool binary_cmp) const; + bool fix_fields(THD *, struct st_table_list *, Item **); + void print(String *str); + table_map used_tables() const { return (table_map)0L; } + void cleanup(); +}; + + class Item_cache: public Item { protected: diff --git a/sql/item_func.cc b/sql/item_func.cc index 45564e01e03..8300d139a71 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2580,6 +2580,16 @@ void Item_func_set_user_var::print(String *str) } +void Item_func_set_user_var::print_as_stmt(String *str) +{ + str->append("set @", 5); + str->append(name.str, name.length); + str->append(":=", 2); + args[0]->print(str); + str->append(')'); +} + + String * Item_func_get_user_var::val_str(String *str) { @@ -3329,6 +3339,11 @@ Item_func_sp::execute(Item **itp) sp_change_security_context(thd, m_sp, &save_ctx); #endif + /* + We don't need to surpress senfing of ok packet here (by setting + thd->net.no_send_ok to true), because we are not allowing statements + in functions now. + */ res= m_sp->execute_function(thd, args, arg_count, itp); #ifndef NO_EMBEDDED_ACCESS_CHECKS diff --git a/sql/item_func.h b/sql/item_func.h index 76d0346531e..48a33bad80d 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -948,6 +948,7 @@ public: bool fix_fields(THD *thd, struct st_table_list *tables, Item **ref); void fix_length_and_dec(); void print(String *str); + void print_as_stmt(String *str); const char *func_name() const { return "set_user_var"; } }; @@ -1130,25 +1131,31 @@ public: double val() { Item *it; + double d; if (execute(&it)) { null_value= 1; return 0.0; } - return it->val(); + d= it->val(); + null_value= it->null_value; + return d; } String *val_str(String *str) { Item *it; + String *s; if (execute(&it)) { null_value= 1; return NULL; } - return it->val_str(str); + s= it->val_str(str); + null_value= it->null_value; + return s; } void fix_length_and_dec(); diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index fc667eb61b0..b3665edad39 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -2850,6 +2850,8 @@ String *Item_func_uuid::val_str(String *str) { DBUG_ASSERT(fixed == 1); char *s; + THD *thd= current_thd; + pthread_mutex_lock(&LOCK_uuid_generator); if (! uuid_time) /* first UUID() call. initializing data */ { @@ -2864,7 +2866,7 @@ String *Item_func_uuid::val_str(String *str) with a clock_seq value (initialized random below), we use a separate randominit() here */ - randominit(&uuid_rand, tmp + (ulong)current_thd, tmp + query_id); + randominit(&uuid_rand, tmp + (ulong) thd, tmp + query_id); for (i=0; i < (int)sizeof(mac); i++) mac[i]=(uchar)(my_rnd(&uuid_rand)*255); } @@ -2874,7 +2876,8 @@ String *Item_func_uuid::val_str(String *str) *--s=_dig_vec_lower[mac[i] & 15]; *--s=_dig_vec_lower[mac[i] >> 4]; } - randominit(&uuid_rand, tmp + (ulong)start_time, tmp + bytes_sent); + randominit(&uuid_rand, tmp + (ulong)start_time, + tmp + thd->status_var.bytes_sent); set_clock_seq_str(); } diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 23dbcf8af48..25bb2701101 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -272,7 +272,8 @@ Item_singlerow_subselect::Item_singlerow_subselect(st_select_lex *select_lex) DBUG_VOID_RETURN; } -Item_maxmin_subselect::Item_maxmin_subselect(Item_subselect *parent, +Item_maxmin_subselect::Item_maxmin_subselect(THD *thd_param, + Item_subselect *parent, st_select_lex *select_lex, bool max_arg) :Item_singlerow_subselect() @@ -291,6 +292,12 @@ Item_maxmin_subselect::Item_maxmin_subselect(Item_subselect *parent, used_tables_cache= parent->get_used_tables_cache(); const_item_cache= parent->get_const_item_cache(); + /* + this subquery alwais creates during preparation, so we can assign + thd here + */ + thd= thd_param; + DBUG_VOID_RETURN; } @@ -316,9 +323,11 @@ Item_singlerow_subselect::select_transformer(JOIN *join) SELECT_LEX *select_lex= join->select_lex; /* Juggle with current arena only if we're in prepared statement prepare */ - Item_arena *arena= join->thd->current_arena; + DBUG_PRINT("TANSF:", ("thd %p, select_lex->join->thd: %s", + thd, select_lex->join->thd)); + Item_arena *arena= thd->current_arena; Item_arena backup; - if (!arena->is_stmt_prepare()) + if (arena->is_conventional()) arena= 0; // For easier test if (!select_lex->master_unit()->first_select()->next_select() && @@ -337,11 +346,11 @@ Item_singlerow_subselect::select_transformer(JOIN *join) { have_to_be_excluded= 1; - if (join->thd->lex->describe) + if (thd->lex->describe) { char warn_buff[MYSQL_ERRMSG_SIZE]; sprintf(warn_buff, ER(ER_SELECT_REDUCED), select_lex->select_number); - push_warning(join->thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_SELECT_REDUCED, warn_buff); } substitution= select_lex->item_list.head(); @@ -367,9 +376,9 @@ Item_singlerow_subselect::select_transformer(JOIN *join) if (!(substitution= new Item_func_if(cond, substitution, new Item_null()))) goto err; + if (arena) + thd->restore_backup_item_arena(arena, &backup); } - if (arena) - thd->restore_backup_item_arena(arena, &backup); return RES_REDUCE; } return RES_OK; @@ -654,11 +663,11 @@ Item_in_subselect::single_value_transformer(JOIN *join, } SELECT_LEX *select_lex= join->select_lex; - Item_arena *arena= join->thd->current_arena, backup; + Item_arena *arena= thd->current_arena, backup; thd->where= "scalar IN/ALL/ANY subquery"; - if (!arena->is_stmt_prepare()) + if (arena->is_conventional()) arena= 0; // For easier test else thd->set_n_backup_item_arena(arena, &backup); @@ -723,7 +732,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, // remove LIMIT placed by ALL/ANY subquery select_lex->master_unit()->global_parameters->select_limit= HA_POS_ERROR; - subs= new Item_maxmin_subselect(this, select_lex, func->l_op()); + subs= new Item_maxmin_subselect(thd, this, select_lex, func->l_op()); } // left expression belong to outer select SELECT_LEX *current= thd->lex->current_select, *up; @@ -899,8 +908,8 @@ Item_in_subselect::row_value_transformer(JOIN *join) } thd->where= "row IN/ALL/ANY subquery"; - Item_arena *arena= join->thd->current_arena, backup; - if (!arena->is_stmt_prepare()) + Item_arena *arena= thd->current_arena, backup; + if (arena->is_conventional()) arena= 0; else thd->set_n_backup_item_arena(arena, &backup); @@ -1192,17 +1201,17 @@ void subselect_uniquesubquery_engine::fix_length_and_dec(Item_cache **row) int subselect_single_select_engine::exec() { DBUG_ENTER("subselect_single_select_engine::exec"); - char const *save_where= join->thd->where; - SELECT_LEX *save_select= join->thd->lex->current_select; - join->thd->lex->current_select= select_lex; + char const *save_where= thd->where; + SELECT_LEX *save_select= thd->lex->current_select; + thd->lex->current_select= select_lex; if (!optimized) { optimized=1; if (join->optimize()) { - join->thd->where= save_where; + thd->where= save_where; executed= 1; - join->thd->lex->current_select= save_select; + thd->lex->current_select= save_select; DBUG_RETURN(join->error ? join->error : 1); } if (item->engine_changed) @@ -1214,8 +1223,8 @@ int subselect_single_select_engine::exec() { if (join->reinit()) { - join->thd->where= save_where; - join->thd->lex->current_select= save_select; + thd->where= save_where; + thd->lex->current_select= save_select; DBUG_RETURN(1); } item->reset(); @@ -1225,20 +1234,20 @@ int subselect_single_select_engine::exec() { join->exec(); executed= 1; - join->thd->where= save_where; - join->thd->lex->current_select= save_select; + thd->where= save_where; + thd->lex->current_select= save_select; DBUG_RETURN(join->error||thd->is_fatal_error); } - join->thd->where= save_where; - join->thd->lex->current_select= save_select; + thd->where= save_where; + thd->lex->current_select= save_select; DBUG_RETURN(0); } int subselect_union_engine::exec() { - char const *save_where= unit->thd->where; + char const *save_where= thd->where; int res= unit->exec(); - unit->thd->where= save_where; + thd->where= save_where; return res; } diff --git a/sql/item_subselect.h b/sql/item_subselect.h index ed3dbfa9855..4ef680cea19 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -154,7 +154,7 @@ class Item_maxmin_subselect :public Item_singlerow_subselect { bool max; public: - Item_maxmin_subselect(Item_subselect *parent, + Item_maxmin_subselect(THD *thd, Item_subselect *parent, st_select_lex *select_lex, bool max); void print(String *str); }; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index bfa0f86c744..d400c198828 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -77,7 +77,7 @@ Item_sum::Item_sum(THD *thd, Item_sum *item): */ bool Item_sum::save_args_for_prepared_statement(THD *thd) { - if (thd->current_arena->is_stmt_prepare() && args_copy == 0) + if (!thd->current_arena->is_conventional() && args_copy == 0) return save_args(thd->current_arena); return 0; } diff --git a/sql/lex.h b/sql/lex.h index 4c44d53d5b1..434c8fb4d47 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -167,6 +167,7 @@ static SYMBOL symbols[] = { { "DUMPFILE", SYM(DUMPFILE)}, { "DUPLICATE", SYM(DUPLICATE_SYM)}, { "DYNAMIC", SYM(DYNAMIC_SYM)}, + { "EACH", SYM(EACH_SYM)}, { "ELSE", SYM(ELSE)}, { "ELSEIF", SYM(ELSEIF_SYM)}, { "ENABLE", SYM(ENABLE_SYM)}, @@ -470,6 +471,7 @@ static SYMBOL symbols[] = { { "TO", SYM(TO_SYM)}, { "TRAILING", SYM(TRAILING)}, { "TRANSACTION", SYM(TRANSACTION_SYM)}, + { "TRIGGER", SYM(TRIGGER_SYM)}, { "TRUE", SYM(TRUE_SYM)}, { "TRUNCATE", SYM(TRUNCATE_SYM)}, { "TYPE", SYM(TYPE_SYM)}, diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 73ac000d953..e7a0778d5cb 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -481,6 +481,7 @@ int mysql_rm_table_part2_with_lock(THD *thd, TABLE_LIST *tables, bool log_query); int quick_rm_table(enum db_type base,const char *db, const char *table_name); +void close_cached_table(THD *thd, TABLE *table); bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list); bool mysql_change_db(THD *thd,const char *name); void mysql_parse(THD *thd,char *inBuf,uint length); @@ -622,6 +623,7 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds); int mysql_delete(THD *thd, TABLE_LIST *table, COND *conds, SQL_LIST *order, ha_rows rows, ulong options); int mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok); +int mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create); TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update); TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT* mem, bool *refresh); @@ -650,6 +652,10 @@ find_field_in_table(THD *thd, TABLE_LIST *table_list, bool check_grants_table, bool check_grants_view, bool allow_rowid, uint *cached_field_index_ptr); +Field * +find_field_in_real_table(THD *thd, TABLE *table, const char *name, + uint length, bool check_grants, bool allow_rowid, + uint *cached_field_index_ptr); #ifdef HAVE_OPENSSL #include <openssl/des.h> struct st_des_keyblock @@ -692,7 +698,8 @@ int mysqld_show_status(THD *thd); int mysqld_show_variables(THD *thd,const char *wild); int mysqld_show(THD *thd, const char *wild, show_var_st *variables, enum enum_var_type value_type, - pthread_mutex_t *mutex); + pthread_mutex_t *mutex, + struct system_status_var *status_var); int mysql_find_files(THD *thd,List<char> *files, const char *db, const char *path, const char *wild, bool dir); int mysqld_show_charsets(THD *thd,const char *wild); @@ -701,6 +708,7 @@ int mysqld_show_storage_engines(THD *thd); int mysqld_show_privileges(THD *thd); int mysqld_show_column_types(THD *thd); int mysqld_help (THD *thd, const char *text); +void calc_sum_of_all_status(STATUS_VAR *to); /* sql_prepare.cc */ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, @@ -756,7 +764,8 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table, List<String> *index_list); bool insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name, const char *table_name, - List_iterator<Item> *it, bool any_privileges); + List_iterator<Item> *it, bool any_privileges, + bool allocate_view_names); bool setup_tables(THD *thd, TABLE_LIST *tables, Item **conds); int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, List<Item> *sum_func_list, uint wild_num); @@ -782,6 +791,7 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table, uint offset_to_list, const char *db_name, const char *table_name); +TABLE_LIST *unique_table(TABLE_LIST *table, TABLE_LIST *table_list); TABLE **find_temporary_table(THD *thd, const char *db, const char *table_name); bool close_temporary_table(THD *thd, const char *db, const char *table_name); void close_temporary(TABLE *table, bool delete_table=1); @@ -918,36 +928,28 @@ extern double last_query_cost; extern double log_10[32]; extern ulonglong log_10_int[20]; extern ulonglong keybuff_size; -extern ulong refresh_version,flush_version, thread_id,query_id,opened_tables; -extern ulong created_tmp_tables, created_tmp_disk_tables, bytes_sent; +extern ulong refresh_version,flush_version, thread_id,query_id; extern ulong binlog_cache_use, binlog_cache_disk_use; extern ulong aborted_threads,aborted_connects; extern ulong delayed_insert_timeout; extern ulong delayed_insert_limit, delayed_queue_size; extern ulong delayed_insert_threads, delayed_insert_writes; extern ulong delayed_rows_in_use,delayed_insert_errors; -extern ulong filesort_rows, filesort_range_count, filesort_scan_count; -extern ulong filesort_merge_passes; -extern ulong select_range_check_count, select_range_count, select_scan_count; -extern ulong select_full_range_join_count,select_full_join_count; extern ulong slave_open_temp_tables; extern ulong query_cache_size, query_cache_min_res_unit; extern ulong thd_startup_options, slow_launch_threads, slow_launch_time; extern ulong server_id, concurrency; -extern ulong ha_read_count, ha_write_count, ha_delete_count, ha_update_count; -extern ulong ha_read_key_count, ha_read_next_count, ha_read_prev_count; -extern ulong ha_read_first_count, ha_read_last_count; -extern ulong ha_read_rnd_count, ha_read_rnd_next_count, ha_discover_count; -extern ulong ha_commit_count, ha_rollback_count,table_cache_size; +extern ulong ha_read_count, ha_discover_count; +extern ulong table_cache_size; extern ulong max_connections,max_connect_errors, connect_timeout; extern ulong slave_net_timeout; extern ulong max_user_connections; -extern ulong long_query_count, what_to_log,flush_time; +extern ulong what_to_log,flush_time; extern ulong query_buff_size, thread_stack,thread_stack_min; extern ulong binlog_cache_size, max_binlog_cache_size, open_files_limit; extern ulong max_binlog_size, max_relay_log_size; extern ulong rpl_recovery_rank, thread_cache_size; -extern ulong com_stat[(uint) SQLCOM_END], com_other, back_log; +extern ulong back_log; extern ulong specialflag, current_pid; extern ulong expire_logs_days, sync_binlog_period, sync_binlog_counter; extern my_bool relay_log_purge, opt_innodb_safe_binlog; @@ -997,6 +999,7 @@ extern SHOW_COMP_OPTION have_berkeley_db; extern SHOW_COMP_OPTION have_ndbcluster; extern struct system_variables global_system_variables; extern struct system_variables max_system_variables; +extern struct system_status_var global_status_var; extern struct rand_struct sql_rand; extern KEY_CACHE *sql_key_cache; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 50eabfaa903..21633858ad8 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -289,20 +289,13 @@ ulong open_files_limit, max_binlog_size, max_relay_log_size; ulong slave_net_timeout; ulong thread_cache_size=0, binlog_cache_size=0, max_binlog_cache_size=0; ulong query_cache_size=0; -ulong com_stat[(uint) SQLCOM_END], com_other; -ulong bytes_sent, bytes_received, net_big_packet_count; ulong refresh_version, flush_version; /* Increments on each reload */ -ulong query_id, long_query_count; +ulong query_id; ulong aborted_threads, killed_threads, aborted_connects; ulong delayed_insert_timeout, delayed_insert_limit, delayed_queue_size; ulong delayed_insert_threads, delayed_insert_writes, delayed_rows_in_use; ulong delayed_insert_errors,flush_time, thread_created; -ulong filesort_rows, filesort_range_count, filesort_scan_count; -ulong filesort_merge_passes; -ulong select_range_check_count, select_range_count, select_scan_count; -ulong select_full_range_join_count,select_full_join_count; -ulong specialflag=0,opened_tables=0,created_tmp_tables=0, - created_tmp_disk_tables=0; +ulong specialflag=0; ulong binlog_cache_use= 0, binlog_cache_disk_use= 0; ulong max_connections,max_used_connections, max_connect_errors, max_user_connections = 0; @@ -361,6 +354,7 @@ I_List<NAMED_LIST> key_caches; struct system_variables global_system_variables; struct system_variables max_system_variables; +struct system_status_var global_status_var; MY_TMPDIR mysql_tmpdir_list; MY_BITMAP temp_pool; @@ -518,6 +512,8 @@ static void close_connections(void) #ifdef EXTRA_DEBUG int count=0; #endif + THD *thd= current_thd; + DBUG_ENTER("close_connections"); /* Clear thread cache */ @@ -5193,118 +5189,137 @@ struct show_var_st status_vars[]= { {"Aborted_connects", (char*) &aborted_connects, SHOW_LONG}, {"Binlog_cache_disk_use", (char*) &binlog_cache_disk_use, SHOW_LONG}, {"Binlog_cache_use", (char*) &binlog_cache_use, SHOW_LONG}, - {"Bytes_received", (char*) &bytes_received, SHOW_LONG}, - {"Bytes_sent", (char*) &bytes_sent, SHOW_LONG}, - {"Com_admin_commands", (char*) &com_other, SHOW_LONG}, - {"Com_alter_db", (char*) (com_stat+(uint) SQLCOM_ALTER_DB),SHOW_LONG}, - {"Com_alter_table", (char*) (com_stat+(uint) SQLCOM_ALTER_TABLE),SHOW_LONG}, - {"Com_analyze", (char*) (com_stat+(uint) SQLCOM_ANALYZE),SHOW_LONG}, - {"Com_backup_table", (char*) (com_stat+(uint) SQLCOM_BACKUP_TABLE),SHOW_LONG}, - {"Com_begin", (char*) (com_stat+(uint) SQLCOM_BEGIN),SHOW_LONG}, - {"Com_change_db", (char*) (com_stat+(uint) SQLCOM_CHANGE_DB),SHOW_LONG}, - {"Com_change_master", (char*) (com_stat+(uint) SQLCOM_CHANGE_MASTER),SHOW_LONG}, - {"Com_check", (char*) (com_stat+(uint) SQLCOM_CHECK),SHOW_LONG}, - {"Com_checksum", (char*) (com_stat+(uint) SQLCOM_CHECKSUM),SHOW_LONG}, - {"Com_commit", (char*) (com_stat+(uint) SQLCOM_COMMIT),SHOW_LONG}, - {"Com_create_db", (char*) (com_stat+(uint) SQLCOM_CREATE_DB),SHOW_LONG}, - {"Com_create_function", (char*) (com_stat+(uint) SQLCOM_CREATE_FUNCTION),SHOW_LONG}, - {"Com_create_index", (char*) (com_stat+(uint) SQLCOM_CREATE_INDEX),SHOW_LONG}, - {"Com_create_table", (char*) (com_stat+(uint) SQLCOM_CREATE_TABLE),SHOW_LONG}, - {"Com_dealloc_sql", (char*) (com_stat+(uint) - SQLCOM_DEALLOCATE_PREPARE), SHOW_LONG}, - {"Com_delete", (char*) (com_stat+(uint) SQLCOM_DELETE),SHOW_LONG}, - {"Com_delete_multi", (char*) (com_stat+(uint) SQLCOM_DELETE_MULTI),SHOW_LONG}, - {"Com_do", (char*) (com_stat+(uint) SQLCOM_DO),SHOW_LONG}, - {"Com_drop_db", (char*) (com_stat+(uint) SQLCOM_DROP_DB),SHOW_LONG}, - {"Com_drop_function", (char*) (com_stat+(uint) SQLCOM_DROP_FUNCTION),SHOW_LONG}, - {"Com_drop_index", (char*) (com_stat+(uint) SQLCOM_DROP_INDEX),SHOW_LONG}, - {"Com_drop_table", (char*) (com_stat+(uint) SQLCOM_DROP_TABLE),SHOW_LONG}, - {"Com_drop_user", (char*) (com_stat+(uint) SQLCOM_DROP_USER),SHOW_LONG}, - {"Com_execute_sql", (char*) (com_stat+(uint) SQLCOM_EXECUTE), - SHOW_LONG}, - {"Com_flush", (char*) (com_stat+(uint) SQLCOM_FLUSH),SHOW_LONG}, - {"Com_grant", (char*) (com_stat+(uint) SQLCOM_GRANT),SHOW_LONG}, - {"Com_ha_close", (char*) (com_stat+(uint) SQLCOM_HA_CLOSE),SHOW_LONG}, - {"Com_ha_open", (char*) (com_stat+(uint) SQLCOM_HA_OPEN),SHOW_LONG}, - {"Com_ha_read", (char*) (com_stat+(uint) SQLCOM_HA_READ),SHOW_LONG}, - {"Com_help", (char*) (com_stat+(uint) SQLCOM_HELP),SHOW_LONG}, - {"Com_insert", (char*) (com_stat+(uint) SQLCOM_INSERT),SHOW_LONG}, - {"Com_insert_select", (char*) (com_stat+(uint) SQLCOM_INSERT_SELECT),SHOW_LONG}, - {"Com_kill", (char*) (com_stat+(uint) SQLCOM_KILL),SHOW_LONG}, - {"Com_load", (char*) (com_stat+(uint) SQLCOM_LOAD),SHOW_LONG}, - {"Com_load_master_data", (char*) (com_stat+(uint) SQLCOM_LOAD_MASTER_DATA),SHOW_LONG}, - {"Com_load_master_table", (char*) (com_stat+(uint) SQLCOM_LOAD_MASTER_TABLE),SHOW_LONG}, - {"Com_lock_tables", (char*) (com_stat+(uint) SQLCOM_LOCK_TABLES),SHOW_LONG}, - {"Com_optimize", (char*) (com_stat+(uint) SQLCOM_OPTIMIZE),SHOW_LONG}, - {"Com_preload_keys", (char*) (com_stat+(uint) SQLCOM_PRELOAD_KEYS),SHOW_LONG}, - {"Com_prepare_sql", (char*) (com_stat+(uint) SQLCOM_PREPARE), - SHOW_LONG}, - {"Com_purge", (char*) (com_stat+(uint) SQLCOM_PURGE),SHOW_LONG}, - {"Com_purge_before_date", (char*) (com_stat+(uint) SQLCOM_PURGE_BEFORE),SHOW_LONG}, - {"Com_rename_table", (char*) (com_stat+(uint) SQLCOM_RENAME_TABLE),SHOW_LONG}, - {"Com_repair", (char*) (com_stat+(uint) SQLCOM_REPAIR),SHOW_LONG}, - {"Com_replace", (char*) (com_stat+(uint) SQLCOM_REPLACE),SHOW_LONG}, - {"Com_replace_select", (char*) (com_stat+(uint) SQLCOM_REPLACE_SELECT),SHOW_LONG}, - {"Com_reset", (char*) (com_stat+(uint) SQLCOM_RESET),SHOW_LONG}, - {"Com_restore_table", (char*) (com_stat+(uint) SQLCOM_RESTORE_TABLE),SHOW_LONG}, - {"Com_revoke", (char*) (com_stat+(uint) SQLCOM_REVOKE),SHOW_LONG}, - {"Com_revoke_all", (char*) (com_stat+(uint) SQLCOM_REVOKE_ALL),SHOW_LONG}, - {"Com_rollback", (char*) (com_stat+(uint) SQLCOM_ROLLBACK),SHOW_LONG}, - {"Com_savepoint", (char*) (com_stat+(uint) SQLCOM_SAVEPOINT),SHOW_LONG}, - {"Com_select", (char*) (com_stat+(uint) SQLCOM_SELECT),SHOW_LONG}, - {"Com_set_option", (char*) (com_stat+(uint) SQLCOM_SET_OPTION),SHOW_LONG}, - {"Com_show_binlog_events", (char*) (com_stat+(uint) SQLCOM_SHOW_BINLOG_EVENTS),SHOW_LONG}, - {"Com_show_binlogs", (char*) (com_stat+(uint) SQLCOM_SHOW_BINLOGS),SHOW_LONG}, - {"Com_show_charsets", (char*) (com_stat+(uint) SQLCOM_SHOW_CHARSETS),SHOW_LONG}, - {"Com_show_collations", (char*) (com_stat+(uint) SQLCOM_SHOW_COLLATIONS),SHOW_LONG}, - {"Com_show_column_types", (char*) (com_stat+(uint) SQLCOM_SHOW_COLUMN_TYPES),SHOW_LONG}, - {"Com_show_create_db", (char*) (com_stat+(uint) SQLCOM_SHOW_CREATE_DB),SHOW_LONG}, - {"Com_show_create_table", (char*) (com_stat+(uint) SQLCOM_SHOW_CREATE),SHOW_LONG}, - {"Com_show_databases", (char*) (com_stat+(uint) SQLCOM_SHOW_DATABASES),SHOW_LONG}, - {"Com_show_errors", (char*) (com_stat+(uint) SQLCOM_SHOW_ERRORS),SHOW_LONG}, - {"Com_show_fields", (char*) (com_stat+(uint) SQLCOM_SHOW_FIELDS),SHOW_LONG}, - {"Com_show_grants", (char*) (com_stat+(uint) SQLCOM_SHOW_GRANTS),SHOW_LONG}, - {"Com_show_innodb_status", (char*) (com_stat+(uint) SQLCOM_SHOW_INNODB_STATUS),SHOW_LONG}, - {"Com_show_keys", (char*) (com_stat+(uint) SQLCOM_SHOW_KEYS),SHOW_LONG}, - {"Com_show_logs", (char*) (com_stat+(uint) SQLCOM_SHOW_LOGS),SHOW_LONG}, - {"Com_show_master_status", (char*) (com_stat+(uint) SQLCOM_SHOW_MASTER_STAT),SHOW_LONG}, - {"Com_show_new_master", (char*) (com_stat+(uint) SQLCOM_SHOW_NEW_MASTER),SHOW_LONG}, - {"Com_show_open_tables", (char*) (com_stat+(uint) SQLCOM_SHOW_OPEN_TABLES),SHOW_LONG}, - {"Com_show_privileges", (char*) (com_stat+(uint) SQLCOM_SHOW_PRIVILEGES),SHOW_LONG}, - {"Com_show_processlist", (char*) (com_stat+(uint) SQLCOM_SHOW_PROCESSLIST),SHOW_LONG}, - {"Com_show_slave_hosts", (char*) (com_stat+(uint) SQLCOM_SHOW_SLAVE_HOSTS),SHOW_LONG}, - {"Com_show_slave_status", (char*) (com_stat+(uint) SQLCOM_SHOW_SLAVE_STAT),SHOW_LONG}, - {"Com_show_status", (char*) (com_stat+(uint) SQLCOM_SHOW_STATUS),SHOW_LONG}, - {"Com_show_storage_engines", (char*) (com_stat+(uint) SQLCOM_SHOW_STORAGE_ENGINES),SHOW_LONG}, - {"Com_show_tables", (char*) (com_stat+(uint) SQLCOM_SHOW_TABLES),SHOW_LONG}, - {"Com_show_variables", (char*) (com_stat+(uint) SQLCOM_SHOW_VARIABLES),SHOW_LONG}, - {"Com_show_warnings", (char*) (com_stat+(uint) SQLCOM_SHOW_WARNS),SHOW_LONG}, - {"Com_slave_start", (char*) (com_stat+(uint) SQLCOM_SLAVE_START),SHOW_LONG}, - {"Com_slave_stop", (char*) (com_stat+(uint) SQLCOM_SLAVE_STOP),SHOW_LONG}, - {"Com_truncate", (char*) (com_stat+(uint) SQLCOM_TRUNCATE),SHOW_LONG}, - {"Com_unlock_tables", (char*) (com_stat+(uint) SQLCOM_UNLOCK_TABLES),SHOW_LONG}, - {"Com_update", (char*) (com_stat+(uint) SQLCOM_UPDATE),SHOW_LONG}, - {"Com_update_multi", (char*) (com_stat+(uint) SQLCOM_UPDATE_MULTI),SHOW_LONG}, + {"Bytes_received", (char*) offsetof(STATUS_VAR, bytes_received), + SHOW_LONG_STATUS}, + {"Bytes_sent", (char*) offsetof(STATUS_VAR, bytes_sent), + SHOW_LONG_STATUS}, + {"Com_admin_commands", (char*) offsetof(STATUS_VAR, com_other), + SHOW_LONG_STATUS}, + {"Com_alter_db", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ALTER_DB]), SHOW_LONG_STATUS}, + {"Com_alter_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ALTER_TABLE]), SHOW_LONG_STATUS}, + {"Com_analyze", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ANALYZE]), SHOW_LONG_STATUS}, + {"Com_backup_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_BACKUP_TABLE]), SHOW_LONG_STATUS}, + {"Com_begin", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_BEGIN]), SHOW_LONG_STATUS}, + {"Com_change_db", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CHANGE_DB]), SHOW_LONG_STATUS}, + {"Com_change_master", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CHANGE_MASTER]), SHOW_LONG_STATUS}, + {"Com_check", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CHECK]), SHOW_LONG_STATUS}, + {"Com_checksum", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CHECKSUM]), SHOW_LONG_STATUS}, + {"Com_commit", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_COMMIT]), SHOW_LONG_STATUS}, + {"Com_create_db", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_DB]), SHOW_LONG_STATUS}, + {"Com_create_function", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_FUNCTION]), SHOW_LONG_STATUS}, + {"Com_create_index", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_INDEX]), SHOW_LONG_STATUS}, + {"Com_create_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_TABLE]), SHOW_LONG_STATUS}, + {"Com_dealloc_sql", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DEALLOCATE_PREPARE]), SHOW_LONG_STATUS}, + {"Com_delete", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DELETE]), SHOW_LONG_STATUS}, + {"Com_delete_multi", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DELETE_MULTI]), SHOW_LONG_STATUS}, + {"Com_do", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DO]), SHOW_LONG_STATUS}, + {"Com_drop_db", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_DB]), SHOW_LONG_STATUS}, + {"Com_drop_function", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_FUNCTION]), SHOW_LONG_STATUS}, + {"Com_drop_index", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_INDEX]), SHOW_LONG_STATUS}, + {"Com_drop_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_TABLE]), SHOW_LONG_STATUS}, + {"Com_drop_user", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_USER]), SHOW_LONG_STATUS}, + {"Com_execute_sql", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_EXECUTE]), SHOW_LONG_STATUS}, + {"Com_flush", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_FLUSH]), SHOW_LONG_STATUS}, + {"Com_grant", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_GRANT]), SHOW_LONG_STATUS}, + {"Com_ha_close", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_HA_CLOSE]), SHOW_LONG_STATUS}, + {"Com_ha_open", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_HA_OPEN]), SHOW_LONG_STATUS}, + {"Com_ha_read", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_HA_READ]), SHOW_LONG_STATUS}, + {"Com_help", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_HELP]), SHOW_LONG_STATUS}, + {"Com_insert", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_INSERT]), SHOW_LONG_STATUS}, + {"Com_insert_select", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_INSERT_SELECT]), SHOW_LONG_STATUS}, + {"Com_kill", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_KILL]), SHOW_LONG_STATUS}, + {"Com_load", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_LOAD]), SHOW_LONG_STATUS}, + {"Com_load_master_data", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_LOAD_MASTER_DATA]), SHOW_LONG_STATUS}, + {"Com_load_master_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_LOAD_MASTER_TABLE]), SHOW_LONG_STATUS}, + {"Com_lock_tables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_LOCK_TABLES]), SHOW_LONG_STATUS}, + {"Com_optimize", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_OPTIMIZE]), SHOW_LONG_STATUS}, + {"Com_preload_keys", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_PRELOAD_KEYS]), SHOW_LONG_STATUS}, + {"Com_prepare_sql", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_PREPARE]), SHOW_LONG_STATUS}, + {"Com_purge", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_PURGE]), SHOW_LONG_STATUS}, + {"Com_purge_before_date", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_PURGE_BEFORE]), SHOW_LONG_STATUS}, + {"Com_rename_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_RENAME_TABLE]), SHOW_LONG_STATUS}, + {"Com_repair", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_REPAIR]), SHOW_LONG_STATUS}, + {"Com_replace", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_REPLACE]), SHOW_LONG_STATUS}, + {"Com_replace_select", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_REPLACE_SELECT]), SHOW_LONG_STATUS}, + {"Com_reset", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_RESET]), SHOW_LONG_STATUS}, + {"Com_restore_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_RESTORE_TABLE]), SHOW_LONG_STATUS}, + {"Com_revoke", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_REVOKE]), SHOW_LONG_STATUS}, + {"Com_revoke_all", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_REVOKE_ALL]), SHOW_LONG_STATUS}, + {"Com_rollback", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ROLLBACK]), SHOW_LONG_STATUS}, + {"Com_savepoint", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SAVEPOINT]), SHOW_LONG_STATUS}, + {"Com_select", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SELECT]), SHOW_LONG_STATUS}, + {"Com_set_option", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SET_OPTION]), SHOW_LONG_STATUS}, + {"Com_show_binlogs", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_BINLOGS]), SHOW_LONG_STATUS}, + {"Com_show_binlog_events", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_BINLOG_EVENTS]), SHOW_LONG_STATUS}, + {"Com_show_charsets", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CHARSETS]), SHOW_LONG_STATUS}, + {"Com_show_collations", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_COLLATIONS]), SHOW_LONG_STATUS}, + {"Com_show_column_types", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_COLUMN_TYPES]), SHOW_LONG_STATUS}, + {"Com_show_create_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CREATE]), SHOW_LONG_STATUS}, + {"Com_show_create_db", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CREATE_DB]), SHOW_LONG_STATUS}, + {"Com_show_databases", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_DATABASES]), SHOW_LONG_STATUS}, + {"Com_show_errors", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_ERRORS]), SHOW_LONG_STATUS}, + {"Com_show_fields", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_FIELDS]), SHOW_LONG_STATUS}, + {"Com_show_grants", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_GRANTS]), SHOW_LONG_STATUS}, + {"Com_show_innodb_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_INNODB_STATUS]), SHOW_LONG_STATUS}, + {"Com_show_keys", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_KEYS]), SHOW_LONG_STATUS}, + {"Com_show_logs", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_LOGS]), SHOW_LONG_STATUS}, + {"Com_show_master_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_MASTER_STAT]), SHOW_LONG_STATUS}, + {"Com_show_new_master", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_NEW_MASTER]), SHOW_LONG_STATUS}, + {"Com_show_open_tables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_OPEN_TABLES]), SHOW_LONG_STATUS}, + {"Com_show_privileges", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_PRIVILEGES]), SHOW_LONG_STATUS}, + {"Com_show_processlist", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_PROCESSLIST]), SHOW_LONG_STATUS}, + {"Com_show_slave_hosts", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_SLAVE_HOSTS]), SHOW_LONG_STATUS}, + {"Com_show_slave_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_SLAVE_STAT]), 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_tables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLES]), 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_slave_start", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SLAVE_START]), SHOW_LONG_STATUS}, + {"Com_slave_stop", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SLAVE_STOP]), SHOW_LONG_STATUS}, + {"Com_truncate", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_TRUNCATE]), SHOW_LONG_STATUS}, + {"Com_unlock_tables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_UNLOCK_TABLES]), SHOW_LONG_STATUS}, + {"Com_update", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_UPDATE]), SHOW_LONG_STATUS}, + {"Com_update_multi", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_UPDATE_MULTI]), SHOW_LONG_STATUS}, {"Connections", (char*) &thread_id, SHOW_LONG_CONST}, - {"Created_tmp_disk_tables", (char*) &created_tmp_disk_tables,SHOW_LONG}, + {"Created_tmp_disk_tables", (char*) offsetof(STATUS_VAR, + created_tmp_disk_tables), + SHOW_LONG_STATUS}, {"Created_tmp_files", (char*) &my_tmp_file_created, SHOW_LONG}, - {"Created_tmp_tables", (char*) &created_tmp_tables, SHOW_LONG}, + {"Created_tmp_tables", (char*) offsetof(STATUS_VAR, + created_tmp_tables), + SHOW_LONG_STATUS}, {"Delayed_errors", (char*) &delayed_insert_errors, SHOW_LONG}, {"Delayed_insert_threads", (char*) &delayed_insert_threads, SHOW_LONG_CONST}, {"Delayed_writes", (char*) &delayed_insert_writes, SHOW_LONG}, {"Flush_commands", (char*) &refresh_version, SHOW_LONG_CONST}, - {"Handler_commit", (char*) &ha_commit_count, SHOW_LONG}, - {"Handler_delete", (char*) &ha_delete_count, SHOW_LONG}, + {"Handler_commit", (char*) offsetof(STATUS_VAR, ha_commit_count), + SHOW_LONG_STATUS}, + {"Handler_delete", (char*) offsetof(STATUS_VAR, ha_delete_count), + SHOW_LONG_STATUS}, {"Handler_discover", (char*) &ha_discover_count, SHOW_LONG}, - {"Handler_read_first", (char*) &ha_read_first_count, SHOW_LONG}, - {"Handler_read_key", (char*) &ha_read_key_count, SHOW_LONG}, - {"Handler_read_next", (char*) &ha_read_next_count, SHOW_LONG}, - {"Handler_read_prev", (char*) &ha_read_prev_count, SHOW_LONG}, - {"Handler_read_rnd", (char*) &ha_read_rnd_count, SHOW_LONG}, - {"Handler_read_rnd_next", (char*) &ha_read_rnd_next_count, SHOW_LONG}, - {"Handler_rollback", (char*) &ha_rollback_count, SHOW_LONG}, - {"Handler_update", (char*) &ha_update_count, SHOW_LONG}, - {"Handler_write", (char*) &ha_write_count, SHOW_LONG}, + {"Handler_read_first", (char*) offsetof(STATUS_VAR, + ha_read_first_count), + SHOW_LONG_STATUS}, + {"Handler_read_key", (char*) offsetof(STATUS_VAR, ha_read_key_count), + SHOW_LONG_STATUS}, + {"Handler_read_next", (char*) offsetof(STATUS_VAR, + ha_read_next_count), + SHOW_LONG_STATUS}, + {"Handler_read_prev", (char*) offsetof(STATUS_VAR, + ha_read_prev_count), + SHOW_LONG_STATUS}, + {"Handler_read_rnd", (char*) offsetof(STATUS_VAR, ha_read_rnd_count), + SHOW_LONG_STATUS}, + {"Handler_read_rnd_next", (char*) offsetof(STATUS_VAR, + ha_read_rnd_next_count), + SHOW_LONG_STATUS}, + {"Handler_rollback", (char*) offsetof(STATUS_VAR, ha_rollback_count), + SHOW_LONG_STATUS}, + {"Handler_update", (char*) offsetof(STATUS_VAR, ha_update_count), + SHOW_LONG_STATUS}, + {"Handler_write", (char*) offsetof(STATUS_VAR, ha_write_count), + SHOW_LONG_STATUS}, {"Key_blocks_not_flushed", (char*) &dflt_key_cache_var.global_blocks_changed, SHOW_KEY_CACHE_LONG}, {"Key_blocks_unused", (char*) &dflt_key_cache_var.blocks_unused, @@ -5325,7 +5340,8 @@ struct show_var_st status_vars[]= { {"Open_files", (char*) &my_file_opened, SHOW_LONG_CONST}, {"Open_streams", (char*) &my_stream_opened, SHOW_LONG_CONST}, {"Open_tables", (char*) 0, SHOW_OPENTABLES}, - {"Opened_tables", (char*) &opened_tables, SHOW_LONG}, + {"Opened_tables", (char*) offsetof(STATUS_VAR, opened_tables), + SHOW_LONG_STATUS}, #ifdef HAVE_QUERY_CACHE {"Qcache_free_blocks", (char*) &query_cache.free_memory_blocks, SHOW_LONG_CONST}, @@ -5341,19 +5357,36 @@ struct show_var_st status_vars[]= { #endif /*HAVE_QUERY_CACHE*/ {"Questions", (char*) 0, SHOW_QUESTION}, {"Rpl_status", (char*) 0, SHOW_RPL_STATUS}, - {"Select_full_join", (char*) &select_full_join_count, SHOW_LONG}, - {"Select_full_range_join", (char*) &select_full_range_join_count, SHOW_LONG}, - {"Select_range", (char*) &select_range_count, SHOW_LONG}, - {"Select_range_check", (char*) &select_range_check_count, SHOW_LONG}, - {"Select_scan", (char*) &select_scan_count, SHOW_LONG}, + {"Select_full_join", (char*) offsetof(STATUS_VAR, + select_full_join_count), + SHOW_LONG_STATUS}, + {"Select_full_range_join", (char*) offsetof(STATUS_VAR, + select_full_range_join_count), + SHOW_LONG_STATUS}, + {"Select_range", (char*) offsetof(STATUS_VAR, + select_range_count), + SHOW_LONG_STATUS}, + {"Select_range_check", (char*) offsetof(STATUS_VAR, + select_range_check_count), + SHOW_LONG_STATUS}, + {"Select_scan", (char*) offsetof(STATUS_VAR, select_scan_count), + SHOW_LONG_STATUS}, {"Slave_open_temp_tables", (char*) &slave_open_temp_tables, SHOW_LONG}, {"Slave_running", (char*) 0, SHOW_SLAVE_RUNNING}, {"Slow_launch_threads", (char*) &slow_launch_threads, SHOW_LONG}, - {"Slow_queries", (char*) &long_query_count, SHOW_LONG}, - {"Sort_merge_passes", (char*) &filesort_merge_passes, SHOW_LONG}, - {"Sort_range", (char*) &filesort_range_count, SHOW_LONG}, - {"Sort_rows", (char*) &filesort_rows, SHOW_LONG}, - {"Sort_scan", (char*) &filesort_scan_count, SHOW_LONG}, + {"Slow_queries", (char*) offsetof(STATUS_VAR, long_query_count), + SHOW_LONG_STATUS}, + {"Sort_merge_passes", (char*) offsetof(STATUS_VAR, + filesort_merge_passes), + SHOW_LONG_STATUS}, + {"Sort_range", (char*) offsetof(STATUS_VAR, + filesort_range_count), + SHOW_LONG_STATUS}, + {"Sort_rows", (char*) offsetof(STATUS_VAR, filesort_rows), + SHOW_LONG_STATUS}, + {"Sort_scan", (char*) offsetof(STATUS_VAR, + filesort_scan_count), + SHOW_LONG_STATUS}, #ifdef HAVE_OPENSSL {"Ssl_accept_renegotiates", (char*) 0, SHOW_SSL_CTX_SESS_ACCEPT_RENEGOTIATE}, {"Ssl_accepts", (char*) 0, SHOW_SSL_CTX_SESS_ACCEPT}, @@ -5484,27 +5517,22 @@ static void mysql_init_variables(void) test_flags= select_errors= dropping_tables= ha_open_options=0; thread_count= thread_running= kill_cached_threads= wake_thread=0; slave_open_temp_tables= 0; - com_other= 0; cached_thread_count= 0; - bytes_sent= bytes_received= 0; opt_endinfo= using_udf_functions= 0; opt_using_transactions= using_update_log= 0; abort_loop= select_thread_in_use= signal_thread_in_use= 0; ready_to_exit= shutdown_in_progress= grant_option= 0; - long_query_count= aborted_threads= aborted_connects= 0; + aborted_threads= aborted_connects= 0; delayed_insert_threads= delayed_insert_writes= delayed_rows_in_use= 0; delayed_insert_errors= thread_created= 0; - filesort_rows= filesort_range_count= filesort_scan_count= 0; - filesort_merge_passes= select_range_check_count= select_range_count= 0; - select_scan_count= select_full_range_join_count= select_full_join_count= 0; - specialflag= opened_tables= created_tmp_tables= created_tmp_disk_tables= 0; + specialflag= 0; binlog_cache_use= binlog_cache_disk_use= 0; max_used_connections= slow_launch_threads = 0; mysqld_user= mysqld_chroot= opt_init_file= opt_bin_logname = 0; errmesg= 0; mysqld_unix_port= opt_mysql_tmpdir= my_bind_addr_str= NullS; bzero((gptr) &mysql_tmpdir_list, sizeof(mysql_tmpdir_list)); - bzero((gptr) &com_stat, sizeof(com_stat)); + bzero((char *) &global_status_var, sizeof(global_status_var)); /* Character sets */ system_charset_info= &my_charset_utf8_general_ci; diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 36a10370508..82a3b1bd520 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -103,11 +103,10 @@ extern uint test_flags; extern ulong bytes_sent, bytes_received, net_big_packet_count; extern pthread_mutex_t LOCK_bytes_sent , LOCK_bytes_received; extern void query_cache_insert(NET *net, const char *packet, ulong length); +#define update_statistics(A) A #else -#undef statistic_add -#undef statistic_increment -#define statistic_add(A,B,C) -#define statistic_increment(A,B) +#define update_statistics(A) +#define thd_increment_bytes_sent() #endif #define TEST_BLOCKING 8 @@ -565,7 +564,7 @@ net_real_write(NET *net,const char *packet,ulong len) break; } pos+=length; - statistic_add(bytes_sent,length,&LOCK_bytes_sent); + update_statistics(thd_increment_bytes_sent(length)); } #ifndef __WIN__ end: @@ -636,7 +635,7 @@ static my_bool my_net_skip_rest(NET *net, uint32 remain, thr_alarm_t *alarmed, DBUG_PRINT("enter",("bytes_to_skip: %u", (uint) remain)); /* The following is good for debugging */ - statistic_increment(net_big_packet_count,&LOCK_bytes_received); + update_statistics(thd_increment_net_big_packet_count(1)); if (!thr_alarm_in_use(alarmed)) { @@ -652,7 +651,7 @@ static my_bool my_net_skip_rest(NET *net, uint32 remain, thr_alarm_t *alarmed, uint length= min(remain, net->max_packet); if (net_safe_read(net, (char*) net->buff, length, alarmed)) DBUG_RETURN(1); - statistic_add(bytes_received, length, &LOCK_bytes_received); + update_statistics(thd_increment_bytes_received(length)); remain -= (uint32) length; } if (old != MAX_PACKET_LENGTH) @@ -777,7 +776,7 @@ my_real_read(NET *net, ulong *complen) } remain -= (uint32) length; pos+= (ulong) length; - statistic_add(bytes_received,(ulong) length,&LOCK_bytes_received); + update_statistics(thd_increment_bytes_received(length)); } if (i == 0) { /* First parts is packet length */ diff --git a/sql/parse_file.cc b/sql/parse_file.cc index 729f49dcbbe..d10d395d6e5 100644 --- a/sql/parse_file.cc +++ b/sql/parse_file.cc @@ -46,7 +46,7 @@ write_escaped_string(IO_CACHE *file, LEX_STRING *val_s) { /* Should be in sync with read_escaped_string() and - parse_quated_escaped_string() + parse_quoted_escaped_string() */ switch(*ptr) { case '\\': // escape character @@ -154,11 +154,10 @@ write_parameter(IO_CACHE *file, gptr base, File_option *parameter, LEX_STRING *str; while ((str= it++)) { - num.set((ulonglong)str->length, &my_charset_bin); - // ',' after string to detect list continuation + // We need ' ' after string to detect list continuation if ((!first && my_b_append(file, (const byte *)" ", 1)) || my_b_append(file, (const byte *)"\'", 1) || - my_b_append(file, (const byte *)str->str, str->length) || + write_escaped_string(file, str) || my_b_append(file, (const byte *)"\'", 1)) { DBUG_RETURN(TRUE); @@ -486,7 +485,7 @@ read_escaped_string(char *ptr, char *eol, LEX_STRING *str) return TRUE; /* Should be in sync with write_escaped_string() and - parse_quated_escaped_string() + parse_quoted_escaped_string() */ switch(*ptr) { case '\\': @@ -562,7 +561,7 @@ parse_escaped_string(char *ptr, char *end, MEM_ROOT *mem_root, LEX_STRING *str) */ static char * -parse_quated_escaped_string(char *ptr, char *end, +parse_quoted_escaped_string(char *ptr, char *end, MEM_ROOT *mem_root, LEX_STRING *str) { char *eol; @@ -684,7 +683,6 @@ File_parser::parse(gptr base, MEM_ROOT *mem_root, my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0), parameter->name.str, line); DBUG_RETURN(TRUE); - DBUG_RETURN(TRUE); } break; } @@ -724,6 +722,7 @@ File_parser::parse(gptr base, MEM_ROOT *mem_root, /* TODO: remove play with mem_root, when List will be able to store MEM_ROOT* pointer for list elements allocation + FIXME: we can't handle empty lists */ sql_mem= my_pthread_getspecific_ptr(MEM_ROOT*, THR_MALLOC); list= (List<LEX_STRING>*)(base + parameter->offset); @@ -741,7 +740,7 @@ File_parser::parse(gptr base, MEM_ROOT *mem_root, sizeof(LEX_STRING))) || list->push_back(str)) goto list_err; - if(!(ptr= parse_quated_escaped_string(ptr, end, mem_root, str))) + if(!(ptr= parse_quoted_escaped_string(ptr, end, mem_root, str))) goto list_err_w_message; switch (*ptr) { case '\n': diff --git a/sql/parse_file.h b/sql/parse_file.h index 6a426aa0423..cf34a5095a4 100644 --- a/sql/parse_file.h +++ b/sql/parse_file.h @@ -27,7 +27,8 @@ enum file_opt_type { FILE_OPTIONS_REV, /* Revision version number (ulonglong) */ FILE_OPTIONS_TIMESTAMP, /* timestamp (LEX_STRING have to be allocated with length 20 (19+1) */ - FILE_OPTIONS_STRLIST /* list of strings (List<char*>) */ + FILE_OPTIONS_STRLIST /* list of escaped strings + (List<LEX_STRING>) */ }; struct File_option diff --git a/sql/protocol_cursor.cc b/sql/protocol_cursor.cc index 31eaa894045..8904aba7b88 100644 --- a/sql/protocol_cursor.cc +++ b/sql/protocol_cursor.cc @@ -112,7 +112,8 @@ bool Protocol_cursor::write() for (; cur_field < fields_end; ++cur_field, ++data_tmp) { - if ((len= net_field_length((uchar **)&cp)) == 0) + if ((len= net_field_length((uchar **)&cp)) == 0 || + len == NULL_LENGTH) { *data_tmp= 0; } diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt index 68f5fbff13c..c613a22088a 100644 --- a/sql/share/czech/errmsg.txt +++ b/sql/share/czech/errmsg.txt @@ -387,3 +387,8 @@ character-set=latin2 "View '%-.64s.%-.64s' references invalid table(s) or column(s)" "Can't drop a %s from within another stored routine" "GOTO is not allowed in a stored procedure handler" +"Trigger already exists" +"Trigger does not exist" +"Trigger's '%-.64s' is view or temporary table" +"Updating of %s row is not allowed in %strigger" +"There is no %s row in %s trigger" diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt index 1153bbecbc8..0f3a8f6ffdb 100644 --- a/sql/share/danish/errmsg.txt +++ b/sql/share/danish/errmsg.txt @@ -378,3 +378,8 @@ character-set=latin1 "View '%-.64s.%-.64s' references invalid table(s) or column(s)" "Can't drop a %s from within another stored routine" "GOTO is not allowed in a stored procedure handler" +"Trigger already exists" +"Trigger does not exist" +"Trigger's '%-.64s' is view or temporary table" +"Updating of %s row is not allowed in %strigger" +"There is no %s row in %s trigger" diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt index 67b0934f2d2..d0d86a07b7e 100644 --- a/sql/share/dutch/errmsg.txt +++ b/sql/share/dutch/errmsg.txt @@ -387,3 +387,8 @@ character-set=latin1 "View '%-.64s.%-.64s' references invalid table(s) or column(s)" "Can't drop a %s from within another stored routine" "GOTO is not allowed in a stored procedure handler" +"Trigger already exists" +"Trigger does not exist" +"Trigger's '%-.64s' is view or temporary table" +"Updating of %s row is not allowed in %strigger" +"There is no %s row in %s trigger" diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt index cfe46055bbc..0b502244a64 100644 --- a/sql/share/english/errmsg.txt +++ b/sql/share/english/errmsg.txt @@ -375,3 +375,8 @@ character-set=latin1 "View '%-.64s.%-.64s' references invalid table(s) or column(s)" "Can't drop a %s from within another stored routine" "GOTO is not allowed in a stored procedure handler" +"Trigger already exists" +"Trigger does not exist" +"Trigger's '%-.64s' is view or temporary table" +"Updating of %s row is not allowed in %strigger" +"There is no %s row in %s trigger" diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt index e4c139d31b1..7886c785c40 100644 --- a/sql/share/estonian/errmsg.txt +++ b/sql/share/estonian/errmsg.txt @@ -380,3 +380,8 @@ character-set=latin7 "View '%-.64s.%-.64s' references invalid table(s) or column(s)" "Can't drop a %s from within another stored routine" "GOTO is not allowed in a stored procedure handler" +"Trigger already exists" +"Trigger does not exist" +"Trigger's '%-.64s' is view or temporary table" +"Updating of %s row is not allowed in %strigger" +"There is no %s row in %s trigger" diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt index ee9e784ada6..9668cbf3c9f 100644 --- a/sql/share/french/errmsg.txt +++ b/sql/share/french/errmsg.txt @@ -375,3 +375,8 @@ character-set=latin1 "View '%-.64s.%-.64s' references invalid table(s) or column(s)" "Can't drop a %s from within another stored routine" "GOTO is not allowed in a stored procedure handler" +"Trigger already exists" +"Trigger does not exist" +"Trigger's '%-.64s' is view or temporary table" +"Updating of %s row is not allowed in %strigger" +"There is no %s row in %s trigger" diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt index 9bd7665ac97..c2c82443f91 100644 --- a/sql/share/german/errmsg.txt +++ b/sql/share/german/errmsg.txt @@ -388,3 +388,8 @@ character-set=latin1 "View '%-.64s.%-.64s' references invalid table(s) or column(s)" "Can't drop a %s from within another stored routine" "GOTO is not allowed in a stored procedure handler" +"Trigger already exists" +"Trigger does not exist" +"Trigger's '%-.64s' is view or temporary table" +"Updating of %s row is not allowed in %strigger" +"There is no %s row in %s trigger" diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt index e8ed72f6a0f..3aac21ec481 100644 --- a/sql/share/greek/errmsg.txt +++ b/sql/share/greek/errmsg.txt @@ -375,3 +375,8 @@ character-set=greek "View '%-.64s.%-.64s' references invalid table(s) or column(s)" "Can't drop a %s from within another stored routine" "GOTO is not allowed in a stored procedure handler" +"Trigger already exists" +"Trigger does not exist" +"Trigger's '%-.64s' is view or temporary table" +"Updating of %s row is not allowed in %strigger" +"There is no %s row in %s trigger" diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt index bd4f4e56d52..8f96a0bf183 100644 --- a/sql/share/hungarian/errmsg.txt +++ b/sql/share/hungarian/errmsg.txt @@ -380,3 +380,8 @@ character-set=latin2 "View '%-.64s.%-.64s' references invalid table(s) or column(s)" "Can't drop a %s from within another stored routine" "GOTO is not allowed in a stored procedure handler" +"Trigger already exists" +"Trigger does not exist" +"Trigger's '%-.64s' is view or temporary table" +"Updating of %s row is not allowed in %strigger" +"There is no %s row in %s trigger" diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt index f1c9b10c8d4..8d63656bac2 100644 --- a/sql/share/italian/errmsg.txt +++ b/sql/share/italian/errmsg.txt @@ -375,3 +375,8 @@ character-set=latin1 "View '%-.64s.%-.64s' references invalid table(s) or column(s)" "Can't drop a %s from within another stored routine" "GOTO is not allowed in a stored procedure handler" +"Trigger already exists" +"Trigger does not exist" +"Trigger's '%-.64s' is view or temporary table" +"Updating of %s row is not allowed in %strigger" +"There is no %s row in %s trigger" diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt index 03af38e9172..30eb8daffa4 100644 --- a/sql/share/japanese/errmsg.txt +++ b/sql/share/japanese/errmsg.txt @@ -379,3 +379,8 @@ character-set=ujis "View '%-.64s.%-.64s' references invalid table(s) or column(s)" "Can't drop a %s from within another stored routine" "GOTO is not allowed in a stored procedure handler" +"Trigger already exists" +"Trigger does not exist" +"Trigger's '%-.64s' is view or temporary table" +"Updating of %s row is not allowed in %strigger" +"There is no %s row in %s trigger" diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt index 43b0cd2640e..dac28683a78 100644 --- a/sql/share/korean/errmsg.txt +++ b/sql/share/korean/errmsg.txt @@ -375,3 +375,8 @@ character-set=euckr "View '%-.64s.%-.64s' references invalid table(s) or column(s)" "Can't drop a %s from within another stored routine" "GOTO is not allowed in a stored procedure handler" +"Trigger already exists" +"Trigger does not exist" +"Trigger's '%-.64s' is view or temporary table" +"Updating of %s row is not allowed in %strigger" +"There is no %s row in %s trigger" diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt index b542ffb6a62..58661f16bcf 100644 --- a/sql/share/norwegian-ny/errmsg.txt +++ b/sql/share/norwegian-ny/errmsg.txt @@ -377,3 +377,8 @@ character-set=latin1 "View '%-.64s.%-.64s' references invalid table(s) or column(s)" "Can't drop a %s from within another stored routine" "GOTO is not allowed in a stored procedure handler" +"Trigger already exists" +"Trigger does not exist" +"Trigger's '%-.64s' is view or temporary table" +"Updating of %s row is not allowed in %strigger" +"There is no %s row in %s trigger" diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt index a435479f8a0..ea204145d54 100644 --- a/sql/share/norwegian/errmsg.txt +++ b/sql/share/norwegian/errmsg.txt @@ -377,3 +377,8 @@ character-set=latin1 "View '%-.64s.%-.64s' references invalid table(s) or column(s)" "Can't drop a %s from within another stored routine" "GOTO is not allowed in a stored procedure handler" +"Trigger already exists" +"Trigger does not exist" +"Trigger's '%-.64s' is view or temporary table" +"Updating of %s row is not allowed in %strigger" +"There is no %s row in %s trigger" diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt index 413e004cec1..db0d4914c19 100644 --- a/sql/share/polish/errmsg.txt +++ b/sql/share/polish/errmsg.txt @@ -380,3 +380,8 @@ character-set=latin2 "View '%-.64s.%-.64s' references invalid table(s) or column(s)" "Can't drop a %s from within another stored routine" "GOTO is not allowed in a stored procedure handler" +"Trigger already exists" +"Trigger does not exist" +"Trigger's '%-.64s' is view or temporary table" +"Updating of %s row is not allowed in %strigger" +"There is no %s row in %s trigger" diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt index 85c8cd00dd5..d3508f120db 100644 --- a/sql/share/portuguese/errmsg.txt +++ b/sql/share/portuguese/errmsg.txt @@ -377,3 +377,8 @@ character-set=latin1 "View '%-.64s.%-.64s' references invalid table(s) or column(s)" "Can't drop a %s from within another stored routine" "GOTO is not allowed in a stored procedure handler" +"Trigger already exists" +"Trigger does not exist" +"Trigger's '%-.64s' is view or temporary table" +"Updating of %s row is not allowed in %strigger" +"There is no %s row in %s trigger" diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt index 1c7e2187084..65ab66e1256 100644 --- a/sql/share/romanian/errmsg.txt +++ b/sql/share/romanian/errmsg.txt @@ -380,3 +380,8 @@ character-set=latin2 "View '%-.64s.%-.64s' references invalid table(s) or column(s)" "Can't drop a %s from within another stored routine" "GOTO is not allowed in a stored procedure handler" +"Trigger already exists" +"Trigger does not exist" +"Trigger's '%-.64s' is view or temporary table" +"Updating of %s row is not allowed in %strigger" +"There is no %s row in %s trigger" diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt index a9f1024db87..6501598383c 100644 --- a/sql/share/russian/errmsg.txt +++ b/sql/share/russian/errmsg.txt @@ -377,6 +377,11 @@ character-set=koi8r "View SELECT É ÓÐÉÓÏË ÐÏÌÅÊ view ÉÍÅÀÔ ÒÁÚÎÏÅ ËÏÌÉÞÅÓÔ×Ï ÓÔÏÌÂÃÏ×" "áÌÇÏÒÉÔÍ ÓÌÉÑÎÉÑ view ÎÅ ÍÏÖÅÔ ÂÙÔØ ÉÓÐÏÌØÚÏ×ÁÎ ÓÅÊÞÁÓ (ÁÌÇÏÒÉÔÍ ÂÕÄÅÔ ÎÅÏÐÅÒÅÄÅÌÅÎÎÙÍ)" "ïÂÎÏ×ÌÑÅÍÙÊ view ÎÅ ÓÏÄÅÒÖÉÔ ËÌÀÞÁ ÉÓÐÏÌØÚÏ×ÁÎÎÏÊ × ÎÅÍ ÔÁÂÌÉÃ(Ù)" -"View '%-.64s.%-.64s' ÓÓÙÌÁÅÔÓÑ ÎÁ ÎÅÓÕÝÅÓÔ×ÕÀÝÉÅ ÔÁÂÌÉÃÙ ÉÌÉ ÓÌÏÌÂÃÙ" +"View '%-.64s.%-.64s' ÓÓÙÌÁÅÔÓÑ ÎÁ ÎÅÓÕÝÅÓÔ×ÕÀÝÉÅ ÔÁÂÌÉÃÙ ÉÌÉ ÓÔÏÌÂÃÙ" "Can't drop a %s from within another stored routine" "GOTO is not allowed in a stored procedure handler" +"Trigger already exists" +"Trigger does not exist" +"Trigger's '%-.64s' is view or temporary table" +"Updating of %s row is not allowed in %strigger" +"There is no %s row in %s trigger" diff --git a/sql/share/serbian/errmsg.txt b/sql/share/serbian/errmsg.txt index 8f3bc3458e1..6e7826e33d6 100644 --- a/sql/share/serbian/errmsg.txt +++ b/sql/share/serbian/errmsg.txt @@ -368,3 +368,8 @@ character-set=cp1250 "View '%-.64s.%-.64s' references invalid table(s) or column(s)" "Can't drop a %s from within another stored routine" "GOTO is not allowed in a stored procedure handler" +"Trigger already exists" +"Trigger does not exist" +"Trigger's '%-.64s' is view or temporary table" +"Updating of %s row is not allowed in %strigger" +"There is no %s row in %s trigger" diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt index c7b67540f2f..4c82ae5e3af 100644 --- a/sql/share/slovak/errmsg.txt +++ b/sql/share/slovak/errmsg.txt @@ -383,3 +383,8 @@ character-set=latin2 "View '%-.64s.%-.64s' references invalid table(s) or column(s)" "Can't drop a %s from within another stored routine" "GOTO is not allowed in a stored procedure handler" +"Trigger already exists" +"Trigger does not exist" +"Trigger's '%-.64s' is view or temporary table" +"Updating of %s row is not allowed in %strigger" +"There is no %s row in %s trigger" diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt index c725caf93c0..c9a1d7229ea 100644 --- a/sql/share/spanish/errmsg.txt +++ b/sql/share/spanish/errmsg.txt @@ -379,3 +379,8 @@ character-set=latin1 "View '%-.64s.%-.64s' references invalid table(s) or column(s)" "Can't drop a %s from within another stored routine" "GOTO is not allowed in a stored procedure handler" +"Trigger already exists" +"Trigger does not exist" +"Trigger's '%-.64s' is view or temporary table" +"Updating of %s row is not allowed in %strigger" +"There is no %s row in %s trigger" diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt index 85d37c2ae9f..e80cb48d157 100644 --- a/sql/share/swedish/errmsg.txt +++ b/sql/share/swedish/errmsg.txt @@ -375,3 +375,8 @@ character-set=latin1 "View '%-.64s.%-.64s' references invalid table(s) or column(s)" "Can't drop a %s from within another stored routine" "GOTO is not allowed in a stored procedure handler" +"Trigger already exists" +"Trigger does not exist" +"Trigger's '%-.64s' is view or temporary table" +"Updating of %s row is not allowed in %strigger" +"There is no %s row in %s trigger" diff --git a/sql/share/ukrainian/errmsg.txt b/sql/share/ukrainian/errmsg.txt index cd3917aa123..2a71aa088f4 100644 --- a/sql/share/ukrainian/errmsg.txt +++ b/sql/share/ukrainian/errmsg.txt @@ -381,3 +381,8 @@ character-set=koi8u "View '%-.64s.%-.64s' ÐÏÓÉÌÁ¤ÔÓÑ ÎÁ ÎŦÓÎÕÀÞ¦ ÔÁÂÌÉæ ÁÂÏ ÓÔÏ×Âæ" "Can't drop a %s from within another stored routine" "GOTO is not allowed in a stored procedure handler" +"Trigger already exists" +"Trigger does not exist" +"Trigger's '%-.64s' is view or temporary table" +"Updating of %s row is not allowed in %strigger" +"There is no %s row in %s trigger" diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 8c35597fe99..6e4269ad8a5 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -293,26 +293,35 @@ sp_head::init_strings(THD *thd, LEX *lex, sp_name *name) /* During parsing, we must use thd->mem_root */ MEM_ROOT *root= &thd->mem_root; - DBUG_PRINT("info", ("name: %*.s%*s", - name->m_db.length, name->m_db.str, - name->m_name.length, name->m_name.str)); /* We have to copy strings to get them into the right memroot */ - m_db.length= name->m_db.length; - if (name->m_db.length == 0) - m_db.str= NULL; - else - m_db.str= strmake_root(root, name->m_db.str, name->m_db.length); - m_name.length= name->m_name.length; - m_name.str= strmake_root(root, name->m_name.str, name->m_name.length); - - if (name->m_qname.length == 0) - name->init_qname(thd); - m_qname.length= name->m_qname.length; - m_qname.str= strmake_root(root, name->m_qname.str, m_qname.length); - - m_params.length= m_param_end- m_param_begin; - m_params.str= strmake_root(root, - (char *)m_param_begin, m_params.length); + if (name) + { + m_db.length= name->m_db.length; + if (name->m_db.length == 0) + m_db.str= NULL; + else + m_db.str= strmake_root(root, name->m_db.str, name->m_db.length); + m_name.length= name->m_name.length; + m_name.str= strmake_root(root, name->m_name.str, name->m_name.length); + + if (name->m_qname.length == 0) + name->init_qname(thd); + m_qname.length= name->m_qname.length; + m_qname.str= strmake_root(root, name->m_qname.str, m_qname.length); + } + else if (thd->db) + { + m_db.length= thd->db_length; + m_db.str= strmake_root(root, thd->db, m_db.length); + } + + if (m_param_begin && m_param_end) + { + m_params.length= m_param_end - m_param_begin; + m_params.str= strmake_root(root, + (char *)m_param_begin, m_params.length); + } + if (m_returns_begin && m_returns_end) { /* QQ KLUDGE: We can't seem to cut out just the type in the parser @@ -490,6 +499,7 @@ sp_head::execute(THD *thd) ip= hip; ret= 0; ctx->clear_handler(); + ctx->in_handler= TRUE; continue; } } @@ -575,8 +585,10 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp) thd->spcont= nctx; ret= execute(thd); - if (ret == 0) + + if (m_type == TYPE_ENUM_FUNCTION && ret == 0) { + /* We need result only in function but not in trigger */ Item *it= nctx->get_result(); if (it) @@ -763,6 +775,9 @@ sp_head::reset_lex(THD *thd) /* And keep the SP stuff too */ sublex->sphead= oldlex->sphead; sublex->spcont= oldlex->spcont; + /* And trigger related stuff too */ + sublex->trg_chistics= oldlex->trg_chistics; + sublex->trg_table= oldlex->trg_table; sublex->sp_lex_in_use= FALSE; DBUG_VOID_RETURN; } @@ -1211,7 +1226,7 @@ sp_instr_set::execute(THD *thd, uint *nextp) thd->spcont->set_item(m_offset, it); } *nextp = m_ip+1; - if (thd->lock || thd->open_tables || thd->derived_tables) + if (tables && (thd->lock || thd->open_tables || thd->derived_tables)) close_thread_tables(thd); DBUG_RETURN(res); } @@ -1227,6 +1242,60 @@ sp_instr_set::print(String *str) } // +// sp_instr_set_user_var +// +int +sp_instr_set_user_var::execute(THD *thd, uint *nextp) +{ + int res= 0; + + DBUG_ENTER("sp_instr_set_user_var::execute"); + /* + It is ok to pass 0 as 3rd argument to fix_fields() since + Item_func_set_user_var::fix_fields() won't use it. + QQ: Still unsure what should we return in case of error 1 or -1 ? + */ + if (!m_set_var_item.fixed && m_set_var_item.fix_fields(thd, 0, 0) || + m_set_var_item.check() || m_set_var_item.update()) + res= -1; + *nextp= m_ip + 1; + DBUG_RETURN(res); +} + +void +sp_instr_set_user_var::print(String *str) +{ + m_set_var_item.print_as_stmt(str); +} + +// +// sp_instr_set_trigger_field +// +int +sp_instr_set_trigger_field::execute(THD *thd, uint *nextp) +{ + int res= 0; + + DBUG_ENTER("sp_instr_set_trigger_field::execute"); + /* QQ: Still unsure what should we return in case of error 1 or -1 ? */ + if (!value->fixed && value->fix_fields(thd, 0, &value) || + trigger_field.fix_fields(thd, 0, 0) || + (value->save_in_field(trigger_field.field, 0) < 0)) + res= -1; + *nextp= m_ip + 1; + DBUG_RETURN(res); +} + +void +sp_instr_set_trigger_field::print(String *str) +{ + str->append("set ", 4); + trigger_field.print(str); + str->append(":=", 2); + value->print(str); +} + +// // sp_instr_jump // int @@ -1314,7 +1383,7 @@ sp_instr_jump_if::execute(THD *thd, uint *nextp) else *nextp = m_ip+1; } - if (thd->lock || thd->open_tables || thd->derived_tables) + if (tables && (thd->lock || thd->open_tables || thd->derived_tables)) close_thread_tables(thd); DBUG_RETURN(res); } @@ -1371,7 +1440,7 @@ sp_instr_jump_if_not::execute(THD *thd, uint *nextp) else *nextp = m_ip+1; } - if (thd->lock || thd->open_tables || thd->derived_tables) + if (tables && (thd->lock || thd->open_tables || thd->derived_tables)) close_thread_tables(thd); DBUG_RETURN(res); } @@ -1518,19 +1587,40 @@ int sp_instr_hreturn::execute(THD *thd, uint *nextp) { DBUG_ENTER("sp_instr_hreturn::execute"); - thd->spcont->restore_variables(m_frame); - *nextp= thd->spcont->pop_hstack(); + if (m_dest) + *nextp= m_dest; + else + { + thd->spcont->restore_variables(m_frame); + *nextp= thd->spcont->pop_hstack(); + } + thd->spcont->in_handler= FALSE; DBUG_RETURN(0); } void sp_instr_hreturn::print(String *str) { - str->reserve(12); + str->reserve(16); str->append("hreturn "); str->qs_append(m_frame); + if (m_dest) + str->qs_append(m_dest); +} + +uint +sp_instr_hreturn::opt_mark(sp_head *sp) +{ + if (m_dest) + return sp_instr_jump::opt_mark(sp); + else + { + marked= 1; + return UINT_MAX; + } } + // // sp_instr_cpush // diff --git a/sql/sp_head.h b/sql/sp_head.h index c0881661ad1..9c308961aa4 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -28,6 +28,7 @@ // in the CREATE TABLE command. #define TYPE_ENUM_FUNCTION 1 #define TYPE_ENUM_PROCEDURE 2 +#define TYPE_ENUM_TRIGGER 3 Item_result sp_map_result_type(enum enum_field_types type); @@ -377,6 +378,72 @@ private: }; // class sp_instr_set : public sp_instr +/* + Set user variable instruction. + Used in functions and triggers to set user variables because we don't + want use sp_instr_stmt + "SET @a:=..." statement in this case since + latter will close all tables and thus will ruin execution of statement + calling/invoking this function/trigger. +*/ +class sp_instr_set_user_var : public sp_instr +{ + sp_instr_set_user_var(const sp_instr_set_user_var &); + void operator=(sp_instr_set_user_var &); + +public: + + sp_instr_set_user_var(uint ip, sp_pcontext *ctx, LEX_STRING var, Item *val) + : sp_instr(ip, ctx), m_set_var_item(var, val) + {} + + virtual ~sp_instr_set_user_var() + {} + + virtual int execute(THD *thd, uint *nextp); + + virtual void print(String *str); + +private: + + Item_func_set_user_var m_set_var_item; +}; // class sp_instr_set_user_var : public sp_instr + + +/* + Set NEW/OLD row field value instruction. Used in triggers. +*/ +class sp_instr_set_trigger_field : public sp_instr +{ + sp_instr_set_trigger_field(const sp_instr_set_trigger_field &); + void operator=(sp_instr_set_trigger_field &); + +public: + + sp_instr_set_trigger_field(uint ip, sp_pcontext *ctx, + LEX_STRING field_name, Item *val) + : sp_instr(ip, ctx), + trigger_field(Item_trigger_field::NEW_ROW, field_name.str), + value(val) + {} + + virtual ~sp_instr_set_trigger_field() + {} + + virtual int execute(THD *thd, uint *nextp); + + virtual void print(String *str); + + bool setup_field(THD *thd, TABLE *table, enum trg_event_type event) + { + return trigger_field.setup_field(thd, table, event); + } +private: + + Item_trigger_field trigger_field; + Item *value; +}; // class sp_instr_trigger_field : public sp_instr + + class sp_instr_jump : public sp_instr { sp_instr_jump(const sp_instr_jump &); /* Prevent use of these */ @@ -610,7 +677,7 @@ private: }; // class sp_instr_hpop : public sp_instr -class sp_instr_hreturn : public sp_instr +class sp_instr_hreturn : public sp_instr_jump { sp_instr_hreturn(const sp_instr_hreturn &); /* Prevent use of these */ void operator=(sp_instr_hreturn &); @@ -618,7 +685,7 @@ class sp_instr_hreturn : public sp_instr public: sp_instr_hreturn(uint ip, sp_pcontext *ctx, uint fp) - : sp_instr(ip, ctx), m_frame(fp) + : sp_instr_jump(ip, ctx), m_frame(fp) {} virtual ~sp_instr_hreturn() @@ -628,11 +695,7 @@ public: virtual void print(String *str); - virtual uint opt_mark(sp_head *sp) - { - marked= 1; - return UINT_MAX; - } + virtual uint opt_mark(sp_head *sp); private: diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc index 7fa44f217f6..2f7bdbffa2b 100644 --- a/sql/sp_rcontext.cc +++ b/sql/sp_rcontext.cc @@ -32,6 +32,7 @@ sp_rcontext::sp_rcontext(uint fsize, uint hmax, uint cmax) : m_count(0), m_fsize(fsize), m_result(NULL), m_hcount(0), m_hsp(0), m_hfound(-1), m_ccount(0) { + in_handler= FALSE; m_frame= (Item **)sql_alloc(fsize * sizeof(Item*)); m_outs= (int *)sql_alloc(fsize * sizeof(int)); m_handler= (sp_handler_t *)sql_alloc(hmax * sizeof(sp_handler_t)); @@ -58,6 +59,8 @@ sp_rcontext::set_item_eval(uint idx, Item *i, enum_field_types type) int sp_rcontext::find_handler(uint sql_errno) { + if (in_handler) + return 0; // Already executing a handler if (m_hfound >= 0) return 1; // Already got one @@ -227,21 +230,24 @@ sp_cursor::fetch(THD *thd, List<struct sp_pvar> *vars) return -1; } s= row[fldcount]; - switch (sp_map_result_type(pv->type)) - { - case INT_RESULT: - it= new Item_int(s); - break; - case REAL_RESULT: - it= new Item_real(s, strlen(s)); - break; - default: + if (!s) + it= new Item_null(); + else + switch (sp_map_result_type(pv->type)) { - uint len= strlen(s); - it= new Item_string(thd->strmake(s, len), len, thd->db_charset); + case INT_RESULT: + it= new Item_int(s); break; + case REAL_RESULT: + it= new Item_real(s, strlen(s)); + break; + default: + { + uint len= strlen(s); + it= new Item_string(thd->strmake(s, len), len, thd->db_charset); + break; + } } - } thd->spcont->set_item(pv->offset, it); } if (fldcount < m_prot->get_field_count()) diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h index 15a2fe62138..f26b6760310 100644 --- a/sql/sp_rcontext.h +++ b/sql/sp_rcontext.h @@ -46,6 +46,8 @@ class sp_rcontext : public Sql_alloc public: + bool in_handler; + sp_rcontext(uint fsize, uint hmax, uint cmax); ~sp_rcontext() diff --git a/sql/sql_base.cc b/sql/sql_base.cc index e9847a09384..80090ba288e 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -20,6 +20,8 @@ #include "mysql_priv.h" #include "sql_acl.h" #include "sql_select.h" +#include "sp_head.h" +#include "sql_trigger.h" #include <m_ctype.h> #include <my_dir.h> #include <hash.h> @@ -42,10 +44,6 @@ static my_bool open_new_frm(const char *path, const char *alias, uint db_stat, uint prgflag, uint ha_open_flags, TABLE *outparam, TABLE_LIST *table_desc, MEM_ROOT *mem_root); -static Field *find_field_in_real_table(THD *thd, TABLE *table, - const char *name, uint length, - bool check_grants, bool allow_rowid, - uint *cached_field_index_ptr); extern "C" byte *table_cache_key(const byte *record,uint *length, my_bool not_used __attribute__((unused))) @@ -210,6 +208,7 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *wild) void intern_close_table(TABLE *table) { // Free all structures free_io_cache(table); + delete table->triggers; if (table->file) VOID(closefrm(table)); // close file } @@ -574,16 +573,87 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table, const char *db_name, const char *table_name) { - for (; table; table= *(TABLE_LIST **) ((char*) table + offset)) + if (lower_case_table_names) { - if (!strcmp(table->db, db_name) && - !strcmp(table->real_name, table_name)) - break; + for (; table; table= *(TABLE_LIST **) ((char*) table + offset)) + { + if ((!strcmp(table->db, db_name) && + !strcmp(table->real_name, table_name)) || + (table->view && + !my_strcasecmp(table_alias_charset, + table->table->table_cache_key, db_name) && + !my_strcasecmp(table_alias_charset, + table->table->table_name, table_name))) + break; + } + } + else + { + for (; table; table= *(TABLE_LIST **) ((char*) table + offset)) + { + if ((!strcmp(table->db, db_name) && + !strcmp(table->real_name, table_name)) || + (table->view && + !strcmp(table->table->table_cache_key, db_name) && + !strcmp(table->table->table_name, table_name))) + break; + } } return table; } +/* + Test that table is unique + + SYNOPSIS + unique_table() + table table which should be chaked + table_list list of tables + + RETURN + found duplicate + 0 if table is unique +*/ + +TABLE_LIST* unique_table(TABLE_LIST *table, TABLE_LIST *table_list) +{ + TABLE_LIST *res; + const char *d_name= table->db, *t_name= table->real_name; + char d_name_buff[MAX_ALIAS_NAME], t_name_buff[MAX_ALIAS_NAME]; + if (table->view) + { + /* it is view and table opened */ + if (lower_case_table_names) + { + strmov(t_name_buff, table->table->table_name); + my_casedn_str(files_charset_info, t_name_buff); + t_name= t_name_buff; + strmov(d_name_buff, table->table->table_cache_key); + my_casedn_str(files_charset_info, d_name_buff); + d_name= d_name_buff; + } + else + { + d_name= table->table->table_cache_key; + t_name= table->table->table_name; + } + if (d_name == 0) + { + /* it's temporary table => always unique */ + return 0; + } + } + if ((res= find_table_in_global_list(table_list, d_name, t_name)) && + res->table && res->table == table->table) + { + // we found entry of this table try again. + return find_table_in_global_list(res->next_global, d_name, t_name); + } + return res; +} + + TABLE **find_temporary_table(THD *thd, const char *db, const char *table_name) { char key[MAX_DBKEY_LENGTH]; @@ -747,6 +817,7 @@ TABLE *reopen_name_locked_table(THD* thd, TABLE_LIST* table_list) !(table->table_cache_key =memdup_root(&table->mem_root,(char*) key, key_length))) { + delete table->triggers; closefrm(table); pthread_mutex_unlock(&LOCK_open); DBUG_RETURN(0); @@ -1010,6 +1081,7 @@ bool reopen_table(TABLE *table,bool locked) if (!(tmp.table_cache_key= memdup_root(&tmp.mem_root,db, table->key_length))) { + delete tmp.triggers; closefrm(&tmp); // End of memory goto end; } @@ -1038,6 +1110,7 @@ bool reopen_table(TABLE *table,bool locked) tmp.next= table->next; tmp.prev= table->prev; + delete table->triggers; if (table->file) VOID(closefrm(table)); // close file, free everything @@ -1424,6 +1497,9 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, if (error == 5) DBUG_RETURN(0); // we have just opened VIEW + if (Table_triggers_list::check_n_load(thd, db, name, entry)) + goto err; + /* If we are here, there was no fatal error (but error may be still unitialized). @@ -1452,6 +1528,7 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, */ sql_print_error("Error: when opening HEAP table, could not allocate \ memory to write 'DELETE FROM `%s`.`%s`' to the binary log",db,name); + delete entry->triggers; if (entry->file) closefrm(entry); goto err; @@ -1999,10 +2076,10 @@ find_field_in_table(THD *thd, TABLE_LIST *table_list, # pointer to field */ -static Field *find_field_in_real_table(THD *thd, TABLE *table, - const char *name, uint length, - bool check_grants, bool allow_rowid, - uint *cached_field_index_ptr) +Field *find_field_in_real_table(THD *thd, TABLE *table, + const char *name, uint length, + bool check_grants, bool allow_rowid, + uint *cached_field_index_ptr) { Field **field_ptr, *field; uint cached_field_index= *cached_field_index_ptr; @@ -2440,7 +2517,7 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, Don't use arena if we are not in prepared statements or stored procedures For PS/SP we have to use arena to remember the changes */ - if (arena->state == Item_arena::CONVENTIONAL_EXECUTION) + if (arena->is_conventional()) arena= 0; // For easier test later one else thd->set_n_backup_item_arena(arena, &backup); @@ -2466,8 +2543,8 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, it.replace(new Item_int("Not_used", (longlong) 1, 21)); } else if (insert_fields(thd,tables,((Item_field*) item)->db_name, - ((Item_field*) item)->table_name, &it, - any_privileges)) + ((Item_field*) item)->table_name, &it, + any_privileges, arena != 0)) { if (arena) thd->restore_backup_item_arena(arena, &backup); @@ -2653,6 +2730,7 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table, any_privileges 0 If we should ensure that we have SELECT privileges for all columns 1 If any privilege is ok + allocate_view_names if true view names will be copied to current Item_arena memory (made for SP/PS) RETURN 0 ok 'it' is updated to point at last inserted @@ -2662,7 +2740,7 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table, bool insert_fields(THD *thd, TABLE_LIST *tables, const char *db_name, const char *table_name, List_iterator<Item> *it, - bool any_privileges) + bool any_privileges, bool allocate_view_names) { /* allocate variables on stack to avoid pool alloaction */ Field_iterator_table table_iter; @@ -2822,7 +2900,7 @@ insert_fields(THD *thd, TABLE_LIST *tables, const char *db_name, field->query_id=thd->query_id; table->used_keys.intersect(field->part_of_key); } - else if (thd->current_arena->is_stmt_prepare() && + else if (allocate_view_names && thd->lex->current_select->first_execution) { Item_field *item= new Item_field(thd->strdup(tables->view_db.str), @@ -2867,7 +2945,7 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) DBUG_ENTER("setup_conds"); if (select_lex->conds_processed_with_permanent_arena || - !arena->is_stmt_prepare()) + arena->is_conventional()) arena= 0; // For easier test thd->set_query_id=1; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 0c42c45bf59..cc6b86f4bc2 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -298,6 +298,7 @@ void THD::init(void) bzero((char*) warn_count, sizeof(warn_count)); total_warn_count= 0; update_charset(); + bzero((char *) &status_var, sizeof(status_var)); } @@ -388,6 +389,7 @@ THD::~THD() /* Ensure that no one is using THD */ pthread_mutex_lock(&LOCK_delete); pthread_mutex_unlock(&LOCK_delete); + add_to_status(&global_status_var, &status_var); /* Close connection */ #ifndef EMBEDDED_LIBRARY @@ -430,6 +432,27 @@ THD::~THD() } +/* + Add to one status variable another status variable + + NOTES + This function assumes that all variables are long/ulong. + If this assumption will change, then we have to explictely add + the other variables after the while loop +*/ + +void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var) +{ + ulong *end= (ulong*) ((byte*) to_var + offsetof(STATUS_VAR, + last_system_status_var) + + sizeof(ulong)); + ulong *to= (ulong*) to_var, *from= (ulong*) from_var; + + while (to != end) + *(to++)+= *(from++); +} + + void THD::awake(THD::killed_state state_to_set) { THD_CHECK_SENTRY(this); @@ -1646,3 +1669,27 @@ void TMP_TABLE_PARAM::init() group_parts= group_length= group_null_parts= 0; quick_group= 1; } + + +void thd_increment_bytes_sent(ulong length) +{ + current_thd->status_var.bytes_sent+= length; +} + + +void thd_increment_bytes_received(ulong length) +{ + current_thd->status_var.bytes_received+= length; +} + + +void thd_increment_net_big_packet_count(ulong length) +{ + current_thd->status_var.net_big_packet_count+= length; +} + + +void THD::set_status_var_init() +{ + bzero((char*) &status_var, sizeof(status_var)); +} diff --git a/sql/sql_class.h b/sql/sql_class.h index 761ffc133a3..d917eeef550 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -443,6 +443,61 @@ struct system_variables DATE_TIME_FORMAT *time_format; }; + +/* per thread status variables */ + +typedef struct system_status_var +{ + ulong bytes_received; + ulong bytes_sent; + ulong com_other; + ulong com_stat[(uint) SQLCOM_END]; + ulong created_tmp_disk_tables; + ulong created_tmp_tables; + ulong ha_commit_count; + ulong ha_delete_count; + ulong ha_read_first_count; + ulong ha_read_last_count; + ulong ha_read_key_count; + ulong ha_read_next_count; + ulong ha_read_prev_count; + ulong ha_read_rnd_count; + ulong ha_read_rnd_next_count; + ulong ha_rollback_count; + ulong ha_update_count; + ulong ha_write_count; + + /* KEY_CACHE parts. These are copies of the original */ + ulong key_blocks_changed; + ulong key_blocks_used; + ulong key_cache_r_requests; + ulong key_cache_read; + ulong key_cache_w_requests; + ulong key_cache_write; + /* END OF KEY_CACHE parts */ + + ulong net_big_packet_count; + ulong opened_tables; + ulong select_full_join_count; + ulong select_full_range_join_count; + ulong select_range_count; + ulong select_range_check_count; + ulong select_scan_count; + ulong long_query_count; + ulong filesort_merge_passes; + ulong filesort_range_count; + ulong filesort_rows; + ulong filesort_scan_count; +} STATUS_VAR; + +/* + This is used for 'show status'. It must be updated to the last ulong + variable in system_status_var +*/ + +#define last_system_status_var filesort_scan_count + + void free_tmp_table(THD *thd, TABLE *entry); @@ -477,6 +532,8 @@ public: inline bool is_stmt_prepare() const { return (int)state < (int)PREPARED; } inline bool is_first_stmt_execute() const { return state == PREPARED; } + inline bool is_conventional() const + { return state == CONVENTIONAL_EXECUTION; } inline gptr alloc(unsigned int size) { return alloc_root(&mem_root,size); } inline gptr calloc(unsigned int size) { @@ -682,6 +739,7 @@ public: struct sockaddr_in remote; // client socket address struct rand_struct rand; // used for authentication struct system_variables variables; // Changeable local variables + struct system_status_var status_var; // Per thread statistic vars pthread_mutex_t LOCK_delete; // Locked before thd is deleted /* Note that (A) if we set query = NULL, we must at the same time set @@ -1073,6 +1131,7 @@ public: { my_error(killed_errno(), MYF(0)); } + void set_status_var_init(); }; /* Flags for the THD::system_thread (bitmap) variable */ @@ -1556,3 +1615,7 @@ public: bool send_eof(); void cleanup(); }; + +/* Functions in sql_class.cc */ + +void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var); diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 030541a0093..f9dba49d2e3 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -26,6 +26,8 @@ #include "mysql_priv.h" #include "ha_innodb.h" #include "sql_select.h" +#include "sp_head.h" +#include "sql_trigger.h" int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SQL_LIST *order, ha_rows limit, ulong options) @@ -160,6 +162,11 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SQL_LIST *order, // thd->net.report_error is tested to disallow delete row on error if (!(select && select->skip_record())&& !thd->net.report_error ) { + + if (table->triggers) + table->triggers->process_triggers(thd, TRG_EVENT_DELETE, + TRG_ACTION_BEFORE); + if (!(error=table->file->delete_row(table->record[0]))) { deleted++; @@ -183,6 +190,10 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SQL_LIST *order, error= 1; break; } + + if (table->triggers) + table->triggers->process_triggers(thd, TRG_EVENT_DELETE, + TRG_ACTION_AFTER); } else table->file->unlock_row(); // Row failed selection, release lock on it @@ -282,8 +293,7 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds) my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "DELETE"); DBUG_RETURN(-1); } - if (find_table_in_global_list(table_list->next_global, - table_list->db, table_list->real_name)) + if (unique_table(table_list, table_list->next_independent())) { my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name); DBUG_RETURN(-1); diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 0df3d617d7f..1443f9f9d5f 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -51,7 +51,11 @@ int mysql_ha_open(THD *thd, TABLE_LIST *tables) { HANDLER_TABLES_HACK(thd); uint counter; + + /* for now HANDLER can be used only for real TABLES */ + tables->required_type= FRMTYPE_TABLE; int err=open_tables(thd, tables, &counter); + HANDLER_TABLES_HACK(thd); if (err) return -1; @@ -249,7 +253,7 @@ int mysql_ha_read(THD *thd, TABLE_LIST *tables, it++; // Skip first NULL field - insert_fields(thd, tables, tables->db, tables->alias, &it, 0); + insert_fields(thd, tables, tables->db, tables->alias, &it, 0, 0); select_limit+=offset_limit; protocol->send_fields(&list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 3508e962c60..c30fdcd805f 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -19,6 +19,8 @@ #include "mysql_priv.h" #include "sql_acl.h" +#include "sp_head.h" +#include "sql_trigger.h" static int check_null_fields(THD *thd,TABLE *entry); #ifndef EMBEDDED_LIBRARY @@ -302,6 +304,12 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, break; } } + + // FIXME: Actually we should do this before check_null_fields. + // Or even go into write_record ? + if (table->triggers) + table->triggers->process_triggers(thd, TRG_EVENT_INSERT, TRG_ACTION_BEFORE); + #ifndef EMBEDDED_LIBRARY if (lock_type == TL_WRITE_DELAYED) { @@ -324,6 +332,9 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, if (error) break; thd->row_count++; + + if (table->triggers) + table->triggers->process_triggers(thd, TRG_EVENT_INSERT, TRG_ACTION_AFTER); } /* @@ -599,8 +610,7 @@ int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, TABLE *table, setup_fields(thd, 0, table_list, update_values, 0, 0, 0)))) DBUG_RETURN(-1); - if (find_table_in_global_list(table_list->next_global, - table_list->db, table_list->real_name)) + if (unique_table(table_list, table_list->next_independent())) { my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name); DBUG_RETURN(-1); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 241e9b863f1..f5382e6df99 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -24,6 +24,11 @@ #include "sp.h" #include "sp_head.h" +/* + We are using pointer to this variable for distinguishing between assignment + to NEW row field (when parsing trigger definition) and structured variable. +*/ +sys_var_long_ptr trg_new_row_fake_var(0, 0); /* Fake table list object, pointer to which is used as special value for @@ -139,6 +144,7 @@ void lex_start(THD *thd, uchar *buf,uint length) lex->duplicates= DUP_ERROR; lex->sphead= NULL; lex->spcont= NULL; + lex->trg_table= NULL; extern byte *sp_lex_spfuns_key(const byte *ptr, uint *plen, my_bool first); hash_free(&lex->spfuns); @@ -1795,7 +1801,7 @@ void st_lex::link_first_table_back(TABLE_LIST *first, void st_select_lex::fix_prepare_information(THD *thd, Item **conds) { - if (thd->current_arena->is_stmt_prepare() && first_execution) + if (!thd->current_arena->is_conventional() && first_execution) { first_execution= 0; prep_where= where; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 95c9772bac1..d43b1f81f3d 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -85,6 +85,7 @@ enum enum_sql_command { SQLCOM_SHOW_STATUS_PROC, SQLCOM_SHOW_STATUS_FUNC, SQLCOM_PREPARE, SQLCOM_EXECUTE, SQLCOM_DEALLOCATE_PREPARE, SQLCOM_CREATE_VIEW, SQLCOM_DROP_VIEW, + SQLCOM_CREATE_TRIGGER, SQLCOM_DROP_TRIGGER, /* This should be the last !!! */ SQLCOM_END }; @@ -602,6 +603,15 @@ struct st_sp_chistics bool detistic; }; + +struct st_trg_chistics +{ + enum trg_action_time_type action_time; + enum trg_event_type event; +}; + +extern sys_var_long_ptr trg_new_row_fake_var; + /* The state of the lex parsing. This is saved in the THD struct */ typedef struct st_lex @@ -633,7 +643,11 @@ typedef struct st_lex THD *thd; CHARSET_INFO *charset; TABLE_LIST *query_tables; /* global list of all tables in this query */ - /* last element next_global of previous list */ + /* + last element next_global of previous list (used only for list building + during parsing and VIEW processing. This pointer is not valid in + mysql_execute_command + */ TABLE_LIST **query_tables_last; TABLE_LIST *proc_table; /* refer to mysql.proc if it was opened by VIEW */ @@ -713,6 +727,14 @@ typedef struct st_lex rexecuton */ bool empty_field_list_on_rset; + /* Characterstics of trigger being created */ + st_trg_chistics trg_chistics; + /* + Points to table being opened when we are parsing trigger definition + while opening table. 0 if we are parsing user provided CREATE TRIGGER + or any other statement. Used for NEW/OLD row field lookup in trigger. + */ + TABLE *trg_table; st_lex() :result(0) { @@ -747,6 +769,11 @@ typedef struct st_lex TABLE_LIST *unlink_first_table(bool *link_to_local); void link_first_table_back(TABLE_LIST *first, bool link_to_local); void first_lists_tables_same(); + inline void add_to_query_tables(TABLE_LIST *table) + { + *(table->prev_global= query_tables_last)= table; + query_tables_last= &table->next_global; + } bool can_be_merged(); bool can_use_merged(); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 1b4769d747e..156897682b3 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1327,6 +1327,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, this so that they will not get logged to the slow query log */ thd->slow_command=FALSE; + thd->lex->sql_command= SQLCOM_END; /* to avoid confusing VIEW detectors */ thd->set_time(); VOID(pthread_mutex_lock(&LOCK_thread_count)); thd->query_id=query_id; @@ -1342,7 +1343,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, case COM_INIT_DB: { LEX_STRING tmp; - statistic_increment(com_stat[SQLCOM_CHANGE_DB],&LOCK_status); + statistic_increment(thd->status_var.com_stat[SQLCOM_CHANGE_DB], + &LOCK_status); thd->convert_string(&tmp, system_charset_info, packet, strlen(packet), thd->charset()); if (!mysql_change_db(thd, tmp.str)) @@ -1363,7 +1365,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, uint db_len= *(uchar*) packet; uint tbl_len= *(uchar*) (packet + db_len + 1); - statistic_increment(com_other, &LOCK_status); + statistic_increment(thd->status_var.com_other, &LOCK_status); thd->slow_command= TRUE; db= thd->alloc(db_len + tbl_len + 2); tbl_name= strmake(db, packet + 1, db_len)+1; @@ -1377,7 +1379,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, thd->change_user(); thd->clear_error(); // if errors from rollback - statistic_increment(com_other, &LOCK_status); + statistic_increment(thd->status_var.com_other, &LOCK_status); char *user= (char*) packet; char *passwd= strend(user)+1; /* @@ -1544,7 +1546,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, TABLE_LIST table_list; LEX_STRING conv_name; - statistic_increment(com_stat[SQLCOM_SHOW_FIELDS],&LOCK_status); + statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_FIELDS], + &LOCK_status); bzero((char*) &table_list,sizeof(table_list)); if (!(table_list.db=thd->db)) { @@ -1570,7 +1573,17 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (grant_option && check_grant(thd, SELECT_ACL, &table_list, 2, UINT_MAX, 0)) break; + /* switch on VIEW optimisation: do not fill temporary tables */ + thd->lex->sql_command= SQLCOM_SHOW_FIELDS; + /* init structures for VIEW processing */ + table_list.select_lex= &(thd->lex->select_lex); + mysql_init_query(thd, (uchar*)"", 0); + thd->lex-> + select_lex.table_list.link_in_list((byte*) &table_list, + (byte**) &table_list.next_local); + mysqld_list_fields(thd,&table_list,fields); + thd->lex->unit.cleanup(); thd->cleanup_after_query(); break; } @@ -1586,7 +1599,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, { char *db=thd->strdup(packet), *alias; - statistic_increment(com_stat[SQLCOM_CREATE_DB],&LOCK_status); + statistic_increment(thd->status_var.com_stat[SQLCOM_CREATE_DB], + &LOCK_status); // null test to handle EOM if (!db || !(alias= thd->strdup(db)) || check_db_name(db)) { @@ -1601,7 +1615,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, } case COM_DROP_DB: // QQ: To be removed { - statistic_increment(com_stat[SQLCOM_DROP_DB],&LOCK_status); + statistic_increment(thd->status_var.com_stat[SQLCOM_DROP_DB], + &LOCK_status); char *db=thd->strdup(packet), *alias; /* null test to handle EOM */ if (!db || !(alias= thd->strdup(db)) || check_db_name(db)) @@ -1623,7 +1638,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, #ifndef EMBEDDED_LIBRARY case COM_BINLOG_DUMP: { - statistic_increment(com_other,&LOCK_status); + statistic_increment(thd->status_var.com_other,&LOCK_status); thd->slow_command = TRUE; if (check_global_access(thd, REPL_SLAVE_ACL)) break; @@ -1649,7 +1664,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, #endif case COM_REFRESH: { - statistic_increment(com_stat[SQLCOM_FLUSH],&LOCK_status); + statistic_increment(thd->status_var.com_stat[SQLCOM_FLUSH], + &LOCK_status); ulong options= (ulong) (uchar) packet[0]; if (check_global_access(thd,RELOAD_ACL)) break; @@ -1663,7 +1679,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, #ifndef EMBEDDED_LIBRARY case COM_SHUTDOWN: { - statistic_increment(com_other,&LOCK_status); + statistic_increment(thd->status_var.com_other, &LOCK_status); if (check_global_access(thd,SHUTDOWN_ACL)) break; /* purecov: inspected */ /* @@ -1702,7 +1718,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, case COM_STATISTICS: { mysql_log.write(thd,command,NullS); - statistic_increment(com_stat[SQLCOM_SHOW_STATUS],&LOCK_status); + statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_STATUS], + &LOCK_status); #ifndef EMBEDDED_LIBRARY char buff[200]; #else @@ -1712,8 +1729,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, sprintf((char*) buff, "Uptime: %ld Threads: %d Questions: %lu Slow queries: %ld Opens: %ld Flush tables: %ld Open tables: %u Queries per second avg: %.3f", uptime, - (int) thread_count,thd->query_id,long_query_count, - opened_tables,refresh_version, cached_tables(), + (int) thread_count,thd->query_id,thd->status_var.long_query_count, + thd->status_var.opened_tables,refresh_version, cached_tables(), uptime ? (float)thd->query_id/(float)uptime : 0); #ifdef SAFEMALLOC if (sf_malloc_cur_memory) // Using SAFEMALLOC @@ -1728,11 +1745,12 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; } case COM_PING: - statistic_increment(com_other,&LOCK_status); + statistic_increment(thd->status_var.com_other, &LOCK_status); send_ok(thd); // Tell client we are alive break; case COM_PROCESS_INFO: - statistic_increment(com_stat[SQLCOM_SHOW_PROCESSLIST],&LOCK_status); + statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_PROCESSLIST], + &LOCK_status); if (!thd->priv_user[0] && check_global_access(thd,PROCESS_ACL)) break; mysql_log.write(thd,command,NullS); @@ -1742,14 +1760,15 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; case COM_PROCESS_KILL: { - statistic_increment(com_stat[SQLCOM_KILL],&LOCK_status); + statistic_increment(thd->status_var.com_stat[SQLCOM_KILL], &LOCK_status); ulong id=(ulong) uint4korr(packet); kill_one_thread(thd,id,false); break; } case COM_SET_OPTION: { - statistic_increment(com_stat[SQLCOM_SET_OPTION], &LOCK_status); + statistic_increment(thd->status_var.com_stat[SQLCOM_SET_OPTION], + &LOCK_status); enum_mysql_set_option command= (enum_mysql_set_option) uint2korr(packet); switch (command) { case MYSQL_OPTION_MULTI_STATEMENTS_ON: @@ -1767,7 +1786,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; } case COM_DEBUG: - statistic_increment(com_other,&LOCK_status); + statistic_increment(thd->status_var.com_other, &LOCK_status); if (check_global_access(thd, SUPER_ACL)) break; /* purecov: inspected */ mysql_print_status(thd); @@ -1806,7 +1825,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, (SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED)) && (specialflag & SPECIAL_LOG_QUERIES_NOT_USING_INDEXES))) { - long_query_count++; + thd->status_var.long_query_count++; mysql_slow_log.write(thd, thd->query, thd->query_length, start_of_query); } } @@ -1983,7 +2002,8 @@ mysql_execute_command(THD *thd) DBUG_RETURN(-1); } - statistic_increment(com_stat[lex->sql_command],&LOCK_status); + statistic_increment(thd->status_var.com_stat[lex->sql_command], + &LOCK_status); switch (lex->sql_command) { case SQLCOM_SELECT: { @@ -2392,31 +2412,6 @@ mysql_execute_command(THD *thd) if (select_lex->item_list.elements) // With select { select_result *result; - /* - Is table which we are changing used somewhere in other parts - of query - */ - if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) && - find_table_in_global_list(select_tables, create_table->db, - create_table->real_name)) - { - net_printf(thd, ER_UPDATE_TABLE_USED, create_table->real_name); - goto create_error; - } - if (lex->create_info.used_fields & HA_CREATE_USED_UNION) - { - TABLE_LIST *tab; - for (tab= select_tables; tab; tab= tab->next_local) - { - if (find_table_in_local_list((TABLE_LIST*) lex->create_info. - merge_list.first, - select_tables->db, tab->real_name)) - { - net_printf(thd, ER_UPDATE_TABLE_USED, tab->real_name); - goto create_error; - } - } - } if (select_tables && check_table_access(thd, SELECT_ACL, select_tables, 0)) @@ -2426,6 +2421,32 @@ mysql_execute_command(THD *thd) if (!(res= open_and_lock_tables(thd, select_tables))) { + /* + Is table which we are changing used somewhere in other parts + of query + */ + if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) && + unique_table(create_table, select_tables)) + { + net_printf(thd, ER_UPDATE_TABLE_USED, create_table->real_name); + goto create_error; + } + /* If we create merge table, we have to test tables in merge, too */ + if (lex->create_info.used_fields & HA_CREATE_USED_UNION) + { + TABLE_LIST *tab; + for (tab= (TABLE_LIST*) lex->create_info.merge_list.first; + tab; + tab= tab->next_local) + { + if (unique_table(tab, select_tables)) + { + net_printf(thd, ER_UPDATE_TABLE_USED, tab->real_name); + goto create_error; + } + } + } + if ((result= new select_create(create_table, &lex->create_info, lex->create_list, @@ -2781,30 +2802,33 @@ unsent_create_error: select_result *result; unit->set_limit(select_lex, select_lex); - // is table which we are changing used somewhere in other parts of query - if (find_table_in_global_list(all_tables->next_global, - first_table->db, first_table->real_name)) - { - /* Using same table for INSERT and SELECT */ - select_lex->options |= OPTION_BUFFER_RESULT; - } - - if (!(res= open_and_lock_tables(thd, all_tables)) && - !(res= mysql_insert_select_prepare(thd)) && - (result= new select_insert(first_table, first_table->table, - &lex->field_list, lex->duplicates))) + if (!(res= open_and_lock_tables(thd, all_tables))) { - /* Skip first table, which is the table we are inserting in */ - lex->select_lex.table_list.first= (byte*) first_table->next_local; /* - insert/replace from SELECT give its SELECT_LEX for SELECT, - and item_list belong to SELECT + Is table which we are changing used somewhere in other parts of + query */ - lex->select_lex.resolve_mode= SELECT_LEX::SELECT_MODE; - res= handle_select(thd, lex, result); - lex->select_lex.table_list.first= (byte*) first_table; - lex->select_lex.resolve_mode= SELECT_LEX::INSERT_MODE; - delete result; + if (unique_table(first_table, all_tables->next_independent())) + { + /* Using same table for INSERT and SELECT */ + select_lex->options |= OPTION_BUFFER_RESULT; + } + + if ((res= mysql_insert_select_prepare(thd))) + break; + if ((result= new select_insert(first_table, first_table->table, + &lex->field_list, lex->duplicates))) + /* Skip first table, which is the table we are inserting in */ + lex->select_lex.table_list.first= (byte*) first_table->next_local; + /* + insert/replace from SELECT give its SELECT_LEX for SELECT, + and item_list belong to SELECT + */ + lex->select_lex.resolve_mode= SELECT_LEX::SELECT_MODE; + res= handle_select(thd, lex, result); + /* revert changes for SP */ + lex->select_lex.table_list.first= (byte*) first_table; + lex->select_lex.resolve_mode= SELECT_LEX::INSERT_MODE; if (thd->net.report_error) res= -1; } @@ -2960,13 +2984,23 @@ unsent_create_error: res= mysqld_show_column_types(thd); break; case SQLCOM_SHOW_STATUS: - res= mysqld_show(thd,(lex->wild ? lex->wild->ptr() : NullS),status_vars, - OPT_GLOBAL, &LOCK_status); + STATUS_VAR tmp; + if (lex->option_type == OPT_GLOBAL) + { + pthread_mutex_lock(&LOCK_status); + calc_sum_of_all_status(&tmp); + } + res= mysqld_show(thd, (lex->wild ? lex->wild->ptr() : NullS), + status_vars, OPT_GLOBAL, &LOCK_status, + (lex->option_type == OPT_GLOBAL ? + &tmp: &thd->status_var)); + if (lex->option_type == OPT_GLOBAL) + pthread_mutex_unlock(&LOCK_status); break; case SQLCOM_SHOW_VARIABLES: res= mysqld_show(thd, (lex->wild ? lex->wild->ptr() : NullS), init_vars, lex->option_type, - &LOCK_global_system_variables); + &LOCK_global_system_variables, 0); break; case SQLCOM_SHOW_LOGS: #ifdef DONT_ALLOW_SHOW_COMMANDS @@ -3861,6 +3895,20 @@ purposes internal to the MySQL server", MYF(0)); res= mysql_drop_view(thd, first_table, thd->lex->drop_mode); break; } + case SQLCOM_CREATE_TRIGGER: + { + /* We don't care much about trigger body at that point */ + delete lex->sphead; + lex->sphead= 0; + + res= mysql_create_or_drop_trigger(thd, all_tables, 1); + break; + } + case SQLCOM_DROP_TRIGGER: + { + res= mysql_create_or_drop_trigger(thd, all_tables, 0); + break; + } default: /* Impossible */ send_ok(thd); break; @@ -5125,8 +5173,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, /* Link table in local list (list for current select) */ table_list.link_in_list((byte*) ptr, (byte**) &ptr->next_local); /* Link table in global list (all used tables) */ - *(ptr->prev_global= lex->query_tables_last)= ptr; - lex->query_tables_last= &ptr->next_global; + lex->add_to_query_tables(ptr); DBUG_RETURN(ptr); } @@ -5135,9 +5182,9 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, Initialize a new table list for a nested join SYNOPSIS - init_table_list() + init_table_list() thd current thread - + DESCRIPTION The function initializes a structure of the TABLE_LIST type for a nested join. It sets up its nested join list as empty. @@ -5157,7 +5204,7 @@ bool st_select_lex::init_nested_join(THD *thd) TABLE_LIST *ptr; NESTED_JOIN *nested_join; DBUG_ENTER("init_nested_join"); - + if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))) || !(nested_join= ptr->nested_join= (NESTED_JOIN *) thd->calloc(sizeof(NESTED_JOIN)))) @@ -5182,7 +5229,7 @@ bool st_select_lex::init_nested_join(THD *thd) DESCRIPTION The function returns to the previous join nest level. If the current level contains only one member, the function - moves it one level up, eliminating the nest. + moves it one level up, eliminating the nest. RETURN VALUE Pointer to TABLE_LIST element added to the total table list, if success @@ -5214,7 +5261,7 @@ TABLE_LIST *st_select_lex::end_nested_join(THD *thd) Nest last join operation SYNOPSIS - nest_last_join() + nest_last_join() thd current thread DESCRIPTION @@ -5230,7 +5277,7 @@ TABLE_LIST *st_select_lex::nest_last_join(THD *thd) TABLE_LIST *ptr; NESTED_JOIN *nested_join; DBUG_ENTER("nest_last_join"); - + if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))) || !(nested_join= ptr->nested_join= (NESTED_JOIN *) thd->calloc(sizeof(NESTED_JOIN)))) @@ -5254,7 +5301,7 @@ TABLE_LIST *st_select_lex::nest_last_join(THD *thd) /* Save names for a join with using clase - + SYNOPSIS save_names_for_using_list tab1 left table in join @@ -5262,11 +5309,11 @@ TABLE_LIST *st_select_lex::nest_last_join(THD *thd) DESCRIPTION The function saves the full names of the tables in st_select_lex - to be able to build later an on expression to replace the using clause. - + to be able to build later an on expression to replace the using clause. + RETURN VALUE - None -*/ + None +*/ void st_select_lex::save_names_for_using_list(TABLE_LIST *tab1, TABLE_LIST *tab2) @@ -5288,7 +5335,7 @@ void st_select_lex::save_names_for_using_list(TABLE_LIST *tab1, db2= tab2->db; table2= tab2->alias; } - + /* Add a table to the current join list @@ -5323,9 +5370,9 @@ void st_select_lex::add_joined_table(TABLE_LIST *table) SYNOPSIS convert_right_join() thd current thread - - DESCRIPTION - The function takes the current join list t[0],t[1] ... and + + DESCRIPTION + The function takes the current join list t[0],t[1] ... and effectively converts it into the list t[1],t[0] ... Although the outer_join flag for the new nested table contains JOIN_TYPE_RIGHT, it will be handled as the inner table of a left join @@ -5349,10 +5396,10 @@ void st_select_lex::add_joined_table(TABLE_LIST *table) 0, otherwise */ -TABLE_LIST *st_select_lex::convert_right_join() +TABLE_LIST *st_select_lex::convert_right_join() { TABLE_LIST *tab2= join_list->pop(); - TABLE_LIST *tab1= join_list->pop(); + TABLE_LIST *tab1= join_list->pop(); DBUG_ENTER("convert_right_join"); join_list->push_front(tab2); @@ -5416,7 +5463,7 @@ void add_join_on(TABLE_LIST *b,Item *expr) add_join_natural() a Table to do normal join with b Do normal join with this table - + IMPLEMENTATION This function just marks that table b should be joined with a. The function setup_cond() will create in b->on_expr a list @@ -5637,6 +5684,13 @@ static void refresh_status(void) (char*) &dflt_key_cache_var)); *(ulong*) value= 0; } + else if (ptr->type == SHOW_LONG_STATUS) + { + THD *thd= current_thd; + /* We must update the global status before cleaning up the thread */ + add_to_status(&global_status_var, &thd->status_var); + bzero((char*) &thd->status_var, sizeof(thd->status_var)); + } } pthread_mutex_unlock(&LOCK_status); } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index def641d9616..cd2eb7c1516 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -232,8 +232,6 @@ int handle_select(THD *thd, LEX *lex, select_result *result) result->abort(); res= 1; // Error sent to client } - if (result != lex->result) - delete result; DBUG_RETURN(res); } @@ -5145,6 +5143,7 @@ static void make_join_readinfo(JOIN *join, uint options) { uint i; + bool statistics= test(!(join->select_options & SELECT_DESCRIBE)); DBUG_ENTER("make_join_readinfo"); @@ -5243,7 +5242,8 @@ make_join_readinfo(JOIN *join, uint options) join->thd->server_status|=SERVER_QUERY_NO_GOOD_INDEX_USED; tab->read_first_record= join_init_quick_read_record; if (statistics) - statistic_increment(select_range_check_count, &LOCK_status); + statistic_increment(join->thd->status_var.select_range_check_count, + &LOCK_status); } else { @@ -5253,13 +5253,15 @@ make_join_readinfo(JOIN *join, uint options) if (tab->select && tab->select->quick) { if (statistics) - statistic_increment(select_range_count, &LOCK_status); + statistic_increment(join->thd->status_var.select_range_count, + &LOCK_status); } else { join->thd->server_status|=SERVER_QUERY_NO_INDEX_USED; if (statistics) - statistic_increment(select_scan_count, &LOCK_status); + statistic_increment(join->thd->status_var.select_scan_count, + &LOCK_status); } } else @@ -5267,13 +5269,15 @@ make_join_readinfo(JOIN *join, uint options) if (tab->select && tab->select->quick) { if (statistics) - statistic_increment(select_full_range_join_count, &LOCK_status); + statistic_increment(join->thd->status_var.select_full_range_join_count, + &LOCK_status); } else { join->thd->server_status|=SERVER_QUERY_NO_INDEX_USED; if (statistics) - statistic_increment(select_full_join_count, &LOCK_status); + statistic_increment(join->thd->status_var.select_full_join_count, + &LOCK_status); } } if (!table->no_keyread) @@ -6156,16 +6160,21 @@ optimize_cond(JOIN *join, COND *conds, Item::cond_result *cond_value) MEMROOT for prepared statements and stored procedures. */ - Item_arena *arena=thd->current_arena, backup; - select->first_cond_optimization= 0; + Item_arena *arena= thd->current_arena, backup; + if (arena->is_conventional()) + arena= 0; // For easier test + else + thd->set_n_backup_item_arena(arena, &backup); - thd->set_n_backup_item_arena(arena, &backup); + select->first_cond_optimization= 0; /* Convert all outer joins to inner joins if possible */ conds= simplify_joins(join, join->join_list, conds, TRUE); select->prep_where= conds ? conds->copy_andor_structure(thd) : 0; - thd->restore_backup_item_arena(arena, &backup); + + if (arena) + thd->restore_backup_item_arena(arena, &backup); } if (!conds) @@ -6649,7 +6658,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, (int) distinct, (int) save_sum_fields, (ulong) rows_limit,test(group))); - statistic_increment(created_tmp_tables, &LOCK_status); + statistic_increment(thd->status_var.created_tmp_tables, &LOCK_status); if (use_temp_pool) temp_pool_slot = bitmap_set_next(&temp_pool); @@ -7235,7 +7244,8 @@ static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param, table->db_stat=0; goto err; } - statistic_increment(created_tmp_disk_tables, &LOCK_status); + statistic_increment(table->in_use->status_var.created_tmp_disk_tables, + &LOCK_status); table->db_record_offset=1; DBUG_RETURN(0); err: diff --git a/sql/sql_show.cc b/sql/sql_show.cc index c31eb4f147f..1209be6ac1d 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1140,14 +1140,19 @@ void mysqld_list_fields(THD *thd, TABLE_LIST *table_list, const char *wild) { TABLE *table; + int res; DBUG_ENTER("mysqld_list_fields"); DBUG_PRINT("enter",("table: %s",table_list->real_name)); - if (!(table = open_ltable(thd, table_list, TL_UNLOCK))) + table_list->lock_type= TL_UNLOCK; + if ((res= open_and_lock_tables(thd, table_list))) { - send_error(thd); + if (res < 0) + send_error(thd); DBUG_VOID_RETURN; } + table= table_list->table; + List<Item> field_list; Field **ptr,*field; @@ -1596,7 +1601,7 @@ view_store_create_info(THD *thd, TABLE_LIST *table, String *buff) { buff->append("ALGORITHM=", 10); if (table->algorithm == VIEW_ALGORITHM_TMPTABLE) - buff->append("TMPTABLE ", 9); + buff->append("TEMPTABLE ", 10); else buff->append("MERGE ", 6); } @@ -1869,7 +1874,8 @@ err: int mysqld_show(THD *thd, const char *wild, show_var_st *variables, enum enum_var_type value_type, - pthread_mutex_t *mutex) + pthread_mutex_t *mutex, + struct system_status_var *status_var) { char buff[1024]; List<Item> field_list; @@ -1907,6 +1913,10 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables, pos= end= buff; switch (show_type) { + case SHOW_LONG_STATUS: + case SHOW_LONG_CONST_STATUS: + value= ((char *) status_var + (uint) value); + /* fall through */ case SHOW_LONG: case SHOW_LONG_CONST: end= int10_to_str(*(long*) value, buff, 10); @@ -2176,6 +2186,31 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables, DBUG_RETURN(1); } + +/* collect status for all running threads */ + +void calc_sum_of_all_status(STATUS_VAR *to) +{ + DBUG_ENTER("calc_sum_of_all_status"); + + /* Ensure that thread id not killed during loop */ + VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list + + I_List_iterator<THD> it(threads); + THD *tmp; + + /* Get global values as base */ + *to= global_status_var; + + /* Add to this status from existing threads */ + while ((tmp= it++)) + add_to_status(to, &tmp->status_var); + + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + DBUG_VOID_RETURN; +} + + #ifdef __GNUC__ template class List_iterator_fast<char>; template class List<char>; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 08cb90d2824..65be24ae537 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1517,7 +1517,7 @@ static void wait_while_table_is_used(THD *thd,TABLE *table, Win32 clients must also have a WRITE LOCK on the table ! */ -static bool close_cached_table(THD *thd, TABLE *table) +void close_cached_table(THD *thd, TABLE *table) { DBUG_ENTER("close_cached_table"); @@ -1533,7 +1533,6 @@ static bool close_cached_table(THD *thd, TABLE *table) /* When lock on LOCK_open is freed other threads can continue */ pthread_cond_broadcast(&COND_refresh); - DBUG_RETURN(0); } static int send_check_errmsg(THD *thd, TABLE_LIST* table, @@ -3140,12 +3139,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, close the original table at before doing the rename */ table_name=thd->strdup(table_name); // must be saved - if (close_cached_table(thd, table)) - { // Aborted - VOID(quick_rm_table(new_db_type,new_db,tmp_name)); - VOID(pthread_mutex_unlock(&LOCK_open)); - goto err; - } + close_cached_table(thd, table); table=0; // Marker that table is closed } #if (!defined( __WIN__) && !defined( __EMX__) && !defined( OS2)) diff --git a/sql/sql_test.cc b/sql/sql_test.cc index 5c467402497..6cffa9df2c6 100644 --- a/sql/sql_test.cc +++ b/sql/sql_test.cc @@ -468,16 +468,20 @@ read_first: %10lu\n\ write: %10lu\n\ delete %10lu\n\ update: %10lu\n", - ha_read_key_count, ha_read_next_count, - ha_read_rnd_count, ha_read_first_count, - ha_write_count, ha_delete_count, ha_update_count); + thd->status_var.ha_read_key_count, + thd->status_var.ha_read_next_count, + thd->status_var.ha_read_rnd_count, + thd->status_var.ha_read_first_count, + thd->status_var.ha_write_count, + thd->status_var.ha_delete_count, + thd->status_var.ha_update_count); pthread_mutex_unlock(&LOCK_status); printf("\nTable status:\n\ Opened tables: %10lu\n\ Open tables: %10lu\n\ Open files: %10lu\n\ Open streams: %10lu\n", - opened_tables, + thd->status_var.opened_tables, (ulong) cached_tables(), (ulong) my_file_opened, (ulong) my_stream_opened); diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc new file mode 100644 index 00000000000..c376e86f18c --- /dev/null +++ b/sql/sql_trigger.cc @@ -0,0 +1,433 @@ +#include "mysql_priv.h" +#include "sp_head.h" +#include "sql_trigger.h" +#include "parse_file.h" + + +static const LEX_STRING triggers_file_type= {(char *)"TRIGGERS", 8}; +static const char * const triggers_file_ext= ".TRG"; + +/* + Table of .TRG file field descriptors. + We have here only one field now because in nearest future .TRG + files will be merged into .FRM files (so we don't need something + like md5 or created fields). +*/ +static File_option triggers_file_parameters[]= +{ + {{(char*)"triggers", 8}, offsetof(Table_triggers_list, definitions_list), + FILE_OPTIONS_STRLIST}, + {{NULL, 0}, 0, FILE_OPTIONS_STRING} +}; + + +/* + Create or drop trigger for table. + + SYNOPSIS + mysql_create_or_drop_trigger() + thd - current thread context (including trigger definition in LEX) + tables - table list containing one table for which trigger is created. + create - whenever we create (true) or drop (false) trigger + + NOTE + This function is mainly responsible for opening and locking of table and + invalidation of all its instances in table cache after trigger creation. + Real work on trigger creation/dropping is done inside Table_triggers_list + methods. + + RETURN VALUE + 0 - Success, non-0 in case of error. +*/ +int mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) +{ + TABLE *table; + int result= 0; + + DBUG_ENTER("mysql_create_or_drop_trigger"); + + /* + QQ: This function could be merged in mysql_alter_table() function + But do we want this ? + */ + + if (open_and_lock_tables(thd, tables)) + DBUG_RETURN(-1); + + // TODO: We should check if user has TRIGGER privilege for table here. + + table= tables->table; + + /* + We do not allow creation of triggers on views or temporary tables. + We have to do this check here and not in + Table_triggers_list::create_trigger() because we want to avoid messing + with table cash for views and temporary tables. + */ + if (tables->view || table->tmp_table != NO_TMP_TABLE) + { + my_error(ER_TRG_ON_VIEW_OR_TEMP_TABLE, MYF(0), tables->alias); + DBUG_RETURN(-1); + } + + if (!table->triggers) + { + if (!create) + { + my_error(ER_TRG_DOES_NOT_EXIST, MYF(0)); + DBUG_RETURN(-1); + } + + if (!(table->triggers= new (&table->mem_root) Table_triggers_list())) + DBUG_RETURN(-1); + } + + /* + We don't want perform our operations while global read lock is held + so we have to wait until its end and then prevent it from occuring + again until we are done. (Acquiring LOCK_open is not enough because + global read lock is held without helding LOCK_open). + */ + if (wait_if_global_read_lock(thd, 0, 0)) + DBUG_RETURN(-1); + + VOID(pthread_mutex_lock(&LOCK_open)); + if ((create ? table->triggers->create_trigger(thd, tables): + table->triggers->drop_trigger(thd, tables))) + result= -1; + + /* It is sensible to invalidate table in any case */ + close_cached_table(thd, table); + VOID(pthread_mutex_unlock(&LOCK_open)); + start_waiting_global_read_lock(thd); + + if (!result) + send_ok(thd); + + DBUG_RETURN(result); +} + + +/* + Create trigger for table. + + SYNOPSIS + create_trigger() + thd - current thread context (including trigger definition in LEX) + tables - table list containing one open table for which trigger is + created. + + RETURN VALUE + False - success + True - error +*/ +bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables) +{ + LEX *lex= thd->lex; + TABLE *table= tables->table; + char dir_buff[FN_REFLEN], file_buff[FN_REFLEN]; + LEX_STRING dir, file; + MEM_ROOT *old_global_root; + LEX_STRING *trg_def, *name; + List_iterator_fast<LEX_STRING> it(names_list); + + /* We don't allow creation of several triggers of the same type yet */ + if (bodies[lex->trg_chistics.event][lex->trg_chistics.action_time]) + { + my_error(ER_TRG_ALREADY_EXISTS, MYF(0)); + return 1; + } + + /* Let us check if trigger with the same name exists */ + while ((name= it++)) + { + if (my_strcasecmp(system_charset_info, lex->name_and_length.str, + name->str) == 0) + { + my_error(ER_TRG_ALREADY_EXISTS, MYF(0)); + return 1; + } + } + + /* + Here we are creating file with triggers and save all triggers in it. + sql_create_definition_file() files handles renaming and backup of older + versions + */ + strxnmov(dir_buff, FN_REFLEN, mysql_data_home, "/", tables->db, "/", NullS); + dir.length= unpack_filename(dir_buff, dir_buff); + dir.str= dir_buff; + file.length= strxnmov(file_buff, FN_REFLEN, tables->real_name, + triggers_file_ext, NullS) - file_buff; + file.str= file_buff; + + old_global_root= my_pthread_getspecific_ptr(MEM_ROOT*, THR_MALLOC); + my_pthread_setspecific_ptr(THR_MALLOC, &table->mem_root); + + /* + Soon we will invalidate table object and thus Table_triggers_list object + so don't care about place to which trg_def->ptr points and other + invariants (e.g. we don't bother to update names_list) + + QQ: Hmm... probably we should not care about setting up active thread + mem_root too. + */ + if (!(trg_def= (LEX_STRING *)alloc_root(&table->mem_root, + sizeof(LEX_STRING))) || + definitions_list.push_back(trg_def)) + { + my_pthread_setspecific_ptr(THR_MALLOC, old_global_root); + return 1; + } + + trg_def->str= thd->query; + trg_def->length= thd->query_length; + + my_pthread_setspecific_ptr(THR_MALLOC, old_global_root); + + return sql_create_definition_file(&dir, &file, &triggers_file_type, + (gptr)this, triggers_file_parameters, 3); +} + + +/* + Drop trigger for table. + + SYNOPSIS + drop_trigger() + thd - current thread context (including trigger definition in LEX) + tables - table list containing one open table for which trigger is + dropped. + + RETURN VALUE + False - success + True - error +*/ +bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables) +{ + LEX *lex= thd->lex; + LEX_STRING *name; + List_iterator_fast<LEX_STRING> it_name(names_list); + List_iterator<LEX_STRING> it_def(definitions_list); + + while ((name= it_name++)) + { + it_def++; + + if (my_strcasecmp(system_charset_info, lex->name_and_length.str, + name->str) == 0) + { + /* + Again we don't care much about other things required for + clean trigger removing since table will be reopened anyway. + */ + it_def.remove(); + + if (definitions_list.is_empty()) + { + char path[FN_REFLEN]; + + /* + TODO: Probably instead of removing .TRG file we should move + to archive directory but this should be done as part of + parse_file.cc functionality (because we will need it + elsewhere). + */ + strxnmov(path, FN_REFLEN, mysql_data_home, "/", tables->db, "/", + tables->real_name, triggers_file_ext, NullS); + unpack_filename(path, path); + return my_delete(path, MYF(MY_WME)); + } + else + { + char dir_buff[FN_REFLEN], file_buff[FN_REFLEN]; + LEX_STRING dir, file; + + strxnmov(dir_buff, FN_REFLEN, mysql_data_home, "/", tables->db, + "/", NullS); + dir.length= unpack_filename(dir_buff, dir_buff); + dir.str= dir_buff; + file.length= strxnmov(file_buff, FN_REFLEN, tables->real_name, + triggers_file_ext, NullS) - file_buff; + file.str= file_buff; + + return sql_create_definition_file(&dir, &file, &triggers_file_type, + (gptr)this, + triggers_file_parameters, 3); + } + } + } + + my_error(ER_TRG_DOES_NOT_EXIST, MYF(0)); + return 1; +} + + +Table_triggers_list::~Table_triggers_list() +{ + for (int i= 0; i < 3; i++) + for (int j= 0; j < 2; j++) + delete bodies[i][j]; + + if (old_field) + for (Field **fld_ptr= old_field; *fld_ptr; fld_ptr++) + delete *fld_ptr; +} + + +/* + Check whenever .TRG file for table exist and load all triggers it contains. + + SYNOPSIS + check_n_load() + thd - current thread context + db - table's database name + table_name - table's name + table - pointer to table object + + RETURN VALUE + False - success + True - error +*/ +bool Table_triggers_list::check_n_load(THD *thd, const char *db, + const char *table_name, TABLE *table) +{ + char path_buff[FN_REFLEN]; + LEX_STRING path; + File_parser *parser; + MEM_ROOT *old_global_mem_root; + + DBUG_ENTER("Table_triggers_list::check_n_load"); + + strxnmov(path_buff, FN_REFLEN, mysql_data_home, "/", db, "/", table_name, + triggers_file_ext, NullS); + path.length= unpack_filename(path_buff, path_buff); + path.str= path_buff; + + // QQ: should we analyze errno somehow ? + if (access(path_buff, F_OK)) + DBUG_RETURN(0); + + /* + File exists so we got to load triggers + FIXME: A lot of things to do here e.g. how about other funcs and being + more paranoical ? + */ + + if ((parser= sql_parse_prepare(&path, &table->mem_root, 1))) + { + if (!strncmp(triggers_file_type.str, parser->type()->str, + parser->type()->length)) + { + Field **fld, **old_fld; + Table_triggers_list *triggers= + new (&table->mem_root) Table_triggers_list(); + + if (!triggers) + DBUG_RETURN(1); + + if (parser->parse((gptr)triggers, &table->mem_root, + triggers_file_parameters, 1)) + DBUG_RETURN(1); + + table->triggers= triggers; + + /* + We have to prepare array of Field objects which will represent OLD.* + row values by referencing to record[1] instead of record[0] + + TODO: This could be avoided if there is no ON UPDATE trigger. + */ + if (!(triggers->old_field= + (Field **)alloc_root(&table->mem_root, (table->fields + 1) * + sizeof(Field*)))) + DBUG_RETURN(1); + + for (fld= table->field, old_fld= triggers->old_field; *fld; + fld++, old_fld++) + { + /* + QQ: it is supposed that it is ok to use this function for field + cloning... + */ + if (!(*old_fld= (*fld)->new_field(&table->mem_root, table))) + DBUG_RETURN(1); + (*old_fld)->move_field((my_ptrdiff_t)(table->record[1] - + table->record[0])); + } + *old_fld= 0; + + List_iterator_fast<LEX_STRING> it(triggers->definitions_list); + LEX_STRING *trg_create_str, *trg_name_str; + char *trg_name_buff; + LEX *old_lex= thd->lex, lex; + + thd->lex= &lex; + + while ((trg_create_str= it++)) + { + mysql_init_query(thd, (uchar*)trg_create_str->str, + trg_create_str->length, true); + lex.trg_table= table; + if (yyparse((void *)thd) || thd->is_fatal_error) + { + /* + Free lex associated resources + QQ: Do we really need all this stuff here ? + */ + if (lex.sphead) + { + if (&lex != thd->lex) + thd->lex->sphead->restore_lex(thd); + delete lex.sphead; + } + goto err_with_lex_cleanup; + } + + triggers->bodies[lex.trg_chistics.event] + [lex.trg_chistics.action_time]= lex.sphead; + lex.sphead= 0; + + if (!(trg_name_buff= alloc_root(&table->mem_root, + sizeof(LEX_STRING) + + lex.name_and_length.length + 1))) + goto err_with_lex_cleanup; + + trg_name_str= (LEX_STRING *)trg_name_buff; + trg_name_buff+= sizeof(LEX_STRING); + memcpy(trg_name_buff, lex.name_and_length.str, + lex.name_and_length.length + 1); + trg_name_str->str= trg_name_buff; + trg_name_str->length= lex.name_and_length.length; + + old_global_mem_root= my_pthread_getspecific_ptr(MEM_ROOT*, THR_MALLOC); + my_pthread_setspecific_ptr(THR_MALLOC, &table->mem_root); + + if (triggers->names_list.push_back(trg_name_str)) + goto err_with_lex_cleanup; + + my_pthread_setspecific_ptr(THR_MALLOC, old_global_mem_root); + + lex_end(&lex); + } + thd->lex= old_lex; + + DBUG_RETURN(0); + +err_with_lex_cleanup: + // QQ: anything else ? + lex_end(&lex); + thd->lex= old_lex; + DBUG_RETURN(1); + } + + /* + We don't care about this error message much because .TRG files will + be merged into .FRM anyway. + */ + my_error(ER_WRONG_OBJECT, MYF(0), table_name, triggers_file_ext, "TRIGGER"); + DBUG_RETURN(1); + } + + DBUG_RETURN(1); +} diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h new file mode 100644 index 00000000000..8ab2ab003f8 --- /dev/null +++ b/sql/sql_trigger.h @@ -0,0 +1,62 @@ +/* + This class holds all information about triggers of table. + + QQ: Will it be merged into TABLE in future ? +*/ +class Table_triggers_list: public Sql_alloc +{ + /* Triggers as SPs grouped by event, action_time */ + sp_head *bodies[3][2]; + /* + Copy of TABLE::Field array with field pointers set to old version + of record, used for OLD values in trigger on UPDATE. + */ + Field **old_field; + /* + Names of triggers. + Should correspond to order of triggers on definitions_list, + used in CREATE/DROP TRIGGER for looking up trigger by name. + */ + List<LEX_STRING> names_list; + +public: + /* + Field responsible for storing triggers definitions in file. + It have to be public because we are using it directly from parser. + */ + List<LEX_STRING> definitions_list; + + Table_triggers_list(): + old_field(0) + { + bzero((char *)bodies, sizeof(bodies)); + } + ~Table_triggers_list(); + + bool create_trigger(THD *thd, TABLE_LIST *table); + bool drop_trigger(THD *thd, TABLE_LIST *table); + bool process_triggers(THD *thd, trg_event_type event, + trg_action_time_type time_type) + { + int res= 0; + + if (bodies[event][time_type]) + { + /* + Similar to function invocation we don't need to surpress sending of + ok packets here because don't allow execute statements from trigger. + + FIXME: We should juggle with security context here (because trigger + should be invoked with creator rights). + */ + res= bodies[event][time_type]->execute_function(thd, 0, 0, 0); + } + + return res; + } + + static bool check_n_load(THD *thd, const char *db, const char *table_name, + TABLE *table); + + friend class Item_trigger_field; +}; diff --git a/sql/sql_union.cc b/sql/sql_union.cc index d23e11d5443..1df419d04c3 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -293,7 +293,7 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, Field **field; Item_arena *arena= thd->current_arena; Item_arena backup; - if (!arena->is_stmt_prepare()) + if (arena->is_conventional()) arena= 0; else thd->set_n_backup_item_arena(arena, &backup); diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 9d7134aee84..dc867968262 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -23,6 +23,8 @@ #include "mysql_priv.h" #include "sql_acl.h" #include "sql_select.h" +#include "sp_head.h" +#include "sql_trigger.h" static bool safe_update_on_fly(JOIN_TAB *join_tab, List<Item> *fields); @@ -354,6 +356,10 @@ int mysql_update(THD *thd, if (fill_record(fields,values, 0) || thd->net.report_error) break; /* purecov: inspected */ found++; + + if (table->triggers) + table->triggers->process_triggers(thd, TRG_EVENT_UPDATE, TRG_ACTION_BEFORE); + if (compare_record(table, query_id)) { if (!(error=table->file->update_row((byte*) table->record[1], @@ -369,6 +375,10 @@ int mysql_update(THD *thd, break; } } + + if (table->triggers) + table->triggers->process_triggers(thd, TRG_EVENT_UPDATE, TRG_ACTION_AFTER); + if (!--limit && using_limit) { error= -1; // Simulate end of file @@ -496,8 +506,7 @@ int mysql_prepare_update(THD *thd, TABLE_LIST *table_list, DBUG_RETURN(-1); /* Check that we are not using table that we are updating in a sub select */ - if (find_table_in_global_list(table_list->next_global, - table_list->db, table_list->real_name)) + if (unique_table(table_list, table_list->next_independent())) { my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name); DBUG_RETURN(-1); @@ -788,7 +797,7 @@ int multi_update::prepare(List<Item> ¬_used_values, { TABLE *table=table_ref->table; if (!(tables_to_update & table->map) && - find_table_in_global_list(update_tables, table_ref->db, + find_table_in_local_list(update_tables, table_ref->db, table_ref->real_name)) table->no_cache= 1; // Disable row cache } diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 15f38f685dc..2364be228f8 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -45,6 +45,7 @@ TYPELIB sql_updatable_view_key_typelib= -1 Error 1 Error and error message given */ + int mysql_create_view(THD *thd, enum_view_create_mode mode) { @@ -356,6 +357,7 @@ static LEX_STRING view_file_type[]= {{(char*)"VIEW", 4}}; -1 Error 1 Error and error message given */ + static int mysql_register_view(THD *thd, TABLE_LIST *view, enum_view_create_mode mode) { @@ -423,7 +425,7 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, /* read revision number - + TODO: read dependense list, too, to process cascade/restrict TODO: special cascade/restrict procedure for alter? */ @@ -501,7 +503,6 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, my_bool mysql_make_view(File_parser *parser, TABLE_LIST *table) { - bool include_proc_table= 0; DBUG_ENTER("mysql_make_view"); if (table->view) @@ -512,7 +513,6 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) DBUG_RETURN(0); } - TABLE_LIST *old_next, *tbl_end, *tbl_next; SELECT_LEX *end; THD *thd= current_thd; LEX *old_lex= thd->lex, *lex; @@ -523,7 +523,7 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) will be TRUE as far as we make new table cache). */ Item_arena *arena= thd->current_arena, backup; - if (!arena->is_stmt_prepare()) + if (arena->is_conventional()) arena= 0; else thd->set_n_backup_item_arena(arena, &backup); @@ -599,11 +599,14 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) TABLE_LIST *top_view= (table->belong_to_view ? table->belong_to_view : table); + TABLE_LIST *view_tables= lex->query_tables; + TABLE_LIST *view_tables_tail= 0; if (lex->spfuns.records) { /* move SP to main LEX */ sp_merge_funs(old_lex, lex); + /* open mysq.proc for functions which are not in cache */ if (old_lex->proc_table == 0 && (old_lex->proc_table= (TABLE_LIST*)thd->calloc(sizeof(TABLE_LIST))) != 0) @@ -614,18 +617,20 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) table->real_name= table->alias= (char*)"proc"; table->real_name_length= 4; table->cacheable_table= 1; - include_proc_table= 1; + old_lex->add_to_query_tables(table); } } + /* cleanup LEX */ if (lex->spfuns.array.buffer) hash_free(&lex->spfuns); - old_next= table->next_global; - if ((table->next_global= lex->query_tables)) - table->next_global->prev_global= &table->next_global; - - /* mark to avoid temporary table using and put view reference*/ - for (TABLE_LIST *tbl= table->next_global; tbl; tbl= tbl->next_global) + /* + mark to avoid temporary table using and put view reference and find + last view table + */ + for (TABLE_LIST *tbl= view_tables; + tbl; + tbl= (view_tables_tail= tbl)->next_global) { tbl->skip_temporary= 1; tbl->belong_to_view= top_view; @@ -638,8 +643,8 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) if ((old_lex->sql_command == SQLCOM_SELECT && old_lex->describe) || old_lex->sql_command == SQLCOM_SHOW_CREATE) { - if (check_table_access(thd, SELECT_ACL, table->next_global, 1) && - check_table_access(thd, SHOW_VIEW_ACL, table->next_global, 1)) + if (check_table_access(thd, SELECT_ACL, view_tables, 1) && + check_table_access(thd, SHOW_VIEW_ACL, view_tables, 1)) { my_error(ER_VIEW_NO_EXPLAIN, MYF(0)); goto err; @@ -654,6 +659,29 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) old_lex->select_lex.options|= OPTION_TO_QUERY_CACHE; /* + Put tables of VIEW after VIEW TABLE_LIST + + NOTE: It is important for UPDATE/INSERT/DELETE checks to have this + tables just after VIEW instead of tail of list, to be able check that + table is unique. Also we store old next table for the same purpose. + */ + table->old_next= table->next_global; + if (view_tables) + { + if (table->next_global) + { + table->next_global->prev_global= &view_tables_tail->next_global; + view_tables_tail->next_global= table->old_next; + } + else + { + lex->query_tables_last= &view_tables_tail->next_global; + } + view_tables->prev_global= &table->next_global; + table->next_global= view_tables; + } + + /* check MERGE algorithm ability - algorithm is not explicit TEMPORARY TABLE - VIEW SELECT allow marging @@ -667,31 +695,26 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) { /* TODO: support multi tables substitutions - - table->next_global should be the same as - (TABLE_LIST *)lex->select_lex.table_list.first; */ - TABLE_LIST *view_table= table->next_global; /* lex should contain at least one table */ - DBUG_ASSERT(view_table != 0); + DBUG_ASSERT(view_tables != 0); table->effective_algorithm= VIEW_ALGORITHM_MERGE; DBUG_PRINT("info", ("algorithm: MERGE")); table->updatable= (table->updatable_view != 0); - if (old_next) - { - if ((view_table->next_global= old_next)) - old_next->prev_global= &view_table->next_global; - } - table->ancestor= view_table; - // next table should include SELECT_LEX under this table SELECT_LEX + table->ancestor= view_tables; + /* + next table should include SELECT_LEX under this table SELECT_LEX + + TODO: ehere should be loop for multi tables substitution + */ table->ancestor->select_lex= table->select_lex; /* move lock type (TODO: should we issue error in case of TMPTABLE algorithm and non-read locking)? */ - view_table->lock_type= table->lock_type; + view_tables->lock_type= table->lock_type; /* Store WHERE clause for postprocessing in setup_ancestor */ table->where= lex->select_lex.where; @@ -714,22 +737,6 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) lex->unit.include_down(table->select_lex); lex->unit.slave= &lex->select_lex; // fix include_down initialisation - if (old_next) - { - if ((tbl_end= table->next_global)) - { - for (; (tbl_next= tbl_end->next_global); tbl_end= tbl_next) - ; - if ((tbl_end->next_global= old_next)) - tbl_end->next_global->prev_global= &tbl_end->next_global; - } - else - { - /* VIEW do not contain tables */ - table->next_global= old_next; - } - } - table->derived= &lex->unit; } else @@ -746,17 +753,6 @@ ok: lex->all_selects_list->link_prev= (st_select_lex_node**)&old_lex->all_selects_list; - if (include_proc_table) - { - TABLE_LIST *proc= old_lex->proc_table; - if((proc->next_global= table->next_global)) - { - table->next_global->prev_global= &proc->next_global; - } - proc->prev_global= &table->next_global; - table->next_global= proc; - } - thd->lex= old_lex; DBUG_RETURN(0); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 91a20e6e8e5..920f6fd4a82 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -242,6 +242,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token DISTINCT %token DUPLICATE_SYM %token DYNAMIC_SYM +%token EACH_SYM %token ENABLE_SYM %token ENCLOSED %token ESCAPED @@ -411,6 +412,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token TO_SYM %token TRAILING %token TRANSACTION_SYM +%token TRIGGER_SYM %token TRUE_SYM %token TYPE_SYM %token TYPES_SYM @@ -1208,6 +1210,65 @@ create: } opt_view_list AS select_init check_option {} + | CREATE TRIGGER_SYM ident trg_action_time trg_event + ON table_ident FOR_SYM EACH_SYM ROW_SYM + { + LEX *lex= Lex; + sp_head *sp; + + if (lex->sphead) + { + net_printf(YYTHD, ER_SP_NO_RECURSIVE_CREATE, "TRIGGER"); + YYABORT; + } + + sp= new sp_head(); + sp->reset_thd_mem_root(YYTHD); + sp->init(lex); + + sp->m_type= TYPE_ENUM_TRIGGER; + lex->sphead= sp; + /* + We have to turn of CLIENT_MULTI_QUERIES while parsing a + stored procedure, otherwise yylex will chop it into pieces + at each ';'. + */ + sp->m_old_cmq= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; + YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES; + + bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); + lex->sphead->m_chistics= &lex->sp_chistics; + lex->sphead->m_body_begin= lex->tok_start; + } + sp_proc_stmt + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + + lex->sql_command= SQLCOM_CREATE_TRIGGER; + sp->init_strings(YYTHD, lex, NULL); + /* Restore flag if it was cleared above */ + if (sp->m_old_cmq) + YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES; + sp->restore_thd_mem_root(YYTHD); + + lex->name_and_length= $3; + + /* + We have to do it after parsing trigger body, because some of + sp_proc_stmt alternatives are not saving/restoring LEX, so + lex->query_tables can be wiped out. + + QQ: What are other consequences of this? + + QQ: Could we loosen lock type in certain cases ? + */ + if (!lex->select_lex.add_table_to_list(YYTHD, $7, + (LEX_STRING*) 0, + TL_OPTION_UPDATING, + TL_WRITE)) + YYABORT; + } ; sp_name: @@ -1538,13 +1599,17 @@ sp_decl: sp_head *sp= lex->sphead; sp_pcontext *ctx= lex->spcont; sp_label_t *hlab= lex->spcont->pop_label(); /* After this hdlr */ + sp_instr_hreturn *i; if ($2 == SP_HANDLER_CONTINUE) - sp->add_instr(new sp_instr_hreturn(sp->instructions(), ctx, - ctx->current_pvars())); + { + i= new sp_instr_hreturn(sp->instructions(), ctx, + ctx->current_pvars()); + sp->add_instr(i); + } else { /* EXIT or UNDO handler, just jump to the end of the block */ - sp_instr_jump *i= new sp_instr_jump(sp->instructions(), ctx); + i= new sp_instr_hreturn(sp->instructions(), ctx, 0); sp->add_instr(i); sp->push_backpatch(i, lex->spcont->last_label()); /* Block end */ @@ -1747,14 +1812,14 @@ sp_proc_stmt: if (lex->sql_command != SQLCOM_SET_OPTION || ! lex->var_list.is_empty()) { - /* Currently we can't handle queries inside a FUNCTION, - ** because of the way table locking works. - ** This is unfortunate, and limits the usefulness of functions - ** a great deal, but it's nothing we can do about this at the - ** moment. - */ - if (lex->sphead->m_type == TYPE_ENUM_FUNCTION && - lex->sql_command != SQLCOM_SET_OPTION) + /* + Currently we can't handle queries inside a FUNCTION or + TRIGGER, because of the way table locking works. This is + unfortunate, and limits the usefulness of functions and + especially triggers a tremendously, but it's nothing we + can do about this at the moment. + */ + if (lex->sphead->m_type != TYPE_ENUM_PROCEDURE) { send_error(YYTHD, ER_SP_BADSTATEMENT); YYABORT; @@ -2289,6 +2354,22 @@ sp_unlabeled_control: } ; +trg_action_time: + BEFORE_SYM + { Lex->trg_chistics.action_time= TRG_ACTION_BEFORE; } + | AFTER_SYM + { Lex->trg_chistics.action_time= TRG_ACTION_AFTER; } + ; + +trg_event: + INSERT + { Lex->trg_chistics.event= TRG_EVENT_INSERT; } + | UPDATE_SYM + { Lex->trg_chistics.event= TRG_EVENT_UPDATE; } + | DELETE_SYM + { Lex->trg_chistics.event= TRG_EVENT_DELETE; } + ; + create2: '(' create2a {} | opt_create_table_options create3 {} @@ -5422,7 +5503,21 @@ drop: lex->sql_command= SQLCOM_DROP_VIEW; lex->drop_if_exists= $3; } - ; + | DROP TRIGGER_SYM ident '.' ident + { + LEX *lex= Lex; + + lex->sql_command= SQLCOM_DROP_TRIGGER; + /* QQ: Could we loosen lock type in certain cases ? */ + if (!lex->select_lex.add_table_to_list(YYTHD, + new Table_ident($3), + (LEX_STRING*) 0, + TL_OPTION_UPDATING, + TL_WRITE)) + YYABORT; + lex->name_and_length= $5; + } + ; table_list: table_name @@ -5842,8 +5937,12 @@ show_param: { Lex->sql_command = SQLCOM_SHOW_WARNS;} | ERRORS opt_limit_clause_init { Lex->sql_command = SQLCOM_SHOW_ERRORS;} - | STATUS_SYM wild - { Lex->sql_command= SQLCOM_SHOW_STATUS; } + | opt_var_type STATUS_SYM wild + { + THD *thd= YYTHD; + thd->lex->sql_command= SQLCOM_SHOW_STATUS; + thd->lex->option_type= (enum_var_type) $1; + } | INNOBASE_SYM STATUS_SYM { Lex->sql_command = SQLCOM_SHOW_INNODB_STATUS; WARN_DEPRECATED("SHOW INNODB STATUS", "SHOW ENGINE INNODB STATUS"); } | opt_full PROCESSLIST_SYM @@ -6413,18 +6512,69 @@ simple_ident_q: { THD *thd= YYTHD; LEX *lex= thd->lex; - SELECT_LEX *sel= lex->current_select; - if (sel->no_table_names_allowed) - { - my_printf_error(ER_TABLENAME_NOT_ALLOWED_HERE, - ER(ER_TABLENAME_NOT_ALLOWED_HERE), - MYF(0), $1.str, thd->where); - } - $$= (sel->parsing_place != IN_HAVING || - sel->get_in_sum_expr() > 0) ? - (Item*) new Item_field(NullS,$1.str,$3.str) : - (Item*) new Item_ref(0,0,NullS,$1.str,$3.str); - } + + /* + FIXME This will work ok in simple_ident_nospvar case because + we can't meet simple_ident_nospvar in trigger now. But it + should be changed in future. + */ + if (lex->sphead && lex->sphead->m_type == TYPE_ENUM_TRIGGER && + (!my_strcasecmp(system_charset_info, $1.str, "NEW") || + !my_strcasecmp(system_charset_info, $1.str, "OLD"))) + { + bool new_row= ($1.str[0]=='N' || $1.str[0]=='n'); + + if (lex->trg_chistics.event == TRG_EVENT_INSERT && + !new_row) + { + net_printf(YYTHD, ER_TRG_NO_SUCH_ROW_IN_TRG, "OLD", + "on INSERT"); + YYABORT; + } + + if (lex->trg_chistics.event == TRG_EVENT_DELETE && + new_row) + { + net_printf(YYTHD, ER_TRG_NO_SUCH_ROW_IN_TRG, "NEW", + "on DELETE"); + YYABORT; + } + + Item_trigger_field *trg_fld= + new Item_trigger_field(new_row ? Item_trigger_field::NEW_ROW : + Item_trigger_field::OLD_ROW, + $3.str); + + if (lex->trg_table && + trg_fld->setup_field(thd, lex->trg_table, + lex->trg_chistics.event)) + { + /* + FIXME. Far from perfect solution. See comment for + "SET NEW.field_name:=..." for more info. + */ + net_printf(YYTHD, ER_BAD_FIELD_ERROR, $3.str, + new_row ? "NEW": "OLD"); + YYABORT; + } + + $$= (Item *)trg_fld; + } + else + { + SELECT_LEX *sel= lex->current_select; + if (sel->no_table_names_allowed) + { + my_printf_error(ER_TABLENAME_NOT_ALLOWED_HERE, + ER(ER_TABLENAME_NOT_ALLOWED_HERE), + MYF(0), $1.str, thd->where); + } + $$= (sel->parsing_place != IN_HAVING || + sel->get_in_sum_expr() > 0) ? + (Item*) new Item_field(NullS,$1.str,$3.str) : + (Item*) new Item_ref(0,0,NullS,$1.str,$3.str); + } + } | '.' ident '.' ident { THD *thd= YYTHD; @@ -6649,6 +6799,7 @@ keyword: | FIRST_SYM {} | FIXED_SYM {} | FLUSH_SYM {} + | FRAC_SECOND_SYM {} | GEOMETRY_SYM {} | GEOMETRYCOLLECTION {} | GET_FORMAT {} @@ -6854,13 +7005,78 @@ opt_var_ident_type: option_value: '@' ident_or_text equal expr { - Lex->var_list.push_back(new set_var_user(new Item_func_set_user_var($2,$4))); + LEX *lex= Lex; + + if (lex->sphead && lex->sphead->m_type != TYPE_ENUM_PROCEDURE) + { + /* + We have to use special instruction in functions and triggers + because sp_instr_stmt will close all tables and thus ruin + execution of statement invoking function or trigger. + + We also do not want to allow expression with subselects in + this case. + */ + if (lex->query_tables) + { + send_error(YYTHD, ER_SP_SUBSELECT_NYI); + YYABORT; + } + sp_instr_set_user_var *i= + new sp_instr_set_user_var(lex->sphead->instructions(), + lex->spcont, $2, $4); + lex->sphead->add_instr(i); + } + else + lex->var_list.push_back(new set_var_user(new Item_func_set_user_var($2,$4))); + } | internal_variable_name equal set_expr_or_default { LEX *lex=Lex; - if ($1.var) + if ($1.var == &trg_new_row_fake_var) + { + /* We are in trigger and assigning value to field of new row */ + Item *it; + sp_instr_set_trigger_field *i; + if (lex->query_tables) + { + send_error(YYTHD, ER_SP_SUBSELECT_NYI); + YYABORT; + } + if ($3) + it= $3; + else + { + /* QQ: Shouldn't this be field's default value ? */ + it= new Item_null(); + } + i= new sp_instr_set_trigger_field(lex->sphead->instructions(), + lex->spcont, $1.base_name, it); + if (lex->trg_table && i->setup_field(YYTHD, lex->trg_table, + lex->trg_chistics.event)) + { + /* + FIXME. Now we are catching this kind of errors only + during opening tables. But this doesn't save us from most + common user error - misspelling field name, because we + will bark too late in this case... Moreover it is easy to + make table unusable with such kind of error... + + So in future we either have to parse trigger definition + second time during create trigger or gather all trigger + fields in one list and perform setup_field() for them as + separate stage. + + Error message also should be improved. + */ + net_printf(YYTHD, ER_BAD_FIELD_ERROR, $1.base_name, "NEW"); + YYABORT; + } + lex->sphead->add_instr(i); + } + else if ($1.var) { /* System variable */ lex->var_list.push_back(new set_var(lex->option_type, $1.var, &$1.base_name, $3)); @@ -6975,18 +7191,46 @@ internal_variable_name: } | ident '.' ident { + LEX *lex= Lex; if (check_reserved_words(&$1)) { yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; } - sys_var *tmp=find_sys_var($3.str, $3.length); - if (!tmp) - YYABORT; - if (!tmp->is_struct()) - net_printf(YYTHD, ER_VARIABLE_IS_NOT_STRUCT, $3.str); - $$.var= tmp; - $$.base_name= $1; + if (lex->sphead && lex->sphead->m_type == TYPE_ENUM_TRIGGER && + (!my_strcasecmp(system_charset_info, $1.str, "NEW") || + !my_strcasecmp(system_charset_info, $1.str, "OLD"))) + { + if ($1.str[0]=='O' || $1.str[0]=='o') + { + net_printf(YYTHD, ER_TRG_CANT_CHANGE_ROW, "OLD", ""); + YYABORT; + } + if (lex->trg_chistics.event == TRG_EVENT_DELETE) + { + net_printf(YYTHD, ER_TRG_NO_SUCH_ROW_IN_TRG, "NEW", + "on DELETE"); + YYABORT; + } + if (lex->trg_chistics.action_time == TRG_ACTION_AFTER) + { + net_printf(YYTHD, ER_TRG_CANT_CHANGE_ROW, "NEW", "after "); + YYABORT; + } + /* This special combination will denote field of NEW row */ + $$.var= &trg_new_row_fake_var; + $$.base_name= $3; + } + else + { + sys_var *tmp=find_sys_var($3.str, $3.length); + if (!tmp) + YYABORT; + if (!tmp->is_struct()) + net_printf(YYTHD, ER_VARIABLE_IS_NOT_STRUCT, $3.str); + $$.var= tmp; + $$.base_name= $1; + } } | DEFAULT '.' ident { diff --git a/sql/structs.h b/sql/structs.h index 9f13ef54ce0..cc053e2e2fd 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -176,7 +176,8 @@ enum SHOW_TYPE SHOW_SSL_GET_CIPHER_LIST, #endif /* HAVE_OPENSSL */ SHOW_RPL_STATUS, SHOW_SLAVE_RUNNING, - SHOW_KEY_CACHE_LONG, SHOW_KEY_CACHE_CONST_LONG + SHOW_KEY_CACHE_LONG, SHOW_KEY_CACHE_CONST_LONG, + SHOW_LONG_STATUS, SHOW_LONG_CONST_STATUS }; enum SHOW_COMP_OPTION { SHOW_OPTION_YES, SHOW_OPTION_NO, SHOW_OPTION_DISABLED}; diff --git a/sql/table.cc b/sql/table.cc index d07d2ca085d..1c216c44f66 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -733,7 +733,7 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, outparam->db_low_byte_first=outparam->file->low_byte_first(); my_pthread_setspecific_ptr(THR_MALLOC,old_root); - opened_tables++; + current_thd->status_var.opened_tables++; #ifndef DBUG_OFF if (use_hash) (void) hash_check(&outparam->name_hash); @@ -1583,7 +1583,8 @@ bool st_table_list::setup_ancestor(THD *thd, Item **conds) if (where) { Item_arena *arena= thd->current_arena, backup; - if (!arena->is_stmt_prepare()) + TABLE_LIST *tbl= this; + if (arena->is_conventional()) arena= 0; // For easier test if (!where->fixed && where->fix_fields(thd, ancestor, &where)) @@ -1591,17 +1592,23 @@ bool st_table_list::setup_ancestor(THD *thd, Item **conds) if (arena) thd->set_n_backup_item_arena(arena, &backup); - if (outer_join) + + /* Go up to join tree and try to find left join */ + for (; tbl; tbl= tbl->embedding) { - /* - Store WHERE condition to ON expression for outer join, because we - can't use WHERE to correctly execute jeft joins on VIEWs and this - expression will not be moved to WHERE condition (i.e. will be clean - correctly for PS/SP) - */ - on_expr= and_conds(on_expr, where); + if (tbl->outer_join) + { + /* + Store WHERE condition to ON expression for outer join, because we + can't use WHERE to correctly execute jeft joins on VIEWs and this + expression will not be moved to WHERE condition (i.e. will be clean + correctly for PS/SP) + */ + tbl->on_expr= and_conds(tbl->on_expr, where); + break; + } } - else + if (tbl == 0) { /* It is conds of JOIN, but it will be stored in st_select_lex::prep_where @@ -1609,6 +1616,7 @@ bool st_table_list::setup_ancestor(THD *thd, Item **conds) */ *conds= and_conds(*conds, where); } + if (arena) thd->restore_backup_item_arena(arena, &backup); } diff --git a/sql/table.h b/sql/table.h index b7cabe21638..f31c3c21d63 100644 --- a/sql/table.h +++ b/sql/table.h @@ -73,6 +73,7 @@ typedef struct st_filesort_info class Field_timestamp; class Field_blob; +class Table_triggers_list; struct st_table { handler *file; @@ -154,6 +155,8 @@ struct st_table { REGINFO reginfo; /* field connections */ MEM_ROOT mem_root; GRANT_INFO grant; + /* Table's triggers, 0 if there are no of them */ + Table_triggers_list *triggers; char *table_cache_key; char *table_name,*real_name,*path; @@ -217,6 +220,8 @@ typedef struct st_table_list st_table_list *ancestor; /* most upper view this table belongs to */ st_table_list *belong_to_view; + /* next_global before adding VIEW tables */ + st_table_list *old_next; Item *where; /* VIEW WHERE clause condition */ LEX_STRING query; /* text of (CRETE/SELECT) statement */ LEX_STRING md5; /* md5 of query tesxt */ @@ -260,6 +265,12 @@ typedef struct st_table_list bool setup_ancestor(THD *thd, Item **conds); bool placeholder() {return derived || view; } void print(THD *thd, String *str); + inline st_table_list *next_independent() + { + if (view) + return old_next; + return next_global; + } } TABLE_LIST; class Item; |