Merge adventure.(none):/home/thek/Development/cpp/bug16470/my51-bug16470

into  adventure.(none):/home/thek/Development/cpp/mysql-5.1-runtime


mysql-test/r/grant.result:
  Auto merged
mysql-test/t/grant.test:
  Auto merged
sql/sql_acl.cc:
  Auto merged
sql/sql_parse.cc:
  manual merge
This commit is contained in:
unknown 2007-11-26 19:31:41 +01:00
commit f1ad502ae1
4 changed files with 260 additions and 132 deletions

View File

@ -1226,3 +1226,22 @@ drop user юзер_юзер@localhost;
grant select on test.* to очень_длинный_юзер@localhost; grant select on test.* to очень_длинный_юзер@localhost;
ERROR HY000: String 'очень_длинный_юзер' is too long for user name (should be no longer than 16) ERROR HY000: String 'очень_длинный_юзер' is too long for user name (should be no longer than 16)
set names default; set names default;
FLUSH PRIVILEGES without procs_priv table.
RENAME TABLE mysql.procs_priv TO mysql.procs_gone;
FLUSH PRIVILEGES;
Warnings:
Error 1146 Table 'mysql.procs_priv' doesn't exist
Error 1547 Cannot load from mysql.mysql.procs_priv. The table is probably corrupted
Assigning privileges without procs_priv table.
CREATE DATABASE mysqltest1;
CREATE PROCEDURE mysqltest1.test() SQL SECURITY DEFINER
SELECT 1;
GRANT EXECUTE ON FUNCTION mysqltest1.test TO mysqltest_1@localhost;
ERROR 42S02: Table 'mysql.procs_priv' doesn't exist
GRANT ALL PRIVILEGES ON test.* TO mysqltest_1@localhost;
CALL mysqltest1.test();
1
1
DROP DATABASE mysqltest1;
RENAME TABLE mysql.procs_gone TO mysql.procs_priv;
FLUSH PRIVILEGES;

View File

@ -1277,3 +1277,23 @@ drop user юзер_юзер@localhost;
--error ER_WRONG_STRING_LENGTH --error ER_WRONG_STRING_LENGTH
grant select on test.* to очень_длинный_юзер@localhost; grant select on test.* to очень_длинный_юзер@localhost;
set names default; set names default;
#
# Bug #16470 crash on grant if old grant tables
#
--echo FLUSH PRIVILEGES without procs_priv table.
RENAME TABLE mysql.procs_priv TO mysql.procs_gone;
FLUSH PRIVILEGES;
--echo Assigning privileges without procs_priv table.
CREATE DATABASE mysqltest1;
CREATE PROCEDURE mysqltest1.test() SQL SECURITY DEFINER
SELECT 1;
--error ER_NO_SUCH_TABLE
GRANT EXECUTE ON FUNCTION mysqltest1.test TO mysqltest_1@localhost;
GRANT ALL PRIVILEGES ON test.* TO mysqltest_1@localhost;
CALL mysqltest1.test();
DROP DATABASE mysqltest1;
RENAME TABLE mysql.procs_gone TO mysql.procs_priv;
FLUSH PRIVILEGES;

View File

@ -3472,16 +3472,13 @@ void grant_free(void)
} }
/* /**
Initialize structures responsible for table/column-level privilege checking @brief Initialize structures responsible for table/column-level privilege
and load information for them from tables in the 'mysql' database. checking and load information for them from tables in the 'mysql' database.
SYNOPSIS @return Error status
grant_init() @retval 0 OK
@retval 1 Could not initialize grant subsystem.
RETURN VALUES
0 ok
1 Could not initialize grant's
*/ */
my_bool grant_init() my_bool grant_init()
@ -3503,87 +3500,38 @@ my_bool grant_init()
} }
/* /**
Initialize structures responsible for table/column-level privilege @brief Helper function to grant_reload_procs_priv
checking and load information about grants from open privilege tables.
SYNOPSIS Reads the procs_priv table into memory hash.
grant_load()
thd Current thread
tables List containing open "mysql.tables_priv" and
"mysql.columns_priv" tables.
RETURN VALUES @param table A pointer to the procs_priv table structure.
FALSE - success
TRUE - error @see grant_reload
@see grant_reload_procs_priv
@return Error state
@retval TRUE An error occurred
@retval FALSE Success
*/ */
static my_bool grant_load(TABLE_LIST *tables) static my_bool grant_load_procs_priv(TABLE *p_table)
{ {
MEM_ROOT *memex_ptr; MEM_ROOT *memex_ptr;
my_bool return_val= 1; my_bool return_val= 1;
TABLE *t_table, *c_table, *p_table;
bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE; bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**, MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**,
THR_MALLOC); THR_MALLOC);
DBUG_ENTER("grant_load"); DBUG_ENTER("grant_load");
(void) hash_init(&column_priv_hash,system_charset_info,
0,0,0, (hash_get_key) get_grant_table,
(hash_free_key) free_grant_table,0);
(void) hash_init(&proc_priv_hash,system_charset_info, (void) hash_init(&proc_priv_hash,system_charset_info,
0,0,0, (hash_get_key) get_grant_table, 0,0,0, (hash_get_key) get_grant_table,
0,0); 0,0);
(void) hash_init(&func_priv_hash,system_charset_info, (void) hash_init(&func_priv_hash,system_charset_info,
0,0,0, (hash_get_key) get_grant_table, 0,0,0, (hash_get_key) get_grant_table,
0,0); 0,0);
init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0);
t_table = tables[0].table;
c_table = tables[1].table;
p_table= tables[2].table;
t_table->file->ha_index_init(0, 1);
p_table->file->ha_index_init(0, 1); p_table->file->ha_index_init(0, 1);
t_table->use_all_columns();
c_table->use_all_columns();
p_table->use_all_columns(); p_table->use_all_columns();
if (!t_table->file->index_first(t_table->record[0]))
{
memex_ptr= &memex;
my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
do
{
GRANT_TABLE *mem_check;
if (!(mem_check=new (memex_ptr) GRANT_TABLE(t_table,c_table)))
{
/* This could only happen if we are out memory */
goto end_unlock;
}
if (check_no_resolve)
{
if (hostname_requires_resolving(mem_check->host.hostname))
{
sql_print_warning("'tables_priv' entry '%s %s@%s' "
"ignored in --skip-name-resolve mode.",
mem_check->tname,
mem_check->user ? mem_check->user : "",
mem_check->host.hostname ?
mem_check->host.hostname : "");
continue;
}
}
if (! mem_check->ok())
delete mem_check;
else if (my_hash_insert(&column_priv_hash,(uchar*) mem_check))
{
delete mem_check;
goto end_unlock;
}
}
while (!t_table->file->index_next(t_table->record[0]));
}
if (!p_table->file->index_first(p_table->record[0])) if (!p_table->file->index_first(p_table->record[0]))
{ {
memex_ptr= &memex; memex_ptr= &memex;
@ -3592,7 +3540,7 @@ static my_bool grant_load(TABLE_LIST *tables)
{ {
GRANT_NAME *mem_check; GRANT_NAME *mem_check;
HASH *hash; HASH *hash;
if (!(mem_check=new (&memex) GRANT_NAME(p_table))) if (!(mem_check=new (memex_ptr) GRANT_NAME(p_table)))
{ {
/* This could only happen if we are out memory */ /* This could only happen if we are out memory */
goto end_unlock; goto end_unlock;
@ -3638,38 +3586,172 @@ static my_bool grant_load(TABLE_LIST *tables)
} }
while (!p_table->file->index_next(p_table->record[0])); while (!p_table->file->index_next(p_table->record[0]));
} }
return_val=0; // Return ok /* Return ok */
return_val= 0;
end_unlock: end_unlock:
t_table->file->ha_index_end();
p_table->file->ha_index_end(); p_table->file->ha_index_end();
my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr); my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr);
DBUG_RETURN(return_val); DBUG_RETURN(return_val);
} }
/* /**
Reload information about table and column level privileges if possible. @brief Initialize structures responsible for table/column-level privilege
checking and load information about grants from open privilege tables.
SYNOPSIS @param thd Current thread
grant_reload() @param tables List containing open "mysql.tables_priv" and
thd Current thread "mysql.columns_priv" tables.
@see grant_reload
@return Error state
@retval FALSE Success
@retval TRUE Error
*/
static my_bool grant_load(TABLE_LIST *tables)
{
MEM_ROOT *memex_ptr;
my_bool return_val= 1;
TABLE *t_table= 0, *c_table= 0;
bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**,
THR_MALLOC);
DBUG_ENTER("grant_load");
(void) hash_init(&column_priv_hash,system_charset_info,
0,0,0, (hash_get_key) get_grant_table,
(hash_free_key) free_grant_table,0);
t_table = tables[0].table;
c_table = tables[1].table;
t_table->file->ha_index_init(0, 1);
t_table->use_all_columns();
c_table->use_all_columns();
if (!t_table->file->index_first(t_table->record[0]))
{
memex_ptr= &memex;
my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
do
{
GRANT_TABLE *mem_check;
if (!(mem_check=new (memex_ptr) GRANT_TABLE(t_table,c_table)))
{
/* This could only happen if we are out memory */
goto end_unlock;
}
if (check_no_resolve)
{
if (hostname_requires_resolving(mem_check->host.hostname))
{
sql_print_warning("'tables_priv' entry '%s %s@%s' "
"ignored in --skip-name-resolve mode.",
mem_check->tname,
mem_check->user ? mem_check->user : "",
mem_check->host.hostname ?
mem_check->host.hostname : "");
continue;
}
}
if (! mem_check->ok())
delete mem_check;
else if (my_hash_insert(&column_priv_hash,(uchar*) mem_check))
{
delete mem_check;
goto end_unlock;
}
}
while (!t_table->file->index_next(t_table->record[0]));
}
return_val=0; // Return ok
end_unlock:
t_table->file->ha_index_end();
my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr);
DBUG_RETURN(return_val);
}
/**
@brief Helper function to grant_reload. Reloads procs_priv table is it
exists.
@param thd A pointer to the thread handler object.
@see grant_reload
@return Error state
@retval FALSE Success
@retval TRUE An error has occurred.
*/
static my_bool grant_reload_procs_priv(THD *thd)
{
HASH old_proc_priv_hash, old_func_priv_hash;
TABLE_LIST table;
my_bool return_val= FALSE;
DBUG_ENTER("grant_reload_procs_priv");
bzero((char*) &table, sizeof(table));
table.alias= table.table_name= (char*) "procs_priv";
table.db= (char *) "mysql";
table.lock_type= TL_READ;
if (simple_open_n_lock_tables(thd, &table))
{
close_thread_tables(thd);
DBUG_RETURN(TRUE);
}
/* Save a copy of the current hash if we need to undo the grant load */
old_proc_priv_hash= proc_priv_hash;
old_func_priv_hash= func_priv_hash;
rw_wrlock(&LOCK_grant);
if ((return_val= grant_load_procs_priv(table.table)))
{
/* Error; Reverting to old hash */
DBUG_PRINT("error",("Reverting to old privileges"));
grant_free();
proc_priv_hash= old_proc_priv_hash;
func_priv_hash= old_func_priv_hash;
}
else
{
hash_free(&old_proc_priv_hash);
hash_free(&old_func_priv_hash);
}
rw_unlock(&LOCK_grant);
close_thread_tables(thd);
DBUG_RETURN(return_val);
}
/**
@brief Reload information about table and column level privileges if possible
@param thd Current thread
NOTES
Locked tables are checked by acl_reload() and doesn't have to be checked Locked tables are checked by acl_reload() and doesn't have to be checked
in this call. in this call.
This function is also used for initialization of structures responsible This function is also used for initialization of structures responsible
for table/column-level privilege checking. for table/column-level privilege checking.
RETURN VALUE @return Error state
FALSE Success @retval FALSE Success
TRUE Error @retval TRUE Error
*/ */
my_bool grant_reload(THD *thd) my_bool grant_reload(THD *thd)
{ {
TABLE_LIST tables[3]; TABLE_LIST tables[2];
HASH old_column_priv_hash, old_proc_priv_hash, old_func_priv_hash; HASH old_column_priv_hash;
MEM_ROOT old_mem; MEM_ROOT old_mem;
my_bool return_val= 1; my_bool return_val= 1;
DBUG_ENTER("grant_reload"); DBUG_ENTER("grant_reload");
@ -3681,11 +3763,9 @@ my_bool grant_reload(THD *thd)
bzero((char*) tables, sizeof(tables)); bzero((char*) tables, sizeof(tables));
tables[0].alias= tables[0].table_name= (char*) "tables_priv"; tables[0].alias= tables[0].table_name= (char*) "tables_priv";
tables[1].alias= tables[1].table_name= (char*) "columns_priv"; tables[1].alias= tables[1].table_name= (char*) "columns_priv";
tables[2].alias= tables[2].table_name= (char*) "procs_priv"; tables[0].db= tables[1].db= (char *) "mysql";
tables[0].db= tables[1].db= tables[2].db= (char *) "mysql";
tables[0].next_local= tables[0].next_global= tables+1; tables[0].next_local= tables[0].next_global= tables+1;
tables[1].next_local= tables[1].next_global= tables+2; tables[0].lock_type= tables[1].lock_type= TL_READ;
tables[0].lock_type= tables[1].lock_type= tables[2].lock_type= TL_READ;
/* /*
To avoid deadlocks we should obtain table locks before To avoid deadlocks we should obtain table locks before
@ -3695,35 +3775,45 @@ my_bool grant_reload(THD *thd)
goto end; goto end;
rw_wrlock(&LOCK_grant); rw_wrlock(&LOCK_grant);
grant_version++;
old_column_priv_hash= column_priv_hash; old_column_priv_hash= column_priv_hash;
old_proc_priv_hash= proc_priv_hash;
old_func_priv_hash= func_priv_hash; /*
Create a new memory pool but save the current memory pool to make an undo
opertion possible in case of failure.
*/
old_mem= memex; old_mem= memex;
init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0);
if ((return_val= grant_load(tables))) if ((return_val= grant_load(tables)))
{ // Error. Revert to old hash { // Error. Revert to old hash
DBUG_PRINT("error",("Reverting to old privileges")); DBUG_PRINT("error",("Reverting to old privileges"));
grant_free(); /* purecov: deadcode */ grant_free(); /* purecov: deadcode */
column_priv_hash= old_column_priv_hash; /* purecov: deadcode */ column_priv_hash= old_column_priv_hash; /* purecov: deadcode */
proc_priv_hash= old_proc_priv_hash;
func_priv_hash= old_func_priv_hash;
memex= old_mem; /* purecov: deadcode */ memex= old_mem; /* purecov: deadcode */
} }
else else
{ {
hash_free(&old_column_priv_hash); hash_free(&old_column_priv_hash);
hash_free(&old_proc_priv_hash);
hash_free(&old_func_priv_hash);
free_root(&old_mem,MYF(0)); free_root(&old_mem,MYF(0));
} }
rw_unlock(&LOCK_grant); rw_unlock(&LOCK_grant);
end:
close_thread_tables(thd); close_thread_tables(thd);
/*
It is ok failing to load procs_priv table because we may be
working with 4.1 privilege tables.
*/
if (grant_reload_procs_priv(thd))
my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0), "mysql.procs_priv");
rw_wrlock(&LOCK_grant);
grant_version++;
rw_unlock(&LOCK_grant);
end:
DBUG_RETURN(return_val); DBUG_RETURN(return_val);
} }
/**************************************************************************** /****************************************************************************
Check table level grants Check table level grants

View File

@ -6291,24 +6291,23 @@ void add_join_natural(TABLE_LIST *a, TABLE_LIST *b, List<String> *using_fields,
} }
/* /**
Reload/resets privileges and the different caches. @brief Reload/resets privileges and the different caches.
SYNOPSIS @param thd Thread handler (can be NULL!)
reload_acl_and_cache() @param options What should be reset/reloaded (tables, privileges, slave...)
thd Thread handler (can be NULL!) @param tables Tables to flush (if any)
options What should be reset/reloaded (tables, privileges, @param write_to_binlog True if we can write to the binlog.
slave...)
tables Tables to flush (if any) @note Depending on 'options', it may be very bad to write the
write_to_binlog Depending on 'options', it may be very bad to write the
query to the binlog (e.g. FLUSH SLAVE); this is a query to the binlog (e.g. FLUSH SLAVE); this is a
pointer where reload_acl_and_cache() will put 0 if pointer where reload_acl_and_cache() will put 0 if
it thinks we really should not write to the binlog. it thinks we really should not write to the binlog.
Otherwise it will put 1. Otherwise it will put 1.
RETURN @return Error status code
0 ok @retval 0 Ok
!=0 error. thd->killed or thd->is_error() is set @retval !=0 Error; thd->killed is set or thd->is_error() is true
*/ */
bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,