From 6e0b468f18c04b7a23e4ba4d1c85a74b5a2aa4a7 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 7 Sep 2004 10:42:23 +0300 Subject: [PATCH 01/26] requre BASE TABLE for HANDLER (BUG#5277) mysql-test/r/view.result: HANDLER with VIEW mysql-test/t/view.test: HANDLER with VIEW sql/sql_handler.cc: requre BASE TABLE for HANDLER --- mysql-test/r/view.result | 6 ++++++ mysql-test/t/view.test | 10 ++++++++++ sql/sql_handler.cc | 4 ++++ 3 files changed, 20 insertions(+) diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 35b76a8ab4b..9e9b9ef9a87 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -1270,3 +1270,9 @@ s1 7 drop view v1; drop table t1; +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; diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index 9464e291e05..d47de266c5e 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -1230,3 +1230,13 @@ insert into v1 values (1) on duplicate key update s1 = 7; select * from t1; drop view v1; drop table t1; + +# +# HANDLER with VIEW +# +create table t1 (s1 int); +create view v1 as select * from t1; +-- error 1346 +handler v1 open as xx; +drop view v1; +drop table t1; diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 0df3d617d7f..4dc76ed6981 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; From 52ac4935e7e00218bf7abde7b841a2e5111cc819 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 7 Sep 2004 16:29:46 +0400 Subject: [PATCH 02/26] WL#1218 "Triggers". Some very preliminary version of patch. Mostly needed for Monty for him getting notion what needed for triggers from new .FRM format. Things to be done: - Right placement of trigger's invocations - Right handling of errors in triggers (including transaction rollback) - Support for priviliges - Right handling of DROP/RENAME table (hope that it will be handled automatically with merging of .TRG into .FRM file) - Saving/restoring some information critical for trigger creation and replication with their definitions (e.g. sql_mode, creator, ...) - Replication Already has some known bugs so probably not for general review. include/mysqld_error.h: Added trigger related error codes. sql/Makefile.am: Added sql_trigger.* files to list of sources from which mysqld is built. sql/item.cc: Added Item_trigger_field class representing field of new/old version of row which is inserted/updated/deleted and for which trigger is invoked. sql/item.h: Added enums for describing trigger properties. (Here because of dependencies). Added Item_trigger_field class representing field of new/old version of row which is inserted/updated/deleted and for which trigger is invoked. sql/item_func.cc: Added Item_func_set_user_var::print_as_stmt() for printing of assignment to ser variable from stored procedures. Added comment clarifying why we don't need set no_send_ok when calling stored functions. sql/item_func.h: Added Item_func_set_user_var::print_as_stmt() for printing of assignment to ser variable from stored procedures. sql/lex.h: Symbols used in statements defining triggers were added. sql/mysql_priv.h: Exported some functions needed for triggers implementation. sql/parse_file.cc: Cleaned up FILE_OPTIONS_STRLIST handling (needed for triggers). sql/parse_file.h: Cleaned up FILE_OPTIONS_STRLIST handling (needed for triggers). sql/share/czech/errmsg.txt: Added trigger related error messages. sql/share/danish/errmsg.txt: Added trigger related error messages. sql/share/dutch/errmsg.txt: Added trigger related error messages. sql/share/english/errmsg.txt: Added trigger related error messages. sql/share/estonian/errmsg.txt: Added trigger related error messages. sql/share/french/errmsg.txt: Added trigger related error messages. sql/share/german/errmsg.txt: Added trigger related error messages. sql/share/greek/errmsg.txt: Added trigger related error messages. sql/share/hungarian/errmsg.txt: Added trigger related error messages. sql/share/italian/errmsg.txt: Added trigger related error messages. sql/share/japanese/errmsg.txt: Added trigger related error messages. sql/share/korean/errmsg.txt: Added trigger related error messages. sql/share/norwegian-ny/errmsg.txt: Added trigger related error messages. sql/share/norwegian/errmsg.txt: Added trigger related error messages. sql/share/polish/errmsg.txt: Added trigger related error messages. sql/share/portuguese/errmsg.txt: Added trigger related error messages. sql/share/romanian/errmsg.txt: Added trigger related error messages. sql/share/russian/errmsg.txt: Added trigger related error messages. sql/share/serbian/errmsg.txt: Added trigger related error messages. sql/share/slovak/errmsg.txt: Added trigger related error messages. sql/share/spanish/errmsg.txt: Added trigger related error messages. sql/share/swedish/errmsg.txt: Added trigger related error messages. sql/share/ukrainian/errmsg.txt: Added trigger related error messages. sql/sp_head.cc: sp_head::init_strings(): added support for triggers (we don't have most of strings for them) sp_head::execute_function(): triggers don't require RETURN sp_head::reset_lex(): added propagation of trigger properties to LEX used for parsing of statements composing trigger body. Added two more SP instructions: - sp_instr_set_user_var for setting user variables in stored functions and triggers (we can't use sp_instr_stmt and "SET @a:=..." statement since it will close open tables and thus break execution of calling statement. - sp_instr_set_trigger_field for assignment to fields of row being updated/inserted in triggers sql/sp_head.h: Added new type of sp_head object - TYPE_ENUM_TRIGGER Added two more SP instructions: - sp_instr_set_user_var for setting user variables in stored functions and triggers (we can't use sp_instr_stmt and "SET @a:=..." statement since it will close open tables and thus break execution of calling statement. - sp_instr_set_trigger_field for assignment to fields of row being updated/inserted in triggers sql/sql_base.cc: Now freeing memory occupied by trigger related structures when closing tables. open_unireg_entry(): added loading of triggers for table. find_field_in_real_table() is now public since it required by Item_trigger_field in item.cc sql/sql_delete.cc: Added triggers invocation for DELETE (should be fixed for sure :)) sql/sql_insert.cc: Added triggers invocation for INSERT (should be fixed for sure :)) sql/sql_lex.cc: Added trg_new_row_fake_var variable pointer to which is used for distinguishing between assignment to NEW row field (when parsing trigger definition) and structured variable. Added initialization of LEX->trg_table to lex_start(). (When triggers are parsed during opening of table it points to table object.) sql/sql_lex.h: Added trigger related commands. Added st_trg_chistics structure describing properties of trigger being created (as struct and as LEX member). Added trg_new_row_fake_var variable pointer to which is used for distinguishing between assignment to NEW row field (when parsing trigger definition) and structured variable. Added LEX::trg_table which points to table being opened when we are parsing trigger definition while opening table. sql/sql_parse.cc: Added support for CREATE/DROP TRIGGER commands. sql/sql_table.cc: Cleanup. close_cached_table() always returns 0 and its result is not analyzed in most places so chenged its return type to void. sql/sql_update.cc: Added triggers invocation for UPDATE (should be fixed for sure :)) sql/sql_yacc.yy: Added support for CREATE/DROP TRIGGER statements. Added support for OLD/NEW row identifiers in trigger. Made assignment to user variables not to break execution of stored function or trigger. sql/table.h: Added TABLE::triggers member representing triggers for this table. --- include/mysqld_error.h | 7 +- mysql-test/r/trigger.result | 171 ++++++++++++ mysql-test/t/trigger.test | 195 ++++++++++++++ sql/Makefile.am | 4 +- sql/item.cc | 93 +++++++ sql/item.h | 54 +++- sql/item_func.cc | 15 ++ sql/item_func.h | 1 + sql/lex.h | 2 + sql/mysql_priv.h | 6 + sql/parse_file.cc | 15 +- sql/parse_file.h | 3 +- sql/share/czech/errmsg.txt | 5 + sql/share/danish/errmsg.txt | 5 + sql/share/dutch/errmsg.txt | 5 + sql/share/english/errmsg.txt | 5 + sql/share/estonian/errmsg.txt | 5 + sql/share/french/errmsg.txt | 5 + sql/share/german/errmsg.txt | 5 + sql/share/greek/errmsg.txt | 5 + sql/share/hungarian/errmsg.txt | 5 + sql/share/italian/errmsg.txt | 5 + sql/share/japanese/errmsg.txt | 5 + sql/share/korean/errmsg.txt | 5 + sql/share/norwegian-ny/errmsg.txt | 5 + sql/share/norwegian/errmsg.txt | 5 + sql/share/polish/errmsg.txt | 5 + sql/share/portuguese/errmsg.txt | 5 + sql/share/romanian/errmsg.txt | 5 + sql/share/russian/errmsg.txt | 5 + sql/share/serbian/errmsg.txt | 5 + sql/share/slovak/errmsg.txt | 5 + sql/share/spanish/errmsg.txt | 5 + sql/share/swedish/errmsg.txt | 5 + sql/share/ukrainian/errmsg.txt | 5 + sql/sp_head.cc | 109 ++++++-- sql/sp_head.h | 66 +++++ sql/sql_base.cc | 22 +- sql/sql_delete.cc | 11 + sql/sql_insert.cc | 11 + sql/sql_lex.cc | 7 + sql/sql_lex.h | 18 ++ sql/sql_parse.cc | 14 + sql/sql_table.cc | 10 +- sql/sql_trigger.cc | 434 ++++++++++++++++++++++++++++++ sql/sql_trigger.h | 62 +++++ sql/sql_update.cc | 10 + sql/sql_yacc.yy | 284 ++++++++++++++++--- sql/table.h | 3 + 49 files changed, 1664 insertions(+), 78 deletions(-) create mode 100644 mysql-test/r/trigger.result create mode 100644 mysql-test/t/trigger.test create mode 100644 sql/sql_trigger.cc create mode 100644 sql/sql_trigger.h diff --git a/include/mysqld_error.h b/include/mysqld_error.h index 0075e51f72d..fa5fd3db2b0 100644 --- a/include/mysqld_error.h +++ b/include/mysqld_error.h @@ -370,4 +370,9 @@ #define ER_VIEW_WRONG_LIST 1351 #define ER_WARN_VIEW_MERGE 1352 #define ER_WARN_VIEW_WITHOUT_KEY 1353 -#define ER_ERROR_MESSAGES 354 +#define ER_TRG_ALREADY_EXISTS 1354 +#define ER_TRG_DOES_NOT_EXIST 1355 +#define ER_TRG_ON_VIEW_OR_TEMP_TABLE 1356 +#define ER_TRG_CANT_CHANGE_ROW 1357 +#define ER_TRG_NO_SUCH_ROW_IN_TRG 1358 +#define ER_ERROR_MESSAGES 359 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/t/trigger.test b/mysql-test/t/trigger.test new file mode 100644 index 00000000000..f7b9ebc8d2c --- /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 1358 +create trigger trg before insert on t1 for each row set @a:= old.i; +--error 1358 +create trigger trg before delete on t1 for each row set @a:= new.i; +--error 1357 +create trigger trg before update on t1 for each row set old.i:=1; +--error 1358 +create trigger trg before delete on t1 for each row set new.i:=1; +--error 1357 +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 1354 +create trigger trg after insert on t1 for each row set @a:=1; +--error 1354 +create trigger trg2 before insert on t1 for each row set @a:=1; +drop trigger t1.trg; + +--error 1355 +drop trigger t1.trg; + +create view v1 as select * from t1; +--error 1356 +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 1356 +create trigger trg before insert on t1 for each row set @a:=1; +drop table t1; diff --git a/sql/Makefile.am b/sql/Makefile.am index 175cc3786cf..e0fcfa0fd3c 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 examples/ha_example.h examples/ha_archive.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 mysqld_SOURCES = sql_lex.cc sql_handler.cc \ item.cc item_sum.cc item_buff.cc item_func.cc \ item_cmpfunc.cc item_strfunc.cc item_timefunc.cc \ @@ -95,7 +95,7 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc \ tztime.cc my_time.c \ examples/ha_example.cc examples/ha_archive.cc \ 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 gen_lex_hash_SOURCES = gen_lex_hash.cc gen_lex_hash_LDADD = $(LDADD) $(CXXLDFLAGS) mysql_tzinfo_to_sql_SOURCES = mysql_tzinfo_to_sql.cc diff --git a/sql/item.cc b/sql/item.cc index 0ca557a8fc4..9cfa12b6365 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, @@ -2251,6 +2253,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 758429c0994..354152611ae 100644 --- a/sql/item.h +++ b/sql/item.h @@ -99,7 +99,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 }; @@ -439,6 +439,7 @@ public: class Item_field :public Item_ident { +protected: void set_field(Field *field); public: Field *field,*result_field; @@ -1152,6 +1153,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 c2c93586af8..a7bfb727ff7 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2563,6 +2563,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) { @@ -3296,6 +3306,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 8636bcd6507..064bbb4937b 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -945,6 +945,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"; } }; diff --git a/sql/lex.h b/sql/lex.h index 023c4752720..fc72435bb52 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)}, @@ -468,6 +469,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 ba19ee15ce6..48db2f37796 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -471,6 +471,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); @@ -611,6 +612,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=0); +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); @@ -637,6 +639,10 @@ Field *find_field_in_table(THD *thd, TABLE_LIST *tables, const char *name, bool check_grant_table, bool check_grant_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 struct st_des_keyblock diff --git a/sql/parse_file.cc b/sql/parse_file.cc index 231780f437e..4bd526d7136 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*)(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) */ + FILE_OPTIONS_STRLIST /* list of escaped strings + (List) */ }; struct File_option diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt index 81bc1e733c6..7b5c2cb327a 100644 --- a/sql/share/czech/errmsg.txt +++ b/sql/share/czech/errmsg.txt @@ -366,3 +366,8 @@ character-set=latin2 "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" "View being update does not have complete key of underlying table in it" +"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 f16d0700be1..7fd0ef6270d 100644 --- a/sql/share/danish/errmsg.txt +++ b/sql/share/danish/errmsg.txt @@ -360,3 +360,8 @@ character-set=latin1 "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" "View being update does not have complete key of underlying table in it" +"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 e4a7d2ffe02..159bace6667 100644 --- a/sql/share/dutch/errmsg.txt +++ b/sql/share/dutch/errmsg.txt @@ -368,3 +368,8 @@ character-set=latin1 "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" "View being update does not have complete key of underlying table in it" +"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 c13eee75392..2306df21321 100644 --- a/sql/share/english/errmsg.txt +++ b/sql/share/english/errmsg.txt @@ -357,3 +357,8 @@ character-set=latin1 "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" "View being update does not have complete key of underlying table in it" +"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 4ff08a9c8c7..03877b4656b 100644 --- a/sql/share/estonian/errmsg.txt +++ b/sql/share/estonian/errmsg.txt @@ -362,3 +362,8 @@ character-set=latin7 "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" "View being update does not have complete key of underlying table in it" +"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 a1d0448ebf1..f1d68fca6d7 100644 --- a/sql/share/french/errmsg.txt +++ b/sql/share/french/errmsg.txt @@ -357,3 +357,8 @@ character-set=latin1 "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" "View being update does not have complete key of underlying table in it" +"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 ac7cf16b7b4..800409bb07f 100644 --- a/sql/share/german/errmsg.txt +++ b/sql/share/german/errmsg.txt @@ -369,3 +369,8 @@ character-set=latin1 "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" "View being update does not have complete key of underlying table in it" +"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 f505766bbe9..b94364d256c 100644 --- a/sql/share/greek/errmsg.txt +++ b/sql/share/greek/errmsg.txt @@ -357,3 +357,8 @@ character-set=greek "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" "View being update does not have complete key of underlying table in it" +"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 4f328d07825..b8560317b48 100644 --- a/sql/share/hungarian/errmsg.txt +++ b/sql/share/hungarian/errmsg.txt @@ -359,3 +359,8 @@ character-set=latin2 "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" "View being update does not have complete key of underlying table in it" +"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 af5ebc4c3e6..7c72fb03476 100644 --- a/sql/share/italian/errmsg.txt +++ b/sql/share/italian/errmsg.txt @@ -357,3 +357,8 @@ character-set=latin1 "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" "View being update does not have complete key of underlying table in it" +"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 2a230bdf17d..4c1bf8e5cc0 100644 --- a/sql/share/japanese/errmsg.txt +++ b/sql/share/japanese/errmsg.txt @@ -359,3 +359,8 @@ character-set=ujis "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" "View being update does not have complete key of underlying table in it" +"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 ed1751bbc18..45479950378 100644 --- a/sql/share/korean/errmsg.txt +++ b/sql/share/korean/errmsg.txt @@ -357,3 +357,8 @@ character-set=euckr "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" "View being update does not have complete key of underlying table in it" +"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 70951dfc1f4..02096bc46b9 100644 --- a/sql/share/norwegian-ny/errmsg.txt +++ b/sql/share/norwegian-ny/errmsg.txt @@ -359,3 +359,8 @@ character-set=latin1 "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" "View being update does not have complete key of underlying table in it" +"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 d6b0fa9303b..615014cef94 100644 --- a/sql/share/norwegian/errmsg.txt +++ b/sql/share/norwegian/errmsg.txt @@ -359,3 +359,8 @@ character-set=latin1 "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" "View being update does not have complete key of underlying table in it" +"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 6914661f188..9368dd927d6 100644 --- a/sql/share/polish/errmsg.txt +++ b/sql/share/polish/errmsg.txt @@ -361,3 +361,8 @@ character-set=latin2 "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" "View being update does not have complete key of underlying table in it" +"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 3aa6071ff5b..12c4c43bfe0 100644 --- a/sql/share/portuguese/errmsg.txt +++ b/sql/share/portuguese/errmsg.txt @@ -358,3 +358,8 @@ character-set=latin1 "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" "View being update does not have complete key of underlying table in it" +"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 87691de7ce9..4b04ef3729e 100644 --- a/sql/share/romanian/errmsg.txt +++ b/sql/share/romanian/errmsg.txt @@ -361,3 +361,8 @@ character-set=latin2 "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" "View being update does not have complete key of underlying table in it" +"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 55116a24b4a..b73b7d8d7b9 100644 --- a/sql/share/russian/errmsg.txt +++ b/sql/share/russian/errmsg.txt @@ -359,3 +359,8 @@ character-set=koi8r "View SELECT и список полей view имеют разное количество столбцов" "Алгоритм слияния view не может быть использован сейчас (алгоритм будет неопеределенным)" "Обновляемый view не содержит ключа использованной в нем таблиц(ы)" +"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 a08e65149ef..d0f4e0e64eb 100644 --- a/sql/share/serbian/errmsg.txt +++ b/sql/share/serbian/errmsg.txt @@ -363,3 +363,8 @@ character-set=cp1250 "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" "View being update does not have complete key of underlying table in it" +"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 222461bab51..66f75e5b44d 100644 --- a/sql/share/slovak/errmsg.txt +++ b/sql/share/slovak/errmsg.txt @@ -365,3 +365,8 @@ character-set=latin2 "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" "View being update does not have complete key of underlying table in it" +"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 791dcacfcc2..080fba045a4 100644 --- a/sql/share/spanish/errmsg.txt +++ b/sql/share/spanish/errmsg.txt @@ -359,3 +359,8 @@ character-set=latin1 "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" "View being update does not have complete key of underlying table in it" +"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 0c562525ffa..0d4e4d489f4 100644 --- a/sql/share/swedish/errmsg.txt +++ b/sql/share/swedish/errmsg.txt @@ -357,3 +357,8 @@ character-set=latin1 "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" "View being update does not have complete key of underlying table in it" +"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 cb483e3ed99..a1369730790 100644 --- a/sql/share/ukrainian/errmsg.txt +++ b/sql/share/ukrainian/errmsg.txt @@ -362,3 +362,8 @@ character-set=koi8u "View SELECT ╕ перел╕к стовбц╕в view мають р╕зну к╕льк╕сть сковбц╕в" "Алгоритм зливання view не може бути використаний зараз (алгоритм буде невизначений)" "View, що оновлюеться, не м╕стить повного ключа таблиц╕(ь), що викор╕стана в ньюому" +"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 89114e4f62b..44357170ff5 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -242,31 +242,43 @@ 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 */ - if (name->m_db.length == 0) + if (name) + { + DBUG_PRINT("info", ("name: %*.s%*s", + name->m_db.length, name->m_db.str, + name->m_name.length, name->m_name.str)); + + if (name->m_db.length == 0) + { + m_db.length= (thd->db ? strlen(thd->db) : 0); + m_db.str= strmake_root(root, (thd->db ? thd->db : ""), m_db.length); + } + else + { + m_db.length= name->m_db.length; + 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 { m_db.length= (thd->db ? strlen(thd->db) : 0); m_db.str= strmake_root(root, (thd->db ? thd->db : ""), m_db.length); } - else + + if (m_param_begin && m_param_end) { - m_db.length= name->m_db.length; - m_db.str= strmake_root(root, name->m_db.str, name->m_db.length); + m_params.length= m_param_end - m_param_begin; + m_params.str= strmake_root(root, + (char *)m_param_begin, m_params.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 (m_returns_begin && m_returns_end) { /* QQ KLUDGE: We can't seem to cut out just the type in the parser @@ -508,8 +520,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) @@ -683,6 +697,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; mysql_init_query(thd, true); // Only init lex sublex->sp_lex_in_use= FALSE; DBUG_VOID_RETURN; @@ -1040,6 +1057,60 @@ sp_instr_set::print(String *str) m_value->print(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 // diff --git a/sql/sp_head.h b/sql/sp_head.h index fd6ecfd7320..1622d197d81 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); @@ -342,6 +343,71 @@ 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, LEX_STRING var, Item *val) + : sp_instr(ip), 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, LEX_STRING field_name, Item *val) + : sp_instr(ip), + 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 */ diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 781e7273e7a..1060c9cd25d 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 #include #include @@ -41,10 +43,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))) @@ -209,6 +207,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 } @@ -790,6 +789,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); @@ -1053,6 +1053,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; } @@ -1081,6 +1082,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 @@ -1469,6 +1471,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). @@ -1497,6 +1502,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; @@ -1998,10 +2004,10 @@ Field *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; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 910b673dc32..0453990fbac 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 diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 33b99d4ccf9..72ca7cf5dea 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 @@ -299,6 +301,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) { @@ -321,6 +329,9 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, id= thd->last_insert_id; } thd->row_count++; + + if (table->triggers) + table->triggers->process_triggers(thd, TRG_EVENT_INSERT, TRG_ACTION_AFTER); } /* diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index d96c6371ff1..8a1b59d9585 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -24,6 +24,12 @@ #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); + /* Macros to look like lex */ #define yyGet() *(lex->ptr++) @@ -130,6 +136,7 @@ LEX *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); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 9e615d2ce9f..83154b277f5 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 }; @@ -606,6 +607,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 @@ -710,6 +720,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() { diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index be063211a7d..212339151a4 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3828,6 +3828,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; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index ea57536d7c1..c79005ad83f 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1509,7 +1509,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"); @@ -1525,7 +1525,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, @@ -3130,12 +3129,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_trigger.cc b/sql/sql_trigger.cc new file mode 100644 index 00000000000..5e08192d0c1 --- /dev/null +++ b/sql/sql_trigger.cc @@ -0,0 +1,434 @@ +#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)) + 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 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 it_name(names_list); + List_iterator 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)) + { + int i; + Table_triggers_list *triggers_info= + new (&table->mem_root) Table_triggers_list(); + + if (!triggers_info) + DBUG_RETURN(1); + + if (parser->parse((gptr)triggers_info, &table->mem_root, + triggers_file_parameters, 1)) + DBUG_RETURN(1); + + table->triggers= triggers_info; + + /* + 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_info->old_field= + (Field **)alloc_root(&table->mem_root, (table->fields + 1) * + sizeof(Field*)))) + DBUG_RETURN(1); + + for (i= 0; i < table->fields; i++) + { + /* + QQ: it is supposed that it is ok to use this function for field + cloning... + */ + if (!(triggers_info->old_field[i]= + table->field[i]->new_field(&table->mem_root, table))) + DBUG_RETURN(1); + triggers_info->old_field[i]->move_field((my_ptrdiff_t) + (table->record[1] - + table->record[0])); + } + triggers_info->old_field[i]= 0; + + List_iterator_fast it(triggers_info->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++)) + { + lex_start(thd, (uchar*)trg_create_str->str, trg_create_str->length); + mysql_init_query(thd, 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_info->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_info->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 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 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_update.cc b/sql/sql_update.cc index 7d49c422194..bc72589b142 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 *fields); @@ -341,6 +343,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], @@ -356,6 +362,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 diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index af6694c6318..65b364c6387 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 @@ -1200,6 +1202,57 @@ 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; + + lex->name_and_length= $3; + + /* 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; + + 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); + } ; sp_name: @@ -1730,14 +1783,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; @@ -2155,6 +2208,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 {} @@ -5210,7 +5279,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 @@ -6200,18 +6283,70 @@ 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 != SELECT_LEX_NODE::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 != SELECT_LEX_NODE::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; @@ -6640,13 +6775,74 @@ 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. + */ + sp_instr_set_user_var *i= + new sp_instr_set_user_var(lex->sphead->instructions(), + $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 ($3 && $3->type() == Item::SUBSELECT_ITEM) + { /* + QQ For now, just disallow subselects as values + Unfortunately this doesn't helps in case when we have + subselect deeper in expression. + */ + 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(), + $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)); @@ -6754,18 +6950,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/table.h b/sql/table.h index 5e00820a6e5..6bf700cc684 100644 --- a/sql/table.h +++ b/sql/table.h @@ -63,6 +63,7 @@ typedef struct st_filesort_info class Field_timestamp; class Field_blob; +class Table_triggers_list; struct st_table { handler *file; @@ -144,6 +145,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; From 3db980958a8edc994d6e41d0c0f9d72225826162 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 7 Sep 2004 15:50:56 +0300 Subject: [PATCH 03/26] support of view to COM_FIELD_LIST added (BUG#5145) sql/sql_parse.cc: support of view to COM_FIELD_LIST added sql/sql_show.cc: support of view to COM_FIELD_LIST added --- sql/sql_parse.cc | 11 +++++++++++ sql/sql_show.cc | 9 +++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 546183563c9..0bfaf940f0e 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; @@ -1570,7 +1571,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(); free_items(thd->free_list); thd->free_list=0; /* free_list should never point to garbage */ break; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index f7b98bb0738..25bb7c63945 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1124,14 +1124,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 field_list; Field **ptr,*field; From 38404d90bec8810e664505678b039d8de8a10fb9 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 7 Sep 2004 19:58:02 +0300 Subject: [PATCH 04/26] ixed table and db name of fields incase of lower_case_table_names (BUG#5154) sql/item.cc: fixed table and db name of fields incase of lower_case_table_names --- mysql-test/r/lowercase_view.result | 9 +++++++++ mysql-test/t/lowercase_view-master.opt | 1 + mysql-test/t/lowercase_view.test | 11 +++++++++++ sql/item.cc | 24 +++++++++++++++++++++--- 4 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 mysql-test/r/lowercase_view.result create mode 100644 mysql-test/t/lowercase_view-master.opt create mode 100644 mysql-test/t/lowercase_view.test diff --git a/mysql-test/r/lowercase_view.result b/mysql-test/r/lowercase_view.result new file mode 100644 index 00000000000..233ca9590b8 --- /dev/null +++ b/mysql-test/r/lowercase_view.result @@ -0,0 +1,9 @@ +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; 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..ed19c2184ab --- /dev/null +++ b/mysql-test/t/lowercase_view.test @@ -0,0 +1,11 @@ +--disable_warnings +drop database if exists MySQLTest; +--enable_warnings + + +create database MySQLTest; +use MySQLTest; +create table TaB (Field int); +create view ViE as select * from TAb; +show create table VIe; +drop database MySQLTest; diff --git a/sql/item.cc b/sql/item.cc index efd94716dc7..a1b308483ab 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -421,6 +421,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"; @@ -429,9 +447,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)); } @@ -439,7 +457,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)); } From c647f600afb2f6967519d3262646ab2d98c6c61c Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 7 Sep 2004 20:40:16 +0300 Subject: [PATCH 05/26] returning test environment --- mysql-test/r/lowercase_view.result | 1 + mysql-test/t/lowercase_view.test | 1 + 2 files changed, 2 insertions(+) diff --git a/mysql-test/r/lowercase_view.result b/mysql-test/r/lowercase_view.result index 233ca9590b8..7c5741fe06a 100644 --- a/mysql-test/r/lowercase_view.result +++ b/mysql-test/r/lowercase_view.result @@ -7,3 +7,4 @@ 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; diff --git a/mysql-test/t/lowercase_view.test b/mysql-test/t/lowercase_view.test index ed19c2184ab..5b1be072c69 100644 --- a/mysql-test/t/lowercase_view.test +++ b/mysql-test/t/lowercase_view.test @@ -9,3 +9,4 @@ create table TaB (Field int); create view ViE as select * from TAb; show create table VIe; drop database MySQLTest; +use test; From 7de077f7dfc209bdfcc847920a394ff4c55a2e96 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 8 Sep 2004 10:18:04 +0300 Subject: [PATCH 06/26] test of updating and fetching from the same table check (BUG##5157) mysql-test/r/lowercase_view.result: test of updating and fetching from the same table check mysql-test/r/view.result: test of updating and fetching from the same table check mysql-test/t/lowercase_view.test: test of updating and fetching from the same table check mysql-test/t/view.test: test of updating and fetching from the same table check sql/mysql_priv.h: unique table test sql/sql_base.cc: unique table test which take into account views added sql/sql_delete.cc: unique table test which take into account views added sql/sql_insert.cc: unique table test which take into account views added sql/sql_parse.cc: unique table test which take into account views added sql/sql_update.cc: unique table test which take into account views added sql/sql_view.cc: unique table test which take into account views added sql/table.h: save next independent (do not belong to current view) table --- mysql-test/r/lowercase_view.result | 14 ++++++ mysql-test/r/view.result | 12 +++++ mysql-test/t/lowercase_view.test | 24 +++++++++- mysql-test/t/view.test | 18 ++++++++ sql/mysql_priv.h | 1 + sql/sql_base.cc | 72 ++++++++++++++++++++++++++++-- sql/sql_delete.cc | 3 +- sql/sql_insert.cc | 3 +- sql/sql_parse.cc | 41 +++++++++-------- sql/sql_update.cc | 5 +-- sql/sql_view.cc | 2 +- sql/table.h | 8 ++++ 12 files changed, 171 insertions(+), 32 deletions(-) diff --git a/mysql-test/r/lowercase_view.result b/mysql-test/r/lowercase_view.result index 7c5741fe06a..51f7dc758eb 100644 --- a/mysql-test/r/lowercase_view.result +++ b/mysql-test/r/lowercase_view.result @@ -1,3 +1,5 @@ +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; @@ -8,3 +10,15 @@ 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/view.result b/mysql-test/r/view.result index 35b76a8ab4b..7b95966937e 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -1270,3 +1270,15 @@ 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; diff --git a/mysql-test/t/lowercase_view.test b/mysql-test/t/lowercase_view.test index 5b1be072c69..2a2757650ae 100644 --- a/mysql-test/t/lowercase_view.test +++ b/mysql-test/t/lowercase_view.test @@ -1,8 +1,12 @@ --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); @@ -10,3 +14,21 @@ 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/view.test b/mysql-test/t/view.test index 9464e291e05..de9a49f479d 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -1230,3 +1230,21 @@ 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; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index a100aa0cd3a..8e9f78e4f0f 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -772,6 +772,7 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table, uint offset_to_list, const char *db_name, const char *table_name); +bool 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); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 5fcfe945f27..1b5ac788929 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -574,16 +574,80 @@ 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 + TRUE test failed + FALSE table is unique +*/ + +bool unique_table(TABLE_LIST *table, TABLE_LIST *table_list) +{ + char d_name_buff[MAX_ALIAS_NAME], t_name_buff[MAX_ALIAS_NAME]; + const char *d_name= table->db, *t_name= table->real_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 */ + return FALSE; + } + } + return find_table_in_global_list(table_list, d_name, t_name); +} + + TABLE **find_temporary_table(THD *thd, const char *db, const char *table_name) { char key[MAX_DBKEY_LENGTH]; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 089c0c00c3b..291504dae88 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -282,8 +282,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_insert.cc b/sql/sql_insert.cc index 857c500f200..71d8efba8cb 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -599,8 +599,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_parse.cc b/sql/sql_parse.cc index 0bfaf940f0e..d3a2d0639a1 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2393,17 +2393,7 @@ 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 (select_tables && check_table_access(thd, SELECT_ACL, select_tables, 0)) goto create_error; // Error message is given @@ -2412,6 +2402,17 @@ 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 ((result= new select_create(create_table, &lex->create_info, lex->create_list, @@ -2767,16 +2768,18 @@ 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))) { + /* + Is table which we are changing used somewhere in other parts of + query + */ + 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, diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 9d7134aee84..be5da76f380 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -496,8 +496,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 +787,7 @@ int multi_update::prepare(List ¬_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 2b1971907b3..cddeca33ebf 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -618,7 +618,7 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) if (lex->spfuns.array.buffer) hash_free(&lex->spfuns); - old_next= table->next_global; + old_next= table->old_next= table->next_global; if ((table->next_global= lex->query_tables)) table->next_global->prev_global= &table->next_global; diff --git a/sql/table.h b/sql/table.h index b7cabe21638..acb60b44282 100644 --- a/sql/table.h +++ b/sql/table.h @@ -217,6 +217,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 +262,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; From 12ce293a7fc18fdc2c6f8d7ec951d816d3661cf8 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 9 Sep 2004 00:46:01 +0400 Subject: [PATCH 07/26] WL#1218 "Triggers" After review and after merge fixes. mysql-test/t/trigger.test: After merge fix. Updated error codes. sql/sp_head.cc: After merge fix. To give some chances for functions/triggers we have to close tables during sp_instr_* execution only if we have opened them before. sql/sp_head.h: After merge fix. sp_instr constructor now takes one more argument. sql/sql_trigger.cc: After merge and review fixes. Some variable renaming and optimizations. sql/sql_yacc.yy: After merge fixes. sp_instr_* classes now require sp context as constructor parameter. Also we should be careful with adding table for which we are creating trigger to table list. Some elements in trigger body can damage LEX::query_tables and so we should add this table to list only after parsing trigger body. --- mysql-test/t/trigger.test | 20 ++++++++-------- sql/sp_head.cc | 6 ++--- sql/sp_head.h | 9 ++++---- sql/sql_trigger.cc | 35 ++++++++++++++-------------- sql/sql_yacc.yy | 48 ++++++++++++++++++++++++--------------- 5 files changed, 65 insertions(+), 53 deletions(-) diff --git a/mysql-test/t/trigger.test b/mysql-test/t/trigger.test index f7b9ebc8d2c..edbd2a92c3c 100644 --- a/mysql-test/t/trigger.test +++ b/mysql-test/t/trigger.test @@ -151,15 +151,15 @@ drop table t1; # create table t1 (i int); ---error 1358 +--error 1362 create trigger trg before insert on t1 for each row set @a:= old.i; ---error 1358 +--error 1362 create trigger trg before delete on t1 for each row set @a:= new.i; ---error 1357 +--error 1361 create trigger trg before update on t1 for each row set old.i:=1; ---error 1358 +--error 1362 create trigger trg before delete on t1 for each row set new.i:=1; ---error 1357 +--error 1361 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. @@ -173,23 +173,23 @@ create trigger trg after update on t1 for each row set new.i:=1; 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 1354 +--error 1358 create trigger trg after insert on t1 for each row set @a:=1; ---error 1354 +--error 1358 create trigger trg2 before insert on t1 for each row set @a:=1; drop trigger t1.trg; ---error 1355 +--error 1359 drop trigger t1.trg; create view v1 as select * from t1; ---error 1356 +--error 1360 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 1356 +--error 1360 create trigger trg before insert on t1 for each row set @a:=1; drop table t1; diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 3ff5f06103f..176a612d33d 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1230,7 +1230,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); } @@ -1387,7 +1387,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); } @@ -1444,7 +1444,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); } diff --git a/sql/sp_head.h b/sql/sp_head.h index a253d9edcbe..f4ed3760b94 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -392,8 +392,8 @@ class sp_instr_set_user_var : public sp_instr public: - sp_instr_set_user_var(uint ip, LEX_STRING var, Item *val) - : sp_instr(ip), m_set_var_item(var, val) + 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() @@ -419,8 +419,9 @@ class sp_instr_set_trigger_field : public sp_instr public: - sp_instr_set_trigger_field(uint ip, LEX_STRING field_name, Item *val) - : sp_instr(ip), + 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) {} diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 5e08192d0c1..56294b9bc80 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -319,18 +319,18 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, if (!strncmp(triggers_file_type.str, parser->type()->str, parser->type()->length)) { - int i; - Table_triggers_list *triggers_info= + Field **fld, **old_fld; + Table_triggers_list *triggers= new (&table->mem_root) Table_triggers_list(); - if (!triggers_info) + if (!triggers) DBUG_RETURN(1); - if (parser->parse((gptr)triggers_info, &table->mem_root, + if (parser->parse((gptr)triggers, &table->mem_root, triggers_file_parameters, 1)) DBUG_RETURN(1); - table->triggers= triggers_info; + table->triggers= triggers; /* We have to prepare array of Field objects which will represent OLD.* @@ -338,27 +338,26 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, TODO: This could be avoided if there is no ON UPDATE trigger. */ - if (!(triggers_info->old_field= + if (!(triggers->old_field= (Field **)alloc_root(&table->mem_root, (table->fields + 1) * sizeof(Field*)))) DBUG_RETURN(1); - for (i= 0; i < table->fields; i++) + 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 (!(triggers_info->old_field[i]= - table->field[i]->new_field(&table->mem_root, table))) + if (!(*old_fld= (*fld)->new_field(&table->mem_root, table))) DBUG_RETURN(1); - triggers_info->old_field[i]->move_field((my_ptrdiff_t) - (table->record[1] - - table->record[0])); + (*old_fld)->move_field((my_ptrdiff_t)(table->record[1] - + table->record[0])); } - triggers_info->old_field[i]= 0; + *old_fld= 0; - List_iterator_fast it(triggers_info->definitions_list); + List_iterator_fast it(triggers->definitions_list); LEX_STRING *trg_create_str, *trg_name_str; char *trg_name_buff; LEX *old_lex= thd->lex, lex; @@ -367,8 +366,8 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, while ((trg_create_str= it++)) { - lex_start(thd, (uchar*)trg_create_str->str, trg_create_str->length); - mysql_init_query(thd, true); + 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) { @@ -385,7 +384,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, goto err_with_lex_cleanup; } - triggers_info->bodies[lex.trg_chistics.event] + triggers->bodies[lex.trg_chistics.event] [lex.trg_chistics.action_time]= lex.sphead; lex.sphead= 0; @@ -404,7 +403,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, old_global_mem_root= my_pthread_getspecific_ptr(MEM_ROOT*, THR_MALLOC); my_pthread_setspecific_ptr(THR_MALLOC, &table->mem_root); - if (triggers_info->names_list.push_back(trg_name_str)) + if (triggers->names_list.push_back(trg_name_str)) goto err_with_lex_cleanup; my_pthread_setspecific_ptr(THR_MALLOC, old_global_mem_root); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 41432cee673..0f65aaa70da 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1212,15 +1212,6 @@ create: LEX *lex= Lex; sp_head *sp; - lex->name_and_length= $3; - - /* 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; - if (lex->sphead) { net_printf(YYTHD, ER_SP_NO_RECURSIVE_CREATE, "TRIGGER"); @@ -1256,6 +1247,23 @@ create: 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; } ; @@ -6922,10 +6930,18 @@ option_value: 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(), - $2, $4); + new sp_instr_set_user_var(lex->sphead->instructions(), + lex->spcont, $2, $4); lex->sphead->add_instr(i); } else @@ -6941,12 +6957,8 @@ option_value: /* We are in trigger and assigning value to field of new row */ Item *it; sp_instr_set_trigger_field *i; - if ($3 && $3->type() == Item::SUBSELECT_ITEM) - { /* - QQ For now, just disallow subselects as values - Unfortunately this doesn't helps in case when we have - subselect deeper in expression. - */ + if (lex->query_tables) + { send_error(YYTHD, ER_SP_SUBSELECT_NYI); YYABORT; } @@ -6958,7 +6970,7 @@ option_value: it= new Item_null(); } i= new sp_instr_set_trigger_field(lex->sphead->instructions(), - $1.base_name, it); + lex->spcont, $1.base_name, it); if (lex->trg_table && i->setup_field(YYTHD, lex->trg_table, lex->trg_chistics.event)) { From 0994dc396e9afe9516fae1b6fa2c707b1164d978 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 9 Sep 2004 19:08:54 +0200 Subject: [PATCH 08/26] Fixed BUG#4487: Stored procedure connection aborted if uninitialized char. mysql-test/r/sp.result: New test case for BUG#4487. mysql-test/t/sp.test: New test case for BUG#4487. sql/item_func.h: Propagate null_value properly in sp functions. --- mysql-test/r/sp.result | 13 +++++++++++++ mysql-test/t/sp.test | 12 ++++++++++++ sql/item_func.h | 10 ++++++++-- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index f0f51945f36..6d743bf5c71 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -1820,6 +1820,19 @@ 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 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/t/sp.test b/mysql-test/t/sp.test index 05d38d25956..1d7efab3841 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -1987,6 +1987,18 @@ 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| + # # Some "real" examples diff --git a/sql/item_func.h b/sql/item_func.h index 76d0346531e..95d0b3f451c 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1130,25 +1130,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(); From 97cbcbb0d7414c4bbb33fdf8a0a5bc90662cc7dc Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 9 Sep 2004 23:44:27 +0400 Subject: [PATCH 09/26] After merge fixes for WL#1218 "Triggers" mysql-test/t/trigger.test: After merge fix, error codes corrected. sql/sp_head.cc: After mrege fix. Fixed typo. sql/sql_trigger.cc: After merge fix. wait_if_gloabl_read_lock has one more argument now. --- mysql-test/t/trigger.test | 20 ++++++++++---------- sql/sp_head.cc | 3 +-- sql/sql_trigger.cc | 2 +- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/mysql-test/t/trigger.test b/mysql-test/t/trigger.test index edbd2a92c3c..8922f73661e 100644 --- a/mysql-test/t/trigger.test +++ b/mysql-test/t/trigger.test @@ -151,15 +151,15 @@ drop table t1; # create table t1 (i int); ---error 1362 +--error 1363 create trigger trg before insert on t1 for each row set @a:= old.i; ---error 1362 +--error 1363 create trigger trg before delete on t1 for each row set @a:= new.i; ---error 1361 -create trigger trg before update on t1 for each row set old.i:=1; --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 1361 +--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. @@ -173,23 +173,23 @@ create trigger trg after update on t1 for each row set new.i:=1; 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 1358 +--error 1359 create trigger trg after insert on t1 for each row set @a:=1; ---error 1358 +--error 1359 create trigger trg2 before insert on t1 for each row set @a:=1; drop trigger t1.trg; ---error 1359 +--error 1360 drop trigger t1.trg; create view v1 as select * from t1; ---error 1360 +--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 1360 +--error 1361 create trigger trg before insert on t1 for each row set @a:=1; drop table t1; diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 8028ac8ba4f..311b464b9c7 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -311,9 +311,8 @@ sp_head::init_strings(THD *thd, LEX *lex, sp_name *name) } else if (thd->db) { - m_db.length= thd->db_length + m_db.length= thd->db_length; m_db.str= strmake_root(root, thd->db, m_db.length); - } if (m_param_begin && m_param_end) diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 56294b9bc80..c376e86f18c 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -88,7 +88,7 @@ int mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) 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)) + if (wait_if_global_read_lock(thd, 0, 0)) DBUG_RETURN(-1); VOID(pthread_mutex_lock(&LOCK_open)); From b386f888da6eaf55c50d5ee9f8c2fa46c6b0cbf2 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 10 Sep 2004 02:22:44 +0300 Subject: [PATCH 10/26] post merge fixes mysql-test/t/merge.test: additional test sql/item.h: parameter to detect need of saving view field names sql/item_subselect.cc: arena fix used nearest thd pointer sql/item_sum.cc: after merge fix sql/mysql_priv.h: after merge fix sql/sql_base.cc: fixed unique test fixed postmerge arena mamgement sql/sql_class.h: mey method to detect conventional execution sql/sql_handler.cc: new parameter sql/sql_lex.cc: fixed postmerge arena mamgement sql/sql_parse.cc: fixed unique test sql/sql_select.cc: fixed postmerge arena mamgement sql/sql_union.cc: fixed postmerge arena mamgement sql/sql_view.cc: fixed postmerge arena mamgement sql/table.cc: fixed postmerge arena mamgement --- mysql-test/t/merge.test | 2 ++ sql/item.h | 2 +- sql/item_subselect.cc | 44 ++++++++++++++++++++--------------------- sql/item_sum.cc | 2 +- sql/mysql_priv.h | 5 +++-- sql/sql_base.cc | 34 +++++++++++++++++++------------ sql/sql_class.h | 2 ++ sql/sql_handler.cc | 2 +- sql/sql_lex.cc | 2 +- sql/sql_parse.cc | 29 ++++++++++++++------------- sql/sql_select.cc | 13 ++++++++---- sql/sql_union.cc | 2 +- sql/sql_view.cc | 2 +- sql/table.cc | 2 +- 14 files changed, 81 insertions(+), 62 deletions(-) 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/sql/item.h b/sql/item.h index 1420a89c76a..d0482c44875 100644 --- a/sql/item.h +++ b/sql/item.h @@ -434,7 +434,7 @@ public: friend bool insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name, const char *table_name, List_iterator *it, - bool any_privileges); + bool any_privileges, bool allocate_view_names); }; diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 23dbcf8af48..9cd01255d43 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -316,9 +316,9 @@ 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; + 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 +337,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 +367,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 +654,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); @@ -900,7 +900,7 @@ 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()) + if (arena->is_conventional()) arena= 0; else thd->set_n_backup_item_arena(arena, &backup); @@ -1192,17 +1192,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 +1214,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 +1225,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_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/mysql_priv.h b/sql/mysql_priv.h index f802c77a0db..8c73c6ad738 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -756,7 +756,8 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table, List *index_list); bool insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name, const char *table_name, - List_iterator *it, bool any_privileges); + List_iterator *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 &fields, List *sum_func_list, uint wild_num); @@ -782,7 +783,7 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table, uint offset_to_list, const char *db_name, const char *table_name); -bool unique_table(TABLE_LIST *table, TABLE_LIST *table_list); +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); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index aad372fd574..51c25940fac 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -613,14 +613,15 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table, table_list list of tables RETURN - TRUE test failed - FALSE table is unique + found duplicate + 0 if table is unique */ -bool unique_table(TABLE_LIST *table, TABLE_LIST *table_list) +TABLE_LIST* unique_table(TABLE_LIST *table, TABLE_LIST *table_list) { - char d_name_buff[MAX_ALIAS_NAME], t_name_buff[MAX_ALIAS_NAME]; + 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 */ @@ -640,11 +641,17 @@ bool unique_table(TABLE_LIST *table, TABLE_LIST *table_list) } if (d_name == 0) { - /* it's temporary table */ - return FALSE; + /* it's temporary table => always unique */ + return 0; } } - return find_table_in_global_list(table_list, d_name, t_name); + 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; } @@ -2504,7 +2511,7 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List &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); @@ -2530,8 +2537,8 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List &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); @@ -2717,6 +2724,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 @@ -2726,7 +2734,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 *it, - bool any_privileges) + bool any_privileges, bool allocate_view_names) { /* allocate variables on stack to avoid pool alloaction */ Field_iterator_table table_iter; @@ -2886,7 +2894,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), @@ -2931,7 +2939,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.h b/sql/sql_class.h index 1612ab6fb17..d1a8366653f 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -476,6 +476,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) { diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 0df3d617d7f..f95291d914e 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -249,7 +249,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_lex.cc b/sql/sql_lex.cc index 241e9b863f1..317033512c0 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1795,7 +1795,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_parse.cc b/sql/sql_parse.cc index 9bad6c05d3c..60c273500e6 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2404,20 +2404,6 @@ mysql_execute_command(THD *thd) if (select_lex->item_list.elements) // With select { select_result *result; - 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)) @@ -2437,6 +2423,21 @@ mysql_execute_command(THD *thd) 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, diff --git a/sql/sql_select.cc b/sql/sql_select.cc index def641d9616..789a4dc3086 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -6156,16 +6156,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) 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_view.cc b/sql/sql_view.cc index 649953897e7..91b66251fd0 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -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); diff --git a/sql/table.cc b/sql/table.cc index d07d2ca085d..00913363635 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1583,7 +1583,7 @@ bool st_table_list::setup_ancestor(THD *thd, Item **conds) if (where) { Item_arena *arena= thd->current_arena, backup; - if (!arena->is_stmt_prepare()) + if (arena->is_conventional()) arena= 0; // For easier test if (!where->fixed && where->fix_fields(thd, ancestor, &where)) From 862e78568e43272002dea1a12ff62e3d097be558 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 10 Sep 2004 10:29:08 +0400 Subject: [PATCH 11/26] Fixed --with-embedded builds. Added previously missing sql_trigger.cc file to embedded library. BitKeeper/etc/ignore: Added libmysqld/sql_trigger.cc to the ignore list libmysqld/Makefile.am: Added previously sql_trigger.cc file to embedded library. --- .bzrignore | 1 + libmysqld/Makefile.am | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.bzrignore b/.bzrignore index be7211af9e2..2ab7737922f 100644 --- a/.bzrignore +++ b/.bzrignore @@ -919,3 +919,4 @@ vio/test-ssl vio/test-sslclient vio/test-sslserver vio/viotest-ssl +libmysqld/sql_trigger.cc 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= From 1912148cec19f4a20eddae9d0143124909872173 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 10 Sep 2004 11:11:52 +0200 Subject: [PATCH 12/26] Fixed BUG#3294: Stored procedure crash if table dropped before use. Dropping the table was not the real problem, the problem was with errors occuring within error handlers. mysql-test/r/sp-error.result: New test case for BUG#3294. mysql-test/t/sp-error.test: New test case for BUG#3294. sql/sp_head.cc: Use hreturn instruction both for continue and exit handlers (a special case of a jump). sql/sp_head.h: Use hreturn instruction both for continue and exit handlers (a special case of a jump). sql/sp_rcontext.cc: Keep track on if we're in a handler already, for error handling. sql/sp_rcontext.h: Keep track on if we're in a handler already, for error handling. sql/sql_yacc.yy: Use hreturn instruction both for continue and exit handlers (a special case of a jump). --- mysql-test/r/sp-error.result | 9 +++++++++ mysql-test/t/sp-error.test | 18 +++++++++++++++++- sql/sp_head.cc | 28 +++++++++++++++++++++++++--- sql/sp_head.h | 10 +++------- sql/sp_rcontext.cc | 3 +++ sql/sp_rcontext.h | 2 ++ sql/sql_yacc.yy | 10 +++++++--- 7 files changed, 66 insertions(+), 14 deletions(-) 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/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/sql/sp_head.cc b/sql/sp_head.cc index 311b464b9c7..6e4269ad8a5 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -499,6 +499,7 @@ sp_head::execute(THD *thd) ip= hip; ret= 0; ctx->clear_handler(); + ctx->in_handler= TRUE; continue; } } @@ -1586,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 f4ed3760b94..9c308961aa4 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -677,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 &); @@ -685,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() @@ -695,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..1bdd022470b 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 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_yacc.yy b/sql/sql_yacc.yy index 8a5526a6b9f..8d1a668e98d 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1599,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 */ From 76001900fd1a13a4dba02472cbbda0f04214db34 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 10 Sep 2004 13:09:27 +0300 Subject: [PATCH 13/26] thd pointer fixed BitKeeper/etc/ignore: Added libmysqld/sql_trigger.cc to the ignore list mysql-test/r/merge.result: fix of test --- .bzrignore | 1 + mysql-test/r/merge.result | 2 ++ sql/item_subselect.cc | 15 ++++++++++++--- sql/item_subselect.h | 2 +- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/.bzrignore b/.bzrignore index be7211af9e2..2ab7737922f 100644 --- a/.bzrignore +++ b/.bzrignore @@ -919,3 +919,4 @@ vio/test-ssl vio/test-sslclient vio/test-sslserver vio/viotest-ssl +libmysqld/sql_trigger.cc 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/sql/item_subselect.cc b/sql/item_subselect.cc index 9cd01255d43..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,6 +323,8 @@ 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 */ + 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_conventional()) @@ -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,7 +908,7 @@ Item_in_subselect::row_value_transformer(JOIN *join) } thd->where= "row IN/ALL/ANY subquery"; - Item_arena *arena= join->thd->current_arena, backup; + Item_arena *arena= thd->current_arena, backup; if (arena->is_conventional()) arena= 0; else 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); }; From b5983daa8cb962957d58b2a3b4e4b65a5b62882c Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 10 Sep 2004 15:08:30 +0300 Subject: [PATCH 14/26] post merge fix --- mysql-test/t/view.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index 90a6656cdcf..174572198b5 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -1254,7 +1254,7 @@ drop table t1,t2; # create table t1 (s1 int); create view v1 as select * from t1; --- error 1346 +-- error 1347 handler v1 open as xx; drop view v1; drop table t1; From 749c03840ade0fe36d78069d54e0f43b60149acb Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 10 Sep 2004 16:28:11 +0200 Subject: [PATCH 15/26] Fixed BUG#4941: Stored procedure crash fetching null value into variable. mysql-test/r/sp.result: New test case for BUG#4941. mysql-test/t/sp.test: New test case for BUG#4941. sql/protocol_cursor.cc: Handle null values. sql/sp_rcontext.cc: Handle null values. --- mysql-test/r/sp.result | 16 ++++++++++++++++ mysql-test/t/sp.test | 22 ++++++++++++++++++++++ sql/protocol_cursor.cc | 3 ++- sql/sp_rcontext.cc | 27 +++++++++++++++------------ 4 files changed, 55 insertions(+), 13 deletions(-) diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 6d743bf5c71..f84b224b8e0 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -1833,6 +1833,22 @@ 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/t/sp.test b/mysql-test/t/sp.test index 1d7efab3841..e2c82c9f0da 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -2000,6 +2000,28 @@ 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/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/sp_rcontext.cc b/sql/sp_rcontext.cc index 1bdd022470b..2f7bdbffa2b 100644 --- a/sql/sp_rcontext.cc +++ b/sql/sp_rcontext.cc @@ -230,21 +230,24 @@ sp_cursor::fetch(THD *thd, List *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()) From 9af3278fa27e65f3b2c984038663ac1b7d720193 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 10 Sep 2004 13:56:06 -0500 Subject: [PATCH 16/26] sql_yacc.yy: Make "FRAC_SECOND"/"SQL_TSI_FRAC_SECOND" non-reserved words, must like "SECOND"/"SQL_TSI_SECOND", "MINUTE"/"SQL_TSI_MINUTE", etc. Will wait for okay to push. (It doesn't break any tests.) sql/sql_yacc.yy: Make "FRAC_SECOND"/"SQL_TSI_FRAC_SECOND" non-reserved words, must like "SECOND"/"SQL_TSI_SECOND", "MINUTE"/"SQL_TSI_MINUTE", etc. --- sql/sql_yacc.yy | 1 + 1 file changed, 1 insertion(+) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 8d1a668e98d..fab8f19950f 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -6795,6 +6795,7 @@ keyword: | FIRST_SYM {} | FIXED_SYM {} | FLUSH_SYM {} + | FRAC_SECOND_SYM {} | GEOMETRY_SYM {} | GEOMETRYCOLLECTION {} | GET_FORMAT {} From 8b2ffdbfb97688d763c69a0563fa2de7504d59a5 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 10 Sep 2004 22:39:04 +0300 Subject: [PATCH 17/26] view with WHERE in nested join (BUG#5511) mysql-test/r/view.result: view with WHERE in nested join mysql-test/t/view.test: view with WHERE in nested join sql/table.cc: fixed merging of VIEW --- mysql-test/r/view.result | 23 +++++++++++++++++++++++ mysql-test/t/view.test | 13 +++++++++++++ sql/table.cc | 26 +++++++++++++++++--------- 3 files changed, 53 insertions(+), 9 deletions(-) diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index ac606469323..f57fff5ed0d 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -1288,3 +1288,26 @@ 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/view.test b/mysql-test/t/view.test index 174572198b5..682646a6c02 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -1258,3 +1258,16 @@ create view v1 as select * from t1; 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/sql/table.cc b/sql/table.cc index 00913363635..4156da2d71a 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1583,6 +1583,7 @@ bool st_table_list::setup_ancestor(THD *thd, Item **conds) if (where) { Item_arena *arena= thd->current_arena, backup; + TABLE_LIST *tbl= this; if (arena->is_conventional()) arena= 0; // For easier test @@ -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) + */ + 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); } From 952a778d23e8adaeaa29aa75e2cf30545cf0c959 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 10 Sep 2004 15:04:28 -0500 Subject: [PATCH 18/26] Fix bad output in SHOW CREATE VIEW, update affected test result. mysql-test/r/view.result: Update test result to match sql_show.cc change. sql/sql_show.cc: Fix bad output in SHOW CREATE VIEW. --- mysql-test/r/view.result | 8 ++++---- sql/sql_show.cc | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index ac606469323..2b9d8d91653 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; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 6e8365b2233..7868bc176bf 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1601,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); } From 15526e4ed7f7f38c83382f6402b37a413e691620 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 11 Sep 2004 00:44:09 +0400 Subject: [PATCH 19/26] Fixed a problem causing load_defaults not to accept some command-line options. mysys/default.c: Fixed search_files function to return the number of used arguments properly. BitKeeper/etc/logging_ok: Logging to logging@openlogging.org accepted --- BitKeeper/etc/logging_ok | 1 + mysys/default.c | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok index b850584df5b..86ee9bcad4f 100644 --- a/BitKeeper/etc/logging_ok +++ b/BitKeeper/etc/logging_ok @@ -144,6 +144,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/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)++; } } From 8ad470e23ff7779a2ccb42c5e770799a1952d940 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 11 Sep 2004 09:36:43 +0300 Subject: [PATCH 20/26] fixed typo (thanks to Igor) sql/table.cc: fixed typo --- sql/table.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/table.cc b/sql/table.cc index 4156da2d71a..e6b84a5acb0 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1604,7 +1604,7 @@ bool st_table_list::setup_ancestor(THD *thd, Item **conds) expression will not be moved to WHERE condition (i.e. will be clean correctly for PS/SP) */ - on_expr= and_conds(tbl->on_expr, where); + tbl->on_expr= and_conds(tbl->on_expr, where); break; } } From 5d7848d8bf874873494ec17bc256490e18d7211c Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 11 Sep 2004 18:46:06 +0300 Subject: [PATCH 21/26] fixed privilege system upgrade script, to allow VIEW privileges for used who have CREATE privilege (BUG#5329) scripts/mysql_fix_privilege_tables.sql: fixed privilege system upgrade script, to allow VIEW privileges for used who have CREATE privilege --- scripts/mysql_fix_privilege_tables.sql | 12 ++++++++++++ 1 file changed, 12 insertions(+) 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) # @@ -158,6 +165,11 @@ ALTER TABLE db ADD Show_view_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Creat ALTER TABLE host ADD Show_view_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Create_view_priv; 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 # From ae70baf21ce2b8e17bacf1ca57aed08c9cf745ed Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 11 Sep 2004 23:52:55 +0300 Subject: [PATCH 22/26] table lists management during VIEW processing code cleanup sql/sql_lex.h: comments added new method of adding table to global query tables list sql/sql_parse.cc: used new method to add table to query tables list removed spaces at end of line in new code sql/sql_view.cc: layout fixed table lists management code cleanup --- sql/sql_lex.h | 11 +++++- sql/sql_parse.cc | 39 +++++++++--------- sql/sql_view.cc | 100 +++++++++++++++++++++++------------------------ 3 files changed, 77 insertions(+), 73 deletions(-) diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 9e724dab6a6..d43b1f81f3d 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -643,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 */ @@ -765,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 556bc5d5d88..e1baf47b234 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5152,8 +5152,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); } @@ -5162,9 +5161,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. @@ -5184,7 +5183,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)))) @@ -5209,7 +5208,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 @@ -5241,7 +5240,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 @@ -5257,7 +5256,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)))) @@ -5281,7 +5280,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 @@ -5289,11 +5288,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) @@ -5315,7 +5314,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 @@ -5350,9 +5349,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 @@ -5376,10 +5375,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); @@ -5443,7 +5442,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 diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 91b66251fd0..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; @@ -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->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; @@ -653,6 +658,29 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) if (lex->select_lex.options & OPTION_TO_QUERY_CACHE) 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 @@ -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); From 93527151118189fb935ade4791ad914c94fab591 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 13 Sep 2004 16:48:01 +0300 Subject: [PATCH 23/26] Added per thread status variables, after review, patch v1.1. --- include/my_sys.h | 4 + sql/filesort.cc | 10 +- sql/ha_berkeley.cc | 38 +++--- sql/ha_heap.cc | 26 ++-- sql/ha_innodb.cc | 27 ++-- sql/ha_myisam.cc | 31 ++--- sql/ha_myisammrg.cc | 28 +++-- sql/ha_ndbcluster.cc | 2 +- sql/handler.cc | 14 +-- sql/item_strfunc.cc | 7 +- sql/mysql_priv.h | 23 ++-- sql/mysqld.cc | 294 +++++++++++++++++++++++-------------------- sql/net_serv.cc | 15 ++- sql/sql_base.cc | 1 + sql/sql_class.cc | 47 +++++++ sql/sql_class.h | 61 +++++++++ sql/sql_parse.cc | 70 +++++++---- sql/sql_select.cc | 21 ++-- sql/sql_show.cc | 32 ++++- sql/sql_test.cc | 12 +- sql/sql_yacc.yy | 8 +- sql/structs.h | 3 +- sql/table.cc | 2 +- 23 files changed, 505 insertions(+), 271 deletions(-) diff --git a/include/my_sys.h b/include/my_sys.h index 2ac86df87e8..746bd573aac 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -781,6 +781,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/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 00df84e3797..9e707b52490 100644 --- a/sql/ha_berkeley.cc +++ b/sql/ha_berkeley.cc @@ -853,7 +853,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]) @@ -1097,10 +1097,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); @@ -1290,7 +1291,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 */ @@ -1437,7 +1438,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; @@ -1456,9 +1457,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) { @@ -1527,7 +1529,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 */ @@ -1551,7 +1554,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)); @@ -1562,7 +1566,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), @@ -1582,7 +1587,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)); @@ -1593,7 +1599,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)); @@ -1603,7 +1610,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)); @@ -1625,7 +1633,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)); @@ -1656,9 +1665,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 cc828b6e6b2..859e8155ac5 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 700b8fafe19..77ef48cbd62 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -2161,7 +2161,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); @@ -2780,7 +2781,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; @@ -2886,7 +2888,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); @@ -3018,7 +3021,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)); } @@ -3035,7 +3039,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)); } @@ -3069,7 +3074,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); @@ -3095,7 +3101,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); @@ -3160,7 +3167,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); @@ -3196,7 +3204,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_myisam.cc b/sql/ha_myisam.cc index 79e1d7b463b..7b8d681b3ec 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) @@ -1050,7 +1050,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); @@ -1058,7 +1058,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); } @@ -1066,7 +1066,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; @@ -1075,7 +1075,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; @@ -1084,7 +1084,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; @@ -1093,7 +1093,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; @@ -1102,7 +1102,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; @@ -1111,7 +1111,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; @@ -1120,7 +1121,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; @@ -1131,7 +1132,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; @@ -1147,7 +1148,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; @@ -1160,7 +1162,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; @@ -1568,7 +1570,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 5b36d6d2b55..8a9ceda42de 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -1562,7 +1562,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); } diff --git a/sql/handler.cc b/sql/handler.cc index 9342d20ec24..86d56e14560 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -51,11 +51,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; @@ -541,7 +537,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(); } } @@ -630,7 +626,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); @@ -703,7 +699,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 */ @@ -898,7 +894,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_strfunc.cc b/sql/item_strfunc.cc index 5f2c37dd8a7..df144925610 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -2844,6 +2844,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 */ { @@ -2858,7 +2860,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); } @@ -2868,7 +2870,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/mysql_priv.h b/sql/mysql_priv.h index a100aa0cd3a..16d99876cb1 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -682,7 +682,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 *files, const char *db, const char *path, const char *wild, bool dir); int mysqld_show_charsets(THD *thd,const char *wild); @@ -691,6 +692,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, @@ -903,36 +905,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; @@ -982,6 +976,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 ac275f0d765..6a9377082f3 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 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 */ @@ -5137,117 +5133,136 @@ 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_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_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_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}, - {"Com_prepare_sql", (char*) (com_stat+(uint) SQLCOM_PREPARE), - SHOW_LONG}, - {"Com_execute_sql", (char*) (com_stat+(uint) SQLCOM_EXECUTE), - SHOW_LONG}, - {"Com_dealloc_sql", (char*) (com_stat+(uint) - SQLCOM_DEALLOCATE_PREPARE), 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_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_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_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}, + {"Com_prepare_sql", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_PREPARE]), SHOW_LONG_STATUS}, + {"Com_execute_sql", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_EXECUTE]), SHOW_LONG_STATUS}, + {"Com_dealloc_sql", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DEALLOCATE_PREPARE]), 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_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_commit", (char*) offsetof(STATUS_VAR, ha_commit_count), + SHOW_LONG_STATUS}, + {"Handler_delete", (char*) offsetof(STATUS_VAR, ha_delete_count), + SHOW_LONG_STATUS}, + {"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}, {"Handler_discover", (char*) &ha_discover_count, SHOW_LONG}, {"Key_blocks_not_flushed", (char*) &dflt_key_cache_var.global_blocks_changed, SHOW_KEY_CACHE_LONG}, @@ -5269,7 +5284,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}, @@ -5285,19 +5301,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}, @@ -5428,27 +5461,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/sql_base.cc b/sql/sql_base.cc index 5fcfe945f27..d6ffa33a890 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1721,6 +1721,7 @@ int open_and_lock_tables(THD *thd, TABLE_LIST *tables) if (open_tables(thd, tables, &counter) || lock_tables(thd, tables, counter) || mysql_handle_derived(thd->lex)) DBUG_RETURN(thd->net.report_error ? -1 : 1); /* purecov: inspected */ + return 0; /* To avoid a compiler warning */ } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index da64479abf2..4f07da890b3 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -297,6 +297,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)); } @@ -387,6 +388,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 @@ -429,6 +431,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); @@ -1553,3 +1576,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 eccaf072008..9d23d366cd2 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -442,6 +442,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); @@ -664,6 +719,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 @@ -1036,6 +1092,7 @@ public: } inline CHARSET_INFO *charset() { return variables.character_set_client; } void update_charset(); + void set_status_var_init(); inline void allocate_temporary_memory_pool_for_ps_preparing() { @@ -1521,3 +1578,7 @@ public: bool send_data(List &items); bool send_eof(); }; + +/* Functions in sql_class.cc */ + +void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 546183563c9..c72d85846ee 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1342,7 +1342,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 +1364,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 +1378,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 +1545,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)) { @@ -1587,7 +1589,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)) { @@ -1602,7 +1605,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)) @@ -1624,7 +1628,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; @@ -1650,7 +1654,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; @@ -1664,7 +1669,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 */ /* @@ -1703,7 +1708,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 @@ -1713,8 +1719,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 @@ -1729,11 +1735,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); @@ -1743,14 +1750,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: @@ -1768,7 +1776,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); @@ -1807,7 +1815,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); } } @@ -1970,7 +1978,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: { @@ -2936,13 +2945,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 @@ -5601,6 +5620,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 a7ce5320ae4..dfe76742e7b 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -5122,6 +5122,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"); @@ -5220,7 +5221,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 { @@ -5230,13 +5232,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 @@ -5244,13 +5248,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) @@ -6672,7 +6678,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &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); @@ -7251,7 +7257,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 629b7a3cf3a..3ca2a077bf9 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1853,7 +1853,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 field_list; @@ -1891,6 +1892,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); @@ -2160,6 +2165,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 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; template class List; 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_yacc.yy b/sql/sql_yacc.yy index 17cc3b7d12c..cffb105952f 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -5784,8 +5784,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 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 1764df75a7e..13021a6ea45 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); From 66434a6c6a2a9d58f039370a0534604414756011 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 14 Sep 2004 00:35:42 +0400 Subject: [PATCH 24/26] Two small fixes to make -debug version usable again. include/my_sys.h: Temporaly reverting Monty's optimization of clear_alloc_root() since it makes -debug server unusable. sql/sql_select.cc: After merge fix. Now deletion is done outside of handle_select(). (We fixed the same issue with deletion of LEX::result in 4.1 for prepared statements but in more general way). --- include/my_sys.h | 2 +- sql/sql_select.cc | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/include/my_sys.h b/include/my_sys.h index 38f1ca1cad4..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); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index ca81e30bb8e..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); } From e32059a93e3cf0827904111ee3d2d18654711f90 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 14 Sep 2004 22:02:23 +0300 Subject: [PATCH 25/26] Fixed a couple of bugs. sql/ha_ndbcluster.cc: Changed some old global variables to per thread specific in ha_ndbcluster.cc sql/sql_help.cc: Added parenthesis. BitKeeper/etc/logging_ok: Logging to logging@openlogging.org accepted --- BitKeeper/etc/logging_ok | 1 + sql/ha_ndbcluster.cc | 26 +++++++++++++++++--------- sql/sql_help.cc | 2 +- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok index 312aab90ddf..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) diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 13086724b8c..542f501c8ee 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); } @@ -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/sql_help.cc b/sql/sql_help.cc index 8fc0671c808..cba74c93a6a 100644 --- a/sql/sql_help.cc +++ b/sql/sql_help.cc @@ -640,7 +640,7 @@ int mysqld_help(THD *thd, const char *mask) uint mlen= strlen(mask); MEM_ROOT *mem_root= &thd->mem_root; - if (res= open_and_lock_tables(thd, tables)) + if ((res= open_and_lock_tables(thd, tables))) goto end; /* Init tables and fields to be usable from items From 4ef01ca25c71ac59e751ea478a13dff480ebb98e Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 15 Sep 2004 22:13:55 +0300 Subject: [PATCH 26/26] Added some missing per status variables. sql/ha_isam.cc: Added per status variables. sql/ha_isammrg.cc: Added per status variables. --- sql/ha_isam.cc | 30 ++++++++++++++++++------------ sql/ha_isammrg.cc | 9 +++++---- 2 files changed, 23 insertions(+), 16 deletions(-) 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;