diff options
author | unknown <pem@mysql.comhem.se> | 2004-03-19 19:01:54 +0100 |
---|---|---|
committer | unknown <pem@mysql.comhem.se> | 2004-03-19 19:01:54 +0100 |
commit | d2ad3cff192de352961ec01f5370821690d7173f (patch) | |
tree | 8a8b60eab5553d3fb3d8304e77dadd313b4c77e6 | |
parent | edf2003009c7fd44c6a8b4a9306685357dbbb399 (diff) | |
download | mariadb-git-d2ad3cff192de352961ec01f5370821690d7173f.tar.gz |
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:
New testcases for functions with qualified names.
mysql-test/t/sp-security.test:
New testcases for functions with qualified names.
sql/item_func.cc:
Added error handling for stored function, if it doesn't exist.
sql/item_func.h:
Set null_value if execution of a stored function fails.
sql/mysql_priv.h:
Reverted previous change: No optional args for mysql_change_db().
(SPs use a specially tailored function instead.)
sql/sp.cc:
Copied mysql_change_db() from sql_db.cc and modified specially for SPs.
sql/sp_head.cc:
Fixed error handling for errors in functions during query/statement execution.
sql/sql_db.cc:
Reverted previous change: No optional args for mysql_change_db().
(SPs use a specially tailored function instead.)
sql/sql_yacc.yy:
Reworked the stored function/UDF invokation parsing and added qualified names
for stored functions. UDFs now have precedence over stored functions (whith
unqualified name). When using an unqualified name, only IDENT_sys is allowed
(i.e. no unreserved keywords), since we get unresolvable reduce/reduce conflicts
otherwise.
-rw-r--r-- | mysql-test/r/sp-security.result | 25 | ||||
-rw-r--r-- | mysql-test/t/sp-security.test | 20 | ||||
-rw-r--r-- | sql/item_func.cc | 15 | ||||
-rw-r--r-- | sql/item_func.h | 6 | ||||
-rw-r--r-- | sql/mysql_priv.h | 3 | ||||
-rw-r--r-- | sql/sp.cc | 106 | ||||
-rw-r--r-- | sql/sp_head.cc | 12 | ||||
-rw-r--r-- | sql/sql_db.cc | 95 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 147 |
9 files changed, 286 insertions, 143 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<Item> *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<Item> 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); - - if (sp_function_exists(YYTHD, name)) - { - LEX *lex= Lex; + LEX *lex= Lex; + sp_name *name= new sp_name($1, $3); - 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 - { + $$= new Item_func_sp(name); + } + | IDENT_sys '(' udf_expr_list ')' + { #ifdef HAVE_DLOPEN - udf_func *udf; + 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; - } - } + 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); |