From a8f0a8be21237bf7e6e5a20dcd38e7af4d671244 Mon Sep 17 00:00:00 2001 From: "pem@mysql.comhem.se" <> Date: Tue, 27 Jan 2004 17:57:19 +0100 Subject: [PATCH 01/10] WL#1447: Remove the UDF/function lookup in lex. Moved the UDF and stored procedure lookup from sql_lex.cc to sql_yacc.yy. This will improve performance (since we don't have to check for stored functions in the lexer all the time), and make it possible to implement db qualified SPs. --- mysql-test/r/show_check.result | 1 - mysql-test/r/status.result | 2 +- sql/sql_lex.cc | 32 ------- sql/sql_yacc.yy | 147 +++++++++++++++++---------------- 4 files changed, 77 insertions(+), 105 deletions(-) diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result index 8ac880982fb..f39fa3e7576 100644 --- a/mysql-test/r/show_check.result +++ b/mysql-test/r/show_check.result @@ -127,7 +127,6 @@ insert into t1 values (1); show open tables; Database Table In_use Name_locked test t1 0 0 -mysql proc 0 0 drop table t1; create table t1 (a int not null, b VARCHAR(10), INDEX (b) ) AVG_ROW_LENGTH=10 CHECKSUM=1 COMMENT="test" ENGINE=MYISAM MIN_ROWS=10 MAX_ROWS=100 PACK_KEYS=1 DELAY_KEY_WRITE=1 ROW_FORMAT=fixed; show create table t1; diff --git a/mysql-test/r/status.result b/mysql-test/r/status.result index 3134fdcf5ee..e9616232fa1 100644 --- a/mysql-test/r/status.result +++ b/mysql-test/r/status.result @@ -14,6 +14,6 @@ update t1 set n = 3; unlock tables; show status like 'Table_lock%'; Variable_name Value -Table_locks_immediate 4 +Table_locks_immediate 3 Table_locks_waited 1 drop table t1; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index b0381ae1d30..b66ec95d5ed 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -166,38 +166,6 @@ static int find_keyword(LEX *lex, uint len, bool function) lex->yylval->symbol.length=len; return symbol->tok; } - - LEX_STRING ls; - ls.str = (char *)tok; ls.length= len; - if (function && sp_function_exists(current_thd, &ls)) // QQ temp fix - { - lex->safe_to_cache_query= 0; - lex->yylval->lex_str.str= lex->thd->strmake((char*)lex->tok_start, len); - lex->yylval->lex_str.length= len; - return SP_FUNC; - } - -#ifdef HAVE_DLOPEN - udf_func *udf; - if (function && using_udf_functions && (udf=find_udf((char*) tok, len))) - { - lex->safe_to_cache_query=0; - lex->yylval->udf=udf; - switch (udf->returns) { - case STRING_RESULT: - return (udf->type == UDFTYPE_FUNCTION) ? UDF_CHAR_FUNC : UDA_CHAR_SUM; - case REAL_RESULT: - return (udf->type == UDFTYPE_FUNCTION) ? UDF_FLOAT_FUNC : UDA_FLOAT_SUM; - case INT_RESULT: - return (udf->type == UDFTYPE_FUNCTION) ? UDF_INT_FUNC : UDA_INT_SUM; - case ROW_RESULT: - default: - // This case should never be choosen - DBUG_ASSERT(0); - return 0; - } - } -#endif return 0; } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index f35b7f12fd1..b0513240f82 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -568,17 +568,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token SECOND_SYM %token SECOND_MICROSECOND_SYM %token SHARE_SYM -%token SP_FUNC %token SUBDATE_SYM %token SUBSTRING %token SUBSTRING_INDEX %token TRIM -%token UDA_CHAR_SUM -%token UDA_FLOAT_SUM -%token UDA_INT_SUM -%token UDF_CHAR_FUNC -%token UDF_FLOAT_FUNC -%token UDF_INT_FUNC %token UNIQUE_USERS %token UNIX_TIMESTAMP %token USER @@ -640,7 +633,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); LEX_HOSTNAME ULONGLONG_NUM field_ident select_alias ident ident_or_text UNDERSCORE_CHARSET IDENT_sys TEXT_STRING_sys TEXT_STRING_literal NCHAR_STRING opt_component key_cache_name - SP_FUNC ident_or_spfunc sp_opt_label + sp_opt_label %type opt_table_alias @@ -682,7 +675,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); simple_ident_nospvar simple_ident_q %type - expr_list sp_expr_list udf_expr_list udf_expr_list2 when_list + expr_list udf_expr_list udf_expr_list2 when_list ident_list ident_list_arg %type @@ -700,10 +693,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %type join_table_list join_table -%type - UDF_CHAR_FUNC UDF_FLOAT_FUNC UDF_INT_FUNC - UDA_CHAR_SUM UDA_FLOAT_SUM UDA_INT_SUM - %type date_time_type; %type interval @@ -1029,7 +1018,7 @@ create: lex->name=$4.str; lex->create_info.options=$3; } - | CREATE udf_func_type FUNCTION_SYM ident_or_spfunc + | CREATE udf_func_type FUNCTION_SYM IDENT_sys { LEX *lex=Lex; lex->udf.name = $4; @@ -1097,11 +1086,6 @@ create: } ; -ident_or_spfunc: - IDENT_sys { $$= $1; } - | SP_FUNC { $$= $1; } - ; - create_function_tail: RETURNS_SYM udf_type UDF_SONAME_SYM TEXT_STRING_sys { @@ -1218,7 +1202,7 @@ sp_suid: ; call: - CALL_SYM ident_or_spfunc + CALL_SYM IDENT_sys { LEX *lex = Lex; @@ -3901,55 +3885,80 @@ simple_expr: { $$= new Item_func_round($3,$5,1); } | TRUE_SYM { $$= new Item_int((char*) "TRUE",1,1); } - | SP_FUNC '(' sp_expr_list ')' + | IDENT_sys '(' udf_expr_list ')' { - sp_add_fun_to_lex(Lex, $1); - if ($3) - $$= new Item_func_sp($1, *$3); + if (sp_function_exists(YYTHD, &$1)) + { + LEX *lex= Lex; + + sp_add_fun_to_lex(lex, $1); + if ($3) + $$= new Item_func_sp($1, *$3); + else + $$= new Item_func_sp($1); + } else - $$= new Item_func_sp($1); - } - | UDA_CHAR_SUM '(' udf_expr_list ')' - { - if ($3 != NULL) - $$ = new Item_sum_udf_str($1, *$3); - else - $$ = new Item_sum_udf_str($1); - } - | UDA_FLOAT_SUM '(' udf_expr_list ')' - { - if ($3 != NULL) - $$ = new Item_sum_udf_float($1, *$3); - else - $$ = new Item_sum_udf_float($1); - } - | UDA_INT_SUM '(' udf_expr_list ')' - { - if ($3 != NULL) - $$ = new Item_sum_udf_int($1, *$3); - else - $$ = new Item_sum_udf_int($1); - } - | UDF_CHAR_FUNC '(' udf_expr_list ')' - { - if ($3 != NULL) - $$ = new Item_func_udf_str($1, *$3); - else - $$ = new Item_func_udf_str($1); - } - | UDF_FLOAT_FUNC '(' udf_expr_list ')' - { - if ($3 != NULL) - $$ = new Item_func_udf_float($1, *$3); - else - $$ = new Item_func_udf_float($1); - } - | UDF_INT_FUNC '(' udf_expr_list ')' - { - if ($3 != NULL) - $$ = new Item_func_udf_int($1, *$3); - else - $$ = new Item_func_udf_int($1); + { +#ifdef HAVE_DLOPEN + udf_func *udf; + + if (using_udf_functions && (udf=find_udf($1.str, $1.length))) + { + switch (udf->returns) { + case STRING_RESULT: + if (udf->type == UDFTYPE_FUNCTION) + { + if ($3 != NULL) + $$ = new Item_func_udf_str(udf, *$3); + else + $$ = new Item_func_udf_str(udf); + } + else + { + if ($3 != NULL) + $$ = new Item_sum_udf_str(udf, *$3); + else + $$ = new Item_sum_udf_str(udf); + } + break; + case REAL_RESULT: + if (udf->type == UDFTYPE_FUNCTION) + { + if ($3 != NULL) + $$ = new Item_func_udf_float(udf, *$3); + else + $$ = new Item_func_udf_float(udf); + } + else + { + if ($3 != NULL) + $$ = new Item_sum_udf_float(udf, *$3); + else + $$ = new Item_sum_udf_float(udf); + } + break; + case INT_RESULT: + if (udf->type == UDFTYPE_FUNCTION) + { + if ($3 != NULL) + $$ = new Item_func_udf_int(udf, *$3); + else + $$ = new Item_func_udf_int(udf); + } + else + { + if ($3 != NULL) + $$ = new Item_sum_udf_int(udf, *$3); + else + $$ = new Item_sum_udf_int(udf); + } + break; + default: + YYABORT; + } + } +#endif /* HAVE_DLOPEN */ + } } | UNIQUE_USERS '(' text_literal ',' NUM ',' NUM ',' expr_list ')' { @@ -3997,10 +4006,6 @@ fulltext_options: | IN_SYM BOOLEAN_SYM MODE_SYM { $$= FT_BOOL; } ; -sp_expr_list: - /* empty */ { $$= NULL; } - | expr_list { $$= $1;}; - udf_expr_list: /* empty */ { $$= NULL; } | udf_expr_list2 { $$= $1;} From b205759949574aef96be069be78a13a69cd19d2c Mon Sep 17 00:00:00 2001 From: "pem@mysql.comhem.se" <> Date: Tue, 17 Feb 2004 17:36:53 +0100 Subject: [PATCH 02/10] WL#1366: Use the schema (db) associated with an SP. Phase 1: Introduced sp_name class, for qualified name support. --- sql/item_func.cc | 27 +++++++++-- sql/item_func.h | 16 ++---- sql/sp.cc | 124 ++++++++++++++++++++++++++--------------------- sql/sp.h | 19 ++++---- sql/sp_cache.cc | 8 +-- sql/sp_cache.h | 4 +- sql/sp_head.cc | 63 +++++++++++++++++++++--- sql/sp_head.h | 33 ++++++++++++- sql/sql_lex.h | 2 + sql/sql_parse.cc | 50 ++++++++++--------- sql/sql_yacc.yy | 61 ++++++++++++++--------- 11 files changed, 266 insertions(+), 141 deletions(-) diff --git a/sql/item_func.cc b/sql/item_func.cc index d1d03b21eed..fdb0a5e5240 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3088,6 +3088,25 @@ longlong Item_func_is_used_lock::val_int() return ull->thread_id; } + +Item_func_sp::Item_func_sp(sp_name *name) + :Item_func(), m_name(name), m_sp(NULL) +{ + m_name->init_qname(current_thd); +} + +Item_func_sp::Item_func_sp(sp_name *name, List &list) + :Item_func(list), m_name(name), m_sp(NULL) +{ + m_name->init_qname(current_thd); +} + +const char * +Item_func_sp::func_name() const +{ + return m_name->m_name.str; +} + int Item_func_sp::execute(Item **itp) { @@ -3099,7 +3118,7 @@ Item_func_sp::execute(Item **itp) #endif if (! m_sp) - m_sp= sp_find_function(thd, &m_name); + m_sp= sp_find_function(thd, m_name); if (! m_sp) DBUG_RETURN(-1); @@ -3122,7 +3141,7 @@ Item_func_sp::field_type() const DBUG_ENTER("Item_func_sp::field_type"); if (! m_sp) - m_sp= sp_find_function(current_thd, const_cast(&m_name)); + m_sp= sp_find_function(current_thd, m_name); if (m_sp) { DBUG_PRINT("info", ("m_returns = %d", m_sp->m_returns)); @@ -3138,7 +3157,7 @@ Item_func_sp::result_type() const DBUG_PRINT("info", ("m_sp = %p", m_sp)); if (! m_sp) - m_sp= sp_find_function(current_thd, const_cast(&m_name)); + m_sp= sp_find_function(current_thd, m_name); if (m_sp) { DBUG_RETURN(m_sp->result()); @@ -3152,7 +3171,7 @@ Item_func_sp::fix_length_and_dec() DBUG_ENTER("Item_func_sp::fix_length_and_dec"); if (! m_sp) - m_sp= sp_find_function(current_thd, &m_name); + m_sp= sp_find_function(current_thd, m_name); if (m_sp) { switch (m_sp->result()) { diff --git a/sql/item_func.h b/sql/item_func.h index 75c8b348347..e68826ca56e 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1072,32 +1072,26 @@ enum Cast_target */ class sp_head; +class sp_name; class Item_func_sp :public Item_func { private: - LEX_STRING m_name; + sp_name *m_name; mutable sp_head *m_sp; int execute(Item **itp); public: - Item_func_sp(LEX_STRING name) - :Item_func(), m_name(name), m_sp(NULL) - {} + Item_func_sp(sp_name *name); - Item_func_sp(LEX_STRING name, List &list) - :Item_func(list), m_name(name), m_sp(NULL) - {} + Item_func_sp(sp_name *name, List &list); virtual ~Item_func_sp() {} - const char *func_name() const - { - return m_name.str; - } + const char *func_name() const; enum enum_field_types field_type() const; diff --git a/sql/sp.cc b/sql/sp.cc index 83fbd8c5173..26f21c31fd8 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -58,21 +58,22 @@ enum /* *opened=true means we opened ourselves */ static int -db_find_routine_aux(THD *thd, int type, char *name, uint namelen, +db_find_routine_aux(THD *thd, int type, sp_name *name, enum thr_lock_type ltype, TABLE **tablep, bool *opened) { TABLE *table; byte key[64+64+1]; // db, name, type uint keylen; DBUG_ENTER("db_find_routine_aux"); - DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name)); + DBUG_PRINT("enter", ("type: %d name: %*s", + type, name->m_name.length, name->m_name.str)); // Put the key used to read the row together memset(key, (int)' ', 64); // QQ Empty db for now - keylen= namelen; + keylen= name->m_name.length; if (keylen > 64) keylen= 64; - memcpy(key+64, name, keylen); + memcpy(key+64, name->m_name.str, keylen); memset(key+64+keylen, (int)' ', 64-keylen); // Pad with space key[128]= type; keylen= sizeof(key); @@ -112,7 +113,7 @@ db_find_routine_aux(THD *thd, int type, char *name, uint namelen, static int -db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp) +db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) { extern int yyparse(void *thd); TABLE *table; @@ -129,9 +130,10 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp) String str(buff, sizeof(buff), &my_charset_bin); ulong sql_mode; DBUG_ENTER("db_find_routine"); - DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name)); + DBUG_PRINT("enter", ("type: %d name: %*s", + type, name->m_name.length, name->m_name.str)); - ret= db_find_routine_aux(thd, type, name, namelen, TL_READ, &table, &opened); + ret= db_find_routine_aux(thd, type, name, TL_READ, &table, &opened); if (ret != SP_OK) goto done; @@ -220,7 +222,7 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp) if (!(defstr= create_string(thd, &deflen, type, - name, namelen, + name->m_name.str, name->m_name.length, params, strlen(params), returns, strlen(returns), body, strlen(body), @@ -289,6 +291,9 @@ db_create_routine(THD *thd, int type, sp_head *sp) ret= SP_GET_FIELD_FAILED; goto done; } +// QQ Not yet +// table->field[MYSQL_PROC_FIELD_DB]-> +// store(sp->m_db.str, sp->m_db.length, system_charset_info); table->field[MYSQL_PROC_FIELD_NAME]-> store(sp->m_name.str, sp->m_name.length, system_charset_info); table->field[MYSQL_PROC_FIELD_TYPE]-> @@ -329,15 +334,16 @@ done: static int -db_drop_routine(THD *thd, int type, char *name, uint namelen) +db_drop_routine(THD *thd, int type, sp_name *name) { TABLE *table; int ret; bool opened; DBUG_ENTER("db_drop_routine"); - DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name)); + DBUG_PRINT("enter", ("type: %d name: %*s", + type, name->m_name.length, name->m_name.str)); - ret= db_find_routine_aux(thd, type, name, namelen, TL_WRITE, &table, &opened); + ret= db_find_routine_aux(thd, type, name, TL_WRITE, &table, &opened); if (ret == SP_OK) { if (table->file->delete_row(table->record[0])) @@ -351,7 +357,7 @@ db_drop_routine(THD *thd, int type, char *name, uint namelen) static int -db_update_routine(THD *thd, int type, char *name, uint namelen, +db_update_routine(THD *thd, int type, sp_name *name, char *newname, uint newnamelen, st_sp_chistics *chistics) { @@ -359,9 +365,10 @@ db_update_routine(THD *thd, int type, char *name, uint namelen, int ret; bool opened; DBUG_ENTER("db_update_routine"); - DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name)); + DBUG_PRINT("enter", ("type: %d name: %*s", + type, name->m_name.length, name->m_name.str)); - ret= db_find_routine_aux(thd, type, name, namelen, TL_WRITE, &table, &opened); + ret= db_find_routine_aux(thd, type, name, TL_WRITE, &table, &opened); if (ret == SP_OK) { store_record(table,record[1]); @@ -395,6 +402,8 @@ struct st_used_field static struct st_used_field init_fields[]= { +// QQ Not yet +// { "Db", NAME_LEN, MYSQL_TYPE_STRING, 0}, { "Name", NAME_LEN, MYSQL_TYPE_STRING, 0}, { "Type", 9, MYSQL_TYPE_STRING, 0}, { "Definer", 77, MYSQL_TYPE_STRING, 0}, @@ -550,16 +559,17 @@ done: ******************************************************************************/ sp_head * -sp_find_procedure(THD *thd, LEX_STRING *name) +sp_find_procedure(THD *thd, sp_name *name) { sp_head *sp; DBUG_ENTER("sp_find_procedure"); - DBUG_PRINT("enter", ("name: %*s", name->length, name->str)); + DBUG_PRINT("enter", ("name: %*s.%*s", + name->m_db.length, name->m_db.str, + name->m_name.length, name->m_name.str)); - if (!(sp= sp_cache_lookup(&thd->sp_proc_cache, name->str, name->length))) + if (!(sp= sp_cache_lookup(&thd->sp_proc_cache, name))) { - if (db_find_routine(thd, TYPE_ENUM_PROCEDURE, - name->str, name->length, &sp) == SP_OK) + if (db_find_routine(thd, TYPE_ENUM_PROCEDURE, name, &sp) == SP_OK) sp_cache_insert(&thd->sp_proc_cache, sp); } @@ -580,40 +590,40 @@ sp_create_procedure(THD *thd, sp_head *sp) int -sp_drop_procedure(THD *thd, char *name, uint namelen) +sp_drop_procedure(THD *thd, sp_name *name) { int ret; DBUG_ENTER("sp_drop_procedure"); - DBUG_PRINT("enter", ("name: %*s", namelen, name)); + DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); - sp_cache_remove(&thd->sp_proc_cache, name, namelen); - ret= db_drop_routine(thd, TYPE_ENUM_PROCEDURE, name, namelen); + sp_cache_remove(&thd->sp_proc_cache, name); + ret= db_drop_routine(thd, TYPE_ENUM_PROCEDURE, name); DBUG_RETURN(ret); } int -sp_update_procedure(THD *thd, char *name, uint namelen, +sp_update_procedure(THD *thd, sp_name *name, char *newname, uint newnamelen, st_sp_chistics *chistics) { int ret; DBUG_ENTER("sp_update_procedure"); - DBUG_PRINT("enter", ("name: %*s", namelen, name)); + DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); - sp_cache_remove(&thd->sp_proc_cache, name, namelen); - ret= db_update_routine(thd, TYPE_ENUM_PROCEDURE, name, namelen, + sp_cache_remove(&thd->sp_proc_cache, name); + ret= db_update_routine(thd, TYPE_ENUM_PROCEDURE, name, newname, newnamelen, chistics); DBUG_RETURN(ret); } int -sp_show_create_procedure(THD *thd, LEX_STRING *name) +sp_show_create_procedure(THD *thd, sp_name *name) { sp_head *sp; DBUG_ENTER("sp_show_create_procedure"); - DBUG_PRINT("enter", ("name: %*s", name->length, name->str)); + DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); if ((sp= sp_find_procedure(thd, name))) { @@ -642,16 +652,15 @@ sp_show_status_procedure(THD *thd, const char *wild) ******************************************************************************/ sp_head * -sp_find_function(THD *thd, LEX_STRING *name) +sp_find_function(THD *thd, sp_name *name) { sp_head *sp; DBUG_ENTER("sp_find_function"); - DBUG_PRINT("enter", ("name: %*s", name->length, name->str)); + DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); - if (!(sp= sp_cache_lookup(&thd->sp_func_cache, name->str, name->length))) + if (!(sp= sp_cache_lookup(&thd->sp_func_cache, name))) { - if (db_find_routine(thd, TYPE_ENUM_FUNCTION, - name->str, name->length, &sp) != SP_OK) + if (db_find_routine(thd, TYPE_ENUM_FUNCTION, name, &sp) != SP_OK) sp= NULL; else sp_cache_insert(&thd->sp_func_cache, sp); @@ -673,40 +682,40 @@ sp_create_function(THD *thd, sp_head *sp) int -sp_drop_function(THD *thd, char *name, uint namelen) +sp_drop_function(THD *thd, sp_name *name) { int ret; DBUG_ENTER("sp_drop_function"); - DBUG_PRINT("enter", ("name: %*s", namelen, name)); + DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); - sp_cache_remove(&thd->sp_func_cache, name, namelen); - ret= db_drop_routine(thd, TYPE_ENUM_FUNCTION, name, namelen); + sp_cache_remove(&thd->sp_func_cache, name); + ret= db_drop_routine(thd, TYPE_ENUM_FUNCTION, name); DBUG_RETURN(ret); } int -sp_update_function(THD *thd, char *name, uint namelen, +sp_update_function(THD *thd, sp_name *name, char *newname, uint newnamelen, st_sp_chistics *chistics) { int ret; DBUG_ENTER("sp_update_procedure"); - DBUG_PRINT("enter", ("name: %*s", namelen, name)); + DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); - sp_cache_remove(&thd->sp_func_cache, name, namelen); - ret= db_update_routine(thd, TYPE_ENUM_FUNCTION, name, namelen, + sp_cache_remove(&thd->sp_func_cache, name); + ret= db_update_routine(thd, TYPE_ENUM_FUNCTION, name, newname, newnamelen, chistics); DBUG_RETURN(ret); } int -sp_show_create_function(THD *thd, LEX_STRING *name) +sp_show_create_function(THD *thd, sp_name *name) { sp_head *sp; DBUG_ENTER("sp_show_create_function"); - DBUG_PRINT("enter", ("name: %*s", name->length, name->str)); + DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); if ((sp= sp_find_function(thd, name))) { @@ -728,18 +737,18 @@ sp_show_status_function(THD *thd, const char *wild) } -// QQ Temporary until the function call detection in sql_lex has been reworked. bool sp_function_exists(THD *thd, LEX_STRING *name) { TABLE *table; bool ret= FALSE; bool opened= FALSE; + sp_name n(*name); DBUG_ENTER("sp_function_exists"); - if (sp_cache_lookup(&thd->sp_func_cache, name->str, name->length) || + if (sp_cache_lookup(&thd->sp_func_cache, &n) || db_find_routine_aux(thd, TYPE_ENUM_FUNCTION, - name->str, name->length, TL_READ, + &n, TL_READ, &table, &opened) == SP_OK) ret= TRUE; if (opened) @@ -758,13 +767,14 @@ sp_lex_spfuns_key(const byte *ptr, uint *plen, my_bool first) void -sp_add_fun_to_lex(LEX *lex, LEX_STRING fun) +sp_add_fun_to_lex(LEX *lex, sp_name *fun) { - if (! hash_search(&lex->spfuns, (byte *)fun.str, fun.length)) + if (! hash_search(&lex->spfuns, + (byte *)fun->m_name.str, fun->m_name.length)) { LEX_STRING *ls= (LEX_STRING *)sql_alloc(sizeof(LEX_STRING)); - ls->str= sql_strmake(fun.str, fun.length); - ls->length= fun.length; + ls->str= sql_strmake(fun->m_name.str, fun->m_name.length); + ls->length= fun->m_name.length; my_hash_insert(&lex->spfuns, (byte *)ls); } @@ -793,15 +803,17 @@ sp_cache_functions(THD *thd, LEX *lex) for (uint i=0 ; i < h->records ; i++) { LEX_STRING *ls= (LEX_STRING *)hash_element(h, i); + sp_name name(*ls); - if (! sp_cache_lookup(&thd->sp_func_cache, ls->str, ls->length)) + if (! sp_cache_lookup(&thd->sp_func_cache, &name)) { sp_head *sp; LEX *oldlex= thd->lex; LEX *newlex= new st_lex; thd->lex= newlex; - if (db_find_routine(thd, TYPE_ENUM_FUNCTION, ls->str, ls->length, &sp) == SP_OK) + if (db_find_routine(thd, TYPE_ENUM_FUNCTION, &name, &sp) + == SP_OK) { ret= sp_cache_functions(thd, newlex); delete newlex; @@ -836,11 +848,13 @@ create_string(THD *thd, ulong *lenp, char *buf, *ptr; ulong buflen; - buflen= 100 + namelen + paramslen + returnslen + bodylen + chistics->comment.length; + buflen= 100 + namelen + paramslen + returnslen + bodylen + + chistics->comment.length; if (!(buf= thd->alloc(buflen))) return 0; - ptr= strxmov(buf, "CREATE ", (type == TYPE_ENUM_FUNCTION) ? "FUNCTION" : "PROCEDURE", + ptr= strxmov(buf, "CREATE ", + (type == TYPE_ENUM_FUNCTION) ? "FUNCTION" : "PROCEDURE", " `", name, "`(", params, ")", NullS); if (type == TYPE_ENUM_FUNCTION) diff --git a/sql/sp.h b/sql/sp.h index b9f29138de2..95632d2e654 100644 --- a/sql/sp.h +++ b/sql/sp.h @@ -29,47 +29,46 @@ #define SP_INTERNAL_ERROR -7 sp_head * -sp_find_procedure(THD *thd, LEX_STRING *name); +sp_find_procedure(THD *thd, sp_name *name); int sp_create_procedure(THD *thd, sp_head *sp); int -sp_drop_procedure(THD *thd, char *name, uint namelen); +sp_drop_procedure(THD *thd, sp_name *name); int -sp_update_procedure(THD *thd, char *name, uint namelen, +sp_update_procedure(THD *thd, sp_name *name, char *newname, uint newnamelen, st_sp_chistics *chistics); int -sp_show_create_procedure(THD *thd, LEX_STRING *name); +sp_show_create_procedure(THD *thd, sp_name *name); int sp_show_status_procedure(THD *thd, const char *wild); sp_head * -sp_find_function(THD *thd, LEX_STRING *name); +sp_find_function(THD *thd, sp_name *name); int sp_create_function(THD *thd, sp_head *sp); int -sp_drop_function(THD *thd, char *name, uint namelen); +sp_drop_function(THD *thd, sp_name *name); int -sp_update_function(THD *thd, char *name, uint namelen, +sp_update_function(THD *thd, sp_name *name, char *newname, uint newnamelen, st_sp_chistics *chistics); int -sp_show_create_function(THD *thd, LEX_STRING *name); +sp_show_create_function(THD *thd, sp_name *name); int sp_show_status_function(THD *thd, const char *wild); -// QQ Temporary until the function call detection in sql_lex has been reworked. bool sp_function_exists(THD *thd, LEX_STRING *name); @@ -77,7 +76,7 @@ sp_function_exists(THD *thd, LEX_STRING *name); // This is needed since we have to read the functions before we // do anything else. void -sp_add_fun_to_lex(LEX *lex, LEX_STRING fun); +sp_add_fun_to_lex(LEX *lex, sp_name *fun); void sp_merge_funs(LEX *dst, LEX *src); int diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc index 657a96ec33d..93f51938000 100644 --- a/sql/sp_cache.cc +++ b/sql/sp_cache.cc @@ -71,7 +71,7 @@ sp_cache_insert(sp_cache **cp, sp_head *sp) } sp_head * -sp_cache_lookup(sp_cache **cp, char *name, uint namelen) +sp_cache_lookup(sp_cache **cp, sp_name *name) { ulong v; sp_cache *c= *cp; @@ -89,11 +89,11 @@ sp_cache_lookup(sp_cache **cp, char *name, uint namelen) c->version= v; return NULL; } - return c->lookup(name, namelen); + return c->lookup(name->m_name.str, name->m_name.length); } bool -sp_cache_remove(sp_cache **cp, char *name, uint namelen) +sp_cache_remove(sp_cache **cp, sp_name *name) { sp_cache *c= *cp; bool found= FALSE; @@ -109,7 +109,7 @@ sp_cache_remove(sp_cache **cp, char *name, uint namelen) if (c->version < v) c->remove_all(); else - found= c->remove(name, namelen); + found= c->remove(name->m_name.str, name->m_name.length); c->version= v+1; } return found; diff --git a/sql/sp_cache.h b/sql/sp_cache.h index da25227303b..253e9b11588 100644 --- a/sql/sp_cache.h +++ b/sql/sp_cache.h @@ -35,10 +35,10 @@ void sp_cache_clear(sp_cache **cp); void sp_cache_insert(sp_cache **cp, sp_head *sp); /* Lookup an SP in cache */ -sp_head *sp_cache_lookup(sp_cache **cp, char *name, uint namelen); +sp_head *sp_cache_lookup(sp_cache **cp, sp_name *name); /* Remove an SP from cache. Returns true if something was removed */ -bool sp_cache_remove(sp_cache **cp, char *name, uint namelen); +bool sp_cache_remove(sp_cache **cp, sp_name *name); /* diff --git a/sql/sp_head.cc b/sql/sp_head.cc index a68118cecd1..d43ac54e57d 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -130,6 +130,32 @@ sp_eval_func_item(THD *thd, Item *it, enum enum_field_types type) DBUG_RETURN(it); } + +/* + * + * sp_name + * + */ + +void +sp_name::init_qname(THD *thd) +{ + m_qname.length= m_db.length+m_name.length+1; + m_qname.str= alloc_root(&thd->mem_root, m_qname.length+1); + sprintf(m_qname.str, "%*s.%*s", + m_db.length, (m_db.length ? m_db.str : ""), + m_name.length, m_name.str); +} + +/* ------------------------------------------------------------------ */ + + +/* + * + * sp_head + * + */ + void * sp_head::operator new(size_t size) { @@ -178,22 +204,42 @@ sp_head::init(LEX *lex) lex->spcont= m_pcont= new sp_pcontext(); my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8); m_param_begin= m_param_end= m_returns_begin= m_returns_end= m_body_begin= 0; - m_name.str= m_params.str= m_retstr.str= m_body.str= m_defstr.str= 0; - m_name.length= m_params.length= m_retstr.length= m_body.length= - m_defstr.length= 0; + m_qname.str= m_db.str= m_name.str= m_params.str= m_retstr.str= + m_body.str= m_defstr.str= 0; + m_qname.length= m_db.length= m_name.length= m_params.length= + m_retstr.length= m_body.length= m_defstr.length= 0; DBUG_VOID_RETURN; } void -sp_head::init_strings(THD *thd, LEX *lex, LEX_STRING *name) +sp_head::init_strings(THD *thd, LEX *lex, sp_name *name) { DBUG_ENTER("sp_head::init_strings"); /* During parsing, we must use thd->mem_root */ MEM_ROOT *root= &thd->mem_root; - DBUG_PRINT("info", ("name: %*s", name->length, name->str)); - m_name.length= name->length; - m_name.str= strmake_root(root, name->str, name->length); + 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) + { + m_db.length= strlen(thd->db); + m_db.str= strmake_root(root, 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); + m_params.length= m_param_end- m_param_begin; m_params.str= strmake_root(root, (char *)m_param_begin, m_params.length); @@ -1089,10 +1135,13 @@ sp_instr_cfetch::execute(THD *thd, uint *nextp) DBUG_RETURN(res); } +/* ------------------------------------------------------------------ */ + // // Security context swapping // + #ifndef NO_EMBEDDED_ACCESS_CHECKS void sp_change_security_context(THD *thd, sp_head *sp, st_sp_security_context *ctxp) diff --git a/sql/sp_head.h b/sql/sp_head.h index b2dee5204bb..b35b4c37501 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -37,6 +37,35 @@ class sp_instr; struct sp_cond_type; struct sp_pvar; +class sp_name : public Sql_alloc +{ +public: + + LEX_STRING m_db; + LEX_STRING m_name; + LEX_STRING m_qname; + + sp_name(LEX_STRING name) + : m_name(name) + { + m_db.str= m_qname.str= 0; + m_db.length= m_qname.length= 0; + } + + sp_name(LEX_STRING db, LEX_STRING name) + : m_db(db), m_name(name) + { + m_qname.str= 0; + m_qname.length= 0; + } + + // Init. the qualified name from the db and name. + void init_qname(THD *thd); // thd for memroot allocation + + ~sp_name() + {} +}; + class sp_head : public Sql_alloc { sp_head(const sp_head &); /* Prevent use of these */ @@ -56,6 +85,8 @@ public: List m_calls; // Called procedures. List m_tables; // Used tables. #endif + LEX_STRING m_qname; // db.name + LEX_STRING m_db; LEX_STRING m_name; LEX_STRING m_params; LEX_STRING m_retstr; // For FUNCTIONs only @@ -83,7 +114,7 @@ public: // Initialize strings after parsing header void - init_strings(THD *thd, LEX *lex, LEX_STRING *name); + init_strings(THD *thd, LEX *lex, sp_name *name); int create(THD *thd); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 50b8f322731..221f864db81 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -22,6 +22,7 @@ class Table_ident; class sql_exchange; class LEX_COLUMN; class sp_head; +class sp_name; class sp_instr; class sp_pcontext; @@ -604,6 +605,7 @@ typedef struct st_lex bool derived_tables; bool safe_to_cache_query; sp_head *sphead; + sp_name *spname; bool sp_lex_in_use; /* Keep track on lex usage in SPs for error handling */ sp_pcontext *spcont; HASH spfuns; /* Called functions */ diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index ae507c34272..9de3247496c 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3101,9 +3101,9 @@ mysql_execute_command(THD *thd) if (check_access(thd,INSERT_ACL,"mysql",0,1,0)) break; #ifdef HAVE_DLOPEN - if ((sph= sp_find_function(thd, &lex->udf.name))) + if ((sph= sp_find_function(thd, lex->spname))) { - net_printf(thd, ER_UDF_EXISTS, lex->udf.name.str); + net_printf(thd, ER_UDF_EXISTS, lex->spname->m_name.str); goto error; } if (!(res = mysql_create_function(thd,&lex->udf))) @@ -3441,9 +3441,10 @@ mysql_execute_command(THD *thd) { sp_head *sp; - if (!(sp= sp_find_procedure(thd, &lex->udf.name))) + if (!(sp= sp_find_procedure(thd, lex->spname))) { - net_printf(thd, ER_SP_DOES_NOT_EXIST, "PROCEDURE", lex->udf.name); + net_printf(thd, ER_SP_DOES_NOT_EXIST, "PROCEDURE", + lex->spname->m_name.str); goto error; } else @@ -3521,10 +3522,10 @@ mysql_execute_command(THD *thd) goto error; } if (lex->sql_command == SQLCOM_ALTER_PROCEDURE) - res= sp_update_procedure(thd, lex->udf.name.str, lex->udf.name.length, + res= sp_update_procedure(thd, lex->spname, lex->name, newname_len, &lex->sp_chistics); else - res= sp_update_function(thd, lex->udf.name.str, lex->udf.name.length, + res= sp_update_function(thd, lex->spname, lex->name, newname_len, &lex->sp_chistics); switch (res) { @@ -3532,10 +3533,12 @@ mysql_execute_command(THD *thd) send_ok(thd); break; case SP_KEY_NOT_FOUND: - net_printf(thd, ER_SP_DOES_NOT_EXIST, SP_COM_STRING(lex),lex->udf.name); + net_printf(thd, ER_SP_DOES_NOT_EXIST, SP_COM_STRING(lex), + lex->spname->m_name.str); goto error; default: - net_printf(thd, ER_SP_CANT_ALTER, SP_COM_STRING(lex),lex->udf.name); + net_printf(thd, ER_SP_CANT_ALTER, SP_COM_STRING(lex), + lex->spname->m_name.str); goto error; } break; @@ -3544,19 +3547,20 @@ mysql_execute_command(THD *thd) case SQLCOM_DROP_FUNCTION: { if (lex->sql_command == SQLCOM_DROP_PROCEDURE) - res= sp_drop_procedure(thd, lex->udf.name.str, lex->udf.name.length); + res= sp_drop_procedure(thd, lex->spname); else { - res= sp_drop_function(thd, lex->udf.name.str, lex->udf.name.length); + res= sp_drop_function(thd, lex->spname); #ifdef HAVE_DLOPEN if (res == SP_KEY_NOT_FOUND) { - udf_func *udf = find_udf(lex->udf.name.str, lex->udf.name.length); + udf_func *udf = find_udf(lex->spname->m_name.str, + lex->spname->m_name.length); if (udf) { if (check_access(thd, DELETE_ACL, "mysql", 0, 1, 0)) goto error; - if (!(res = mysql_drop_function(thd,&lex->udf.name))) + if (!(res = mysql_drop_function(thd,&lex->spname->m_name))) { send_ok(thd); break; @@ -3575,17 +3579,17 @@ mysql_execute_command(THD *thd) { push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), - SP_COM_STRING(lex), lex->udf.name.str); + SP_COM_STRING(lex), lex->spname->m_name.str); res= 0; send_ok(thd); break; } net_printf(thd, ER_SP_DOES_NOT_EXIST, SP_COM_STRING(lex), - lex->udf.name.str); + lex->spname->m_name.str); goto error; default: net_printf(thd, ER_SP_DROP_FAILED, SP_COM_STRING(lex), - lex->udf.name.str); + lex->spname->m_name.str); goto error; } break; @@ -3593,16 +3597,16 @@ mysql_execute_command(THD *thd) case SQLCOM_SHOW_CREATE_PROC: { res= -1; - if (lex->udf.name.length > NAME_LEN) + if (lex->spname->m_name.length > NAME_LEN) { - net_printf(thd, ER_TOO_LONG_IDENT, lex->udf.name.str); + net_printf(thd, ER_TOO_LONG_IDENT, lex->spname->m_name.str); goto error; } - res= sp_show_create_procedure(thd, &lex->udf.name); + res= sp_show_create_procedure(thd, lex->spname); if (res != SP_OK) { /* We don't distinguish between errors for now */ net_printf(thd, ER_SP_DOES_NOT_EXIST, - SP_COM_STRING(lex), lex->udf.name.str); + SP_COM_STRING(lex), lex->spname->m_name.str); res= 0; goto error; } @@ -3610,16 +3614,16 @@ mysql_execute_command(THD *thd) } case SQLCOM_SHOW_CREATE_FUNC: { - if (lex->udf.name.length > NAME_LEN) + if (lex->spname->m_name.length > NAME_LEN) { - net_printf(thd, ER_TOO_LONG_IDENT, lex->udf.name.str); + net_printf(thd, ER_TOO_LONG_IDENT, lex->spname->m_name.str); goto error; } - res= sp_show_create_function(thd, &lex->udf.name); + res= sp_show_create_function(thd, lex->spname); if (res != SP_OK) { /* We don't distinguish between errors for now */ net_printf(thd, ER_SP_DOES_NOT_EXIST, - SP_COM_STRING(lex), lex->udf.name.str); + SP_COM_STRING(lex), lex->spname->m_name.str); res= 0; goto error; } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 27937999004..4224971fd47 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -92,6 +92,7 @@ inline Item *or_or_concat(THD *thd, Item* A, Item* B) chooser_compare_func_creator boolfunc2creator; struct sp_cond_type *spcondtype; struct { int vars, conds, hndlrs, curs; } spblock; + sp_name *spname; struct st_lex *lex; } @@ -771,6 +772,7 @@ END_OF_INPUT %type sp_cond sp_hcond %type sp_decls sp_decl %type sp_cursor_stmt +%type sp_name %type '-' '+' '*' '/' '%' '(' ')' @@ -1019,15 +1021,15 @@ create: lex->name=$4.str; lex->create_info.options=$3; } - | CREATE udf_func_type FUNCTION_SYM IDENT_sys + | CREATE udf_func_type FUNCTION_SYM sp_name { LEX *lex=Lex; - lex->udf.name = $4; + lex->spname= $4; lex->udf.type= $2; } create_function_tail {} - | CREATE PROCEDURE ident + | CREATE PROCEDURE sp_name { LEX *lex= Lex; sp_head *sp; @@ -1078,7 +1080,7 @@ create: { LEX *lex= Lex; - lex->sphead->init_strings(YYTHD, lex, &$3); + lex->sphead->init_strings(YYTHD, lex, $3); lex->sql_command= SQLCOM_CREATE_PROCEDURE; /* Restore flag if it was cleared above */ if (lex->sphead->m_old_cmq) @@ -1087,11 +1089,17 @@ create: } ; +sp_name: + IDENT_sys '.' IDENT_sys { $$= new sp_name($1, $3); } + | IDENT_sys { $$= new sp_name($1); } + ; + create_function_tail: RETURNS_SYM udf_type UDF_SONAME_SYM TEXT_STRING_sys { LEX *lex=Lex; lex->sql_command = SQLCOM_CREATE_FUNCTION; + lex->udf.name = lex->spname->m_name; lex->udf.returns=(Item_result) $2; lex->udf.dl=$4.str; } @@ -1153,7 +1161,7 @@ create_function_tail: sp_head *sp= lex->sphead; lex->sql_command= SQLCOM_CREATE_SPFUNCTION; - sp->init_strings(YYTHD, lex, &lex->udf.name); + sp->init_strings(YYTHD, lex, lex->spname); /* Restore flag if it was cleared above */ if (sp->m_old_cmq) YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES; @@ -1203,12 +1211,12 @@ sp_suid: ; call: - CALL_SYM IDENT_sys + CALL_SYM sp_name { LEX *lex = Lex; lex->sql_command= SQLCOM_CALL; - lex->udf.name= $2; + lex->spname= $2; lex->value_list.empty(); } '(' sp_cparam_list ')' {} @@ -2723,7 +2731,7 @@ alter: lex->sql_command=SQLCOM_ALTER_DB; lex->name=$3.str; } - | ALTER PROCEDURE ident + | ALTER PROCEDURE sp_name { LEX *lex= Lex; @@ -2736,9 +2744,9 @@ alter: LEX *lex=Lex; lex->sql_command= SQLCOM_ALTER_PROCEDURE; - lex->udf.name= $3; + lex->spname= $3; } - | ALTER FUNCTION_SYM ident + | ALTER FUNCTION_SYM sp_name { LEX *lex= Lex; @@ -2751,7 +2759,7 @@ alter: LEX *lex=Lex; lex->sql_command= SQLCOM_ALTER_FUNCTION; - lex->udf.name= $3; + lex->spname= $3; } ; @@ -3908,12 +3916,13 @@ simple_expr: if (sp_function_exists(YYTHD, &$1)) { LEX *lex= Lex; + sp_name *name= new sp_name($1); - sp_add_fun_to_lex(lex, $1); + sp_add_fun_to_lex(lex, name); if ($3) - $$= new Item_func_sp($1, *$3); + $$= new Item_func_sp(name, *$3); else - $$= new Item_func_sp($1); + $$= new Item_func_sp(name); } else { @@ -4830,19 +4839,19 @@ drop: lex->drop_if_exists=$3; lex->name=$4.str; } - | DROP FUNCTION_SYM if_exists IDENT_sys opt_restrict + | DROP FUNCTION_SYM if_exists sp_name opt_restrict { LEX *lex=Lex; lex->sql_command = SQLCOM_DROP_FUNCTION; lex->drop_if_exists= $3; - lex->udf.name= $4; + lex->spname= $4; } - | DROP PROCEDURE if_exists IDENT_sys opt_restrict + | DROP PROCEDURE if_exists sp_name opt_restrict { LEX *lex=Lex; lex->sql_command = SQLCOM_DROP_PROCEDURE; lex->drop_if_exists= $3; - lex->udf.name= $4; + lex->spname= $4; } | DROP USER { @@ -5321,15 +5330,19 @@ show_param: { Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT; } - | CREATE PROCEDURE ident + | CREATE PROCEDURE sp_name { - Lex->sql_command = SQLCOM_SHOW_CREATE_PROC; - Lex->udf.name= $3; + LEX *lex= Lex; + + lex->sql_command = SQLCOM_SHOW_CREATE_PROC; + lex->spname= $3; } - | CREATE FUNCTION_SYM ident + | CREATE FUNCTION_SYM sp_name { - Lex->sql_command = SQLCOM_SHOW_CREATE_FUNC; - Lex->udf.name= $3; + LEX *lex= Lex; + + lex->sql_command = SQLCOM_SHOW_CREATE_FUNC; + lex->spname= $3; } | PROCEDURE STATUS_SYM wild { From 3c58ae9e74d306b09ed6de6c82c9b4079f199169 Mon Sep 17 00:00:00 2001 From: "dlenev@brandersnatch.localdomain" <> Date: Mon, 1 Mar 2004 09:15:33 +0300 Subject: [PATCH 03/10] Fixed small race condition, when global query_id was modified without proper locking. --- BitKeeper/etc/logging_ok | 1 + sql/sp_head.cc | 3 +++ sql/sql_parse.cc | 4 ++++ 3 files changed, 8 insertions(+) diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok index 6e53347b0a6..b8607af3b3c 100644 --- a/BitKeeper/etc/logging_ok +++ b/BitKeeper/etc/logging_ok @@ -24,6 +24,7 @@ bk@admin.bk bk@mysql.r18.ru carsten@tsort.bitbybit.dk davida@isil.mysql.com +dlenev@brandersnatch.localdomain dlenev@build.mysql.com dlenev@mysql.com gerberb@ou800.zenez.com diff --git a/sql/sp_head.cc b/sql/sp_head.cc index a68118cecd1..bfd7ad259f6 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -796,7 +796,10 @@ sp_instr_stmt::exec_stmt(THD *thd, LEX *lex) thd->lex->unit.thd= thd; // QQ Not reentrant freelist= thd->free_list; thd->free_list= NULL; + + VOID(pthread_mutex_lock(&LOCK_thread_count)); thd->query_id= query_id++; + VOID(pthread_mutex_unlock(&LOCK_thread_count)); // Copy WHERE clause pointers to avoid damaging by optimisation // Also clear ref_pointer_arrays. diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index ae507c34272..d332b6985cb 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1086,6 +1086,10 @@ extern "C" pthread_handler_decl(handle_bootstrap,arg) thd->query_length=length; thd->query= thd->memdup_w_gap(buff, length+1, thd->db_length+1); thd->query[length] = '\0'; + /* + We don't need to obtain LOCK_thread_count here because in bootstrap + mode we have only one thread. + */ thd->query_id=query_id++; if (mqh_used && thd->user_connect && check_mqh(thd, SQLCOM_END)) { From 1631ba625f9db24372b1f02b73fa0d5ffcab85fd Mon Sep 17 00:00:00 2001 From: "pem@mysql.comhem.se" <> Date: Tue, 2 Mar 2004 11:52:19 +0100 Subject: [PATCH 04/10] Fixed BUG#2777: Stored procedure doesn't observe definer's rights. SQL SECURITY DEFINER must enforce reduced rights too, not just additional rights. --- mysql-test/r/sp-security.result | 49 +++++++++++++++++++--- mysql-test/t/sp-security.test | 73 +++++++++++++++++++++++++++++---- sql/sql_acl.cc | 22 +++++++++- 3 files changed, 129 insertions(+), 15 deletions(-) diff --git a/mysql-test/r/sp-security.result b/mysql-test/r/sp-security.result index 9d5f71225b6..c4fbece9d72 100644 --- a/mysql-test/r/sp-security.result +++ b/mysql-test/r/sp-security.result @@ -1,5 +1,6 @@ use test; -grant usage on *.* to dummy@localhost; +grant usage on *.* to user1@localhost; +flush privileges; drop database if exists db1_secret; create database db1_secret; use db1_secret; @@ -15,14 +16,14 @@ u i root@localhost 1 call stamp(2); select * from db1_secret.t1; -ERROR 42000: Access denied for user: 'dummy'@'localhost' to database 'db1_secret' +ERROR 42000: Access denied for user: 'user1'@'localhost' to database 'db1_secret' call stamp(3); select * from db1_secret.t1; ERROR 42000: Access denied for user: ''@'localhost' to database 'db1_secret' select * from t1; u i root@localhost 1 -dummy@localhost 2 +user1@localhost 2 anon@localhost 3 alter procedure stamp sql security invoker; show procedure status like 'stamp'; @@ -32,14 +33,50 @@ call stamp(4); select * from t1; u i root@localhost 1 -dummy@localhost 2 +user1@localhost 2 anon@localhost 3 root@localhost 4 call stamp(5); -ERROR 42000: Access denied for user: 'dummy'@'localhost' to database 'db1_secret' +ERROR 42000: Access denied for user: 'user1'@'localhost' to database 'db1_secret' call stamp(6); ERROR 42000: Access denied for user: ''@'localhost' to database 'db1_secret' +drop database if exists db2; +create database db2; +use db2; +create table t2 (s1 int); +insert into t2 values (0); +grant usage on db2.* to user1@localhost; +grant select on db2.* to user1@localhost; +grant usage on db2.* to user2@localhost; +grant select,insert,update,delete on db2.* to user2@localhost; +flush privileges; +use db2; +create procedure p () insert into t2 values (1); +call p(); +ERROR 42000: Access denied for user: 'user1'@'localhost' to database 'db2' +use db2; +call p(); +ERROR 42000: Access denied for user: 'user1'@'localhost' to database 'db2' +select * from t2; +s1 +0 +create procedure q () insert into t2 values (2); +call q(); +select * from t2; +s1 +0 +2 +use db2; +call q(); +select * from t2; +s1 +0 +2 +2 drop procedure stamp; +drop procedure p; +drop procedure q; use test; drop database db1_secret; -delete from mysql.user where user='dummy'; +drop database db2; +delete from mysql.user where user='user1' or user='user2'; diff --git a/mysql-test/t/sp-security.test b/mysql-test/t/sp-security.test index 0d77b53210e..ac7477869a1 100644 --- a/mysql-test/t/sp-security.test +++ b/mysql-test/t/sp-security.test @@ -7,8 +7,9 @@ connect (con1root,localhost,root,,); connection con1root; use test; -# Create dummy user with no particular access rights -grant usage on *.* to dummy@localhost; +# Create user user1 with no particular access rights +grant usage on *.* to user1@localhost; +flush privileges; --disable_warnings drop database if exists db1_secret; @@ -30,13 +31,13 @@ show procedure status like 'stamp'; call stamp(1); select * from t1; -connect (con2dummy,localhost,dummy,,); +connect (con2user1,localhost,user1,,); connect (con3anon,localhost,anon,,); # -# Dummy can +# User1 can # -connection con2dummy; +connection con2user1; # This should work... call stamp(2); @@ -75,9 +76,9 @@ call stamp(4); select * from t1; # -# Dummy cannot +# User1 cannot # -connection con2dummy; +connection con2user1; # This should not work --error 1044 @@ -92,9 +93,65 @@ connection con3anon; --error 1044 call stamp(6); + +# +# BUG#2777 +# + +connection con1root; +--disable_warnings +drop database if exists db2; +--enable_warnings +create database db2; + +use db2; + +create table t2 (s1 int); +insert into t2 values (0); + +grant usage on db2.* to user1@localhost; +grant select on db2.* to user1@localhost; +grant usage on db2.* to user2@localhost; +grant select,insert,update,delete on db2.* to user2@localhost; +flush privileges; + +connection con2user1; +use db2; + +create procedure p () insert into t2 values (1); + +# Check that this doesn't work. +--error 1044 +call p(); + +connect (con4user2,localhost,user2,,); + +connection con4user2; +use db2; + +# This should not work, since p is executed with definer's (user1's) rights. +--error 1044 +call p(); +select * from t2; + +create procedure q () insert into t2 values (2); + +call q(); +select * from t2; + +connection con2user1; +use db2; + +# This should work +call q(); +select * from t2; + # Clean up connection con1root; drop procedure stamp; +drop procedure p; +drop procedure q; use test; drop database db1_secret; -delete from mysql.user where user='dummy'; +drop database db2; +delete from mysql.user where user='user1' or user='user2'; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 5febb49e110..d294055ff8a 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -794,6 +794,7 @@ int acl_getroot_no_password(THD *thd) { ulong user_access= NO_ACCESS; int res= 1; + uint i; ACL_USER *acl_user= 0; DBUG_ENTER("acl_getroot_no_password"); @@ -810,13 +811,16 @@ int acl_getroot_no_password(THD *thd) VOID(pthread_mutex_lock(&acl_cache->lock)); + thd->master_access= 0; + thd->db_access= 0; + /* Find acl entry in user database. This is specially tailored to suit the check we do for CALL of a stored procedure; thd->user is set to what is actually a priv_user, which can be ''. */ - for (uint i=0 ; i < acl_users.elements ; i++) + for (i=0 ; i < acl_users.elements ; i++) { acl_user= dynamic_element(&acl_users,i,ACL_USER*); if ((!acl_user->user && (!thd->user || !thd->user[0])) || @@ -832,6 +836,22 @@ int acl_getroot_no_password(THD *thd) if (acl_user) { + for (i=0 ; i < acl_dbs.elements ; i++) + { + ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*); + if (!acl_db->user || + (thd->user && thd->user[0] && !strcmp(thd->user, acl_db->user))) + { + if (compare_hostname(&acl_db->host, thd->host, thd->ip)) + { + if (!acl_db->db || (thd->db && !strcmp(acl_db->db, thd->db))) + { + thd->db_access= acl_db->access; + break; + } + } + } + } thd->master_access= acl_user->access; thd->priv_user= acl_user->user ? thd->user : (char *) ""; From 6c6bbba00770b241818b35fe4b1d195e0d34142a Mon Sep 17 00:00:00 2001 From: "pem@mysql.comhem.se" <> Date: Thu, 11 Mar 2004 17:18:59 +0100 Subject: [PATCH 05/10] WL#1366: Use the schema (db) associated with an SP. Phase 2: Make SPs belong to a DB, and use qualified names. As a side effect, using USE in an SP is no longer allowed. (It just doesn't work otherwise.) --- include/mysqld_error.h | 3 +- include/sql_state.h | 1 + mysql-test/r/sp-error.result | 4 + mysql-test/r/sp-security.result | 22 ++--- mysql-test/r/sp.result | 59 +++++------- mysql-test/t/sp-error.test | 6 ++ mysql-test/t/sp-security.test | 18 ++-- mysql-test/t/sp.test | 31 ++----- sql/mysql_priv.h | 3 +- sql/share/czech/errmsg.txt | 1 + sql/share/danish/errmsg.txt | 1 + sql/share/dutch/errmsg.txt | 1 + sql/share/english/errmsg.txt | 1 + sql/share/estonian/errmsg.txt | 1 + sql/share/french/errmsg.txt | 1 + sql/share/german/errmsg.txt | 1 + sql/share/greek/errmsg.txt | 1 + sql/share/hungarian/errmsg.txt | 1 + sql/share/italian/errmsg.txt | 1 + sql/share/japanese/errmsg.txt | 1 + sql/share/korean/errmsg.txt | 1 + sql/share/norwegian-ny/errmsg.txt | 1 + sql/share/norwegian/errmsg.txt | 1 + sql/share/polish/errmsg.txt | 1 + sql/share/portuguese/errmsg.txt | 1 + sql/share/romanian/errmsg.txt | 1 + sql/share/russian/errmsg.txt | 1 + sql/share/serbian/errmsg.txt | 1 + sql/share/slovak/errmsg.txt | 1 + sql/share/spanish/errmsg.txt | 1 + sql/share/swedish/errmsg.txt | 1 + sql/share/ukrainian/errmsg.txt | 1 + sql/sp.cc | 147 ++++++++++++++++++++++++------ sql/sp.h | 17 +++- sql/sp_cache.cc | 9 +- sql/sp_head.cc | 83 +++++++++++------ sql/sp_head.h | 27 ++---- sql/sql_db.cc | 95 +++++++++++-------- sql/sql_yacc.yy | 21 ++++- 39 files changed, 367 insertions(+), 202 deletions(-) diff --git a/include/mysqld_error.h b/include/mysqld_error.h index a3e063d4ed7..113eb07ed66 100644 --- a/include/mysqld_error.h +++ b/include/mysqld_error.h @@ -339,4 +339,5 @@ #define ER_SP_DUP_CURS 1320 #define ER_SP_CANT_ALTER 1321 #define ER_SP_SUBSELECT_NYI 1322 -#define ER_ERROR_MESSAGES 323 +#define ER_SP_NO_USE 1323 +#define ER_ERROR_MESSAGES 324 diff --git a/include/sql_state.h b/include/sql_state.h index f4fbe434490..c5a71daceb9 100644 --- a/include/sql_state.h +++ b/include/sql_state.h @@ -196,3 +196,4 @@ ER_SP_DUP_COND, "42000", "", ER_SP_DUP_CURS, "42000", "", /*ER_SP_CANT_ALTER*/ ER_SP_SUBSELECT_NYI, "0A000", "", +ER_SP_NO_USE, "42000", "", diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result index f965cf90eb4..1877789e2b0 100644 --- a/mysql-test/r/sp-error.result +++ b/mysql-test/r/sp-error.result @@ -251,6 +251,10 @@ declare c cursor for select * from t1; declare c cursor for select field from t1; end| ERROR 42000: Duplicate cursor: c +create procedure u() +use sptmp; +#| +ERROR 42000: USE is not allowed in a stored procedure create procedure bug1965() begin declare c cursor for select val from t1 order by valname; diff --git a/mysql-test/r/sp-security.result b/mysql-test/r/sp-security.result index c4fbece9d72..51439e08782 100644 --- a/mysql-test/r/sp-security.result +++ b/mysql-test/r/sp-security.result @@ -8,16 +8,16 @@ create table t1 ( u varchar(64), i int ); create procedure stamp(i int) insert into db1_secret.t1 values (user(), i); show procedure status like 'stamp'; -Name Type Definer Modified Created Security_type Comment -stamp PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER +Db Name Type Definer Modified Created Security_type Comment +db1_secret stamp PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER call stamp(1); select * from t1; u i root@localhost 1 -call stamp(2); +call db1_secret.stamp(2); select * from db1_secret.t1; ERROR 42000: Access denied for user: 'user1'@'localhost' to database 'db1_secret' -call stamp(3); +call db1_secret.stamp(3); select * from db1_secret.t1; ERROR 42000: Access denied for user: ''@'localhost' to database 'db1_secret' select * from t1; @@ -27,8 +27,8 @@ user1@localhost 2 anon@localhost 3 alter procedure stamp sql security invoker; show procedure status like 'stamp'; -Name Type Definer Modified Created Security_type Comment -stamp PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 INVOKER +Db Name Type Definer Modified Created Security_type Comment +db1_secret stamp PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 INVOKER call stamp(4); select * from t1; u i @@ -36,9 +36,9 @@ root@localhost 1 user1@localhost 2 anon@localhost 3 root@localhost 4 -call stamp(5); +call db1_secret.stamp(5); ERROR 42000: Access denied for user: 'user1'@'localhost' to database 'db1_secret' -call stamp(6); +call db1_secret.stamp(6); ERROR 42000: Access denied for user: ''@'localhost' to database 'db1_secret' drop database if exists db2; create database db2; @@ -73,9 +73,9 @@ s1 0 2 2 -drop procedure stamp; -drop procedure p; -drop procedure q; +drop procedure db1_secret.stamp; +drop procedure db2.p; +drop procedure db2.q; use test; drop database db1_secret; drop database db2; diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index bf515e4b4cd..0a934817d85 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -18,17 +18,6 @@ id data foo 42 delete from t1; drop procedure foo42; -create procedure u() -use sptmp; -drop database if exists sptmp; -create database sptmp; -use test; -call u(); -select database(); -database() -test -drop database sptmp; -drop procedure u; create procedure bar(x char(16), y int) insert into test.t1 values (x, y); call bar("bar", 666); @@ -746,7 +735,7 @@ delete from t1| alter procedure chistics sql security invoker name chistics2| show create procedure chistics2| Procedure Create Procedure -chistics2 CREATE PROCEDURE `chistics2`() +chistics2 CREATE PROCEDURE `test`.`chistics2`() SQL SECURITY INVOKER COMMENT 'Characteristics procedure test' insert into t1 values ("chistics", 1) @@ -763,7 +752,7 @@ chistics() alter function chistics name chistics2 comment 'Characteristics function test'| show create function chistics2| Function Create Function -chistics2 CREATE FUNCTION `chistics2`() RETURNS int +chistics2 CREATE FUNCTION `test`.`chistics2`() RETURNS int DETERMINISTIC SQL SECURITY INVOKER COMMENT 'Characteristics function test' @@ -939,23 +928,23 @@ begin show create function fac; end| call bug2267_1()| -Name Type Definer Modified Created Security_type Comment -bug2267_1 PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER -bug2267_2 PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER -bug2267_3 PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER -bug2267_4 PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER +Db Name Type Definer Modified Created Security_type Comment +test bug2267_1 PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER +test bug2267_2 PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER +test bug2267_3 PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER +test bug2267_4 PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER call bug2267_2()| -Name Type Definer Modified Created Security_type Comment -fac FUNCTION root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER +Db Name Type Definer Modified Created Security_type Comment +test fac FUNCTION root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER call bug2267_3()| Procedure Create Procedure -bug2267_1 CREATE PROCEDURE `bug2267_1`() +bug2267_1 CREATE PROCEDURE `test`.`bug2267_1`() begin show procedure status; end call bug2267_4()| Function Create Function -fac CREATE FUNCTION `fac`(n int unsigned) RETURNS bigint unsigned +fac CREATE FUNCTION `test`.`fac`(n int unsigned) RETURNS bigint unsigned begin declare f bigint unsigned default 1; while n > 1 do @@ -1029,12 +1018,12 @@ n f 20 2432902008176640000 drop table fac| show function status like '%f%'| -Name Type Definer Modified Created Security_type Comment -fac FUNCTION root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER +Db Name Type Definer Modified Created Security_type Comment +test fac FUNCTION root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER drop procedure ifac| drop function fac| show function status like '%f%'| -Name Type Definer Modified Created Security_type Comment +Db Name Type Definer Modified Created Security_type Comment drop table if exists primes| create table primes ( i int unsigned not null primary key, @@ -1095,7 +1084,7 @@ end while; end| show create procedure opp| Procedure Create Procedure -opp CREATE PROCEDURE `opp`(n bigint unsigned, out pp bool) +opp CREATE PROCEDURE `test`.`opp`(n bigint unsigned, out pp bool) begin declare r double; declare b, s bigint unsigned default 0; @@ -1122,9 +1111,9 @@ end if; end loop; end show procedure status like '%p%'| -Name Type Definer Modified Created Security_type Comment -ip PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER -opp PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER +Db Name Type Definer Modified Created Security_type Comment +test ip PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER +test opp PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER call ip(200)| select * from primes where i=45 or i=100 or i=199| i p @@ -1135,7 +1124,7 @@ drop table primes| drop procedure opp| drop procedure ip| show procedure status like '%p%'| -Name Type Definer Modified Created Security_type Comment +Db Name Type Definer Modified Created Security_type Comment drop table if exists fib| create table fib ( f bigint unsigned not null )| insert into fib values (1), (1)| @@ -1185,19 +1174,19 @@ create procedure bar(x char(16), y int) comment "111111111111" sql security invoker insert into test.t1 values (x, y)| show procedure status like 'bar'| -Name Type Definer Modified Created Security_type Comment -bar PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 INVOKER 111111111111 +Db Name Type Definer Modified Created Security_type Comment +test bar PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 INVOKER 111111111111 alter procedure bar name bar2 comment "2222222222" sql security definer| alter procedure bar2 name bar comment "3333333333"| alter procedure bar| show create procedure bar| Procedure Create Procedure -bar CREATE PROCEDURE `bar`(x char(16), y int) +bar CREATE PROCEDURE `test`.`bar`(x char(16), y int) COMMENT '3333333333' insert into test.t1 values (x, y) show procedure status like 'bar'| -Name Type Definer Modified Created Security_type Comment -bar PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER 3333333333 +Db Name Type Definer Modified Created Security_type Comment +test bar PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER 3333333333 drop procedure bar| drop table t1; drop table t2; diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test index 68a5ccdbe42..43b5c04766a 100644 --- a/mysql-test/t/sp-error.test +++ b/mysql-test/t/sp-error.test @@ -330,6 +330,12 @@ begin declare c cursor for select field from t1; end| +# USE is not allowed +--error 1323 +create procedure u() + use sptmp; + + # # BUG#1965 # diff --git a/mysql-test/t/sp-security.test b/mysql-test/t/sp-security.test index ac7477869a1..2d089e72d0b 100644 --- a/mysql-test/t/sp-security.test +++ b/mysql-test/t/sp-security.test @@ -24,7 +24,7 @@ create table t1 ( u varchar(64), i int ); # Our test procedure create procedure stamp(i int) insert into db1_secret.t1 values (user(), i); ---replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00' +--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00' show procedure status like 'stamp'; # root can, of course @@ -40,7 +40,7 @@ connect (con3anon,localhost,anon,,); connection con2user1; # This should work... -call stamp(2); +call db1_secret.stamp(2); # ...but not this --error 1044 @@ -52,7 +52,7 @@ select * from db1_secret.t1; connection con3anon; # This should work... -call stamp(3); +call db1_secret.stamp(3); # ...but not this --error 1044 @@ -68,7 +68,7 @@ select * from t1; # Change to invoker's rights # alter procedure stamp sql security invoker; ---replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00' +--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00' show procedure status like 'stamp'; # root still can @@ -82,7 +82,7 @@ connection con2user1; # This should not work --error 1044 -call stamp(5); +call db1_secret.stamp(5); # # Anonymous cannot @@ -91,7 +91,7 @@ connection con3anon; # This should not work --error 1044 -call stamp(6); +call db1_secret.stamp(6); # @@ -148,9 +148,9 @@ select * from t2; # Clean up connection con1root; -drop procedure stamp; -drop procedure p; -drop procedure q; +drop procedure db1_secret.stamp; +drop procedure db2.p; +drop procedure db2.q; use test; drop database db1_secret; drop database db2; diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 3cb88ec5717..1ea3914a5c5 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -31,21 +31,6 @@ delete from t1; drop procedure foo42; -# USE test: Make sure we remain in the same DB. -create procedure u() - use sptmp; - ---disable_warnings -drop database if exists sptmp; ---enable_warnings -create database sptmp; -use test; -call u(); -select database(); -drop database sptmp; -drop procedure u; - - # Single statement, two IN params. create procedure bar(x char(16), y int) insert into test.t1 values (x, y); @@ -1094,9 +1079,9 @@ begin show create function fac; end| ---replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00' +--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00' call bug2267_1()| ---replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00' +--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00' call bug2267_2()| call bug2267_3()| call bug2267_4()| @@ -1168,11 +1153,11 @@ end| call ifac(20)| select * from fac| drop table fac| ---replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00' +--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00' show function status like '%f%'| drop procedure ifac| drop function fac| ---replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00' +--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00' show function status like '%f%'| @@ -1249,7 +1234,7 @@ begin end while; end| show create procedure opp| ---replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00' +--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00' show procedure status like '%p%'| # This isn't the fastest way in the world to compute prime numbers, so @@ -1261,7 +1246,7 @@ select * from primes where i=45 or i=100 or i=199| drop table primes| drop procedure opp| drop procedure ip| ---replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00' +--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00' show procedure status like '%p%'| @@ -1308,13 +1293,13 @@ drop procedure fib| create procedure bar(x char(16), y int) comment "111111111111" sql security invoker insert into test.t1 values (x, y)| ---replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00' +--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00' show procedure status like 'bar'| alter procedure bar name bar2 comment "2222222222" sql security definer| alter procedure bar2 name bar comment "3333333333"| alter procedure bar| show create procedure bar| ---replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00' +--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00' show procedure status like 'bar'| drop procedure bar| delimiter ;| diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index e17847ebe24..21822e02d29 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -445,7 +445,8 @@ int mysql_rm_table_part2_with_lock(THD *thd, TABLE_LIST *tables, int quick_rm_table(enum db_type base,const char *db, const char *table_name); bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list); -bool mysql_change_db(THD *thd,const char *name); +bool mysql_change_db(THD *thd,const char *name, + bool empty_is_ok=0, bool no_access_check=0); void mysql_parse(THD *thd,char *inBuf,uint length); bool is_update_query(enum enum_sql_command command); void free_items(Item *item); diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt index 117bf4b37d4..eea5912cbdf 100644 --- a/sql/share/czech/errmsg.txt +++ b/sql/share/czech/errmsg.txt @@ -335,3 +335,4 @@ character-set=latin2 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt index ba012e0eea3..2c2782d4cf0 100644 --- a/sql/share/danish/errmsg.txt +++ b/sql/share/danish/errmsg.txt @@ -329,3 +329,4 @@ character-set=latin1 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt index 0349929f0d3..84bd5781ffb 100644 --- a/sql/share/dutch/errmsg.txt +++ b/sql/share/dutch/errmsg.txt @@ -337,3 +337,4 @@ character-set=latin1 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt index 6bf4bde9b22..2c88e1b445f 100644 --- a/sql/share/english/errmsg.txt +++ b/sql/share/english/errmsg.txt @@ -326,3 +326,4 @@ character-set=latin1 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt index 03e2d4d6c90..d72d7f94309 100644 --- a/sql/share/estonian/errmsg.txt +++ b/sql/share/estonian/errmsg.txt @@ -331,3 +331,4 @@ character-set=latin7 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt index dab2ab9f51d..227b5138e98 100644 --- a/sql/share/french/errmsg.txt +++ b/sql/share/french/errmsg.txt @@ -326,3 +326,4 @@ character-set=latin1 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt index 5b2378dde22..d4e42ed5d58 100644 --- a/sql/share/german/errmsg.txt +++ b/sql/share/german/errmsg.txt @@ -338,3 +338,4 @@ character-set=latin1 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt index a840e95de97..44e072bb20e 100644 --- a/sql/share/greek/errmsg.txt +++ b/sql/share/greek/errmsg.txt @@ -326,3 +326,4 @@ character-set=greek "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt index 0b24ca6afb8..72fdf4f8c51 100644 --- a/sql/share/hungarian/errmsg.txt +++ b/sql/share/hungarian/errmsg.txt @@ -328,3 +328,4 @@ character-set=latin2 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt index a298baa7682..6694f220848 100644 --- a/sql/share/italian/errmsg.txt +++ b/sql/share/italian/errmsg.txt @@ -326,3 +326,4 @@ character-set=latin1 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt index a25357ae079..896146749a9 100644 --- a/sql/share/japanese/errmsg.txt +++ b/sql/share/japanese/errmsg.txt @@ -328,3 +328,4 @@ character-set=ujis "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt index d3799d881ed..3a92c060c67 100644 --- a/sql/share/korean/errmsg.txt +++ b/sql/share/korean/errmsg.txt @@ -326,3 +326,4 @@ character-set=euckr "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt index d69d52408ff..ada0435b52a 100644 --- a/sql/share/norwegian-ny/errmsg.txt +++ b/sql/share/norwegian-ny/errmsg.txt @@ -328,3 +328,4 @@ character-set=latin1 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt index 5fbbaf19480..d0688b2858c 100644 --- a/sql/share/norwegian/errmsg.txt +++ b/sql/share/norwegian/errmsg.txt @@ -328,3 +328,4 @@ character-set=latin1 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt index 92572db5fdc..d0c96146441 100644 --- a/sql/share/polish/errmsg.txt +++ b/sql/share/polish/errmsg.txt @@ -330,3 +330,4 @@ character-set=latin2 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt index 580fec472b6..71be50ba262 100644 --- a/sql/share/portuguese/errmsg.txt +++ b/sql/share/portuguese/errmsg.txt @@ -327,3 +327,4 @@ character-set=latin1 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt index 3e6bff75591..d800906e663 100644 --- a/sql/share/romanian/errmsg.txt +++ b/sql/share/romanian/errmsg.txt @@ -330,3 +330,4 @@ character-set=latin2 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt index b04cda84efd..633e29e8c48 100644 --- a/sql/share/russian/errmsg.txt +++ b/sql/share/russian/errmsg.txt @@ -328,3 +328,4 @@ character-set=koi8r "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/serbian/errmsg.txt b/sql/share/serbian/errmsg.txt index 2737ce26873..efda5bb12cc 100644 --- a/sql/share/serbian/errmsg.txt +++ b/sql/share/serbian/errmsg.txt @@ -321,3 +321,4 @@ character-set=cp1250 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt index bf9fe6d3519..8fa84320a8f 100644 --- a/sql/share/slovak/errmsg.txt +++ b/sql/share/slovak/errmsg.txt @@ -334,3 +334,4 @@ character-set=latin2 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt index fec6bbb4342..75a6d42bec8 100644 --- a/sql/share/spanish/errmsg.txt +++ b/sql/share/spanish/errmsg.txt @@ -328,3 +328,4 @@ character-set=latin1 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt index 8b2892172ba..9b743acb930 100644 --- a/sql/share/swedish/errmsg.txt +++ b/sql/share/swedish/errmsg.txt @@ -326,3 +326,4 @@ character-set=latin1 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/ukrainian/errmsg.txt b/sql/share/ukrainian/errmsg.txt index 393c765a8ca..2b3587e2028 100644 --- a/sql/share/ukrainian/errmsg.txt +++ b/sql/share/ukrainian/errmsg.txt @@ -331,3 +331,4 @@ character-set=koi8u "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/sp.cc b/sql/sp.cc index 26f21c31fd8..20cd43c2ad3 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -23,7 +23,7 @@ static char * create_string(THD *thd, ulong *lenp, int sp_type, - char *name, ulong namelen, + sp_name *name, const char *params, ulong paramslen, const char *returns, ulong returnslen, const char *body, ulong bodylen, @@ -69,7 +69,11 @@ db_find_routine_aux(THD *thd, int type, sp_name *name, type, name->m_name.length, name->m_name.str)); // Put the key used to read the row together - memset(key, (int)' ', 64); // QQ Empty db for now + keylen= name->m_db.length; + if (keylen > 64) + keylen= 64; + memcpy(key, name->m_db.str, keylen); + memset(key+keylen, (int)' ', 64-keylen); // Pad with space keylen= name->m_name.length; if (keylen > 64) keylen= 64; @@ -213,6 +217,8 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) char *defstr; ulong deflen; LEX *oldlex= thd->lex; + char olddb[128]; + char *olddbptr; enum enum_sql_command oldcmd= thd->lex->sql_command; ulong old_sql_mode= thd->variables.sql_mode; ha_rows select_limit= thd->variables.select_limit; @@ -221,23 +227,30 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) thd->variables.select_limit= HA_POS_ERROR; if (!(defstr= create_string(thd, &deflen, - type, - name->m_name.str, name->m_name.length, - params, strlen(params), - returns, strlen(returns), - body, strlen(body), + type, + name, + params, strlen(params), + returns, strlen(returns), + body, strlen(body), &chistics))) { ret= SP_INTERNAL_ERROR; goto done; } + olddbptr= thd->db; + if ((ret= sp_use_new_db(thd, name->m_db.str, olddb, sizeof(olddb), 1))) + goto done; + lex_start(thd, (uchar*)defstr, deflen); if (yyparse(thd) || thd->is_fatal_error || thd->lex->sphead == NULL) { LEX *newlex= thd->lex; sp_head *sp= newlex->sphead; + if (olddbptr != thd->db && + (ret= sp_change_db(thd, olddb, 1))) + goto done; if (sp) { if (oldlex != newlex) @@ -249,6 +262,9 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) } else { + if (olddbptr != thd->db && + (ret= sp_change_db(thd, olddb, 1))) + goto done; *sphp= thd->lex->sphead; (*sphp)->set_info((char *)definer, (uint)strlen(definer), created, modified, &chistics); @@ -259,6 +275,7 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) } done: + if (opened) close_thread_tables(thd); DBUG_RETURN(ret); @@ -291,9 +308,8 @@ db_create_routine(THD *thd, int type, sp_head *sp) ret= SP_GET_FIELD_FAILED; goto done; } -// QQ Not yet -// table->field[MYSQL_PROC_FIELD_DB]-> -// store(sp->m_db.str, sp->m_db.length, system_charset_info); + table->field[MYSQL_PROC_FIELD_DB]-> + store(sp->m_db.str, sp->m_db.length, system_charset_info); table->field[MYSQL_PROC_FIELD_NAME]-> store(sp->m_name.str, sp->m_name.length, system_charset_info); table->field[MYSQL_PROC_FIELD_TYPE]-> @@ -402,8 +418,7 @@ struct st_used_field static struct st_used_field init_fields[]= { -// QQ Not yet -// { "Db", NAME_LEN, MYSQL_TYPE_STRING, 0}, + { "Db", NAME_LEN, MYSQL_TYPE_STRING, 0}, { "Name", NAME_LEN, MYSQL_TYPE_STRING, 0}, { "Type", 9, MYSQL_TYPE_STRING, 0}, { "Definer", 77, MYSQL_TYPE_STRING, 0}, @@ -424,14 +439,20 @@ print_field_values(THD *thd, TABLE *table, if (table->field[MYSQL_PROC_FIELD_TYPE]->val_int() == type) { - String *tmp_string= new String(); + String db_string; + String name_string; struct st_used_field *used_field= used_fields; - get_field(&thd->mem_root, used_field->field, tmp_string); - if (!wild || !wild[0] || !wild_compare(tmp_string->ptr(), wild, 0)) + if (get_field(&thd->mem_root, used_field->field, &db_string)) + db_string.set_ascii("", 0); + used_field+= 1; + get_field(&thd->mem_root, used_field->field, &name_string); + + if (!wild || !wild[0] || !wild_compare(name_string.ptr(), wild, 0)) { protocol->prepare_for_resend(); - protocol->store(tmp_string); + protocol->store(&db_string); + protocol->store(&name_string); for (used_field++; used_field->field_name; used_field++) @@ -448,10 +469,10 @@ print_field_values(THD *thd, TABLE *table, break; default: { - String *tmp_string1= new String(); + String tmp_string; - get_field(&thd->mem_root, used_field->field, tmp_string1); - protocol->store(tmp_string1); + get_field(&thd->mem_root, used_field->field, &tmp_string); + protocol->store(&tmp_string); } break; } @@ -738,17 +759,16 @@ sp_show_status_function(THD *thd, const char *wild) bool -sp_function_exists(THD *thd, LEX_STRING *name) +sp_function_exists(THD *thd, sp_name *name) { TABLE *table; bool ret= FALSE; bool opened= FALSE; - sp_name n(*name); DBUG_ENTER("sp_function_exists"); - if (sp_cache_lookup(&thd->sp_func_cache, &n) || + if (sp_cache_lookup(&thd->sp_func_cache, name) || db_find_routine_aux(thd, TYPE_ENUM_FUNCTION, - &n, TL_READ, + name, TL_READ, &table, &opened) == SP_OK) ret= TRUE; if (opened) @@ -770,11 +790,11 @@ void sp_add_fun_to_lex(LEX *lex, sp_name *fun) { if (! hash_search(&lex->spfuns, - (byte *)fun->m_name.str, fun->m_name.length)) + (byte *)fun->m_qname.str, fun->m_qname.length)) { LEX_STRING *ls= (LEX_STRING *)sql_alloc(sizeof(LEX_STRING)); - ls->str= sql_strmake(fun->m_name.str, fun->m_name.length); - ls->length= fun->m_name.length; + ls->str= sql_strmake(fun->m_qname.str, fun->m_qname.length); + ls->length= fun->m_qname.length; my_hash_insert(&lex->spfuns, (byte *)ls); } @@ -805,6 +825,7 @@ sp_cache_functions(THD *thd, LEX *lex) LEX_STRING *ls= (LEX_STRING *)hash_element(h, i); sp_name name(*ls); + name.m_qname= *ls; if (! sp_cache_lookup(&thd->sp_func_cache, &name)) { sp_head *sp; @@ -812,6 +833,13 @@ sp_cache_functions(THD *thd, LEX *lex) LEX *newlex= new st_lex; thd->lex= newlex; + name.m_name.str= strchr(name.m_qname.str, '.'); + name.m_db.length= name.m_name.str - name.m_qname.str; + name.m_db.str= strmake_root(&thd->mem_root, + name.m_qname.str, name.m_db.length); + name.m_name.str+= 1; + name.m_name.length= name.m_qname.length - name.m_db.length - 1; + if (db_find_routine(thd, TYPE_ENUM_FUNCTION, &name, &sp) == SP_OK) { @@ -839,7 +867,7 @@ sp_cache_functions(THD *thd, LEX *lex) static char * create_string(THD *thd, ulong *lenp, int type, - char *name, ulong namelen, + sp_name *name, const char *params, ulong paramslen, const char *returns, ulong returnslen, const char *body, ulong bodylen, @@ -848,14 +876,15 @@ create_string(THD *thd, ulong *lenp, char *buf, *ptr; ulong buflen; - buflen= 100 + namelen + paramslen + returnslen + bodylen + + buflen= 100 + name->m_qname.length + paramslen + returnslen + bodylen + chistics->comment.length; if (!(buf= thd->alloc(buflen))) return 0; ptr= strxmov(buf, "CREATE ", (type == TYPE_ENUM_FUNCTION) ? "FUNCTION" : "PROCEDURE", - " `", name, "`(", params, ")", NullS); + " `", name->m_db.str, "`.`", name->m_name.str, "`(", params, ")", + NullS); if (type == TYPE_ENUM_FUNCTION) ptr= strxmov(ptr, " RETURNS ", returns, NullS); @@ -874,3 +903,63 @@ create_string(THD *thd, ulong *lenp, *lenp= (ptr-buf); return buf; } + + +// +// Utilities... +// + +int +sp_use_new_db(THD *thd, char *newdb, char *olddb, uint olddblen, + bool no_access_check) +{ + bool changeit; + DBUG_ENTER("sp_use_new_db"); + DBUG_PRINT("enter", ("newdb: %s", newdb)); + + if (thd->db && thd->db[0]) + { + if (my_strcasecmp(system_charset_info, thd->db, newdb) == 0) + changeit= 0; + else + { + changeit= 1; + strnmov(olddb, thd->db, olddblen); + } + } + else + { // thd->db empty + if (newdb[0]) + changeit= 1; + else + changeit= 0; + olddb[0] = '\0'; + } + if (!changeit) + { + DBUG_RETURN(0); + } + else + { + int ret= sp_change_db(thd, newdb, no_access_check); + + DBUG_RETURN(ret); + } +} + +int +sp_change_db(THD *thd, char *db, bool no_access_check) +{ + int ret; + ulong dbaccess= thd->db_access; /* mysql_change_db() changes this */ + my_bool nsok= thd->net.no_send_ok; /* mysql_change_db() does send_ok() */ + thd->net.no_send_ok= TRUE; + DBUG_ENTER("sp_change_db"); + DBUG_PRINT("enter", ("db: %s, no_access_check: %d", db, no_access_check)); + + ret= mysql_change_db(thd, db, 1, no_access_check); + + thd->net.no_send_ok= nsok; + thd->db_access= dbaccess; + DBUG_RETURN(ret); +} diff --git a/sql/sp.h b/sql/sp.h index 95632d2e654..ffe3f31c157 100644 --- a/sql/sp.h +++ b/sql/sp.h @@ -70,7 +70,7 @@ int sp_show_status_function(THD *thd, const char *wild); bool -sp_function_exists(THD *thd, LEX_STRING *name); +sp_function_exists(THD *thd, sp_name *name); // This is needed since we have to read the functions before we @@ -82,4 +82,19 @@ sp_merge_funs(LEX *dst, LEX *src); int sp_cache_functions(THD *thd, LEX *lex); + +// +// Utilities... +// + +// Do a "use newdb". The current db is stored at olddb. +// If newdb is the same as the current one, nothing is changed. +int +sp_use_new_db(THD *thd, char *newdb, char *olddb, uint olddbmax, + bool no_access_check); + +// Like mysql_change_db() but handles empty db name and the send_ok() problem. +int +sp_change_db(THD *thd, char *db, bool no_access_check); + #endif /* _SP_H_ */ diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc index 93f51938000..e13fb2695e7 100644 --- a/sql/sp_cache.cc +++ b/sql/sp_cache.cc @@ -89,7 +89,7 @@ sp_cache_lookup(sp_cache **cp, sp_name *name) c->version= v; return NULL; } - return c->lookup(name->m_name.str, name->m_name.length); + return c->lookup(name->m_qname.str, name->m_qname.length); } bool @@ -109,7 +109,7 @@ sp_cache_remove(sp_cache **cp, sp_name *name) if (c->version < v) c->remove_all(); else - found= c->remove(name->m_name.str, name->m_name.length); + found= c->remove(name->m_qname.str, name->m_qname.length); c->version= v+1; } return found; @@ -120,7 +120,10 @@ static byte * hash_get_key_for_sp_head(const byte *ptr, uint *plen, my_bool first) { - return (byte*) ((sp_head*)ptr)->name(plen); + sp_head *sp= (sp_head *)ptr; + + *plen= sp->m_qname.length; + return (byte*) sp->m_qname.str; } static void diff --git a/sql/sp_head.cc b/sql/sp_head.cc index eebe02c1834..ebf74e25bbe 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -147,6 +147,26 @@ 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; +} + + /* ------------------------------------------------------------------ */ @@ -224,8 +244,8 @@ sp_head::init_strings(THD *thd, LEX *lex, sp_name *name) /* We have to copy strings to get them into the right memroot */ if (name->m_db.length == 0) { - m_db.length= strlen(thd->db); - m_db.str= strmake_root(root, thd->db, m_db.length); + m_db.length= (thd->db ? strlen(thd->db) : 0); + m_db.str= strmake_root(root, (thd->db ? thd->db : ""), m_db.length); } else { @@ -317,30 +337,22 @@ int sp_head::execute(THD *thd) { DBUG_ENTER("sp_head::execute"); - char olddbname[128]; - char *olddbptr= thd->db; + char olddb[128]; + char *olddbptr; sp_rcontext *ctx= thd->spcont; int ret= 0; uint ip= 0; #ifndef EMBEDDED_LIBRARY - if (check_stack_overrun(thd, olddbptr)) + if (check_stack_overrun(thd, olddb)) { DBUG_RETURN(-1); } #endif - if (olddbptr) - { - uint i= 0; - char *p= olddbptr; - /* Fast inline strncpy without padding... */ - while (*p && i < sizeof(olddbname)) - olddbname[i++]= *p++; - if (i == sizeof(olddbname)) - i-= 1; // QQ Error or warning for truncate? - olddbname[i]= '\0'; - } + olddbptr= thd->db; + if ((ret= sp_use_new_db(thd, m_db.str, olddb, sizeof(olddb), 0))) + goto done; if (ctx) ctx->clear_handler(); @@ -379,18 +391,17 @@ sp_head::execute(THD *thd) } } while (ret == 0 && !thd->killed && !thd->query_error); + done: DBUG_PRINT("info", ("ret=%d killed=%d query_error=%d", ret, thd->killed, thd->query_error)); if (thd->killed || thd->query_error) ret= -1; /* If the DB has changed, the pointer has changed too, but the original thd->db will then have been freed */ - if (olddbptr && olddbptr != thd->db) + if (olddbptr != thd->db) { - /* QQ Maybe we should issue some special error message or warning here, - if this fails?? */ if (! thd->killed) - ret= mysql_change_db(thd, olddbname); + ret= sp_change_db(thd, olddb, 0); } DBUG_RETURN(ret); } @@ -757,6 +768,32 @@ sp_head::set_info(char *definer, uint definerlen, m_chistics->comment.length); } +void +sp_head::reset_thd_mem_root(THD *thd) +{ + m_thd_root= thd->mem_root; + thd->mem_root= m_mem_root; + m_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= strmake_root(&thd->mem_root, thd->db, thd->db_length); + m_thd= thd; +} + +void +sp_head::restore_thd_mem_root(THD *thd) +{ + Item *flist= m_free_list; // The old list + m_free_list= thd->free_list; // Get the new one + thd->free_list= flist; // Restore the old one + thd->db= m_thd_db; // Restore the original db pointer + m_mem_root= thd->mem_root; + thd->mem_root= m_thd_root; + m_thd= NULL; +} + + int sp_head::show_create_procedure(THD *thd) { @@ -1157,8 +1194,6 @@ sp_change_security_context(THD *thd, sp_head *sp, st_sp_security_context *ctxp) { ctxp->master_access= thd->master_access; ctxp->db_access= thd->db_access; - ctxp->db= thd->db; - ctxp->db_length= thd->db_length; ctxp->priv_user= thd->priv_user; strncpy(ctxp->priv_host, thd->priv_host, sizeof(ctxp->priv_host)); ctxp->user= thd->user; @@ -1174,8 +1209,6 @@ sp_change_security_context(THD *thd, sp_head *sp, st_sp_security_context *ctxp) ctxp->changed= FALSE; thd->master_access= ctxp->master_access; thd->db_access= ctxp->db_access; - thd->db= ctxp->db; - thd->db_length= ctxp->db_length; thd->priv_user= ctxp->priv_user; strncpy(thd->priv_host, ctxp->priv_host, sizeof(thd->priv_host)); } @@ -1195,8 +1228,6 @@ sp_restore_security_context(THD *thd, sp_head *sp, st_sp_security_context *ctxp) ctxp->changed= FALSE; thd->master_access= ctxp->master_access; thd->db_access= ctxp->db_access; - thd->db= ctxp->db; - thd->db_length= ctxp->db_length; thd->priv_user= ctxp->priv_user; strncpy(thd->priv_host, ctxp->priv_host, sizeof(thd->priv_host)); } diff --git a/sql/sp_head.h b/sql/sp_head.h index b35b4c37501..791c6697693 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -66,6 +66,10 @@ public: {} }; +sp_name * +sp_name_current_db_new(THD *thd, LEX_STRING name); + + class sp_head : public Sql_alloc { sp_head(const sp_head &); /* Prevent use of these */ @@ -194,24 +198,10 @@ public: longlong created, longlong modified, st_sp_chistics *chistics); - inline void reset_thd_mem_root(THD *thd) - { - m_thd_root= thd->mem_root; - thd->mem_root= m_mem_root; - m_free_list= thd->free_list; // Keep the old list - thd->free_list= NULL; // Start a new one - m_thd= thd; - } + void reset_thd_mem_root(THD *thd); + + void restore_thd_mem_root(THD *thd); - inline void restore_thd_mem_root(THD *thd) - { - Item *flist= m_free_list; // The old list - m_free_list= thd->free_list; // Get the new one - thd->free_list= flist; // Restore the old one - m_mem_root= thd->mem_root; - thd->mem_root= m_thd_root; - m_thd= NULL; - } private: @@ -219,6 +209,7 @@ private: MEM_ROOT m_thd_root; // Temp. store for thd's mem_root Item *m_free_list; // Where the items go 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 @@ -671,8 +662,6 @@ struct st_sp_security_context bool changed; uint master_access; uint db_access; - char *db; - uint db_length; char *priv_user; char priv_host[MAX_HOSTNAME]; char *user; diff --git a/sql/sql_db.cc b/sql/sql_db.cc index bc6b30040d6..3ea6821ef80 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -595,7 +595,8 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, 1 error */ -bool mysql_change_db(THD *thd, const char *name) +bool mysql_change_db(THD *thd, const char *name, + bool empty_is_ok, bool no_access_check) { int length, db_length; char *dbname=my_strdup((char*) name,MYF(MY_WME)); @@ -604,62 +605,76 @@ bool mysql_change_db(THD *thd, const char *name) HA_CREATE_INFO create; DBUG_ENTER("mysql_change_db"); - if (!dbname || !(db_length=strip_sp(dbname))) + if ((!dbname || !(db_length=strip_sp(dbname))) && !empty_is_ok) { x_free(dbname); /* purecov: inspected */ send_error(thd,ER_NO_DB_ERROR); /* purecov: inspected */ DBUG_RETURN(1); /* purecov: inspected */ } - if ((db_length > NAME_LEN) || check_db_name(dbname)) + if (!empty_is_ok || (dbname && db_length)) { - net_printf(thd, ER_WRONG_DB_NAME, dbname); - x_free(dbname); - DBUG_RETURN(1); + if ((db_length > NAME_LEN) || check_db_name(dbname)) + { + net_printf(thd, ER_WRONG_DB_NAME, dbname); + x_free(dbname); + DBUG_RETURN(1); + } } DBUG_PRINT("info",("Use database: %s", dbname)); + if (!empty_is_ok || (dbname && db_length)) + { #ifndef NO_EMBEDDED_ACCESS_CHECKS - if (test_all_bits(thd->master_access,DB_ACLS)) - db_access=DB_ACLS; - else - db_access= (acl_get(thd->host,thd->ip, thd->priv_user,dbname,0) | - thd->master_access); - if (!(db_access & DB_ACLS) && (!grant_option || check_grant_db(thd,dbname))) - { - net_printf(thd,ER_DBACCESS_DENIED_ERROR, - thd->priv_user, - thd->priv_host, - dbname); - mysql_log.write(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR), - thd->priv_user, - thd->priv_host, - dbname); - my_free(dbname,MYF(0)); - DBUG_RETURN(1); - } + if (! no_access_check) + { + if (test_all_bits(thd->master_access,DB_ACLS)) + db_access=DB_ACLS; + else + db_access= (acl_get(thd->host,thd->ip, thd->priv_user,dbname,0) | + thd->master_access); + if (!(db_access & DB_ACLS) && + (!grant_option || check_grant_db(thd,dbname))) + { + net_printf(thd,ER_DBACCESS_DENIED_ERROR, + thd->priv_user, + thd->priv_host, + dbname); + mysql_log.write(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR), + thd->priv_user, + thd->priv_host, + dbname); + my_free(dbname,MYF(0)); + DBUG_RETURN(1); + } + } #endif - (void) sprintf(path,"%s/%s",mysql_data_home,dbname); - length=unpack_dirname(path,path); // Convert if not unix - if (length && path[length-1] == FN_LIBCHAR) - path[length-1]=0; // remove ending '\' - if (access(path,F_OK)) - { - net_printf(thd,ER_BAD_DB_ERROR,dbname); - my_free(dbname,MYF(0)); - DBUG_RETURN(1); + (void) sprintf(path,"%s/%s",mysql_data_home,dbname); + length=unpack_dirname(path,path); // Convert if not unix + if (length && path[length-1] == FN_LIBCHAR) + path[length-1]=0; // remove ending '\' + if (access(path,F_OK)) + { + net_printf(thd,ER_BAD_DB_ERROR,dbname); + my_free(dbname,MYF(0)); + DBUG_RETURN(1); + } } send_ok(thd); x_free(thd->db); thd->db=dbname; // THD::~THD will free this thd->db_length=db_length; + if (!empty_is_ok || (dbname && db_length)) + { #ifndef NO_EMBEDDED_ACCESS_CHECKS - thd->db_access=db_access; + if (! no_access_check) + thd->db_access=db_access; #endif - strmov(path+unpack_dirname(path,path), MY_DB_OPT_FILE); - load_db_opt(thd, path, &create); - thd->db_charset= create.default_table_charset ? - create.default_table_charset : - thd->variables.collation_server; - thd->variables.collation_database= thd->db_charset; + strmov(path+unpack_dirname(path,path), MY_DB_OPT_FILE); + load_db_opt(thd, path, &create); + thd->db_charset= create.default_table_charset ? + create.default_table_charset : + thd->variables.collation_server; + thd->variables.collation_database= thd->db_charset; + } DBUG_RETURN(0); } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 4224971fd47..50f475eb68c 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1090,8 +1090,15 @@ create: ; sp_name: - IDENT_sys '.' IDENT_sys { $$= new sp_name($1, $3); } - | IDENT_sys { $$= new sp_name($1); } + IDENT_sys '.' IDENT_sys + { + $$= new sp_name($1, $3); + $$->init_qname(YYTHD); + } + | IDENT_sys + { + $$= sp_name_current_db_new(YYTHD, $1); + } ; create_function_tail: @@ -1576,6 +1583,11 @@ sp_proc_stmt: /* We maybe have one or more SELECT without INTO */ lex->sphead->m_multi_results= TRUE; } + if (lex->sql_command == SQLCOM_CHANGE_DB) + { /* "USE db" doesn't work in a procedure */ + send_error(YYTHD, ER_SP_NO_USE); + YYABORT; + } /* Don't add an instruction for empty SET statements. ** (This happens if the SET only contained local variables, ** which get their set instructions generated separately.) @@ -3913,10 +3925,11 @@ simple_expr: { $$= new Item_int((char*) "TRUE",1,1); } | IDENT_sys '(' udf_expr_list ')' { - if (sp_function_exists(YYTHD, &$1)) + sp_name *name= sp_name_current_db_new(YYTHD, $1); + + if (sp_function_exists(YYTHD, name)) { LEX *lex= Lex; - sp_name *name= new sp_name($1); sp_add_fun_to_lex(lex, name); if ($3) From 3d2794ae9ab21262677fccbb68b0b041bfd6af33 Mon Sep 17 00:00:00 2001 From: "sergefp@mysql.com" <> Date: Mon, 15 Mar 2004 23:11:58 +0300 Subject: [PATCH 06/10] Fix for Bug#3183 --- mysql-test/r/index_merge.result | 20 ++++++++++++++++++++ mysql-test/t/index_merge.test | 10 ++++++++++ sql/opt_range.h | 9 +++++++++ sql/sql_delete.cc | 7 +++++++ sql/sql_update.cc | 4 ++++ 5 files changed, 50 insertions(+) diff --git a/mysql-test/r/index_merge.result b/mysql-test/r/index_merge.result index a847f2d7025..6859d9db728 100644 --- a/mysql-test/r/index_merge.result +++ b/mysql-test/r/index_merge.result @@ -314,4 +314,24 @@ key1 key2 key3 key4 key5 key6 key7 key8 key9 keyA keyB keyC 11 11 11 11 11 11 11 1013 11 11 11 11 12 12 12 12 12 12 12 1012 12 12 12 12 1016 1016 1016 1016 1016 1016 1016 8 1016 1016 1016 1016 +explain select * from t0 where key1 < 3 or key2 < 4; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 7 Using where +select * from t0 where key1 < 3 or key2 < 4; +key1 key2 key3 key4 key5 key6 key7 key8 +1 1 1 1 1 1 1 1023 +2 2 2 2 2 2 2 1022 +3 3 3 3 3 3 3 1021 +update t0 set key8=123 where key1 < 3 or key2 < 4; +select * from t0 where key1 < 3 or key2 < 4; +key1 key2 key3 key4 key5 key6 key7 key8 +1 1 1 1 1 1 1 123 +2 2 2 2 2 2 2 123 +3 3 3 3 3 3 3 123 +delete from t0 where key1 < 3 or key2 < 4; +select * from t0 where key1 < 3 or key2 < 4; +key1 key2 key3 key4 key5 key6 key7 key8 +select count(*) from t0; +count(*) +1021 drop table t0, t1, t2, t3, t4; diff --git a/mysql-test/t/index_merge.test b/mysql-test/t/index_merge.test index 029c2e4a013..9745723ca59 100644 --- a/mysql-test/t/index_merge.test +++ b/mysql-test/t/index_merge.test @@ -267,5 +267,15 @@ select * from t3 where key5=5 or key6=6 or key7=7 or key8=8 or key9=9 or keyA=10 or keyB=11 or keyC=12; +# Test for Bug#3183 +explain select * from t0 where key1 < 3 or key2 < 4; +select * from t0 where key1 < 3 or key2 < 4; + +update t0 set key8=123 where key1 < 3 or key2 < 4; +select * from t0 where key1 < 3 or key2 < 4; + +delete from t0 where key1 < 3 or key2 < 4; +select * from t0 where key1 < 3 or key2 < 4; +select count(*) from t0; drop table t0, t1, t2, t3, t4; diff --git a/sql/opt_range.h b/sql/opt_range.h index 1e5f58bc1f5..3c528719b29 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -87,7 +87,16 @@ public: QUICK_SELECT_I(); virtual ~QUICK_SELECT_I(){}; + /* + Call init() immediately after creation of quick select. if init() call + fails, reset() or get_next() must not be called. + */ virtual int init() = 0; + + /* + Call reset() before first get_next call. get_next must not be called if + reset() call fails. + */ virtual int reset(void) = 0; virtual int get_next() = 0; /* get next record to retrieve */ virtual bool reverse_sorted() = 0; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 1fa216fdb58..7ebe9cb6002 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -150,6 +150,13 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SQL_LIST *order, select= 0; } + /* If quick select is used, initialize it before retrieving rows. */ + if (select && select->quick && select->quick->reset()) + { + delete select; + free_underlaid_joins(thd, &thd->lex->select_lex); + DBUG_RETURN(-1); // This will force out message + } init_read_record(&info,thd,table,select,1,1); deleted=0L; init_ftfuncs(thd, &thd->lex->select_lex, 1); diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 03ab3d01c1a..24199df9b84 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -246,7 +246,11 @@ int mysql_update(THD *thd, DISK_BUFFER_SIZE, MYF(MY_WME))) goto err; + /* If quick select is used, initialize it before retrieving rows. */ + if (select && select->quick && select->quick->reset()) + goto err; init_read_record(&info,thd,table,select,0,1); + thd->proc_info="Searching rows for update"; uint tmp_limit= limit; From 0411f4580184d20d907fc6e0e18794bc3fdc4624 Mon Sep 17 00:00:00 2001 From: "pem@mysql.comhem.se" <> Date: Wed, 17 Mar 2004 12:09:03 +0100 Subject: [PATCH 07/10] Fix BUG#2674: Crash if return system variable in stored procedures. Had to initialize some fields in lex before parsing an SP read from mysql.proc. --- mysql-test/r/sp.result | 6 ++++++ mysql-test/t/sp.test | 10 ++++++++++ sql/sp.cc | 15 ++++++++++++++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index bf515e4b4cd..2b91e8de270 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -989,6 +989,12 @@ call bug2614()| call bug2614()| drop table t3| drop procedure bug2614| +create function bug2674 () returns int +return @@sort_buffer_size| +select bug2674()| +bug2674() +262136 +drop function bug2674| 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 3cb88ec5717..fd6cb4a300a 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -1138,6 +1138,16 @@ call bug2614()| drop table t3| drop procedure bug2614| +# +# BUG#2674 +# + +create function bug2674 () returns int + return @@sort_buffer_size| + +select bug2674()| +drop function bug2674| + # # Some "real" examples diff --git a/sql/sp.cc b/sql/sp.cc index 83fbd8c5173..f517504e1f5 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -230,7 +230,20 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp) goto done; } - lex_start(thd, (uchar*)defstr, deflen); + { + /* This is something of a kludge. We need to initialize some fields + * in thd->lex (the unit and master stuff), and the easiest way to + * do it is, is to call mysql_init_query(), but this unfortunately + * resets teh value_list where we keep the CALL parameters. So we + * copy the list and then restore it. + */ + List vals= thd->lex->value_list; + + mysql_init_query(thd, TRUE); + lex_start(thd, (uchar*)defstr, deflen); + thd->lex->value_list= vals; + } + if (yyparse(thd) || thd->is_fatal_error || thd->lex->sphead == NULL) { LEX *newlex= thd->lex; From 13987057f64b97776e9491447186bebc54ee1439 Mon Sep 17 00:00:00 2001 From: "pem@mysql.comhem.se" <> Date: Fri, 19 Mar 2004 19:01:54 +0100 Subject: [PATCH 08/10] WL#1366: Use the schema (db) associated with an SP. Phase 3: Made qualified names work for functions as well. --- mysql-test/r/sp-security.result | 25 ++++++ mysql-test/t/sp-security.test | 20 ++++- sql/item_func.cc | 15 +++- sql/item_func.h | 6 ++ sql/mysql_priv.h | 3 +- sql/sp.cc | 106 +++++++++++++++++++--- sql/sp_head.cc | 12 ++- sql/sql_db.cc | 95 +++++++++----------- sql/sql_yacc.yy | 153 +++++++++++++++++--------------- 9 files changed, 289 insertions(+), 146 deletions(-) diff --git a/mysql-test/r/sp-security.result b/mysql-test/r/sp-security.result index 51439e08782..25bceb0f54f 100644 --- a/mysql-test/r/sp-security.result +++ b/mysql-test/r/sp-security.result @@ -10,14 +10,27 @@ insert into db1_secret.t1 values (user(), i); show procedure status like 'stamp'; Db Name Type Definer Modified Created Security_type Comment db1_secret stamp PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER +create function db() returns varchar(64) return database(); +show function status like 'db'; +Db Name Type Definer Modified Created Security_type Comment +db1_secret db FUNCTION root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER call stamp(1); select * from t1; u i root@localhost 1 +select db(); +db() +db1_secret call db1_secret.stamp(2); +select db1_secret.db(); +db1_secret.db() +db1_secret select * from db1_secret.t1; ERROR 42000: Access denied for user: 'user1'@'localhost' to database 'db1_secret' call db1_secret.stamp(3); +select db1_secret.db(); +db1_secret.db() +db1_secret select * from db1_secret.t1; ERROR 42000: Access denied for user: ''@'localhost' to database 'db1_secret' select * from t1; @@ -29,6 +42,10 @@ alter procedure stamp sql security invoker; show procedure status like 'stamp'; Db Name Type Definer Modified Created Security_type Comment db1_secret stamp PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 INVOKER +alter function db sql security invoker; +show function status like 'db'; +Db Name Type Definer Modified Created Security_type Comment +db1_secret db FUNCTION root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 INVOKER call stamp(4); select * from t1; u i @@ -36,10 +53,17 @@ root@localhost 1 user1@localhost 2 anon@localhost 3 root@localhost 4 +select db(); +db() +db1_secret call db1_secret.stamp(5); ERROR 42000: Access denied for user: 'user1'@'localhost' to database 'db1_secret' +select db1_secret.db(); +ERROR 42000: Access denied for user: 'user1'@'localhost' to database 'db1_secret' call db1_secret.stamp(6); ERROR 42000: Access denied for user: ''@'localhost' to database 'db1_secret' +select db1_secret.db(); +ERROR 42000: Access denied for user: ''@'localhost' to database 'db1_secret' drop database if exists db2; create database db2; use db2; @@ -74,6 +98,7 @@ s1 2 2 drop procedure db1_secret.stamp; +drop function db1_secret.db; drop procedure db2.p; drop procedure db2.q; use test; diff --git a/mysql-test/t/sp-security.test b/mysql-test/t/sp-security.test index 2d089e72d0b..ae977684129 100644 --- a/mysql-test/t/sp-security.test +++ b/mysql-test/t/sp-security.test @@ -21,15 +21,20 @@ use db1_secret; create table t1 ( u varchar(64), i int ); -# Our test procedure +# A test procedure and function create procedure stamp(i int) insert into db1_secret.t1 values (user(), i); --replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00' show procedure status like 'stamp'; +create function db() returns varchar(64) return database(); +--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00' +show function status like 'db'; + # root can, of course call stamp(1); select * from t1; +select db(); connect (con2user1,localhost,user1,,); connect (con3anon,localhost,anon,,); @@ -41,6 +46,7 @@ connection con2user1; # This should work... call db1_secret.stamp(2); +select db1_secret.db(); # ...but not this --error 1044 @@ -53,6 +59,7 @@ connection con3anon; # This should work... call db1_secret.stamp(3); +select db1_secret.db(); # ...but not this --error 1044 @@ -71,9 +78,14 @@ alter procedure stamp sql security invoker; --replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00' show procedure status like 'stamp'; +alter function db sql security invoker; +--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00' +show function status like 'db'; + # root still can call stamp(4); select * from t1; +select db(); # # User1 cannot @@ -83,6 +95,8 @@ connection con2user1; # This should not work --error 1044 call db1_secret.stamp(5); +--error 1044 +select db1_secret.db(); # # Anonymous cannot @@ -92,7 +106,8 @@ connection con3anon; # This should not work --error 1044 call db1_secret.stamp(6); - +--error 1044 +select db1_secret.db(); # # BUG#2777 @@ -149,6 +164,7 @@ select * from t2; # Clean up connection con1root; drop procedure db1_secret.stamp; +drop function db1_secret.db; drop procedure db2.p; drop procedure db2.q; use test; diff --git a/sql/item_func.cc b/sql/item_func.cc index fdb0a5e5240..2a74f2801c0 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3120,7 +3120,11 @@ Item_func_sp::execute(Item **itp) if (! m_sp) m_sp= sp_find_function(thd, m_name); if (! m_sp) + { + my_printf_error(ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), MYF(0), + "FUNCTION", m_name->m_qname); DBUG_RETURN(-1); + } #ifndef NO_EMBEDDED_ACCESS_CHECKS sp_change_security_context(thd, m_sp, &save_ctx); @@ -3147,6 +3151,8 @@ Item_func_sp::field_type() const DBUG_PRINT("info", ("m_returns = %d", m_sp->m_returns)); DBUG_RETURN(m_sp->m_returns); } + my_printf_error(ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), MYF(0), + "FUNCTION", m_name->m_qname); DBUG_RETURN(MYSQL_TYPE_STRING); } @@ -3162,6 +3168,8 @@ Item_func_sp::result_type() const { DBUG_RETURN(m_sp->result()); } + my_printf_error(ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), MYF(0), + "FUNCTION", m_name->m_qname); DBUG_RETURN(STRING_RESULT); } @@ -3172,7 +3180,12 @@ Item_func_sp::fix_length_and_dec() if (! m_sp) m_sp= sp_find_function(current_thd, m_name); - if (m_sp) + if (! m_sp) + { + my_printf_error(ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), MYF(0), + "FUNCTION", m_name->m_qname); + } + else { switch (m_sp->result()) { case STRING_RESULT: diff --git a/sql/item_func.h b/sql/item_func.h index e68826ca56e..6d313a8ea66 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1107,7 +1107,10 @@ public: Item *it; if (execute(&it)) + { + null_value= 1; return 0.0; + } return it->val(); } @@ -1116,7 +1119,10 @@ public: Item *it; if (execute(&it)) + { + null_value= 1; return NULL; + } return it->val_str(str); } diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 21822e02d29..e17847ebe24 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -445,8 +445,7 @@ int mysql_rm_table_part2_with_lock(THD *thd, TABLE_LIST *tables, int quick_rm_table(enum db_type base,const char *db, const char *table_name); bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list); -bool mysql_change_db(THD *thd,const char *name, - bool empty_is_ok=0, bool no_access_check=0); +bool mysql_change_db(THD *thd,const char *name); void mysql_parse(THD *thd,char *inBuf,uint length); bool is_update_query(enum enum_sql_command command); void free_items(Item *item); diff --git a/sql/sp.cc b/sql/sp.cc index 1b58c709e4e..389c627f5f3 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -16,6 +16,7 @@ #include "mysql_priv.h" +#include "sql_acl.h" #include "sp.h" #include "sp_head.h" #include "sp_cache.h" @@ -960,19 +961,104 @@ sp_use_new_db(THD *thd, char *newdb, char *olddb, uint olddblen, } } +/* + Change database. + + SYNOPSIS + sp_change_db() + thd Thread handler + name Database name + empty_is_ok True= it's ok with "" as name + no_access_check True= don't do access check + + DESCRIPTION + This is the same as mysql_change_db(), but with some extra + arguments for Stored Procedure usage; doing implicit "use" + when executing an SP in a different database. + We also use different error routines, since this might be + invoked from a function when executing a query or statement. + Note: We would have prefered to reuse mysql_change_db(), but + the error handling in particular made that too awkward, so + we (reluctantly) have a "copy" here. + + RETURN VALUES + 0 ok + 1 error +*/ + int -sp_change_db(THD *thd, char *db, bool no_access_check) +sp_change_db(THD *thd, char *name, bool no_access_check) { - int ret; - ulong dbaccess= thd->db_access; /* mysql_change_db() changes this */ - my_bool nsok= thd->net.no_send_ok; /* mysql_change_db() does send_ok() */ - thd->net.no_send_ok= TRUE; + int length, db_length; + char *dbname=my_strdup((char*) name,MYF(MY_WME)); + char path[FN_REFLEN]; + ulong db_access; + HA_CREATE_INFO create; DBUG_ENTER("sp_change_db"); - DBUG_PRINT("enter", ("db: %s, no_access_check: %d", db, no_access_check)); + DBUG_PRINT("enter", ("db: %s, no_access_check: %d", name, no_access_check)); - ret= mysql_change_db(thd, db, 1, no_access_check); + db_length= (!dbname ? 0 : strip_sp(dbname)); + if (dbname && db_length) + { + if ((db_length > NAME_LEN) || check_db_name(dbname)) + { + my_printf_error(ER_WRONG_DB_NAME, ER(ER_WRONG_DB_NAME), MYF(0), dbname); + x_free(dbname); + DBUG_RETURN(1); + } + } - thd->net.no_send_ok= nsok; - thd->db_access= dbaccess; - DBUG_RETURN(ret); + if (dbname && db_length) + { +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (! no_access_check) + { + if (test_all_bits(thd->master_access,DB_ACLS)) + db_access=DB_ACLS; + else + db_access= (acl_get(thd->host,thd->ip, thd->priv_user,dbname,0) | + thd->master_access); + if (!(db_access & DB_ACLS) && + (!grant_option || check_grant_db(thd,dbname))) + { + my_printf_error(ER_DBACCESS_DENIED_ERROR, ER(ER_DBACCESS_DENIED_ERROR), + MYF(0), + thd->priv_user, + thd->priv_host, + dbname); + mysql_log.write(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR), + thd->priv_user, + thd->priv_host, + dbname); + my_free(dbname,MYF(0)); + DBUG_RETURN(1); + } + } +#endif + (void) sprintf(path,"%s/%s",mysql_data_home,dbname); + length=unpack_dirname(path,path); // Convert if not unix + if (length && path[length-1] == FN_LIBCHAR) + path[length-1]=0; // remove ending '\' + if (access(path,F_OK)) + { + my_printf_error(ER_BAD_DB_ERROR, ER(ER_BAD_DB_ERROR), MYF(0), dbname); + my_free(dbname,MYF(0)); + DBUG_RETURN(1); + } + } + + x_free(thd->db); + thd->db=dbname; // THD::~THD will free this + thd->db_length=db_length; + + if (dbname && db_length) + { + strmov(path+unpack_dirname(path,path), MY_DB_OPT_FILE); + load_db_opt(thd, path, &create); + thd->db_charset= create.default_table_charset ? + create.default_table_charset : + thd->variables.collation_server; + thd->variables.collation_database= thd->db_charset; + } + DBUG_RETURN(0); } diff --git a/sql/sp_head.cc b/sql/sp_head.cc index ebf74e25bbe..c8be113e2e1 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -389,12 +389,13 @@ sp_head::execute(THD *thd) continue; } } - } while (ret == 0 && !thd->killed && !thd->query_error); + } while (ret == 0 && !thd->killed && !thd->query_error && + !thd->net.report_error); done: DBUG_PRINT("info", ("ret=%d killed=%d query_error=%d", ret, thd->killed, thd->query_error)); - if (thd->killed || thd->query_error) + if (thd->killed || thd->query_error || thd->net.report_error) ret= -1; /* If the DB has changed, the pointer has changed too, but the original thd->db will then have been freed */ @@ -553,7 +554,12 @@ sp_head::execute_procedure(THD *thd, List *args) ret= execute(thd); // Don't copy back OUT values if we got an error - if (ret == 0 && csize > 0) + if (ret) + { + if (thd->net.report_error) + send_error(thd, 0, NullS); + } + else if (csize > 0) { List_iterator_fast li(*args); Item *it; diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 3ea6821ef80..bc6b30040d6 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -595,8 +595,7 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, 1 error */ -bool mysql_change_db(THD *thd, const char *name, - bool empty_is_ok, bool no_access_check) +bool mysql_change_db(THD *thd, const char *name) { int length, db_length; char *dbname=my_strdup((char*) name,MYF(MY_WME)); @@ -605,76 +604,62 @@ bool mysql_change_db(THD *thd, const char *name, HA_CREATE_INFO create; DBUG_ENTER("mysql_change_db"); - if ((!dbname || !(db_length=strip_sp(dbname))) && !empty_is_ok) + if (!dbname || !(db_length=strip_sp(dbname))) { x_free(dbname); /* purecov: inspected */ send_error(thd,ER_NO_DB_ERROR); /* purecov: inspected */ DBUG_RETURN(1); /* purecov: inspected */ } - if (!empty_is_ok || (dbname && db_length)) + if ((db_length > NAME_LEN) || check_db_name(dbname)) { - if ((db_length > NAME_LEN) || check_db_name(dbname)) - { - net_printf(thd, ER_WRONG_DB_NAME, dbname); - x_free(dbname); - DBUG_RETURN(1); - } + net_printf(thd, ER_WRONG_DB_NAME, dbname); + x_free(dbname); + DBUG_RETURN(1); } DBUG_PRINT("info",("Use database: %s", dbname)); - if (!empty_is_ok || (dbname && db_length)) - { #ifndef NO_EMBEDDED_ACCESS_CHECKS - if (! no_access_check) - { - if (test_all_bits(thd->master_access,DB_ACLS)) - db_access=DB_ACLS; - else - db_access= (acl_get(thd->host,thd->ip, thd->priv_user,dbname,0) | - thd->master_access); - if (!(db_access & DB_ACLS) && - (!grant_option || check_grant_db(thd,dbname))) - { - net_printf(thd,ER_DBACCESS_DENIED_ERROR, - thd->priv_user, - thd->priv_host, - dbname); - mysql_log.write(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR), - thd->priv_user, - thd->priv_host, - dbname); - my_free(dbname,MYF(0)); - DBUG_RETURN(1); - } - } + if (test_all_bits(thd->master_access,DB_ACLS)) + db_access=DB_ACLS; + else + db_access= (acl_get(thd->host,thd->ip, thd->priv_user,dbname,0) | + thd->master_access); + if (!(db_access & DB_ACLS) && (!grant_option || check_grant_db(thd,dbname))) + { + net_printf(thd,ER_DBACCESS_DENIED_ERROR, + thd->priv_user, + thd->priv_host, + dbname); + mysql_log.write(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR), + thd->priv_user, + thd->priv_host, + dbname); + my_free(dbname,MYF(0)); + DBUG_RETURN(1); + } #endif - (void) sprintf(path,"%s/%s",mysql_data_home,dbname); - length=unpack_dirname(path,path); // Convert if not unix - if (length && path[length-1] == FN_LIBCHAR) - path[length-1]=0; // remove ending '\' - if (access(path,F_OK)) - { - net_printf(thd,ER_BAD_DB_ERROR,dbname); - my_free(dbname,MYF(0)); - DBUG_RETURN(1); - } + (void) sprintf(path,"%s/%s",mysql_data_home,dbname); + length=unpack_dirname(path,path); // Convert if not unix + if (length && path[length-1] == FN_LIBCHAR) + path[length-1]=0; // remove ending '\' + if (access(path,F_OK)) + { + net_printf(thd,ER_BAD_DB_ERROR,dbname); + my_free(dbname,MYF(0)); + DBUG_RETURN(1); } send_ok(thd); x_free(thd->db); thd->db=dbname; // THD::~THD will free this thd->db_length=db_length; - if (!empty_is_ok || (dbname && db_length)) - { #ifndef NO_EMBEDDED_ACCESS_CHECKS - if (! no_access_check) - thd->db_access=db_access; + thd->db_access=db_access; #endif - strmov(path+unpack_dirname(path,path), MY_DB_OPT_FILE); - load_db_opt(thd, path, &create); - thd->db_charset= create.default_table_charset ? - create.default_table_charset : - thd->variables.collation_server; - thd->variables.collation_database= thd->db_charset; - } + strmov(path+unpack_dirname(path,path), MY_DB_OPT_FILE); + load_db_opt(thd, path, &create); + thd->db_charset= create.default_table_charset ? + create.default_table_charset : + thd->variables.collation_server; + thd->variables.collation_database= thd->db_charset; DBUG_RETURN(0); } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 50f475eb68c..d2f7e73b2b8 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -3923,83 +3923,90 @@ simple_expr: { $$= new Item_func_round($3,$5,1); } | TRUE_SYM { $$= new Item_int((char*) "TRUE",1,1); } - | IDENT_sys '(' udf_expr_list ')' + | ident '.' ident '(' udf_expr_list ')' { - sp_name *name= sp_name_current_db_new(YYTHD, $1); + LEX *lex= Lex; + sp_name *name= new sp_name($1, $3); - if (sp_function_exists(YYTHD, name)) - { - LEX *lex= Lex; - - sp_add_fun_to_lex(lex, name); - if ($3) - $$= new Item_func_sp(name, *$3); - else - $$= new Item_func_sp(name); - } + name->init_qname(YYTHD); + sp_add_fun_to_lex(Lex, name); + if ($5) + $$= new Item_func_sp(name, *$5); else - { -#ifdef HAVE_DLOPEN - udf_func *udf; - - if (using_udf_functions && (udf=find_udf($1.str, $1.length))) - { - switch (udf->returns) { - case STRING_RESULT: - if (udf->type == UDFTYPE_FUNCTION) - { - if ($3 != NULL) - $$ = new Item_func_udf_str(udf, *$3); - else - $$ = new Item_func_udf_str(udf); - } - else - { - if ($3 != NULL) - $$ = new Item_sum_udf_str(udf, *$3); - else - $$ = new Item_sum_udf_str(udf); - } - break; - case REAL_RESULT: - if (udf->type == UDFTYPE_FUNCTION) - { - if ($3 != NULL) - $$ = new Item_func_udf_float(udf, *$3); - else - $$ = new Item_func_udf_float(udf); - } - else - { - if ($3 != NULL) - $$ = new Item_sum_udf_float(udf, *$3); - else - $$ = new Item_sum_udf_float(udf); - } - break; - case INT_RESULT: - if (udf->type == UDFTYPE_FUNCTION) - { - if ($3 != NULL) - $$ = new Item_func_udf_int(udf, *$3); - else - $$ = new Item_func_udf_int(udf); - } - else - { - if ($3 != NULL) - $$ = new Item_sum_udf_int(udf, *$3); - else - $$ = new Item_sum_udf_int(udf); - } - break; - default: - YYABORT; - } - } -#endif /* HAVE_DLOPEN */ - } + $$= new Item_func_sp(name); } + | IDENT_sys '(' udf_expr_list ')' + { +#ifdef HAVE_DLOPEN + udf_func *udf; + + if (using_udf_functions && (udf=find_udf($1.str, $1.length))) + { + switch (udf->returns) { + case STRING_RESULT: + if (udf->type == UDFTYPE_FUNCTION) + { + if ($3 != NULL) + $$ = new Item_func_udf_str(udf, *$3); + else + $$ = new Item_func_udf_str(udf); + } + else + { + if ($3 != NULL) + $$ = new Item_sum_udf_str(udf, *$3); + else + $$ = new Item_sum_udf_str(udf); + } + break; + case REAL_RESULT: + if (udf->type == UDFTYPE_FUNCTION) + { + if ($3 != NULL) + $$ = new Item_func_udf_float(udf, *$3); + else + $$ = new Item_func_udf_float(udf); + } + else + { + if ($3 != NULL) + $$ = new Item_sum_udf_float(udf, *$3); + else + $$ = new Item_sum_udf_float(udf); + } + break; + case INT_RESULT: + if (udf->type == UDFTYPE_FUNCTION) + { + if ($3 != NULL) + $$ = new Item_func_udf_int(udf, *$3); + else + $$ = new Item_func_udf_int(udf); + } + else + { + if ($3 != NULL) + $$ = new Item_sum_udf_int(udf, *$3); + else + $$ = new Item_sum_udf_int(udf); + } + break; + default: + YYABORT; + } + } + else +#endif /* HAVE_DLOPEN */ + { + sp_name *name= sp_name_current_db_new(YYTHD, $1); + + sp_add_fun_to_lex(Lex, name); + if ($3) + $$= new Item_func_sp(name, *$3); + else + $$= new Item_func_sp(name); + } + } | UNIQUE_USERS '(' text_literal ',' NUM ',' NUM ',' expr_list ')' { $$= new Item_func_unique_users($3,atoi($5.str),atoi($7.str), * $9); From 5476eb0709c3a109fc47e1f7434ff196c5ec366b Mon Sep 17 00:00:00 2001 From: "pem@mysql.comhem.se" <> Date: Mon, 22 Mar 2004 14:44:41 +0100 Subject: [PATCH 09/10] WL#1366: Use the schema (db) associated with an SP. Phase 4 (final): Remove associated stored procedures when a database is dropped. --- mysql-test/r/sp-security.result | 12 ++++--- mysql-test/r/sp.result | 16 +++++++++ mysql-test/t/sp-security.test | 8 ++--- mysql-test/t/sp.test | 26 ++++++++++++++ sql/sp.cc | 64 +++++++++++++++++++++++++++++++++ sql/sp.h | 4 +++ sql/sp_cache.cc | 7 ++++ sql/sp_cache.h | 3 ++ sql/sql_db.cc | 2 ++ 9 files changed, 134 insertions(+), 8 deletions(-) diff --git a/mysql-test/r/sp-security.result b/mysql-test/r/sp-security.result index 25bceb0f54f..60adad0181c 100644 --- a/mysql-test/r/sp-security.result +++ b/mysql-test/r/sp-security.result @@ -97,11 +97,15 @@ s1 0 2 2 -drop procedure db1_secret.stamp; -drop function db1_secret.db; -drop procedure db2.p; -drop procedure db2.q; use test; +select type,db,name from mysql.proc; +type db name +FUNCTION db1_secret db +PROCEDURE db1_secret stamp +PROCEDURE db2 p +PROCEDURE db2 q drop database db1_secret; drop database db2; +select type,db,name from mysql.proc; +type db name delete from mysql.user where user='user1' or user='user2'; diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index f1fa1735ead..620f22aca68 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -786,6 +786,22 @@ select @c1, @c2| 12 3 delete from t1| drop procedure modes| +create database sp_db1| +drop database sp_db1| +create database sp_db2| +use sp_db2| +create table t3 ( s char(4), t int )| +insert into t3 values ("abcd", 42), ("dcba", 666)| +use test| +drop database sp_db2| +create database sp_db3| +use sp_db3| +create procedure dummy(out x int) +set x = 42| +use test| +drop database sp_db3| +select type,db,name from mysql.proc where db = 'sp_db3'| +type db name create procedure bug822(a_id char(16), a_data int) begin declare n int; diff --git a/mysql-test/t/sp-security.test b/mysql-test/t/sp-security.test index ae977684129..8fc51357bc4 100644 --- a/mysql-test/t/sp-security.test +++ b/mysql-test/t/sp-security.test @@ -163,11 +163,11 @@ select * from t2; # Clean up connection con1root; -drop procedure db1_secret.stamp; -drop function db1_secret.db; -drop procedure db2.p; -drop procedure db2.q; use test; +select type,db,name from mysql.proc; drop database db1_secret; drop database db2; +# Make sure the routines are gone +select type,db,name from mysql.proc; +# Get rid of the users delete from mysql.user where user='user1' or user='user2'; diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 5dae97b371e..943e150ce1f 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -905,6 +905,32 @@ delete from t1| drop procedure modes| +# Check that dropping a database without routines works. +# (Dropping with routines is tested in sp-security.test) +# First an empty db. +create database sp_db1| +drop database sp_db1| + +# Again, with a table. +create database sp_db2| +use sp_db2| +# Just put something in here... +create table t3 ( s char(4), t int )| +insert into t3 values ("abcd", 42), ("dcba", 666)| +use test| +drop database sp_db2| + +# And yet again, with just a procedure. +create database sp_db3| +use sp_db3| +create procedure dummy(out x int) + set x = 42| +use test| +drop database sp_db3| +# Check that it's gone +select type,db,name from mysql.proc where db = 'sp_db3'| + + # # Test cases for old bugs # diff --git a/sql/sp.cc b/sql/sp.cc index 389c627f5f3..ede6cfeb84b 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -589,6 +589,70 @@ done: } +/* Drop all routines in database 'db' */ +int +sp_drop_db_routines(THD *thd, char *db) +{ + TABLE *table; + byte key[64]; // db + uint keylen; + int ret; + DBUG_ENTER("sp_drop_db_routines"); + DBUG_PRINT("enter", ("db: %s", db)); + + // Put the key used to read the row together + keylen= strlen(db); + if (keylen > 64) + keylen= 64; + memcpy(key, db, keylen); + memset(key+keylen, (int)' ', 64-keylen); // Pad with space + keylen= sizeof(key); + + for (table= thd->open_tables ; table ; table= table->next) + if (strcmp(table->table_cache_key, "mysql") == 0 && + strcmp(table->real_name, "proc") == 0) + break; + if (! table) + { + TABLE_LIST tables; + + memset(&tables, 0, sizeof(tables)); + tables.db= (char*)"mysql"; + tables.real_name= tables.alias= (char*)"proc"; + if (! (table= open_ltable(thd, &tables, TL_WRITE))) + DBUG_RETURN(SP_OPEN_TABLE_FAILED); + } + + ret= SP_OK; + table->file->index_init(0); + if (! table->file->index_read(table->record[0], + key, keylen, HA_READ_KEY_EXACT)) + { + int nxtres; + bool deleted= FALSE; + + do { + if (! table->file->delete_row(table->record[0])) + deleted= TRUE; /* We deleted something */ + else + { + ret= SP_DELETE_ROW_FAILED; + break; + } + } while (! (nxtres= table->file->index_next_same(table->record[0], + key, keylen))); + if (nxtres != HA_ERR_END_OF_FILE) + ret= SP_KEY_NOT_FOUND; + if (deleted) + sp_cache_invalidate(); + } + + close_thread_tables(thd); + + DBUG_RETURN(ret); +} + + /***************************************************************************** PROCEDURE ******************************************************************************/ diff --git a/sql/sp.h b/sql/sp.h index ffe3f31c157..a4fec50aca2 100644 --- a/sql/sp.h +++ b/sql/sp.h @@ -28,6 +28,10 @@ #define SP_PARSE_ERROR -6 #define SP_INTERNAL_ERROR -7 +/* Drop all routines in database 'db' */ +int +sp_drop_db_routines(THD *thd, char *db); + sp_head * sp_find_procedure(THD *thd, sp_name *name); diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc index e13fb2695e7..056ac6d7e96 100644 --- a/sql/sp_cache.cc +++ b/sql/sp_cache.cc @@ -115,6 +115,13 @@ sp_cache_remove(sp_cache **cp, sp_name *name) return found; } +void +sp_cache_invalidate() +{ + pthread_mutex_lock(&Cversion_lock); // LOCK + Cversion++; + pthread_mutex_unlock(&Cversion_lock); // UNLOCK +} static byte * hash_get_key_for_sp_head(const byte *ptr, uint *plen, diff --git a/sql/sp_cache.h b/sql/sp_cache.h index 253e9b11588..754a987090e 100644 --- a/sql/sp_cache.h +++ b/sql/sp_cache.h @@ -40,6 +40,9 @@ sp_head *sp_cache_lookup(sp_cache **cp, sp_name *name); /* Remove an SP from cache. Returns true if something was removed */ bool sp_cache_remove(sp_cache **cp, sp_name *name); +/* Invalidate a cache */ +void sp_cache_invalidate(); + /* * diff --git a/sql/sql_db.cc b/sql/sql_db.cc index bc6b30040d6..54265b58bd4 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -19,6 +19,7 @@ #include "mysql_priv.h" #include "sql_acl.h" +#include "sp.h" #include #include #ifdef __WIN__ @@ -386,6 +387,7 @@ int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) } exit: + (void)sp_drop_db_routines(thd, db); /* QQ Ignore errors for now */ start_waiting_global_read_lock(thd); /* If this database was the client's selected database, we silently change the From 5954e94fa7b4c32011d209e03c87dbd8e5d22e99 Mon Sep 17 00:00:00 2001 From: "pem@mysql.comhem.se" <> Date: Tue, 23 Mar 2004 12:04:40 +0100 Subject: [PATCH 10/10] Fixed BUG#3259: Stored procedure names are case sensitive. Procedure names were unintentionally case-sensitive when read from the database (but case-insensitive when fetched from the cache). Note that the DB-part of qualified names is still case-sensitive (for consistency with other usage in mysql). --- Docs/sp-imp-spec.txt | 4 ++-- mysql-test/r/sp.result | 12 ++++++++++++ mysql-test/t/sp.test | 19 +++++++++++++++++++ scripts/mysql_create_system_tables.sh | 4 ++-- scripts/mysql_fix_privilege_tables.sql | 8 ++++++-- sql/sp.cc | 1 + 6 files changed, 42 insertions(+), 6 deletions(-) diff --git a/Docs/sp-imp-spec.txt b/Docs/sp-imp-spec.txt index 24a47aa2c38..ac17a375926 100644 --- a/Docs/sp-imp-spec.txt +++ b/Docs/sp-imp-spec.txt @@ -1057,9 +1057,9 @@ CREATE TABLE proc ( db char(64) binary DEFAULT '' NOT NULL, - name char(64) binary DEFAULT '' NOT NULL, + name char(64) DEFAULT '' NOT NULL, type enum('FUNCTION','PROCEDURE') NOT NULL, - specific_name char(64) binary DEFAULT '' NOT NULL, + specific_name char(64) DEFAULT '' NOT NULL, language enum('SQL') DEFAULT 'SQL' NOT NULL, sql_data_access enum('CONTAINS_SQL') DEFAULT 'CONTAINS_SQL' NOT NULL, is_deterministic enum('YES','NO') DEFAULT 'NO' NOT NULL, diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 620f22aca68..1f3064605bc 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -1000,6 +1000,18 @@ select bug2674()| bug2674() 262136 drop function bug2674| +create procedure bug3259_1 () begin end| +create procedure BUG3259_2 () begin end| +create procedure Bug3259_3 () begin end| +call BUG3259_1()| +call BUG3259_1()| +call bug3259_2()| +call Bug3259_2()| +call bug3259_3()| +call bUG3259_3()| +drop procedure bUg3259_1| +drop procedure BuG3259_2| +drop procedure BUG3259_3| 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 943e150ce1f..69433514728 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -1159,6 +1159,25 @@ create function bug2674 () returns int select bug2674()| drop function bug2674| +# +# BUG#3259 +# + +create procedure bug3259_1 () begin end| +create procedure BUG3259_2 () begin end| +create procedure Bug3259_3 () begin end| + +call BUG3259_1()| +call BUG3259_1()| +call bug3259_2()| +call Bug3259_2()| +call bug3259_3()| +call bUG3259_3()| + +drop procedure bUg3259_1| +drop procedure BuG3259_2| +drop procedure BUG3259_3| + # # Some "real" examples diff --git a/scripts/mysql_create_system_tables.sh b/scripts/mysql_create_system_tables.sh index d20d745d514..06999b81ca1 100644 --- a/scripts/mysql_create_system_tables.sh +++ b/scripts/mysql_create_system_tables.sh @@ -290,9 +290,9 @@ if test ! -f $mdata/proc.frm then c_p="$c_p CREATE TABLE proc (" c_p="$c_p db char(64) binary DEFAULT '' NOT NULL," - c_p="$c_p name char(64) binary DEFAULT '' NOT NULL," + c_p="$c_p name char(64) DEFAULT '' NOT NULL," c_p="$c_p type enum('FUNCTION','PROCEDURE') NOT NULL," - c_p="$c_p specific_name char(64) binary DEFAULT '' NOT NULL," + c_p="$c_p specific_name char(64) DEFAULT '' NOT NULL," c_p="$c_p language enum('SQL') DEFAULT 'SQL' NOT NULL," c_p="$c_p sql_data_access enum('CONTAINS_SQL') DEFAULT 'CONTAINS_SQL' NOT NULL," c_p="$c_p is_deterministic enum('YES','NO') DEFAULT 'NO' NOT NULL," diff --git a/scripts/mysql_fix_privilege_tables.sql b/scripts/mysql_fix_privilege_tables.sql index 115236948c9..e64bf59353f 100644 --- a/scripts/mysql_fix_privilege_tables.sql +++ b/scripts/mysql_fix_privilege_tables.sql @@ -141,9 +141,9 @@ unique index (name) CREATE TABLE IF NOT EXISTS proc ( db char(64) binary DEFAULT '' NOT NULL, - name char(64) binary DEFAULT '' NOT NULL, + name char(64) DEFAULT '' NOT NULL, type enum('FUNCTION','PROCEDURE') NOT NULL, - specific_name char(64) binary DEFAULT '' NOT NULL, + specific_name char(64) DEFAULT '' NOT NULL, language enum('SQL') DEFAULT 'SQL' NOT NULL, sql_data_access enum('CONTAINS_SQL') DEFAULT 'CONTAINS_SQL' NOT NULL, is_deterministic enum('YES','NO') DEFAULT 'NO' NOT NULL, @@ -179,3 +179,7 @@ CREATE TABLE IF NOT EXISTS proc ( comment char(64) binary DEFAULT '' NOT NULL, PRIMARY KEY (db,name,type) ) comment='Stored Procedures'; + +# Correct the name fields to not binary +ALTER TABLE proc MODIFY name char(64) DEFAULT '' NOT NULL, + MODIFY specific_name char(64) DEFAULT '' NOT NULL; diff --git a/sql/sp.cc b/sql/sp.cc index ede6cfeb84b..2be9ceeaad3 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -637,6 +637,7 @@ sp_drop_db_routines(THD *thd, char *db) else { ret= SP_DELETE_ROW_FAILED; + nxtres= 0; break; } } while (! (nxtres= table->file->index_next_same(table->record[0],