diff --git a/mysql-test/r/create.result b/mysql-test/r/create.result index 27a6c8a9d03..c5b77ea4925 100644 --- a/mysql-test/r/create.result +++ b/mysql-test/r/create.result @@ -607,7 +607,7 @@ create database mysqltest; use mysqltest; drop database mysqltest; create table test.t1 like x; -ERROR 42000: Incorrect database name 'NULL' +ERROR 3D000: No database selected drop table if exists test.t1; create database mysqltest; use mysqltest; diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index abebfc8cd93..3ce2f5169e2 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -1158,3 +1158,108 @@ Warnings: Error 1146 Table 'test.t4' doesn't exist deallocate prepare stmt; drop table t1, t2, t3; +create database mysqltest_long_database_name_to_thrash_heap; +use test; +create table t1 (i int); +prepare stmt from "alter table test.t1 rename t1"; +use mysqltest_long_database_name_to_thrash_heap; +execute stmt; +show tables like 't1'; +Tables_in_mysqltest_long_database_name_to_thrash_heap (t1) +prepare stmt from "alter table test.t1 rename t1"; +use test; +execute stmt; +show tables like 't1'; +Tables_in_test (t1) +use mysqltest_long_database_name_to_thrash_heap; +show tables like 't1'; +Tables_in_mysqltest_long_database_name_to_thrash_heap (t1) +t1 +deallocate prepare stmt; +use mysqltest_long_database_name_to_thrash_heap; +prepare stmt_create from "create table t1 (i int)"; +prepare stmt_insert from "insert into t1 (i) values (1)"; +prepare stmt_update from "update t1 set i=2"; +prepare stmt_delete from "delete from t1 where i=2"; +prepare stmt_select from "select * from t1"; +prepare stmt_alter from "alter table t1 add column (b int)"; +prepare stmt_alter1 from "alter table t1 drop column b"; +prepare stmt_analyze from "analyze table t1"; +prepare stmt_optimize from "optimize table t1"; +prepare stmt_show from "show tables like 't1'"; +prepare stmt_truncate from "truncate table t1"; +prepare stmt_drop from "drop table t1"; +drop table t1; +use test; +execute stmt_create; +show tables like 't1'; +Tables_in_test (t1) +use mysqltest_long_database_name_to_thrash_heap; +show tables like 't1'; +Tables_in_mysqltest_long_database_name_to_thrash_heap (t1) +t1 +use test; +execute stmt_insert; +select * from mysqltest_long_database_name_to_thrash_heap.t1; +i +1 +execute stmt_update; +select * from mysqltest_long_database_name_to_thrash_heap.t1; +i +2 +execute stmt_delete; +execute stmt_select; +i +execute stmt_alter; +show columns from mysqltest_long_database_name_to_thrash_heap.t1; +Field Type Null Key Default Extra +i int(11) YES NULL +b int(11) YES NULL +execute stmt_alter1; +show columns from mysqltest_long_database_name_to_thrash_heap.t1; +Field Type Null Key Default Extra +i int(11) YES NULL +execute stmt_analyze; +Table Op Msg_type Msg_text +mysqltest_long_database_name_to_thrash_heap.t1 analyze status Table is already up to date +execute stmt_optimize; +Table Op Msg_type Msg_text +mysqltest_long_database_name_to_thrash_heap.t1 optimize status Table is already up to date +execute stmt_show; +Tables_in_mysqltest_long_database_name_to_thrash_heap (t1) +t1 +execute stmt_truncate; +execute stmt_drop; +show tables like 't1'; +Tables_in_test (t1) +use mysqltest_long_database_name_to_thrash_heap; +show tables like 't1'; +Tables_in_mysqltest_long_database_name_to_thrash_heap (t1) +drop database mysqltest_long_database_name_to_thrash_heap; +prepare stmt_create from "create table t1 (i int)"; +ERROR 3D000: No database selected +prepare stmt_insert from "insert into t1 (i) values (1)"; +ERROR 3D000: No database selected +prepare stmt_update from "update t1 set i=2"; +ERROR 3D000: No database selected +prepare stmt_delete from "delete from t1 where i=2"; +ERROR 3D000: No database selected +prepare stmt_select from "select * from t1"; +ERROR 3D000: No database selected +prepare stmt_alter from "alter table t1 add column (b int)"; +ERROR 3D000: No database selected +prepare stmt_alter1 from "alter table t1 drop column b"; +ERROR 3D000: No database selected +prepare stmt_analyze from "analyze table t1"; +ERROR 3D000: No database selected +prepare stmt_optimize from "optimize table t1"; +ERROR 3D000: No database selected +prepare stmt_show from "show tables like 't1'"; +ERROR 3D000: No database selected +prepare stmt_truncate from "truncate table t1"; +ERROR 3D000: No database selected +prepare stmt_drop from "drop table t1"; +ERROR 3D000: No database selected +create temporary table t1 (i int); +ERROR 3D000: No database selected +use test; diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index d3874c769fa..96bf2f01f86 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -4990,6 +4990,52 @@ CALL bug18037_p2()| DROP FUNCTION bug18037_f1| DROP PROCEDURE bug18037_p1| DROP PROCEDURE bug18037_p2| +use test| +create table t3 (i int)| +insert into t3 values (1), (2)| +create database mysqltest1| +use mysqltest1| +create function bug17199() returns varchar(2) deterministic return 'ok'| +use test| +select *, mysqltest1.bug17199() from t3| +i mysqltest1.bug17199() +1 ok +2 ok +use mysqltest1| +create function bug18444(i int) returns int no sql deterministic return i + 1| +use test| +select mysqltest1.bug18444(i) from t3| +mysqltest1.bug18444(i) +2 +3 +drop database mysqltest1| +create database mysqltest1 charset=utf8| +create database mysqltest2 charset=utf8| +create procedure mysqltest1.p1() +begin +-- alters the default collation of database test +alter database character set koi8r; +end| +use mysqltest1| +call p1()| +show create database mysqltest1| +Database Create Database +mysqltest1 CREATE DATABASE `mysqltest1` /*!40100 DEFAULT CHARACTER SET koi8r */ +show create database mysqltest2| +Database Create Database +mysqltest2 CREATE DATABASE `mysqltest2` /*!40100 DEFAULT CHARACTER SET utf8 */ +alter database mysqltest1 character set utf8| +use mysqltest2| +call mysqltest1.p1()| +show create database mysqltest1| +Database Create Database +mysqltest1 CREATE DATABASE `mysqltest1` /*!40100 DEFAULT CHARACTER SET koi8r */ +show create database mysqltest2| +Database Create Database +mysqltest2 CREATE DATABASE `mysqltest2` /*!40100 DEFAULT CHARACTER SET utf8 */ +drop database mysqltest1| +drop database mysqltest2| +use test| drop table if exists t3| drop procedure if exists bug15217| create table t3 as select 1| diff --git a/mysql-test/t/create.test b/mysql-test/t/create.test index e22c2b5c426..07edbf206fe 100644 --- a/mysql-test/t/create.test +++ b/mysql-test/t/create.test @@ -517,7 +517,7 @@ DROP TABLE t12913; create database mysqltest; use mysqltest; drop database mysqltest; ---error 1102 +--error ER_NO_DB_ERROR create table test.t1 like x; --disable_warnings drop table if exists test.t1; diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index e3f3e37cd4c..ff66b265fae 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -1146,4 +1146,122 @@ execute stmt; execute stmt; deallocate prepare stmt; drop table t1, t2, t3; + +# +# Bug#17199 "Table not found" error occurs if the query contains a call +# to a function from another database. +# Test prepared statements- related behaviour. +# +# +# ALTER TABLE RENAME and Prepared Statements: wrong DB name buffer was used +# in ALTER ... RENAME which caused memory corruption in prepared statements. +# No need to fix this problem in 4.1 as ALTER TABLE is not allowed in +# Prepared Statements in 4.1. +# +create database mysqltest_long_database_name_to_thrash_heap; +use test; +create table t1 (i int); +prepare stmt from "alter table test.t1 rename t1"; +use mysqltest_long_database_name_to_thrash_heap; +execute stmt; +show tables like 't1'; +prepare stmt from "alter table test.t1 rename t1"; +use test; +execute stmt; +show tables like 't1'; +use mysqltest_long_database_name_to_thrash_heap; +show tables like 't1'; +deallocate prepare stmt; +# +# Check that a prepared statement initializes its current database at +# PREPARE, and then works correctly even if the current database has been +# changed. +# +use mysqltest_long_database_name_to_thrash_heap; +# Necessary for preparation of INSERT/UPDATE/DELETE to succeed +prepare stmt_create from "create table t1 (i int)"; +prepare stmt_insert from "insert into t1 (i) values (1)"; +prepare stmt_update from "update t1 set i=2"; +prepare stmt_delete from "delete from t1 where i=2"; +prepare stmt_select from "select * from t1"; +prepare stmt_alter from "alter table t1 add column (b int)"; +prepare stmt_alter1 from "alter table t1 drop column b"; +prepare stmt_analyze from "analyze table t1"; +prepare stmt_optimize from "optimize table t1"; +prepare stmt_show from "show tables like 't1'"; +prepare stmt_truncate from "truncate table t1"; +prepare stmt_drop from "drop table t1"; +# Drop the table that was used to prepare INSERT/UPDATE/DELETE: we will +# create a new one by executing stmt_create +drop table t1; +# Switch the current database +use test; +# Check that all prepared statements operate on the database that was +# active at PREPARE +execute stmt_create; +# should return empty set +show tables like 't1'; +use mysqltest_long_database_name_to_thrash_heap; +show tables like 't1'; +use test; +execute stmt_insert; +select * from mysqltest_long_database_name_to_thrash_heap.t1; +execute stmt_update; +select * from mysqltest_long_database_name_to_thrash_heap.t1; +execute stmt_delete; +execute stmt_select; +execute stmt_alter; +show columns from mysqltest_long_database_name_to_thrash_heap.t1; +execute stmt_alter1; +show columns from mysqltest_long_database_name_to_thrash_heap.t1; +execute stmt_analyze; +execute stmt_optimize; +execute stmt_show; +execute stmt_truncate; +execute stmt_drop; +show tables like 't1'; +use mysqltest_long_database_name_to_thrash_heap; +show tables like 't1'; +# +# Attempt a statement PREPARE when there is no current database: +# is expected to return an error. +# +drop database mysqltest_long_database_name_to_thrash_heap; +--error ER_NO_DB_ERROR +prepare stmt_create from "create table t1 (i int)"; +--error ER_NO_DB_ERROR +prepare stmt_insert from "insert into t1 (i) values (1)"; +--error ER_NO_DB_ERROR +prepare stmt_update from "update t1 set i=2"; +--error ER_NO_DB_ERROR +prepare stmt_delete from "delete from t1 where i=2"; +--error ER_NO_DB_ERROR +prepare stmt_select from "select * from t1"; +--error ER_NO_DB_ERROR +prepare stmt_alter from "alter table t1 add column (b int)"; +--error ER_NO_DB_ERROR +prepare stmt_alter1 from "alter table t1 drop column b"; +--error ER_NO_DB_ERROR +prepare stmt_analyze from "analyze table t1"; +--error ER_NO_DB_ERROR +prepare stmt_optimize from "optimize table t1"; +--error ER_NO_DB_ERROR +prepare stmt_show from "show tables like 't1'"; +--error ER_NO_DB_ERROR +prepare stmt_truncate from "truncate table t1"; +--error ER_NO_DB_ERROR +prepare stmt_drop from "drop table t1"; +# +# The above has automatically deallocated all our statements. +# +# Attempt to CREATE a temporary table when no DB used: it should fail +# This proves that no table can be used without explicit specification of +# its database if there is no current database. +# +--error ER_NO_DB_ERROR +create temporary table t1 (i int); +# +# Restore the old environemnt +# +use test; # End of 5.0 tests diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 66498198157..25c96042e6f 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -5888,6 +5888,52 @@ DROP FUNCTION bug18037_f1| DROP PROCEDURE bug18037_p1| DROP PROCEDURE bug18037_p2| +# +# Bug#17199: "Table not found" error occurs if the query contains a call +# to a function from another database. +# See also ps.test for an additional test case for this bug. +# +use test| +create table t3 (i int)| +insert into t3 values (1), (2)| +create database mysqltest1| +use mysqltest1| +create function bug17199() returns varchar(2) deterministic return 'ok'| +use test| +select *, mysqltest1.bug17199() from t3| +# +# Bug#18444: Fully qualified stored function names don't work correctly +# in select statements +# +use mysqltest1| +create function bug18444(i int) returns int no sql deterministic return i + 1| +use test| +select mysqltest1.bug18444(i) from t3| +drop database mysqltest1| +# +# Check that current database has no influence to a stored procedure +# +create database mysqltest1 charset=utf8| +create database mysqltest2 charset=utf8| +create procedure mysqltest1.p1() +begin +-- alters the default collation of database test + alter database character set koi8r; +end| +use mysqltest1| +call p1()| +show create database mysqltest1| +show create database mysqltest2| +alter database mysqltest1 character set utf8| +use mysqltest2| +call mysqltest1.p1()| +show create database mysqltest1| +show create database mysqltest2| +drop database mysqltest1| +drop database mysqltest2| +# +# Restore the old environemnt +use test| # # Bug#15217 "Using a SP cursor on a table created with PREPARE fails with # weird error". Check that the code that is supposed to work at diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index a51ebd39147..7a35dedc08a 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -1667,13 +1667,13 @@ String *Item_func_database::val_str(String *str) { DBUG_ASSERT(fixed == 1); THD *thd= current_thd; - if (!thd->db) + if (thd->db == NULL) { null_value= 1; return 0; } else - str->copy((const char*) thd->db,(uint) strlen(thd->db),system_charset_info); + str->copy(thd->db, thd->db_length, system_charset_info); return str; } diff --git a/sql/log_event.cc b/sql/log_event.cc index cabf4631284..e93c2855199 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1868,9 +1868,10 @@ end: don't suffer from these assignments to 0 as DROP TEMPORARY TABLE uses the db.table syntax. */ - thd->db= thd->catalog= 0; // prevent db from being freed + thd->catalog= 0; + thd->reset_db(NULL, 0); // prevent db from being freed thd->query= 0; // just to be sure - thd->query_length= thd->db_length =0; + thd->query_length= 0; VOID(pthread_mutex_unlock(&LOCK_thread_count)); close_thread_tables(thd); free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC)); @@ -2872,7 +2873,7 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli, TABLE_LIST tables; bzero((char*) &tables,sizeof(tables)); - tables.db = thd->db; + tables.db= thd->strmake(thd->db, thd->db_length); tables.alias = tables.table_name = (char*) table_name; tables.lock_type = TL_WRITE; tables.updating= 1; @@ -2967,7 +2968,7 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli, ex.skip_lines = skip_lines; List field_list; thd->main_lex.select_lex.context.resolve_in_table_list_only(&tables); - set_fields(thd->db, field_list, &thd->main_lex.select_lex.context); + set_fields(tables.db, field_list, &thd->main_lex.select_lex.context); thd->variables.pseudo_thread_id= thread_id; List set_fields; if (net) @@ -3014,11 +3015,12 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli, error: thd->net.vio = 0; - char *save_db= thd->db; + const char *remember_db= thd->db; VOID(pthread_mutex_lock(&LOCK_thread_count)); - thd->db= thd->catalog= 0; + thd->catalog= 0; + thd->reset_db(NULL, 0); thd->query= 0; - thd->query_length= thd->db_length= 0; + thd->query_length= 0; VOID(pthread_mutex_unlock(&LOCK_thread_count)); close_thread_tables(thd); if (thd->query_error) @@ -3035,7 +3037,7 @@ error: } slave_print_error(rli,sql_errno,"\ Error '%s' running LOAD DATA INFILE on table '%s'. Default database: '%s'", - err, (char*)table_name, print_slave_db_safe(save_db)); + err, (char*)table_name, print_slave_db_safe(remember_db)); free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC)); return 1; } @@ -3045,7 +3047,7 @@ Error '%s' running LOAD DATA INFILE on table '%s'. Default database: '%s'", { slave_print_error(rli,ER_UNKNOWN_ERROR, "\ Fatal error running LOAD DATA INFILE on table '%s'. Default database: '%s'", - (char*)table_name, print_slave_db_safe(save_db)); + (char*)table_name, print_slave_db_safe(remember_db)); return 1; } diff --git a/sql/slave.cc b/sql/slave.cc index 2b31d722f26..aed2a41a1e6 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -1581,9 +1581,8 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db, // save old db in case we are creating in a different database save_db = thd->db; save_db_length= thd->db_length; - thd->db = (char*)db; - DBUG_ASSERT(thd->db != 0); - thd->db_length= strlen(thd->db); + DBUG_ASSERT(db != 0); + thd->reset_db((char*)db, strlen(db)); mysql_parse(thd, thd->query, packet_len); // run create table thd->db = save_db; // leave things the way the were before thd->db_length= save_db_length; @@ -3713,8 +3712,9 @@ err: sql_print_information("Slave I/O thread exiting, read up to log '%s', position %s", IO_RPL_LOG_NAME, llstr(mi->master_log_pos,llbuff)); VOID(pthread_mutex_lock(&LOCK_thread_count)); - thd->query = thd->db = 0; // extra safety - thd->query_length= thd->db_length= 0; + thd->query= 0; // extra safety + thd->query_length= 0; + thd->reset_db(NULL, 0); VOID(pthread_mutex_unlock(&LOCK_thread_count)); if (mysql) { @@ -3932,8 +3932,10 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \ should already have done these assignments (each event which sets these variables is supposed to set them to 0 before terminating)). */ - thd->query= thd->db= thd->catalog= 0; - thd->query_length= thd->db_length= 0; + thd->catalog= 0; + thd->reset_db(NULL, 0); + thd->query= 0; + thd->query_length= 0; VOID(pthread_mutex_unlock(&LOCK_thread_count)); thd->proc_info = "Waiting for slave mutex on exit"; pthread_mutex_lock(&rli->run_lock); diff --git a/sql/slave.h b/sql/slave.h index 040ce4eaf85..ebbb1e64df5 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -526,10 +526,6 @@ int start_slave_thread(pthread_handler h_func, pthread_mutex_t* start_lock, MASTER_INFO* mi, bool high_priority); -/* If fd is -1, dump to NET */ -int mysql_table_dump(THD* thd, const char* db, - const char* tbl_name, int fd = -1); - /* retrieve table from master and copy to slave*/ int fetch_master_table(THD* thd, const char* db_name, const char* table_name, MASTER_INFO* mi, MYSQL* mysql, bool overwrite); diff --git a/sql/sp.cc b/sql/sp.cc index cae7a56fa57..553465ebff8 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -404,7 +404,8 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, { LEX *old_lex= thd->lex, newlex; String defstr; - char olddb[128]; + char old_db_buf[NAME_LEN+1]; + LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) }; bool dbchanged; ulong old_sql_mode= thd->variables.sql_mode; ha_rows old_select_limit= thd->variables.select_limit; @@ -450,9 +451,7 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, goto end; } - dbchanged= FALSE; - if ((ret= sp_use_new_db(thd, name->m_db.str, olddb, sizeof(olddb), - 1, &dbchanged))) + if ((ret= sp_use_new_db(thd, name->m_db, &old_db, 1, &dbchanged))) goto end; lex_start(thd, (uchar*)defstr.c_ptr(), defstr.length()); @@ -462,14 +461,14 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, { sp_head *sp= newlex.sphead; - if (dbchanged && (ret= mysql_change_db(thd, olddb, 1))) + if (dbchanged && (ret= mysql_change_db(thd, old_db.str, 1))) goto end; delete sp; ret= SP_PARSE_ERROR; } else { - if (dbchanged && (ret= mysql_change_db(thd, olddb, 1))) + if (dbchanged && (ret= mysql_change_db(thd, old_db.str, 1))) goto end; *sphp= newlex.sphead; (*sphp)->set_definer(&definer_user_name, &definer_host_name); @@ -505,15 +504,14 @@ db_create_routine(THD *thd, int type, sp_head *sp) int ret; TABLE *table; char definer[USER_HOST_BUFF_SIZE]; - char olddb[128]; + char old_db_buf[NAME_LEN+1]; + LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) }; bool dbchanged; DBUG_ENTER("db_create_routine"); DBUG_PRINT("enter", ("type: %d name: %.*s",type,sp->m_name.length, sp->m_name.str)); - dbchanged= FALSE; - if ((ret= sp_use_new_db(thd, sp->m_db.str, olddb, sizeof(olddb), - 0, &dbchanged))) + if ((ret= sp_use_new_db(thd, sp->m_db, &old_db, 0, &dbchanged))) { ret= SP_NO_DB_ERROR; goto done; @@ -641,7 +639,7 @@ db_create_routine(THD *thd, int type, sp_head *sp) done: close_thread_tables(thd); if (dbchanged) - (void)mysql_change_db(thd, olddb, 1); + (void) mysql_change_db(thd, old_db.str, 1); DBUG_RETURN(ret); } @@ -1814,49 +1812,76 @@ create_string(THD *thd, String *buf, } -// -// Utilities... -// + +/* + Change the current database if needed. + + SYNOPSIS + sp_use_new_db() + thd thread handle + + new_db new database name (a string and its length) + + old_db [IN] str points to a buffer where to store the old + database, length contains the size of the buffer + [OUT] if old db was not NULL, its name is copied + to the buffer pointed at by str and length is updated + accordingly. Otherwise str[0] is set to '\0' and length + is set to 0. The out parameter should be used only if + the database name has been changed (see dbchangedp). + + dbchangedp [OUT] is set to TRUE if the current database is changed, + FALSE otherwise. A database is not changed if the old + name is the same as the new one, both names are empty, + or an error has occurred. + + RETURN VALUE + 0 success + 1 access denied or out of memory (the error message is + set in THD) +*/ int -sp_use_new_db(THD *thd, char *newdb, char *olddb, uint olddblen, +sp_use_new_db(THD *thd, LEX_STRING new_db, LEX_STRING *old_db, bool no_access_check, bool *dbchangedp) { - bool changeit; + int ret; + static char empty_c_string[1]= {0}; /* used for not defined db */ DBUG_ENTER("sp_use_new_db"); - DBUG_PRINT("enter", ("newdb: %s", newdb)); + DBUG_PRINT("enter", ("newdb: %s", new_db.str)); - if (! newdb) - newdb= (char *)""; - if (thd->db && thd->db[0]) + /* + Set new_db to an empty string if it's NULL, because mysql_change_db + requires a non-NULL argument. + new_db.str can be NULL only if we're restoring the old database after + execution of a stored procedure and there were no current database + selected. The stored procedure itself must always have its database + initialized. + */ + if (new_db.str == NULL) + new_db.str= empty_c_string; + + if (thd->db) { - if (my_strcasecmp(system_charset_info, thd->db, newdb) == 0) - changeit= 0; - else - { - changeit= 1; - strnmov(olddb, thd->db, olddblen); - } + old_db->length= (strmake(old_db->str, thd->db, old_db->length) - + old_db->str); } else - { // thd->db empty - if (newdb[0]) - changeit= 1; - else - changeit= 0; - olddb[0] = '\0'; + { + old_db->str[0]= '\0'; + old_db->length= 0; } - if (!changeit) + + /* Don't change the database if the new name is the same as the old one. */ + if (my_strcasecmp(system_charset_info, old_db->str, new_db.str) == 0) { *dbchangedp= FALSE; DBUG_RETURN(0); } - else - { - int ret= mysql_change_db(thd, newdb, no_access_check); - if (! ret) - *dbchangedp= TRUE; - DBUG_RETURN(ret); - } + ret= mysql_change_db(thd, new_db.str, no_access_check); + + *dbchangedp= ret == 0; + DBUG_RETURN(ret); } + diff --git a/sql/sp.h b/sql/sp.h index 2587a9b115a..631b8a87aa2 100644 --- a/sql/sp.h +++ b/sql/sp.h @@ -104,15 +104,15 @@ extern "C" byte* sp_sroutine_key(const byte *ptr, uint *plen, my_bool first); TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup); void close_proc_table(THD *thd, Open_tables_state *backup); -// -// Utilities... -// -// Do a "use newdb". The current db is stored at olddb. -// If newdb is the same as the current one, nothing is changed. -// dbchangedp is set to true if the db was actually changed. +/* + Do a "use new_db". The current db is stored at old_db. If new_db is the + same as the current one, nothing is changed. dbchangedp is set to true if + the db was actually changed. +*/ + int -sp_use_new_db(THD *thd, char *newdb, char *olddb, uint olddbmax, +sp_use_new_db(THD *thd, LEX_STRING new_db, LEX_STRING *old_db, bool no_access_check, bool *dbchangedp); #endif /* _SP_H_ */ diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 2a486e3f93d..b3b99557b63 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -376,24 +376,6 @@ sp_name::init_qname(THD *thd) m_name.length, m_name.str); } -sp_name * -sp_name_current_db_new(THD *thd, LEX_STRING name) -{ - sp_name *qname; - - if (! thd->db) - qname= new sp_name(name); - else - { - LEX_STRING db; - - db.length= strlen(thd->db); - db.str= thd->strmake(thd->db, db.length); - qname= new sp_name(db, name); - } - qname->init_qname(thd); - return qname; -} /* Check that the name 'ident' is ok. It's assumed to be an 'ident' @@ -504,27 +486,20 @@ 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; - /* We have to copy strings to get them into the right memroot */ - if (name) - { - m_db.length= name->m_db.length; - if (name->m_db.length == 0) - m_db.str= NULL; - else - m_db.str= strmake_root(root, name->m_db.str, name->m_db.length); - m_name.length= name->m_name.length; - m_name.str= strmake_root(root, name->m_name.str, name->m_name.length); + DBUG_ASSERT(name); + /* Must be initialized in the parser */ + DBUG_ASSERT(name->m_db.str && name->m_db.length); - if (name->m_qname.length == 0) - name->init_qname(thd); - m_qname.length= name->m_qname.length; - m_qname.str= strmake_root(root, name->m_qname.str, m_qname.length); - } - else if (thd->db) - { - m_db.length= thd->db_length; - m_db.str= strmake_root(root, thd->db, m_db.length); - } + /* We have to copy strings to get them into the right memroot */ + 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); if (m_param_begin && m_param_end) { @@ -933,7 +908,8 @@ bool sp_head::execute(THD *thd) { DBUG_ENTER("sp_head::execute"); - char olddb[128]; + char old_db_buf[NAME_LEN+1]; + LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) }; bool dbchanged; sp_rcontext *ctx; bool err_status= FALSE; @@ -980,10 +956,8 @@ sp_head::execute(THD *thd) m_first_instance->m_last_cached_sp == this) || (m_recursion_level + 1 == m_next_cached_sp->m_recursion_level)); - dbchanged= FALSE; if (m_db.length && - (err_status= sp_use_new_db(thd, m_db.str, olddb, sizeof(olddb), 0, - &dbchanged))) + (err_status= sp_use_new_db(thd, m_db, &old_db, 0, &dbchanged))) goto done; if ((ctx= thd->spcont)) @@ -1154,10 +1128,10 @@ sp_head::execute(THD *thd) { /* No access check when changing back to where we came from. - (It would generate an error from mysql_change_db() when olddb=="") + (It would generate an error from mysql_change_db() when old_db=="") */ if (! thd->killed) - err_status|= mysql_change_db(thd, olddb, 1); + err_status|= mysql_change_db(thd, old_db.str, 1); } m_flags&= ~IS_INVOKED; DBUG_PRINT("info", @@ -1815,9 +1789,6 @@ sp_head::reset_thd_mem_root(THD *thd) (ulong) &mem_root, (ulong) &thd->mem_root)); free_list= thd->free_list; // Keep the old list thd->free_list= NULL; // Start a new one - /* Copy the db, since substatements will point to it */ - m_thd_db= thd->db; - thd->db= thd->strmake(thd->db, thd->db_length); m_thd= thd; DBUG_VOID_RETURN; } @@ -1833,7 +1804,6 @@ sp_head::restore_thd_mem_root(THD *thd) DBUG_PRINT("info", ("mem_root 0x%lx returned from thd mem root 0x%lx", (ulong) &mem_root, (ulong) &thd->mem_root)); thd->free_list= flist; // Restore the old one - thd->db= m_thd_db; // Restore the original db pointer thd->mem_root= m_thd_root; m_thd= NULL; DBUG_VOID_RETURN; diff --git a/sql/sp_head.h b/sql/sp_head.h index d5f49d8a964..073cca2cd12 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -61,13 +61,6 @@ public: */ LEX_STRING m_sroutines_key; - sp_name(LEX_STRING name) - : m_name(name) - { - m_db.str= m_qname.str= m_sroutines_key.str= 0; - m_db.length= m_qname.length= m_sroutines_key.length= 0; - } - sp_name(LEX_STRING db, LEX_STRING name) : m_db(db), m_name(name) { @@ -101,8 +94,6 @@ public: {} }; -sp_name * -sp_name_current_db_new(THD *thd, LEX_STRING name); bool check_routine_name(LEX_STRING name); @@ -355,7 +346,6 @@ private: MEM_ROOT *m_thd_root; // Temp. store for thd's mem_root THD *m_thd; // Set if we have reset mem_root - char *m_thd_db; // Original thd->db pointer sp_pcontext *m_pcont; // Parse context List m_lex; // Temp. store for the other lex diff --git a/sql/sql_class.h b/sql/sql_class.h index 2cfc9142453..47150912c52 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1571,6 +1571,47 @@ public: void restore_sub_statement_state(Sub_statement_state *backup); void set_n_backup_active_arena(Query_arena *set, Query_arena *backup); void restore_active_arena(Query_arena *set, Query_arena *backup); + + /* + Initialize the current database from a NULL-terminated string with length + */ + void set_db(const char *new_db, uint new_db_len) + { + if (new_db) + { + /* Do not reallocate memory if current chunk is big enough. */ + if (db && db_length >= new_db_len) + memcpy(db, new_db, new_db_len+1); + else + { + safeFree(db); + db= my_strdup_with_length(new_db, new_db_len, MYF(MY_WME)); + } + db_length= db ? new_db_len: 0; + } + } + void reset_db(char *new_db, uint new_db_len) + { + db= new_db; + db_length= new_db_len; + } + /* + Copy the current database to the argument. Use the current arena to + allocate memory for a deep copy: current database may be freed after + a statement is parsed but before it's executed. + */ + bool copy_db_to(char **p_db, uint *p_db_length) + { + if (db == NULL) + { + my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); + return TRUE; + } + *p_db= strmake(db, db_length); + if (p_db_length) + *p_db_length= db_length; + return FALSE; + } }; @@ -1916,7 +1957,7 @@ typedef struct st_sort_buffer { class Table_ident :public Sql_alloc { - public: +public: LEX_STRING db; LEX_STRING table; SELECT_LEX_UNIT *sel; diff --git a/sql/sql_db.cc b/sql/sql_db.cc index a52972753a7..44947384b32 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -805,8 +805,7 @@ exit: { if (!(thd->slave_thread)) /* a slave thread will free it itself */ x_free(thd->db); - thd->db= 0; - thd->db_length= 0; + thd->reset_db(NULL, 0); } VOID(pthread_mutex_unlock(&LOCK_mysql_create_db)); start_waiting_global_read_lock(thd); @@ -1218,14 +1217,10 @@ end: { if (!(thd->slave_thread)) my_free(dbname, MYF(0)); - thd->db= NULL; - thd->db_length= 0; + thd->reset_db(NULL, 0); } else - { - thd->db= dbname; // THD::~THD will free this - thd->db_length= db_length; - } + thd->reset_db(dbname, db_length); // THD::~THD will free this #ifndef NO_EMBEDDED_ACCESS_CHECKS if (!no_access_check) sctx->db_access= db_access; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 15c7f91ba83..ba0d2d00f2c 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -298,9 +298,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, { if (thd->locked_tables) { - if (find_locked_table(thd, - table_list->db ? table_list->db : thd->db, - table_list->table_name)) + DBUG_ASSERT(table_list->db); /* Must be set in the parser */ + if (find_locked_table(thd, table_list->db, table_list->table_name)) { my_error(ER_DELAYED_INSERT_TABLE_LOCKED, MYF(0), table_list->table_name); @@ -1329,8 +1328,8 @@ static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list) TABLE *table; DBUG_ENTER("delayed_get_table"); - if (!table_list->db) - table_list->db=thd->db; + /* Must be set in the parser */ + DBUG_ASSERT(table_list->db); /* Find the thread which handles this table. */ if (!(tmp=find_handler(thd,table_list))) @@ -1357,15 +1356,15 @@ static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list) pthread_mutex_lock(&LOCK_thread_count); thread_count++; pthread_mutex_unlock(&LOCK_thread_count); - if (!(tmp->thd.db=my_strdup(table_list->db,MYF(MY_WME))) || - !(tmp->thd.query=my_strdup(table_list->table_name,MYF(MY_WME)))) + tmp->thd.set_db(table_list->db, strlen(table_list->db)); + tmp->thd.query= my_strdup(table_list->table_name,MYF(MY_WME)); + if (tmp->thd.db == NULL || tmp->thd.query == NULL) { delete tmp; my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); goto err1; } tmp->table_list= *table_list; // Needed to open table - tmp->table_list.db= tmp->thd.db; tmp->table_list.alias= tmp->table_list.table_name= tmp->thd.query; tmp->lock(); pthread_mutex_lock(&tmp->mutex); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index c75aa8f31b9..d63c6ef9f20 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -758,6 +758,11 @@ public: *this= *state; } + /* + Direct addition to the list of query tables. + If you are using this function, you must ensure that the table + object, in particular table->db member, is initialized. + */ void add_to_query_tables(TABLE_LIST *table) { *(table->prev_global= query_tables_last)= table; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 0f05b09e6b9..29201e2a2f9 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -93,8 +93,6 @@ const char *xa_state_names[]={ "NON-EXISTING", "ACTIVE", "IDLE", "PREPARED" }; -static char empty_c_string[1]= {0}; // Used for not defined 'db' - #ifdef __WIN__ static void test_signal(int sig_ptr) { @@ -300,8 +298,7 @@ int check_user(THD *thd, enum enum_server_command command, thd->db is saved in caller and needs to be freed by caller if this function returns 0 */ - thd->db= 0; - thd->db_length= 0; + thd->reset_db(NULL, 0); if (mysql_change_db(thd, db, FALSE)) { /* Send the error to the client */ @@ -341,9 +338,8 @@ int check_user(THD *thd, enum enum_server_command command, if connect failed. Also in case of 'CHANGE USER' failure, current database will be switched to 'no database selected'. */ - thd->db= 0; - thd->db_length= 0; - + thd->reset_db(NULL, 0); + USER_RESOURCES ur; int res= acl_getroot(thd, &ur, passwd, passwd_len); #ifndef EMBEDDED_LIBRARY @@ -1316,19 +1312,6 @@ end: DBUG_RETURN(0); } - /* This works because items are allocated with sql_alloc() */ - -void free_items(Item *item) -{ - Item *next; - DBUG_ENTER("free_items"); - for (; item ; item=next) - { - next=item->next; - item->delete_self(); - } - DBUG_VOID_RETURN; -} /* This works because items are allocated with sql_alloc() */ @@ -1340,7 +1323,26 @@ void cleanup_items(Item *item) DBUG_VOID_RETURN; } -int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd) +/* + Handle COM_TABLE_DUMP command + + SYNOPSIS + mysql_table_dump + thd thread handle + db database name or an empty string. If empty, + the current database of the connection is used + tbl_name name of the table to dump + + NOTES + This function is written to handle one specific command only. + + RETURN VALUE + 0 success + 1 error, the error message is set in THD +*/ + +static +int mysql_table_dump(THD* thd, char* db, char* tbl_name) { TABLE* table; TABLE_LIST* table_list; @@ -1377,7 +1379,7 @@ int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd) goto err; } net_flush(&thd->net); - if ((error= table->file->dump(thd,fd))) + if ((error= table->file->dump(thd,-1))) my_error(ER_GET_ERRNO, MYF(0), error); err: @@ -1627,7 +1629,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, } tbl_name= strmake(db, packet + 1, db_len)+1; strmake(tbl_name, packet + db_len + 2, tbl_len); - mysql_table_dump(thd, db, tbl_name, -1); + mysql_table_dump(thd, db, tbl_name); break; } case COM_CHANGE_USER: @@ -1801,11 +1803,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, 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)) - { - my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); + if (thd->copy_db_to(&table_list.db, 0)) break; - } pend= strend(packet); thd->convert_string(&conv_name, system_charset_info, packet, (uint) (pend-packet), thd->charset()); @@ -2152,6 +2151,34 @@ void log_slow_statement(THD *thd) } +/* + Create a TABLE_LIST object for an INFORMATION_SCHEMA table. + + SYNOPSIS + prepare_schema_table() + thd thread handle + lex current lex + table_ident table alias if it's used + schema_table_idx the type of the INFORMATION_SCHEMA table to be + created + + DESCRIPTION + This function is used in the parser to convert a SHOW or DESCRIBE + table_name command to a SELECT from INFORMATION_SCHEMA. + It prepares a SELECT_LEX and a TABLE_LIST object to represent the + given command as a SELECT parse tree. + + NOTES + Due to the way this function works with memory and LEX it cannot + be used outside the parser (parse tree transformations outside + the parser break PS and SP). + + RETURN VALUE + 0 success + 1 out of memory or SHOW commands are not allowed + in this version of the server. +*/ + int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, enum enum_schema_tables schema_table_idx) { @@ -2179,13 +2206,13 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, DBUG_RETURN(1); #else { - char *db= lex->select_lex.db ? lex->select_lex.db : thd->db; - if (!db) + char *db; + if (lex->select_lex.db == NULL && + thd->copy_db_to(&lex->select_lex.db, 0)) { - my_message(ER_NO_DB_ERROR, - ER(ER_NO_DB_ERROR), MYF(0)); /* purecov: inspected */ - DBUG_RETURN(1); /* purecov: inspected */ + DBUG_RETURN(1); } + db= lex->select_lex.db; remove_escape(db); // Fix escaped '_' if (check_db_name(db)) { @@ -2202,11 +2229,6 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, db); DBUG_RETURN(1); } - /* - We need to do a copy to make this prepared statement safe if this - was thd->db - */ - lex->select_lex.db= thd->strdup(db); break; } #endif @@ -2759,8 +2781,8 @@ mysql_execute_command(THD *thd) case SQLCOM_LOAD_MASTER_TABLE: { DBUG_ASSERT(first_table == all_tables && first_table != 0); - if (!first_table->db) - first_table->db= thd->db; + DBUG_ASSERT(first_table->db); /* Must be set in the parser */ + if (check_access(thd, CREATE_ACL, first_table->db, &first_table->grant.privilege, 0, 0, test(first_table->schema_table))) @@ -3002,25 +3024,8 @@ end_with_restore_list: my_error(ER_WRONG_TABLE_NAME, MYF(0), lex->name); goto error; } - if (!select_lex->db) - { - /* - In the case of ALTER TABLE ... RENAME we should supply the - default database if the new name is not explicitly qualified - by a database. (Bug #11493) - */ - if (lex->alter_info.flags & ALTER_RENAME) - { - if (! thd->db) - { - my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); - goto error; - } - select_lex->db= thd->db; - } - else - select_lex->db= first_table->db; - } + /* Must be set in the parser */ + DBUG_ASSERT(select_lex->db); if (check_access(thd, ALTER_ACL, first_table->db, &first_table->grant.privilege, 0, 0, test(first_table->schema_table)) || @@ -3728,12 +3733,8 @@ end_with_restore_list: } case SQLCOM_ALTER_DB: { - char *db= lex->name ? lex->name : thd->db; - if (!db) - { - my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); - break; - } + char *db= lex->name; + DBUG_ASSERT(db); /* Must be set in the parser */ if (!strip_sp(db) || check_db_name(db)) { my_error(ER_WRONG_DB_NAME, MYF(0), lex->name); @@ -4182,23 +4183,11 @@ end_with_restore_list: case SQLCOM_CREATE_SPFUNCTION: { uint namelen; - char *name, *db; + char *name; int result; DBUG_ASSERT(lex->sphead != 0); - - if (!lex->sphead->m_db.str || !lex->sphead->m_db.str[0]) - { - if (!thd->db) - { - my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); - delete lex->sphead; - lex->sphead= 0; - goto error; - } - lex->sphead->m_db.length= strlen(thd->db); - lex->sphead->m_db.str= thd->db; - } + DBUG_ASSERT(lex->sphead->m_db.str); /* Must be initialized in the parser */ if (check_access(thd, CREATE_PROC_ACL, lex->sphead->m_db.str, 0, 0, 0, is_schema_db(lex->sphead->m_db.str))) @@ -4315,41 +4304,27 @@ end_with_restore_list: } #endif /* NO_EMBEDDED_ACCESS_CHECKS */ - /* - We need to copy name and db in order to use them for - check_routine_access which is called after lex->sphead has - been deleted. - */ - name= thd->strdup(name); - lex->sphead->m_db.str= db= thd->strmake(lex->sphead->m_db.str, - lex->sphead->m_db.length); res= (result= lex->sphead->create(thd)); if (result == SP_OK) { - /* - We must cleanup the unit and the lex here because - sp_grant_privileges calls (indirectly) db_find_routine, - which in turn may call MYSQLparse with THD::lex. - TODO: fix db_find_routine to use a temporary lex. - */ - lex->unit.cleanup(); - delete lex->sphead; - lex->sphead= 0; #ifndef NO_EMBEDDED_ACCESS_CHECKS /* only add privileges if really neccessary */ if (sp_automatic_privileges && !opt_noacl && check_routine_access(thd, DEFAULT_CREATE_PROC_ACLS, - db, name, + lex->sphead->m_db.str, name, lex->sql_command == SQLCOM_CREATE_PROCEDURE, 1)) { close_thread_tables(thd); - if (sp_grant_privileges(thd, db, name, + if (sp_grant_privileges(thd, lex->sphead->m_db.str, name, lex->sql_command == SQLCOM_CREATE_PROCEDURE)) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_PROC_AUTO_GRANT_FAIL, ER(ER_PROC_AUTO_GRANT_FAIL)); } #endif + lex->unit.cleanup(); + delete lex->sphead; + lex->sphead= 0; send_ok(thd); } else @@ -4764,7 +4739,8 @@ end_with_restore_list: view_store_options(thd, first_table, &buff); buff.append(STRING_WITH_LEN("VIEW ")); /* Test if user supplied a db (ie: we did not use thd->db) */ - if (first_table->db != thd->db && first_table->db[0]) + if (first_table->db && first_table->db[0] && + (thd->db == NULL || strcmp(first_table->db, thd->db))) { append_identifier(thd, &buff, first_table->db, first_table->db_length); @@ -5346,7 +5322,7 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, (want_access & ~EXTRA_ACL) && thd->db) tables->grant.privilege= want_access; - else if (tables->db && tables->db == thd->db) + else if (tables->db && thd->db && strcmp(tables->db, thd->db) == 0) { if (found && !grant_option) // db already checked tables->grant.privilege=found_access; @@ -5494,22 +5470,25 @@ bool check_merge_table_access(THD *thd, char *db, static bool check_db_used(THD *thd,TABLE_LIST *tables) { + char *current_db= NULL; for (; tables; tables= tables->next_global) { - if (!tables->db) + if (tables->db == NULL) { - if (!(tables->db=thd->db)) - { - my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), - MYF(0)); /* purecov: tested */ - return TRUE; /* purecov: tested */ - } + /* + This code never works and should be removed in 5.1. All tables + that are added to the list of tables should already have its + database field initialized properly (see st_lex::add_table_to_list). + */ + DBUG_ASSERT(0); + if (thd->copy_db_to(¤t_db, 0)) + return TRUE; + tables->db= current_db; } } return FALSE; } - /**************************************************************************** Check stack size; Send error if there isn't enough stack to continue ****************************************************************************/ @@ -6129,19 +6108,8 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, ptr->db= table->db.str; ptr->db_length= table->db.length; } - else if (thd->db) - { - ptr->db= thd->db; - ptr->db_length= thd->db_length; - } - else - { - /* The following can't be "" as we may do 'casedn_str()' on it */ - ptr->db= empty_c_string; - ptr->db_length= 0; - } - if (thd->stmt_arena->is_stmt_prepare_or_first_sp_execute()) - ptr->db= thd->strdup(ptr->db); + else if (thd->copy_db_to(&ptr->db, &ptr->db_length)) + DBUG_RETURN(0); ptr->alias= alias_str; if (lower_case_table_names && table->table.length) @@ -7332,6 +7300,8 @@ bool insert_precheck(THD *thd, TABLE_LIST *tables) my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0)); DBUG_RETURN(TRUE); } + if (check_db_used(thd, tables)) + DBUG_RETURN(TRUE); DBUG_RETURN(FALSE); } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index c9e6384ba72..91c71193df2 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -2684,7 +2684,8 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST src_tables_list; DBUG_ENTER("mysql_create_like_table"); - src_db= table_ident->db.str ? table_ident->db.str : thd->db; + DBUG_ASSERT(table_ident->db.str); /* Must be set in the parser */ + src_db= table_ident->db.str; /* Validate the source table diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index f943b014118..db1d1a10b11 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -932,8 +932,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, save_db.str= thd->db; save_db.length= thd->db_length; - thd->db_length= strlen(db); - thd->db= (char *) db; + thd->reset_db((char*) db, strlen(db)); while ((trg_create_str= it++)) { trg_sql_mode= itm++; @@ -1035,8 +1034,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, lex_end(&lex); } - thd->db= save_db.str; - thd->db_length= save_db.length; + thd->reset_db(save_db.str, save_db.length); thd->lex= old_lex; thd->spcont= save_spcont; thd->variables.sql_mode= save_sql_mode; @@ -1049,8 +1047,7 @@ err_with_lex_cleanup: thd->lex= old_lex; thd->spcont= save_spcont; thd->variables.sql_mode= save_sql_mode; - thd->db= save_db.str; - thd->db_length= save_db.length; + thd->reset_db(save_db.str, save_db.length); DBUG_RETURN(1); } diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc index 6269c0a2eb3..95589a58b37 100644 --- a/sql/sql_udf.cc +++ b/sql/sql_udf.cc @@ -140,6 +140,7 @@ void udf_init() READ_RECORD read_record_info; TABLE *table; int error; + char db[]= "mysql"; /* A subject to casednstr, can't be constant */ DBUG_ENTER("ufd_init"); if (initialized) @@ -161,13 +162,12 @@ void udf_init() initialized = 1; new_thd->thread_stack= (char*) &new_thd; new_thd->store_globals(); - new_thd->db= my_strdup("mysql", MYF(0)); - new_thd->db_length=5; + new_thd->set_db(db, sizeof(db)-1); bzero((gptr) &tables,sizeof(tables)); tables.alias= tables.table_name= (char*) "func"; tables.lock_type = TL_READ; - tables.db=new_thd->db; + tables.db= db; if (simple_open_n_lock_tables(new_thd, &tables)) { diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 0f836bd58ff..1561ade78af 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -452,15 +452,15 @@ bool mysql_create_view(THD *thd, */ for (sl= select_lex; sl; sl= sl->next_select()) { - char *db= view->db ? view->db : thd->db; + DBUG_ASSERT(view->db); /* Must be set in the parser */ List_iterator_fast it(sl->item_list); Item *item; - fill_effective_table_privileges(thd, &view->grant, db, + fill_effective_table_privileges(thd, &view->grant, view->db, view->table_name); while ((item= it++)) { Item_field *fld; - uint priv= (get_column_grant(thd, &view->grant, db, + uint priv= (get_column_grant(thd, &view->grant, view->db, view->table_name, item->name) & VIEW_ANY_ACL); if ((fld= item->filed_for_view_update())) @@ -641,8 +641,7 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, if (!parser->ok() || !is_equal(&view_type, parser->type())) { - my_error(ER_WRONG_OBJECT, MYF(0), - (view->db ? view->db : thd->db), view->table_name, "VIEW"); + my_error(ER_WRONG_OBJECT, MYF(0), view->db, view->table_name, "VIEW"); DBUG_RETURN(-1); } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 4f3cf4d8554..e45be1ef148 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1237,12 +1237,18 @@ sp_name: } | ident { + THD *thd= YYTHD; + LEX_STRING db; if (check_routine_name($1)) { my_error(ER_SP_WRONG_NAME, MYF(0), $1.str); YYABORT; } - $$= sp_name_current_db_new(YYTHD, $1); + if (thd->copy_db_to(&db.str, &db.length)) + YYABORT; + $$= new sp_name(db, $1); + if ($$) + $$->init_qname(YYTHD); } ; @@ -2405,14 +2411,26 @@ create2: | LIKE table_ident { LEX *lex=Lex; + THD *thd= lex->thd; if (!(lex->name= (char *)$2)) YYABORT; + if ($2->db.str == NULL && + thd->copy_db_to(&($2->db.str), &($2->db.length))) + { + YYABORT; + } } | '(' LIKE table_ident ')' { LEX *lex=Lex; + THD *thd= lex->thd; if (!(lex->name= (char *)$3)) YYABORT; + if ($3->db.str == NULL && + thd->copy_db_to(&($3->db.str), &($3->db.length))) + { + YYABORT; + } } ; @@ -3240,7 +3258,9 @@ alter: lex->key_list.empty(); lex->col_list.empty(); lex->select_lex.init_order(); - lex->select_lex.db=lex->name=0; + lex->select_lex.db= + ((TABLE_LIST*) lex->select_lex.table_list.first)->db; + lex->name=0; bzero((char*) &lex->create_info,sizeof(lex->create_info)); lex->create_info.db_type= DB_TYPE_DEFAULT; lex->create_info.default_table_charset= NULL; @@ -3258,8 +3278,11 @@ alter: opt_create_database_options { LEX *lex=Lex; + THD *thd= Lex->thd; lex->sql_command=SQLCOM_ALTER_DB; lex->name= $3; + if (lex->name == NULL && thd->copy_db_to(&lex->name, NULL)) + YYABORT; } | ALTER PROCEDURE sp_name { @@ -3421,14 +3444,20 @@ alter_list_item: | RENAME opt_to table_ident { LEX *lex=Lex; + THD *thd= lex->thd; lex->select_lex.db=$3->db.str; - lex->name= $3->table.str; + if (lex->select_lex.db == NULL && + thd->copy_db_to(&lex->select_lex.db, NULL)) + { + YYABORT; + } if (check_table_name($3->table.str,$3->table.length) || $3->db.str && check_db_name($3->db.str)) { my_error(ER_WRONG_TABLE_NAME, MYF(0), $3->table.str); YYABORT; } + lex->name= $3->table.str; lex->alter_info.flags|= ALTER_RENAME; } | CONVERT_SYM TO_SYM charset charset_name_or_default opt_collate @@ -4742,7 +4771,13 @@ simple_expr: #endif /* HAVE_DLOPEN */ { LEX *lex= Lex; - sp_name *name= sp_name_current_db_new(YYTHD, $1); + THD *thd= lex->thd; + LEX_STRING db; + if (thd->copy_db_to(&db.str, &db.length)) + YYABORT; + sp_name *name= new sp_name(db, $1); + if (name) + name->init_qname(thd); sp_add_used_routine(lex, YYTHD, name, TYPE_ENUM_FUNCTION); if ($4) @@ -8460,7 +8495,9 @@ grant_ident: '*' { LEX *lex= Lex; - lex->current_select->db= lex->thd->db; + THD *thd= lex->thd; + if (thd->copy_db_to(&lex->current_select->db, NULL)) + YYABORT; if (lex->grant == GLOBAL_ACLS) lex->grant = DB_ACLS & ~GRANT_ACL; else if (lex->columns.elements) diff --git a/sql/tztime.cc b/sql/tztime.cc index 079abfc9299..d12aef47b40 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -1548,6 +1548,7 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap) TABLE *table; Tz_names_entry *tmp_tzname; my_bool return_val= 1; + char db[]= "mysql"; int res; DBUG_ENTER("my_tz_init"); @@ -1604,13 +1605,12 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap) leap seconds shared by all time zones. */ - thd->db= my_strdup("mysql",MYF(0)); - thd->db_length= 5; // Safety + thd->set_db(db, sizeof(db)-1); bzero((char*) &tables_buff, sizeof(TABLE_LIST)); tables_buff[0].alias= tables_buff[0].table_name= (char*)"time_zone_leap_second"; tables_buff[0].lock_type= TL_READ; - tables_buff[0].db= thd->db; + tables_buff[0].db= db; /* Fill TABLE_LIST for the rest of the time zone describing tables and link it to first one.