Commit e8966dee authored by unknown's avatar unknown

A fix and a test case for Bug#29050 Creation of a legal stored procedure

fails if a database is not selected prior.

The problem manifested itself when a user tried to
create a routine that had non-fully-qualified identifiers in its bodies
and there was no current database selected.

This is a regression introduced by the fix for Bug 19022:

The patch for Bug 19022 changes the code to always produce a warning
if we can't resolve the current database in the parser. 
In this case this was not necessary, since even though the produced
parsed tree was incorrect, we never re-use sphead
that was obtained at first parsing of CREATE PROCEDURE.
The sphead that is anyhow used is always obtained through db_load_routine,
and there we change the current database to sphead->m_db before
calling yyparse.

The idea of the fix is to resolve the current database directly using 
lex->sphead->m_db member when parsing a stored routine body, when
such is present.

This patch removes the need to reset the current database
when loading a trigger or routine definition into SP cache.
The redundant code will be removed in 5.1.


mysql-test/r/sp.result:
  Update test results (Bug#29050)
mysql-test/r/trigger.result:
  Update results.
mysql-test/t/sp.test:
  Add a test case for Bug#29050
mysql-test/t/trigger.test:
  Fix wrong behavior covered with tests.
sql/sql_lex.cc:
  Implement st_lex::copy_db_to().
sql/sql_lex.h:
  Declare st_lex::copy_db_to().
sql/sql_parse.cc:
  Use st_lex::copy_db_to() in add_table_to_list, rather than
  THD::copy_db_to(). The former will use the database of the sphead,
  if we're parsing a stored routine, not the default database in
  THD. The default database is needed to initialize tables->db
  when the database part was not explicitly specified in the identifier.
sql/sql_yacc.yy:
  Use st_lex::copy_db_to() in the parser, rather than
  THD::copy_db_to(). The former will use the database of the sphead,
  if we're parsing a stored routine, not the default database in
  THD.
parent b1ec3b53
......@@ -6183,4 +6183,22 @@ call mysqltest_db1.sp_bug28551();
show warnings;
Level Code Message
drop database mysqltest_db1;
drop database if exists mysqltest_db1;
drop table if exists test.t1;
create database mysqltest_db1;
use mysqltest_db1;
drop database mysqltest_db1;
create table test.t1 (id int);
insert into test.t1 (id) values (1);
create procedure test.sp_bug29050() begin select * from t1; end//
show warnings;
Level Code Message
call test.sp_bug29050();
id
1
show warnings;
Level Code Message
use test;
drop procedure sp_bug29050;
drop table t1;
End of 5.0 tests
......@@ -351,7 +351,7 @@ create trigger trg1 before insert on mysqltest.t1 for each row set @a:= 1;
ERROR HY000: Trigger in wrong schema
use mysqltest;
create trigger test.trg1 before insert on t1 for each row set @a:= 1;
ERROR HY000: Trigger in wrong schema
ERROR 42S02: Table 'test.t1' doesn't exist
drop database mysqltest;
use test;
create table t1 (i int, j int default 10, k int not null, key (k));
......@@ -842,7 +842,7 @@ drop table t1;
create trigger t1_bi before insert on test.t1 for each row set @a:=0;
ERROR 3D000: No database selected
create trigger test.t1_bi before insert on t1 for each row set @a:=0;
ERROR 3D000: No database selected
ERROR 42S02: Table 'test.t1' doesn't exist
drop trigger t1_bi;
ERROR 3D000: No database selected
create table t1 (id int);
......
......@@ -7142,5 +7142,30 @@ create procedure mysqltest_db1.sp_bug28551() begin end;
call mysqltest_db1.sp_bug28551();
show warnings;
drop database mysqltest_db1;
#
# Bug#29050 Creation of a legal stored procedure fails if a database is not
# selected prior
#
--disable_warnings
drop database if exists mysqltest_db1;
drop table if exists test.t1;
--enable_warnings
create database mysqltest_db1;
use mysqltest_db1;
# For the sake of its side effect
drop database mysqltest_db1;
# Now we have no current database selected.
create table test.t1 (id int);
insert into test.t1 (id) values (1);
delimiter //;
create procedure test.sp_bug29050() begin select * from t1; end//
delimiter ;//
show warnings;
call test.sp_bug29050();
show warnings;
# Restore the old current database
use test;
drop procedure sp_bug29050;
drop table t1;
--echo End of 5.0 tests
......@@ -406,7 +406,7 @@ create table mysqltest.t1 (i int);
--error ER_TRG_IN_WRONG_SCHEMA
create trigger trg1 before insert on mysqltest.t1 for each row set @a:= 1;
use mysqltest;
--error ER_TRG_IN_WRONG_SCHEMA
--error ER_NO_SUCH_TABLE
create trigger test.trg1 before insert on t1 for each row set @a:= 1;
drop database mysqltest;
use test;
......@@ -1040,7 +1040,7 @@ drop table t1;
connection addconwithoutdb;
--error ER_NO_DB_ERROR
create trigger t1_bi before insert on test.t1 for each row set @a:=0;
--error ER_NO_DB_ERROR
--error ER_NO_SUCH_TABLE
create trigger test.t1_bi before insert on t1 for each row set @a:=0;
--error ER_NO_DB_ERROR
drop trigger t1_bi;
......
......@@ -1974,6 +1974,43 @@ uint8 st_lex::get_effective_with_check(st_table_list *view)
}
/**
This method should be called only during parsing.
It is aware of compound statements (stored routine bodies)
and will initialize the destination with the default
database of the stored routine, rather than the default
database of the connection it is parsed in.
E.g. if one has no current database selected, or current database
set to 'bar' and then issues:
CREATE PROCEDURE foo.p1() BEGIN SELECT * FROM t1 END//
t1 is meant to refer to foo.t1, not to bar.t1.
This method is needed to support this rule.
@return TRUE in case of error (parsing should be aborted, FALSE in
case of success
*/
bool
st_lex::copy_db_to(char **p_db, uint *p_db_length) const
{
if (sphead)
{
DBUG_ASSERT(sphead->m_db.str && sphead->m_db.length);
/*
It is safe to assign the string by-pointer, both sphead and
its statements reside in the same memory root.
*/
*p_db= sphead->m_db.str;
if (p_db_length)
*p_db_length= sphead->m_db.length;
return FALSE;
}
return thd->copy_db_to(p_db, p_db_length);
}
/*
initialize limit counters
......
......@@ -1237,6 +1237,8 @@ typedef struct st_lex : public Query_tables_list
context_stack.pop();
}
bool copy_db_to(char **p_db, uint *p_db_length) const;
Name_resolution_context *current_context()
{
return context_stack.head();
......
......@@ -6397,7 +6397,7 @@ 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->copy_db_to(&ptr->db, &ptr->db_length))
else if (lex->copy_db_to(&ptr->db, &ptr->db_length))
DBUG_RETURN(0);
ptr->alias= alias_str;
......
......@@ -1565,14 +1565,14 @@ sp_name:
}
| ident
{
THD *thd= YYTHD;
LEX *lex= Lex;
LEX_STRING db;
if (check_routine_name($1))
{
my_error(ER_SP_WRONG_NAME, MYF(0), $1.str);
MYSQL_YYABORT;
}
if (thd->copy_db_to(&db.str, &db.length))
if (lex->copy_db_to(&db.str, &db.length))
MYSQL_YYABORT;
$$= new sp_name(db, $1, false);
if ($$)
......@@ -3624,10 +3624,9 @@ 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))
if (lex->name == NULL && lex->copy_db_to(&lex->name, NULL))
MYSQL_YYABORT;
}
| ALTER PROCEDURE sp_name
......@@ -3795,10 +3794,9 @@ alter_list_item:
| RENAME opt_to table_ident
{
LEX *lex=Lex;
THD *thd= lex->thd;
lex->select_lex.db=$3->db.str;
if (lex->select_lex.db == NULL &&
thd->copy_db_to(&lex->select_lex.db, NULL))
lex->copy_db_to(&lex->select_lex.db, NULL))
{
MYSQL_YYABORT;
}
......@@ -5148,7 +5146,7 @@ simple_expr:
{
THD *thd= lex->thd;
LEX_STRING db;
if (thd->copy_db_to(&db.str, &db.length))
if (lex->copy_db_to(&db.str, &db.length))
MYSQL_YYABORT;
sp_name *name= new sp_name(db, $1, false);
if (name)
......@@ -9025,8 +9023,7 @@ grant_ident:
'*'
{
LEX *lex= Lex;
THD *thd= lex->thd;
if (thd->copy_db_to(&lex->current_select->db, NULL))
if (lex->copy_db_to(&lex->current_select->db, NULL))
MYSQL_YYABORT;
if (lex->grant == GLOBAL_ACLS)
lex->grant = DB_ACLS & ~GRANT_ACL;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment