From 1626c2d9e1372bb5c7d26bf381220271bd9736bd Mon Sep 17 00:00:00 2001 From: "bell@book.local" <> Date: Wed, 15 Mar 2006 19:15:52 +0200 Subject: [PATCH 1/7] We should prohobit concurent read of inserting file in SP because it can couse problem with Query cache (BUG#14767) --- mysql-test/r/query_cache_notembedded.result | 30 ++++++++++++ mysql-test/t/query_cache_notembedded.test | 40 ++++++++++++++++ sql/sql_insert.cc | 51 ++++++++++++--------- sql/sql_parse.cc | 13 ++++++ sql/sql_yacc.yy | 25 +++++++++- 5 files changed, 136 insertions(+), 23 deletions(-) diff --git a/mysql-test/r/query_cache_notembedded.result b/mysql-test/r/query_cache_notembedded.result index 77fa198eb80..4226738725a 100644 --- a/mysql-test/r/query_cache_notembedded.result +++ b/mysql-test/r/query_cache_notembedded.result @@ -314,4 +314,34 @@ drop procedure f2; drop procedure f3; drop procedure f4; drop table t1; +reset query cache; +drop function if exists f1; +create table t1 (id int); +create function f1 () +returns int +begin +declare i_var int; +set i_var = sleep(3); +insert into t1 values(3); +set i_var = sleep(3); +return 0; +end;| + select f1(); +select sleep(4); +sleep(4) +0 +select * from t1; +id +3 +f1() +0 +select * from t1; +id +3 +reset query cache; +select * from t1; +id +3 +drop table t1; +drop function f1; set GLOBAL query_cache_size=0; diff --git a/mysql-test/t/query_cache_notembedded.test b/mysql-test/t/query_cache_notembedded.test index 5e1ab7051e5..97be9f9f7ca 100644 --- a/mysql-test/t/query_cache_notembedded.test +++ b/mysql-test/t/query_cache_notembedded.test @@ -180,5 +180,45 @@ drop procedure f3; drop procedure f4; drop table t1; +# +# bug#14767: INSERT in SF + concurrent SELECT with query cache +# +reset query cache; +--disable_warnings +drop function if exists f1; +--enable_warnings +create table t1 (id int); +delimiter |; +create function f1 () + returns int +begin + declare i_var int; + set i_var = sleep(3); + insert into t1 values(3); + set i_var = sleep(3); + return 0; +end;| +delimiter ;| + +connect (con1,localhost,root,,); +connect (con2,localhost,root,,); + +connection con1; +send select f1(); +connection con2; +select sleep(4); +select * from t1; +connection con1; +reap; +connection con2; +# This gives wrong result i.e. 't' table seems to be empty +select * from t1; +reset query cache; +select * from t1; +drop table t1; +drop function f1; +disconnect con1; +disconnect con2; +connection default; set GLOBAL query_cache_size=0; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index eb74144c1ea..b6ff0ecc216 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -258,6 +258,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, bool log_on= (thd->options & OPTION_BIN_LOG) || (!(thd->security_ctx->master_access & SUPER_ACL)); bool transactional_table, joins_freed= FALSE; + bool changed; uint value_count; ulong counter = 1; ulonglong id; @@ -544,32 +545,30 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, else if (table->next_number_field && info.copied) id=table->next_number_field->val_int(); // Return auto_increment value - /* - Invalidate the table in the query cache if something changed. - For the transactional algorithm to work the invalidation must be - before binlog writing and ha_autocommit_or_rollback - */ - if (info.copied || info.deleted || info.updated) - { - query_cache_invalidate3(thd, table_list, 1); - } - transactional_table= table->file->has_transactions(); - if ((info.copied || info.deleted || info.updated) && - (error <= 0 || !transactional_table)) + if ((changed= (info.copied || info.deleted || info.updated))) { - if (mysql_bin_log.is_open()) + /* + Invalidate the table in the query cache if something changed. + For the transactional algorithm to work the invalidation must be + before binlog writing and ha_autocommit_or_rollback + */ + query_cache_invalidate3(thd, table_list, 1); + if (error <= 0 || !transactional_table) { - if (error <= 0) - thd->clear_error(); - Query_log_event qinfo(thd, thd->query, thd->query_length, - transactional_table, FALSE); - if (mysql_bin_log.write(&qinfo) && transactional_table) - error=1; + if (mysql_bin_log.is_open()) + { + if (error <= 0) + thd->clear_error(); + Query_log_event qinfo(thd, thd->query, thd->query_length, + transactional_table, FALSE); + if (mysql_bin_log.write(&qinfo) && transactional_table) + error=1; + } + if (!transactional_table) + thd->options|=OPTION_STATUS_NO_TRANS_UPDATE; } - if (!transactional_table) - thd->options|=OPTION_STATUS_NO_TRANS_UPDATE; } if (transactional_table) error=ha_autocommit_or_rollback(thd,error); @@ -577,6 +576,16 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, if (thd->lock) { mysql_unlock_tables(thd, thd->lock); + /* + Invalidate the table in the query cache if something changed + after unlocking when changes become fisible. + TODO: this is workaround. right way will be move invalidating in + the unlock procedure. + */ + if (lock_type == TL_WRITE_CONCURRENT_INSERT && changed) + { + query_cache_invalidate3(thd, table_list, 1); + } thd->lock=0; } } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index d9f5499f362..f9d04fc873e 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3314,6 +3314,19 @@ end_with_restore_list: select_lex->context.table_list= select_lex->context.first_name_resolution_table= second_table; res= handle_select(thd, lex, result, OPTION_SETUP_TABLES_DONE); + /* + Invalidate the table in the query cache if something changed + after unlocking when changes become visible. + TODO: this is workaround. right way will be move invalidating in + the unlock procedure. + */ + if (first_table->lock_type == TL_WRITE_CONCURRENT_INSERT && + thd->lock) + { + mysql_unlock_tables(thd, thd->lock); + query_cache_invalidate3(thd, first_table, 1); + thd->lock=0; + } delete result; } /* revert changes for SP */ diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 03da97b91c9..ddc267eb970 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -6068,7 +6068,19 @@ replace: ; insert_lock_option: - /* empty */ { $$= TL_WRITE_CONCURRENT_INSERT; } + /* empty */ + { +#ifdef HAVE_QUERY_CACHE + /* + If it is SP we do not allow insert optimisation whan result of + insert visible only after the table unlocking but everyone can + read table. + */ + $$= (Lex->sphead ? TL_WRITE :TL_WRITE_CONCURRENT_INSERT); +#else + $$= TL_WRITE_CONCURRENT_INSERT; +#endif + } | LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; } | DELAYED_SYM { $$= TL_WRITE_DELAYED; } | HIGH_PRIORITY { $$= TL_WRITE; } @@ -6925,7 +6937,16 @@ opt_local: load_data_lock: /* empty */ { $$= YYTHD->update_lock_default; } - | CONCURRENT { $$= TL_WRITE_CONCURRENT_INSERT ; } + | CONCURRENT + { +#ifdef HAVE_QUERY_CACHE + /* + Ignore this option in SP to avoid problem with query cache + */ + if (Lex->sphead != 0) +#endif + $$= TL_WRITE_CONCURRENT_INSERT; + } | LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; }; From 56fca811bb9caae713a3477dfbe3ae5fe0991431 Mon Sep 17 00:00:00 2001 From: "elliot@mysql.com" <> Date: Fri, 17 Mar 2006 10:25:29 +0100 Subject: [PATCH 2/7] BUG#18283 When InnoDB returns error 'lock table full', MySQL can write to binlog too much. When InnoDB has to rollback a transaction because the lock table has filled up, it also needs to inform the upper layer that the transaction was rolled back so that the cached transaction is not written to the binary log. --- sql/ha_innodb.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index d24587e23ea..8455bbaf4d0 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -332,6 +332,13 @@ convert_error_code_to_mysql( return(HA_ERR_NO_SAVEPOINT); } else if (error == (int) DB_LOCK_TABLE_FULL) { + /* Since we rolled back the whole transaction, we must + tell it also to MySQL so that MySQL knows to empty the + cached binlog for this transaction */ + + if (thd) { + ha_rollback(thd); + } return(HA_ERR_LOCK_TABLE_FULL); } else { From 882d9b6e1f93be14b5bb75de0fe937b32239e392 Mon Sep 17 00:00:00 2001 From: "kent@mysql.com" <> Date: Fri, 17 Mar 2006 11:32:02 +0100 Subject: [PATCH 3/7] configure.in: Changed release name to 5.0.19a ha_innodb.cc: InnoDB used table locks (not row locks) within stored functions. (Bug #18077) --- configure.in | 4 ++-- sql/ha_innodb.cc | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/configure.in b/configure.in index 7a571da7d77..5a111432071 100644 --- a/configure.in +++ b/configure.in @@ -7,7 +7,7 @@ AC_INIT(sql/mysqld.cc) AC_CANONICAL_SYSTEM # The Docs Makefile.am parses this line! # remember to also change ndb version below and update version.c in ndb -AM_INIT_AUTOMAKE(mysql, 5.0.19) +AM_INIT_AUTOMAKE(mysql, 5.0.19a) AM_CONFIG_HEADER(config.h) PROTOCOL_VERSION=10 @@ -19,7 +19,7 @@ SHARED_LIB_VERSION=$SHARED_LIB_MAJOR_VERSION:0:0 # ndb version NDB_VERSION_MAJOR=5 NDB_VERSION_MINOR=0 -NDB_VERSION_BUILD=19 +NDB_VERSION_BUILD=19a NDB_VERSION_STATUS="" # Set all version vars based on $VERSION. How do we do this more elegant ? diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 1b1326920ad..4a10c5f7fae 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -6884,8 +6884,8 @@ ha_innobase::store_lock( if ((lock_type >= TL_WRITE_CONCURRENT_INSERT && lock_type <= TL_WRITE) - && (!thd->in_lock_tables - || thd->lex->sql_command == SQLCOM_CALL) + && !(thd->in_lock_tables + && thd->lex->sql_command == SQLCOM_LOCK_TABLES) && !thd->tablespace_op && thd->lex->sql_command != SQLCOM_TRUNCATE && thd->lex->sql_command != SQLCOM_OPTIMIZE From 7e48f7b34b944626db08aa80037f9e6cf9d99e42 Mon Sep 17 00:00:00 2001 From: "kent@mysql.com" <> Date: Fri, 17 Mar 2006 12:10:08 +0100 Subject: [PATCH 4/7] configure.in: NDB_VERSION_BUILD has to be numeric --- configure.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.in b/configure.in index 5a111432071..729ae443ed9 100644 --- a/configure.in +++ b/configure.in @@ -19,8 +19,8 @@ SHARED_LIB_VERSION=$SHARED_LIB_MAJOR_VERSION:0:0 # ndb version NDB_VERSION_MAJOR=5 NDB_VERSION_MINOR=0 -NDB_VERSION_BUILD=19a -NDB_VERSION_STATUS="" +NDB_VERSION_BUILD=19 +NDB_VERSION_STATUS="a" # Set all version vars based on $VERSION. How do we do this more elegant ? # Remember that regexps needs to quote [ and ] since this is run through m4 From 34638c283a9e85e4b3cc54a8bf1c0305432fb5db Mon Sep 17 00:00:00 2001 From: "cmiller@calliope.local" <> Date: Fri, 17 Mar 2006 18:46:34 +0100 Subject: [PATCH 5/7] No longer leak file descriptors in mysql_close. Fixes Bug#15846. --- vio/viosocket.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/vio/viosocket.c b/vio/viosocket.c index 5e0ed20b039..710f7a93607 100644 --- a/vio/viosocket.c +++ b/vio/viosocket.c @@ -560,14 +560,20 @@ int vio_close_shared_memory(Vio * vio) Close all handlers. UnmapViewOfFile and CloseHandle return non-zero result if they are success. */ - r= UnmapViewOfFile(vio->handle_map) || CloseHandle(vio->event_server_wrote) || - CloseHandle(vio->event_server_read) || CloseHandle(vio->event_client_wrote) || - CloseHandle(vio->event_client_read) || CloseHandle(vio->handle_file_map); - if (!r) - { - DBUG_PRINT("vio_error", ("close() failed, error: %d",r)); - /* FIXME: error handling (not critical for MySQL) */ - } + if (UnmapViewOfFile(vio->handle_map) == 0) + DBUG_PRINT("vio_error", ("UnmapViewOfFile() failed")); + if (CloseHandle(vio->event_server_wrote) == 0) + DBUG_PRINT("vio_error", ("CloseHandle(vio->esw) failed")); + if (CloseHandle(vio->event_server_read) == 0) + DBUG_PRINT("vio_error", ("CloseHandle(vio->esr) failed")); + if (CloseHandle(vio->event_client_wrote) == 0) + DBUG_PRINT("vio_error", ("CloseHandle(vio->ecw) failed")); + if (CloseHandle(vio->event_client_read) == 0) + DBUG_PRINT("vio_error", ("CloseHandle(vio->ecr) failed")); + if (CloseHandle(vio->handle_file_map) == 0) + DBUG_PRINT("vio_error", ("CloseHandle(vio->hfm) failed")); + if (CloseHandle(vio->event_conn_closed) == 0) + DBUG_PRINT("vio_error", ("CloseHandle(vio->ecc) failed")); } vio->type= VIO_CLOSED; vio->sd= -1; From a184a00eae2a785ab5489904c369322e5d3874be Mon Sep 17 00:00:00 2001 From: "gluh@eagle.intranet.mysql.r18.ru" <> Date: Mon, 20 Mar 2006 13:42:02 +0400 Subject: [PATCH 6/7] Fix for bug #18113 "SELECT * FROM information_schema.xxx crashes server" Crash happened when one selected data from one of INFORMATION_SCHEMA tables and in order to build its contents server had to open view which used stored function and table or view on which one had not global or database-level privileges (e.g. had only table-level or had no privileges at all). The crash was caused by usage of check_grant() function, which assumes that either number of tables to be inspected by it is limited explicitly or table list used and thd->lex->query_tables_own_last value correspond to each other (the latter should be either 0 or point to next_global member of one of elements of this table list), in conditions when above assumptions were not true. This fix just explicitly limits number of tables to be inspected. Other negative effects which are caused by the fact that thd->lex->query_tables_own_last might not be set properly during processing of I_S tables are less disastrous and will be reported and fixed separetely. --- mysql-test/r/information_schema_db.result | 14 ++++++++++ mysql-test/t/information_schema_db.test | 31 +++++++++++++++++++++++ sql/sql_acl.cc | 7 +++++ sql/sql_show.cc | 2 +- 4 files changed, 53 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/information_schema_db.result b/mysql-test/r/information_schema_db.result index 0229fdef2d5..6295bac34a0 100644 --- a/mysql-test/r/information_schema_db.result +++ b/mysql-test/r/information_schema_db.result @@ -27,4 +27,18 @@ create database `inf%`; use `inf%`; show tables; Tables_in_inf% +grant all privileges on `inf%`.* to 'mysqltest_1'@'localhost'; +create table t1 (f1 int); +create function func1(curr_int int) returns int +begin +declare ret_val int; +select max(f1) from t1 into ret_val; +return ret_val; +end| +create view v1 as select f1 from t1 where f1 = func1(f1); +select * from information_schema.tables; +drop user mysqltest_1@localhost; +drop view v1; +drop function func1; +drop table t1; drop database `inf%`; diff --git a/mysql-test/t/information_schema_db.test b/mysql-test/t/information_schema_db.test index efb738d682c..b65135a621d 100644 --- a/mysql-test/t/information_schema_db.test +++ b/mysql-test/t/information_schema_db.test @@ -8,4 +8,35 @@ show tables from INFORMATION_SCHEMA like 'T%'; create database `inf%`; use `inf%`; show tables; + +# +# Bug#18113 SELECT * FROM information_schema.xxx crashes server +# Crash happened when one selected data from one of INFORMATION_SCHEMA +# tables and in order to build its contents server had to open view which +# used stored function and table or view on which one had not global or +# database-level privileges (e.g. had only table-level or had no +# privileges at all). +# +grant all privileges on `inf%`.* to 'mysqltest_1'@'localhost'; +create table t1 (f1 int); +delimiter |; +create function func1(curr_int int) returns int +begin + declare ret_val int; + select max(f1) from t1 into ret_val; + return ret_val; +end| +delimiter ;| +create view v1 as select f1 from t1 where f1 = func1(f1); +connect (user1,localhost,mysqltest_1,,); +connection user1; +--disable_result_log +select * from information_schema.tables; +--enable_result_log +connection default; +drop user mysqltest_1@localhost; +drop view v1; +drop function func1; +drop table t1; + drop database `inf%`; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index ee604ae1daf..d66a631dbcc 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -3537,6 +3537,13 @@ end: RETURN 0 ok 1 Error: User did not have the requested privileges + + NOTE + This functions assumes that either number of tables to be inspected + by it is limited explicitly (i.e. is is not UINT_MAX) or table list + used and thd->lex->query_tables_own_last value correspond to each + other (the latter should be either 0 or point to next_global member + of one of elements of this table list). ****************************************************************************/ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 403781b330d..4b5b4d0dc0e 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -328,7 +328,7 @@ mysql_find_files(THD *thd,List *files, const char *db,const char *path, table_list.table_name= file->name; table_list.table_name_length= strlen(file->name); table_list.grant.privilege=col_access; - if (check_grant(thd, TABLE_ACLS, &table_list, 1, UINT_MAX, 1)) + if (check_grant(thd, TABLE_ACLS, &table_list, 1, 1, 1)) continue; } #endif From f78ae59f0f4751c5608f54fd194e0073789600c5 Mon Sep 17 00:00:00 2001 From: "gluh@eagle.intranet.mysql.r18.ru" <> Date: Mon, 20 Mar 2006 14:17:47 +0400 Subject: [PATCH 7/7] Fix for bug#18224 VIEW on information_schema crashes the server additional check for subselect --- mysql-test/r/information_schema.result | 7 +++++++ mysql-test/t/information_schema.test | 5 +++++ sql/sql_show.cc | 3 ++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index 1c845c73e4b..2324fc7440d 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -281,6 +281,13 @@ sub1 sub1 select count(*) from information_schema.ROUTINES; count(*) 2 +create view v1 as select routine_schema, routine_name from information_schema.routines +order by routine_schema, routine_name; +select * from v1; +routine_schema routine_name +test sel2 +test sub1 +drop view v1; select ROUTINE_NAME, ROUTINE_DEFINITION from information_schema.ROUTINES; ROUTINE_NAME ROUTINE_DEFINITION show create function sub1; diff --git a/mysql-test/t/information_schema.test b/mysql-test/t/information_schema.test index 5e0d57e10dd..35f6a511d98 100644 --- a/mysql-test/t/information_schema.test +++ b/mysql-test/t/information_schema.test @@ -142,6 +142,11 @@ select a.ROUTINE_NAME, b.name from information_schema.ROUTINES a, mysql.proc b where a.ROUTINE_NAME = convert(b.name using utf8) order by 1; select count(*) from information_schema.ROUTINES; +create view v1 as select routine_schema, routine_name from information_schema.routines +order by routine_schema, routine_name; +select * from v1; +drop view v1; + connect (user1,localhost,mysqltest_1,,); connection user1; select ROUTINE_NAME, ROUTINE_DEFINITION from information_schema.ROUTINES; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 4b5b4d0dc0e..6350fceae74 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3864,7 +3864,8 @@ bool get_schema_tables_result(JOIN *join) TABLE_LIST *table_list= tab->table->pos_in_table_list; if (table_list->schema_table && thd->fill_derived_tables()) { - bool is_subselect= (&lex->unit != lex->current_select->master_unit()); + bool is_subselect= (&lex->unit != lex->current_select->master_unit() && + lex->current_select->master_unit()->item); /* The schema table is already processed and the statement is not a subselect.